Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Copy-paste patch objects using system clipboard text buffer #2086

Draft
wants to merge 12 commits into
base: master
Choose a base branch
from
Draft
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);
}
umlaeute marked this conversation as resolved.
Show resolved Hide resolved

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
9 changes: 9 additions & 0 deletions tcl/pd_bindings.tcl
Expand Up @@ -120,6 +120,12 @@ proc ::pd_bindings::global_bindings {} {
bind_capslock all $::modifier-Key m {::pd_menucommands::scheduleAction menu_minimize %W}
bind all <$::modifier-quoteleft> {::pd_menucommands::scheduleAction menu_raisenextwindow}
}

# On mac, the copy/paste menu command bindings also execute the commands, results in paste executed twice
# however, if we don't set this binding, the regular copy and paste gets called even with alt pressed
bind_capslock all $::modifier-$::alt-Key c {}
bind_capslock all $::modifier-$::alt-Key v {}

# BackSpace/Delete report the wrong isos (unicode representations) on OSX,
# so we set them to the empty string and let ::pd_bindings::sendkey guess the correct values
bind all <KeyPress-BackSpace> {::pd_bindings::sendkey %W 1 %K "" 1 %k}
Expand All @@ -134,6 +140,9 @@ proc ::pd_bindings::global_bindings {} {
bind_capslock all $::modifier-Key q {::pd_connect::menu_quit}
bind_capslock all $::modifier-Key m {menu_minimize %W}

bind_capslock all $::modifier-$::alt-Key c {menu_send %W copy-to-clipboard-as-text}
bind_capslock all $::modifier-$::alt-Key v {menu_send %W paste-from-clipboard-text}

bind all <$::modifier-Next> {menu_raisenextwindow} ;# PgUp
bind all <$::modifier-Prior> {menu_raisepreviouswindow};# PageDown
# these can conflict with CMD+comma & CMD+period bindings in Tk Cococa
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]
eeropic marked this conversation as resolved.
Show resolved Hide resolved
# 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