Skip to content

Commit

Permalink
Add copy & paste patch clipboard text functions
Browse files Browse the repository at this point in the history
canvas_copy_to_clipboard_as_text() performs canvas_docopy and gets the binary object buffer as text, and passes it to TCL to copy to system clipboard

canvas_paste_from_clipboard_text() calls TCL to send system clipboard text, tcl then calls canvas_got_clipboard_content()
which transforms the text back to binary buffer and performs canvas_dopaste

TODO: split clipboard append to chunks
  • Loading branch information
eeropic committed Sep 6, 2023
1 parent c0a7c16 commit de783d1
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 0 deletions.
51 changes: 51 additions & 0 deletions src/g_editor.c
Expand Up @@ -3,6 +3,7 @@
* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */

#include <stdio.h>
#include <stdlib.h>
#include "m_pd.h"
#include "m_imp.h"
#include "s_stuff.h"
Expand Down Expand Up @@ -3718,6 +3719,17 @@ static void canvas_copy(t_canvas *x)
}
}

static void canvas_copy_to_clipboard_as_text(t_canvas *x)
{
t_binbuf *bb = canvas_docopy(x);
if (!bb)
return;
t_atom *atoms = binbuf_getvec(bb);
int num_atoms = binbuf_getnatom(bb);
pdgui_vmess("pdtk_copy_to_clipboard_as_text", "ca", x, num_atoms, atoms);
binbuf_free(bb);
}

static void canvas_clearline(t_canvas *x)
{
if (x->gl_editor->e_selectedline)
Expand Down Expand Up @@ -4123,6 +4135,39 @@ static void canvas_paste(t_canvas *x)
}
}

static void canvas_paste_from_clipboard_text(t_canvas *x)
{
pdgui_vmess("pdtk_get_clipboard_text", "^", x);
}

void unescape_clipboard_patch(char *str) {
char *temp = malloc(strlen(str) + 1);
if (!temp) return;
int i = 0, j = 0;
while (str[i]) {
if (str[i] == ';' && str[i+1] == 'n') {
temp[j++] = ';';
temp[j++] = '\n';
i += 2;
} else {
temp[j++] = str[i++];
}
}
temp[j] = '\0';
strcpy(str, temp);
free(temp);
}

void canvas_got_clipboard_contents(t_canvas *x, t_symbol *s) {
char* received_data = strdup(s->s_name);
unescape_clipboard_patch(received_data);
t_binbuf *bb = binbuf_new();
binbuf_text(bb, received_data, strlen(received_data));
canvas_dopaste(x, bb);
binbuf_free(bb);
free(received_data);
}

static void canvas_duplicate(t_canvas *x)
{
if (!x->gl_editor)
Expand Down Expand Up @@ -4916,6 +4961,8 @@ void g_editor_setup(void)
A_GIMME, A_NULL);
class_addmethod(canvas_class, (t_method)canvas_motion, gensym("motion"),
A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);
class_addmethod(canvas_class, (t_method)canvas_got_clipboard_contents,
gensym("got-clipboard-contents"), A_SYMBOL, A_NULL);

/* ------------------------ menu actions ---------------------------- */
class_addmethod(canvas_class, (t_method)canvas_menuclose,
Expand All @@ -4924,8 +4971,12 @@ void g_editor_setup(void)
gensym("cut"), A_NULL);
class_addmethod(canvas_class, (t_method)canvas_copy,
gensym("copy"), A_NULL);
class_addmethod(canvas_class, (t_method)canvas_copy_to_clipboard_as_text,
gensym("copy-to-clipboard-as-text"), A_NULL);
class_addmethod(canvas_class, (t_method)canvas_paste,
gensym("paste"), A_NULL);
class_addmethod(canvas_class, (t_method)canvas_paste_from_clipboard_text,
gensym("paste-from-clipboard-text"), A_NULL);
class_addmethod(canvas_class, (t_method)canvas_paste_replace,
gensym("paste-replace"), A_NULL);
class_addmethod(canvas_class, (t_method)canvas_duplicate,
Expand Down
5 changes: 5 additions & 0 deletions tcl/pd-gui.tcl
Expand Up @@ -200,6 +200,8 @@ set recentfiles_list {}
set total_recentfiles 5
# modifier for key commands (Ctrl/Control on most platforms, Cmd/Mod1 on MacOSX)
set modifier ""
# on Mac OS X/Aqua, the Alt/Option key is called Option in Tcl
set alt ""
# current state of the Edit Mode menu item
set editmode_button 0

Expand Down Expand Up @@ -294,6 +296,7 @@ proc init_for_platform {} {
switch -- $::windowingsystem {
"x11" {
set ::modifier "Control"
set ::alt "Alt"
option add *PatchWindow*Canvas.background "white" startupFile
# add control to show/hide hidden files in the open panel (load
# the tk_getOpenFile dialog once, otherwise it will not work)
Expand Down Expand Up @@ -334,6 +337,7 @@ proc init_for_platform {} {
# from the commandline incorporates the special mac event handling
package require apple_events
set ::modifier "Mod1"
set ::alt "Option"
if {$::tcl_version < 8.5} {
# old default font for Tk 8.4 on macOS
# since font detection requires 8.5+
Expand Down Expand Up @@ -371,6 +375,7 @@ proc init_for_platform {} {
}
"win32" {
set ::modifier "Control"
set ::alt "Alt"
option add *PatchWindow*Canvas.background "white" startupFile
# fix menu font size on Windows with tk scaling = 1
font create menufont -family Tahoma -size -11
Expand Down
2 changes: 2 additions & 0 deletions tcl/pd_bindings.tcl
Expand Up @@ -57,6 +57,7 @@ proc ::pd_bindings::global_bindings {} {
bind_capslock all $::modifier-Key a {menu_send %W selectall}
bind_capslock all $::modifier-Key b {menu_helpbrowser}
bind_capslock all $::modifier-Key c {menu_send %W copy}
bind_capslock all $::modifier-$::alt-Key c {menu_send %W copy-to-clipboard-as-text}
bind_capslock all $::modifier-Key d {menu_send %W duplicate}
bind_capslock all $::modifier-Key e {menu_toggle_editmode}
bind_capslock all $::modifier-Key f {menu_find_dialog}
Expand All @@ -69,6 +70,7 @@ proc ::pd_bindings::global_bindings {} {
bind_capslock all $::modifier-Key s {menu_send %W menusave}
bind_capslock all $::modifier-Key t {menu_send %W triggerize}
bind_capslock all $::modifier-Key v {menu_send %W paste}
bind_capslock all $::modifier-$::alt-Key v {menu_send %W paste-from-clipboard-text}
bind_capslock all $::modifier-Key w {::pd_bindings::window_close %W}
bind_capslock all $::modifier-Key x {menu_send %W cut}
bind_capslock all $::modifier-Key z {menu_undo}
Expand Down
10 changes: 10 additions & 0 deletions tcl/pd_menus.tcl
Expand Up @@ -64,6 +64,8 @@ proc ::pd_menus::configure_for_pdwindow {} {
$menubar.file entryconfigure [_ "Print..."] -state disabled

# Edit menu
$menubar.edit entryconfigure [_ "Copy to clipboard (text)"] -state disabled
$menubar.edit entryconfigure [_ "Paste from clipboard (text)"] -state disabled
$menubar.edit entryconfigure [_ "Paste Replace"] -state disabled
$menubar.edit entryconfigure [_ "Duplicate"] -state disabled
$menubar.edit entryconfigure [_ "Font"] -state normal
Expand Down Expand Up @@ -96,6 +98,8 @@ proc ::pd_menus::configure_for_canvas {mytoplevel} {
$menubar.file entryconfigure [_ "Save As..."] -state normal
$menubar.file entryconfigure [_ "Print..."] -state normal
# Edit menu
$menubar.edit entryconfigure [_ "Copy to clipboard (text)"] -state normal
$menubar.edit entryconfigure [_ "Paste from clipboard (text)"] -state normal
$menubar.edit entryconfigure [_ "Paste Replace"] -state normal
$menubar.edit entryconfigure [_ "Duplicate"] -state normal
$menubar.edit entryconfigure [_ "Font"] -state normal
Expand Down Expand Up @@ -142,6 +146,8 @@ proc ::pd_menus::configure_for_dialog {mytoplevel} {

# Edit menu
$menubar.edit entryconfigure [_ "Font"] -state disabled
$menubar.edit entryconfigure [_ "Copy to clipboard (text)"] -state disabled
$menubar.edit entryconfigure [_ "Paste from clipboard (text)"] -state disabled
$menubar.edit entryconfigure [_ "Paste Replace"] -state disabled
$menubar.edit entryconfigure [_ "Duplicate"] -state disabled
$menubar.edit entryconfigure [_ "Zoom In"] -state disabled
Expand Down Expand Up @@ -196,8 +202,12 @@ proc ::pd_menus::build_edit_menu {mymenu} {
-command {::pd_menucommands::scheduleAction menu_send $::focused_window cut}
$mymenu add command -label [_ "Copy"] -accelerator "$accelerator+C" \
-command {::pd_menucommands::scheduleAction menu_send $::focused_window copy}
$mymenu add command -label [_ "Copy to clipboard (text)"] -accelerator "$accelerator+$::alt+C" \
-command {::pd_menucommands::scheduleAction menu_send $::focused_window copy-to-clipboard-as-text}
$mymenu add command -label [_ "Paste"] -accelerator "$accelerator+V" \
-command {::pd_menucommands::scheduleAction menu_send $::focused_window paste}
$mymenu add command -label [_ "Paste from clipboard (text)"] -accelerator "$accelerator+$::alt+V" \
-command {::pd_menucommands::scheduleAction menu_send $::focused_window paste-from-clipboard-text}
$mymenu add command -label [_ "Duplicate"] -accelerator "$accelerator+D" \
-command {::pd_menucommands::scheduleAction menu_send $::focused_window duplicate}
$mymenu add command -label [_ "Paste Replace" ] \
Expand Down
26 changes: 26 additions & 0 deletions tcl/pdtk_canvas.tcl
Expand Up @@ -250,6 +250,32 @@ proc pdtk_canvas_clickpaste {tkcanvas x y b} {
}
}

proc pdtk_get_clipboard_text {tkcanvas} {
set clipboard_data [clipboard get]
# TODO: better validation of PD patch clipboard data
if {[string index $clipboard_data 0] != "#"} {
::pdwindow::post "Warning: Clipboard content does not seem to be valid PD patch: \n"
::pdwindow::post $clipboard_data
return
}
set escaped_data [string map {" " "\\ " ";" "\\;" "\n" "\\n"} $clipboard_data]
pdsend "[winfo toplevel $tkcanvas] got-clipboard-contents $escaped_data"
}

proc pdtk_copy_to_clipboard_as_text {tkcanvas args} {
clipboard clear
set atom_line ""
foreach atom $args {
if {$atom == ";"} {
set trimmed_line [string trim $atom_line]
clipboard append "${trimmed_line};\n"
set atom_line ""
} else {
append atom_line $atom " "
}
}
}

#------------------------------------------------------------------------------#
# canvas popup menu

Expand Down

0 comments on commit de783d1

Please sign in to comment.