aboutsummaryrefslogtreecommitdiff
path: root/pd/tcl/pd_bindings.tcl
blob: 0cef04742de3f51049c12e0677b77fb4b2c2ba1b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
package provide pd_bindings 0.1

package require pd_menucommands
package require dialog_find

namespace eval ::pd_bindings:: {
    variable modifier

    namespace export window_bindings
    namespace export dialog_bindings
    namespace export canvas_bindings
}

# the commands are bound using "" quotations so that the $mytoplevel is
# interpreted immediately.  Since the command is being bound to $mytoplevel,
# it makes sense to have value of $mytoplevel already in the command.  This is
# the opposite of the menu commands in pd_menus.tcl

# binding by class is not recursive, so its useful for certain things
proc ::pd_bindings::class_bindings {} {
    # and the Pd window is in a class to itself
    bind PdWindow <Configure>              "::pd_bindings::window_configure %W"
    bind PdWindow <FocusIn>                "::pd_bindings::window_focusin %W"
    # bind to all the canvas windows
    bind CanvasWindow <Map>                    "::pd_bindings::map %W"
    bind CanvasWindow <Unmap>                  "::pd_bindings::unmap %W"
    bind CanvasWindow <Configure>              "::pd_bindings::window_configure %W"
    bind CanvasWindow <FocusIn>                "::pd_bindings::window_focusin %W"
    # bindings for dialog windows, which behave differently than canvas windows
    bind DialogWindow <Configure>              "::pd_bindings::dialog_configure %W"
    bind DialogWindow <FocusIn>                "::pd_bindings::dialog_focusin %W"
}

proc ::pd_bindings::window_bindings {mytoplevel} {
    variable modifier

    # for key bindings
    if {$::windowingsystem eq "aqua"} {
        set modifier "Mod1"
    } else {
        set modifier "Control"
    }

    # File menu
    bind $mytoplevel <$modifier-Key-b>        "menu_helpbrowser"
    bind $mytoplevel <$modifier-Key-f>        "::dialog_find::menu_find_dialog $mytoplevel"
    bind $mytoplevel <$modifier-Key-n>        "menu_new"
    bind $mytoplevel <$modifier-Key-o>        "menu_open"
    bind $mytoplevel <$modifier-Key-p>        "menu_print $mytoplevel"
    bind $mytoplevel <$modifier-Key-q>        "pdsend \"pd verifyquit\""
    bind $mytoplevel <$modifier-Key-r>        "menu_raise_pdwindow"
    bind $mytoplevel <$modifier-Shift-Key-L>  "menu_clear_console"
    bind $mytoplevel <$modifier-Shift-Key-Q>  "pdsend \"pd quit\""
    bind $mytoplevel <$modifier-Shift-Key-R>  "menu_toggle_console"

    # DSP control
    bind $mytoplevel <$modifier-Key-slash>    "pdsend \"pd dsp 1\""
    bind $mytoplevel <$modifier-Key-period>   "pdsend \"pd dsp 0\""
}

proc ::pd_bindings::pdwindow_bindings {mytoplevel} {
    variable modifier

    window_bindings $mytoplevel

    # TODO update this to work with the console, if it is used
    bind $mytoplevel <$modifier-Key-a> ".pdwindow.text tag add sel 1.0 end"
    bind $mytoplevel <$modifier-Key-x> "tk_textCut .pdwindow.text"
    bind $mytoplevel <$modifier-Key-c> "tk_textCopy .pdwindow.text"
    bind $mytoplevel <$modifier-Key-v> "tk_textPaste .pdwindow.text"
    bind $mytoplevel <$modifier-Key-w> "wm iconify $mytoplevel"

    if {$::windowingsystem eq "aqua"} {
        bind $mytoplevel <$modifier-Key-m>   "menu_minimize $mytoplevel"
        bind $mytoplevel <$modifier-Key-t>   "menu_font_dialog $mytoplevel"
        bind $mytoplevel <$modifier-quoteleft> "menu_raisenextwindow"
    } else {
        bind $mytoplevel <$modifier-Key-m>    "menu_message_dialog"
        bind $mytoplevel <$modifier-Key-t>    "menu_texteditor"
    }

    # Tcl event bindings
    wm protocol $mytoplevel WM_DELETE_WINDOW "pdsend \"pd verifyquit\""
}

# this is for the dialogs: find, font, sendmessage, gatom properties, array
# properties, iemgui properties, canvas properties, data structures
# properties, Audio setup, and MIDI setup
proc ::pd_bindings::dialog_bindings {mytoplevel dialogname} {
    variable modifier

    window_bindings $mytoplevel

    bind $mytoplevel <KeyPress-Escape> "dialog_${dialogname}::cancel $mytoplevel"
    bind $mytoplevel <KeyPress-Return> "dialog_${dialogname}::ok $mytoplevel"
    bind $mytoplevel <$modifier-Key-w> "dialog_${dialogname}::cancel $mytoplevel"

    $mytoplevel configure -padx 10 -pady 5
    wm group $mytoplevel .
    wm resizable $mytoplevel 0 0
    wm protocol $mytoplevel WM_DELETE_WINDOW "dialog_${dialogname}::cancel $mytoplevel"
    catch { # not all platforms/Tcls versions have these options
        wm attributes $mytoplevel -topmost 1
        #wm attributes $mytoplevel -transparent 1
        #$mytoplevel configure -highlightthickness 1
    }
}

proc ::pd_bindings::canvas_bindings {mytoplevel} {
    variable modifier
    set mycanvas $mytoplevel.c

    window_bindings $mytoplevel

    # key bindings -------------------------------------------------------------
    bind $mytoplevel <$modifier-Key-1>        "pdsend \"$mytoplevel obj\""
    bind $mytoplevel <$modifier-Key-2>        "pdsend \"$mytoplevel msg\""
    bind $mytoplevel <$modifier-Key-3>        "pdsend \"$mytoplevel floatatom\""
    bind $mytoplevel <$modifier-Key-4>        "pdsend \"$mytoplevel symbolatom\""
    bind $mytoplevel <$modifier-Key-5>        "pdsend \"$mytoplevel text\""
    bind $mytoplevel <$modifier-Key-a>        "pdsend \"$mytoplevel selectall\""
    bind $mytoplevel <$modifier-Key-c>        "pdsend \"$mytoplevel copy\""
    bind $mytoplevel <$modifier-Key-d>        "pdsend \"$mytoplevel duplicate\""
    bind $mytoplevel <$modifier-Key-e>        "pdsend \"$mytoplevel editmode 0\""
    bind $mytoplevel <$modifier-Key-g>        "pdsend \"$mytoplevel findagain\""
    bind $mytoplevel <$modifier-Key-s>        "pdsend \"$mytoplevel menusave\""
    bind $mytoplevel <$modifier-Key-v>        "pdsend \"$mytoplevel paste\""
    bind $mytoplevel <$modifier-Key-w>        "pdsend \"$mytoplevel menuclose 0\""
    bind $mytoplevel <$modifier-Key-x>        "pdsend \"$mytoplevel cut\""
    bind $mytoplevel <$modifier-Key-z>        "menu_undo $mytoplevel"

    # annoying, but Tk's bind needs uppercase letter to get the Shift
    bind $mytoplevel <$modifier-Shift-Key-B> "pdsend \"$mytoplevel bng 1\""
    bind $mytoplevel <$modifier-Shift-Key-C> "pdsend \"$mytoplevel mycnv 1\""
    bind $mytoplevel <$modifier-Shift-Key-D> "pdsend \"$mytoplevel vradio 1\""
    bind $mytoplevel <$modifier-Shift-Key-H> "pdsend \"$mytoplevel hslider 1\""
    bind $mytoplevel <$modifier-Shift-Key-I> "pdsend \"$mytoplevel hradio 1\""
    bind $mytoplevel <$modifier-Shift-Key-N> "pdsend \"$mytoplevel numbox 1\""
    bind $mytoplevel <$modifier-Shift-Key-S> "pdsend \"$mytoplevel menusaveas\""
    bind $mytoplevel <$modifier-Shift-Key-T> "pdsend \"$mytoplevel toggle 1\""
    bind $mytoplevel <$modifier-Shift-Key-U> "pdsend \"$mytoplevel vumeter 1\""
    bind $mytoplevel <$modifier-Shift-Key-V> "pdsend \"$mytoplevel vslider 1\""
    bind $mytoplevel <$modifier-Shift-Key-W> "pdsend \"$mytoplevel menuclose 1\""
    bind $mytoplevel <$modifier-Shift-Key-Z> "menu_redo $mytoplevel"
    
    if {$::windowingsystem eq "aqua"} {
        bind $mytoplevel <$modifier-Key-m>   "menu_minimize $mytoplevel"
        bind $mytoplevel <$modifier-Key-t>   "menu_font_dialog $mytoplevel"
        bind $mytoplevel <$modifier-quoteleft> "menu_raisenextwindow"
    } else {
        bind $mytoplevel <$modifier-Key-m>    "menu_message_dialog"
        bind $mytoplevel <$modifier-Key-t>    "menu_texteditor"
    }
    bind $mytoplevel <KeyPress>         "::pd_bindings::sendkey %W 1 %K %A 0"
    bind $mytoplevel <KeyRelease>       "::pd_bindings::sendkey %W 0 %K %A 0"
    bind $mytoplevel <Shift-KeyPress>   "::pd_bindings::sendkey %W 1 %K %A 1"
    bind $mytoplevel <Shift-KeyRelease> "::pd_bindings::sendkey %W 0 %K %A 1"

    # mouse bindings -----------------------------------------------------------
    # these need to be bound to $mycanvas because %W will return $mytoplevel for
    # events over the window frame and $mytoplevel.c for events over the canvas
    bind $mycanvas <Motion>                   "pdtk_canvas_motion %W %x %y 0"
    bind $mycanvas <$modifier-Motion>         "pdtk_canvas_motion %W %x %y 2"
    bind $mycanvas <ButtonPress-1>            "pdtk_canvas_mouse %W %x %y %b 0"
    bind $mycanvas <ButtonRelease-1>          "pdtk_canvas_mouseup %W %x %y %b"
    bind $mycanvas <$modifier-ButtonPress-1>  "pdtk_canvas_mouse %W %x %y %b 2"
    # TODO look into "virtual events' for a means for getting Shift-Button, etc.
    switch -- $::windowingsystem { 
        "aqua" {
            bind $mycanvas <ButtonPress-2>      "pdtk_canvas_rightclick %W %x %y %b"
            # on Mac OS X, make a rightclick with Ctrl-click for 1 button mice
            bind $mycanvas <Control-Button-1> "pdtk_canvas_rightclick %W %x %y %b"
            # TODO try replacing the above with this
            #bind all <Control-Button-1> {event generate %W <Button-2> \
            #                                      -x %x -y %y -rootx %X -rooty %Y \
            #                                      -button 2 -time %t}
        } "x11" {
            bind $mycanvas <ButtonPress-3>    "pdtk_canvas_rightclick %W %x %y %b"
            # on X11, button 2 "pastes" from the X windows clipboard
            bind $mycanvas <ButtonPress-2>   "pdtk_canvas_clickpaste %W %x %y %b"
        } "win32" {
            bind $mycanvas <ButtonPress-3>   "pdtk_canvas_rightclick %W %x %y %b"
        }
    }
    #TODO bind $mytoplevel <MouseWheel>

    # window protocol bindings
    wm protocol $mytoplevel WM_DELETE_WINDOW "pdsend \"$mytoplevel menuclose 0\""
    bind $mycanvas <Destroy> "::pd_bindings::window_destroy %W"
}


#------------------------------------------------------------------------------#
# event handlers

proc ::pd_bindings::window_configure {mytoplevel} {
    pdtk_canvas_getscroll $mytoplevel
}
    
proc ::pd_bindings::window_destroy {mycanvas} {
    set mytoplevel [winfo toplevel $mycanvas]
    unset ::editmode($mytoplevel)
}

# do tasks when changing focus (Window menu, scrollbars, etc.)
proc ::pd_bindings::window_focusin {mytoplevel} {
    # pdtk_post "::pd_bindings::window_focusin $mytoplevel"
    set ::focused_window $mytoplevel
    ::dialog_find::set_canvas_to_search $mytoplevel
    ::pd_menucommands::set_menu_new_dir $mytoplevel
    ::dialog_font::update_font_dialog $mytoplevel
    if {$mytoplevel eq ".pdwindow"} {
        ::pd_menus::configure_for_pdwindow 
    } else {
        ::pd_menus::configure_for_canvas $mytoplevel
    }
    # TODO handle enabling/disabling the Undo and Redo menu items in Edit
    # TODO handle enabling/disabling the Cut/Copy/Paste menu items in Edit
    # TODO enable menu items that the Pd window or dialogs might have disabled
    # TODO update "Open Recent" menu
}

proc ::pd_bindings::dialog_configure {mytoplevel} {
}

proc ::pd_bindings::dialog_focusin {mytoplevel} {
    # TODO disable things on the menus that don't work for dialogs
    ::pd_menus::configure_for_dialog $mytoplevel
}

# "map" event tells us when the canvas becomes visible, and "unmap",
# invisible.  Invisibility means the Window Manager has minimized us.  We
# don't get a final "unmap" event when we destroy the window.
proc ::pd_bindings::map {mytoplevel} {
    pdsend "$mytoplevel map 1"
}

proc ::pd_bindings::unmap {mytoplevel} {
    pdsend "$mytoplevel map 0"
}


#------------------------------------------------------------------------------#
# key usage

proc ::pd_bindings::sendkey {mycanvas state key iso shift} {
    # TODO canvas_key on the C side should be refactored with this proc as well
    switch -- $key {
        "BackSpace" { set iso ""; set key 8    }
        "Tab"       { set iso ""; set key 9 }
        "Return"    { set iso ""; set key 10 }
        "Escape"    { set iso ""; set key 27 }
        "Space"     { set iso ""; set key 32 }
        "Delete"    { set iso ""; set key 127 }
        "KP_Delete" { set iso ""; set key 127 }
    }
    if {$iso ne ""} {
        scan $iso %c key
    }
    puts "::pd_bindings::sendkey {%W:$mycanvas $state %K$key %A$iso $shift}"
    # $mycanvas might be a toplevel, but [winfo toplevel] does the right thing
    pdsend "[winfo toplevel $mycanvas] key $state $key $shift"
}