aboutsummaryrefslogtreecommitdiff
path: root/pd/tcl/pd_menus.tcl
blob: 99b6be94d339932131d52c49eaa044e4aaa79656 (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
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
# Copyright (c) 1997-2009 Miller Puckette.
#(c) 2008 WordTech Communications LLC. License: standard Tcl license, http://www.tcl.tk/software/tcltk/license.html

package provide pd_menus 0.1

package require pd_menucommands
package require Tk
#package require tile
## replace Tk widgets with Ttk widgets on 8.5
#namespace import -force ttk::*

# TODO figure out Undo/Redo/Cut/Copy/Paste state changes for menus
# TODO figure out parent window/window list for Window menu
# TODO what is the Tcl package constructor or init()?
# TODO $::pd_menus::menubar or .menubar globally?

# since there is one menubar that is used for all windows, the menu -commands
# use {} quotes so that $::focused_window is interpreted when the menu item
# is called, not when the command is mapped to the menu item.  This is the
# opposite of the 'bind' commands in pd_bindings.tcl
    

# ------------------------------------------------------------------------------
# global variables

# TODO this should properly be inside the pd_menus namespace, now it is global
namespace import ::pd_menucommands::* 

namespace eval ::pd_menus:: {
    variable accelerator
    variable menubar ".menubar"
    variable current_toplevel ".pdwindow"
    
    namespace export create_menubar
    namespace export configure_for_pdwindow
    namespace export configure_for_canvas
    namespace export configure_for_dialog

    # turn off tearoff menus globally
    option add *tearOff 0
}

# ------------------------------------------------------------------------------
# 
proc ::pd_menus::create_menubar {} {
    variable accelerator
    variable menubar
    if {$::windowingsystem eq "aqua"} {
        set accelerator "Cmd"
    } else {
        set accelerator "Ctrl"
    }
    menu $menubar
    set menulist "file edit put find media window help"
    if { $::windowingsystem eq "aqua" } {create_apple_menu $menubar}
    # FIXME why does the following (if uncommented) kill my menubar?
    # if { $::windowingsystem eq "win32" } {create_system_menu $menubar}
    foreach mymenu $menulist {    
        menu $menubar.$mymenu
        $menubar add cascade -label [_ [string totitle $mymenu]] \
            -menu $menubar.$mymenu
        [format build_%s_menu $mymenu] $menubar.$mymenu .
        if {$::windowingsystem eq "win32"} {
            # fix menu font size on Windows with tk scaling = 1
            $menubar.$mymenu configure -font menufont
        }
    }
}

proc ::pd_menus::configure_for_pdwindow {} {
    variable menubar
    # these are meaningless for the Pd window, so disable them
    set file_items_to_disable {"Save" "Save As..." "Print..." "Close"}
    foreach menuitem $file_items_to_disable {
        $menubar.file entryconfigure [_ $menuitem] -state disabled
    }
    set edit_items_to_disable {"Undo" "Redo" "Duplicate" "Tidy Up" "Edit Mode"}
    foreach menuitem $edit_items_to_disable {
        $menubar.edit entryconfigure [_ $menuitem] -state disabled
    }
    # disable everything on the Put menu
    for {set i 0} {$i <= [$menubar.put index end]} {incr i} {
        # catch errors that happen when trying to disable separators
        catch {$menubar.put entryconfigure $i -state disabled }
    }
}

proc ::pd_menus::configure_for_canvas {mytoplevel} {
    variable menubar
    set file_items_to_disable {"Save" "Save As..." "Print..." "Close"}
    foreach menuitem $file_items_to_disable {
        $menubar.file entryconfigure [_ $menuitem] -state normal
    }
    set edit_items_to_disable {"Undo" "Redo" "Duplicate" "Tidy Up" "Edit Mode"}
    foreach menuitem $edit_items_to_disable {
        $menubar.edit entryconfigure [_ $menuitem] -state normal
    }
    for {set i 0} {$i <= [$menubar.put index end]} {incr i} {
        # catch errors that happen when trying to disable separators
        catch {$menubar.put entryconfigure $i -state normal }
    }
    # TODO set "Edit Mode" state using editmode($mytoplevel)
}

proc ::pd_menus::configure_for_dialog {mytoplevel} {
    variable menubar
    # these are meaningless for the dialog panels, so disable them
    set file_items_to_disable {"Save" "Save As..." "Print..." "Close"}
    foreach menuitem $file_items_to_disable {
        $menubar.file entryconfigure [_ $menuitem] -state disabled
    }
    set edit_items_to_disable {"Undo" "Redo" "Duplicate" "Tidy Up" "Edit Mode"}
    foreach menuitem $edit_items_to_disable {
        $menubar.edit entryconfigure [_ $menuitem] -state disabled
    }
    # disable everything on the Put menu
    for {set i 0} {$i <= [$menubar.put index end]} {incr i} {
        # catch errors that happen when trying to disable separators
        catch {$menubar.put entryconfigure $i -state disabled }
    }
}


# ------------------------------------------------------------------------------
# menu building functions
proc ::pd_menus::build_file_menu {mymenu mytoplevel} {
    [format build_file_menu_%s $::windowingsystem] $mymenu
    $mymenu entryconfigure [_ "New"]        -command {menu_new}
    $mymenu entryconfigure [_ "Open"]       -command {menu_open}
    $mymenu entryconfigure [_ "Save"]       -command {pdsend "$::focused_window menusave"}
    $mymenu entryconfigure [_ "Save As..."] -command {pdsend "$::focused_window menusaveas"}
    #$mymenu entryconfigure [_ "Revert*"]    -command {menu_revert $current_toplevel}
    $mymenu entryconfigure [_ "Close"]      -command {pdsend "$::focused_window menuclose 0"}
    $mymenu entryconfigure [_ "Message"]    -command {menu_message_dialog}
    $mymenu entryconfigure [_ "Print..."]   -command {menu_print $::focused_window}
}

proc ::pd_menus::build_edit_menu {mymenu mytoplevel} {
    variable accelerator
    $mymenu add command -label [_ "Undo"]       -accelerator "$accelerator+Z" \
        -command {menu_undo $::focused_window}
    $mymenu add command -label [_ "Redo"]       -accelerator "Shift+$accelerator+Z" \
        -command {menu_redo $::focused_window}
    $mymenu add  separator
    $mymenu add command -label [_ "Cut"]        -accelerator "$accelerator+X" \
        -command {pdsend "$::focused_window cut"}
    $mymenu add command -label [_ "Copy"]       -accelerator "$accelerator+C" \
        -command {pdsend "$::focused_window copy"}
    $mymenu add command -label [_ "Paste"]      -accelerator "$accelerator+V" \
        -command {pdsend "$::focused_window paste"}
    $mymenu add command -label [_ "Duplicate"]  -accelerator "$accelerator+D" \
        -command {pdsend "$::focused_window duplicate"}
    $mymenu add command -label [_ "Select All"] -accelerator "$accelerator+A" \
        -command {pdsend "$::focused_window selectall"}
    $mymenu add  separator
    if {$::windowingsystem eq "aqua"} {
        $mymenu add command -label [_ "Text Editor"] \
            -command {menu_texteditor $::focused_window}
        $mymenu add command -label [_ "Font"]  -accelerator "$accelerator+T" \
            -command {menu_font_dialog $::focused_window}
    } else {
        $mymenu add command -label [_ "Text Editor"] -accelerator "$accelerator+T"\
            -command {menu_texteditor $::focused_window}
        $mymenu add command -label [_ "Font"] \
            -command {menu_font_dialog $::focused_window}
    }
    $mymenu add command -label [_ "Tidy Up"] \
        -command {pdsend "$::focused_window tidy"}
    $mymenu add command -label [_ "Toggle Console"] -accelerator "Shift+$accelerator+R" \
        -command {.controls.switches.console invoke}
    $mymenu add command -label [_ "Clear Console"] -accelerator "Shift+$accelerator+L" \
        -command {menu_clear_console}
    $mymenu add  separator
    #TODO madness! how to do set the state of the check box without invoking the menu!
    $mymenu add check -label [_ "Edit Mode"] -accelerator "$accelerator+E" \
        -selectcolor grey85 \
        -command {pdsend "$::focused_window editmode 0"}
    #if { ! [catch {console hide}]} { 
    # TODO set up menu item to show/hide the Tcl/Tk console, if it available
    #}

    if {$::windowingsystem ne "aqua"} {
        $mymenu add  separator
        $mymenu add command -label [_ "Preferences"] \
            -command {menu_preferences_dialog}
    }
}

proc ::pd_menus::build_put_menu {mymenu mytoplevel} {
    variable accelerator
    $mymenu add command -label [_ "Object"] -accelerator "$accelerator+1" \
        -command {pdsend "$::focused_window obj 0"} 
    $mymenu add command -label [_ "Message"] -accelerator "$accelerator+2" \
        -command {pdsend "$::focused_window msg 0"}
    $mymenu add command -label [_ "Number"] -accelerator "$accelerator+3" \
        -command {pdsend "$::focused_window floatatom  0"}
    $mymenu add command -label [_ "Symbol"] -accelerator "$accelerator+4" \
        -command {pdsend "$::focused_window symbolatom  0"}
    $mymenu add command -label [_ "Comment"] -accelerator "$accelerator+5" \
        -command {pdsend "$::focused_window text  0"}
    $mymenu add  separator
    $mymenu add command -label [_ "Bang"]    -accelerator "Shift+$accelerator+B" \
        -command {pdsend "$::focused_window bng  0"}
    $mymenu add command -label [_ "Toggle"]  -accelerator "Shift+$accelerator+T" \
        -command {pdsend "$::focused_window toggle  0"}
    $mymenu add command -label [_ "Number2"] -accelerator "Shift+$accelerator+N" \
        -command {pdsend "$::focused_window numbox  0"}
    $mymenu add command -label [_ "Vslider"] -accelerator "Shift+$accelerator+V" \
        -command {pdsend "$::focused_window vslider  0"}
    $mymenu add command -label [_ "Hslider"] -accelerator "Shift+$accelerator+H" \
        -command {pdsend "$::focused_window hslider  0"}
    $mymenu add command -label [_ "Vradio"]  -accelerator "Shift+$accelerator+D" \
        -command {pdsend "$::focused_window vradio  0"}
    $mymenu add command -label [_ "Hradio"]  -accelerator "Shift+$accelerator+I" \
        -command {pdsend "$::focused_window hradio  0"}
    $mymenu add command -label [_ "VU Meter"] -accelerator "Shift+$accelerator+U"\
        -command {pdsend "$::focused_window vumeter  0"}
    $mymenu add command -label [_ "Canvas"]  -accelerator "Shift+$accelerator+C" \
        -command {pdsend "$::focused_window mycnv  0"}
    $mymenu add  separator
    $mymenu add command -label [_ "Graph"] -command {pdsend "$::focused_window graph"} 
    $mymenu add command -label [_ "Array"] -command {pdsend "$::focused_window menuarray"}
}

proc ::pd_menus::build_find_menu {mymenu mytoplevel} {
    variable accelerator
    $mymenu add command -label [_ "Find..."]    -accelerator "$accelerator+F" \
        -command {::dialog_find::menu_find_dialog $::focused_window}
    $mymenu add command -label [_ "Find Again"] -accelerator "$accelerator+G" \
        -command {pdsend "$::focused_window findagain"}
    $mymenu add command -label [_ "Find Last Error"] \
        -command {pdsend "$::focused_window finderror"} 
}

proc ::pd_menus::build_media_menu {mymenu mytoplevel} {
    variable accelerator
    $mymenu add radiobutton -label [_ "DSP On"] -accelerator "$accelerator+/" \
        -variable ::dsp -value 1 -command {pdsend "pd dsp 1"}
    $mymenu add radiobutton -label [_ "DSP Off"] -accelerator "$accelerator+." \
        -variable ::dsp -value 0 -command {pdsend "pd dsp 0"}
    $mymenu add  separator

    set audioapi_list_length [llength $::audioapi_list]
    for {set x 0} {$x<$audioapi_list_length} {incr x} {
        # pdtk_post "audio [lindex [lindex $::audioapi_list $x] 0]"
        $mymenu add radiobutton -label [lindex [lindex $::audioapi_list $x] 0] \
            -command {menu_audio 0} -variable ::pd_whichapi \
            -value [lindex [lindex $::audioapi_list $x] 1]\
            -command {pdsend "pd audio-setapi $::pd_whichapi"}
    }
    if {$audioapi_list_length > 0} {$mymenu add separator}

    set midiapi_list_length [llength $::midiapi_list]
    for {set x 0} {$x<$midiapi_list_length} {incr x} {
        # pdtk_post "midi [lindex [lindex $::midiapi_list $x] 0]"
        $mymenu add radiobutton -label [lindex [lindex $::midiapi_list $x] 0] \
            -command {menu_midi 0} -variable ::pd_whichmidiapi \
            -value [lindex [lindex $::midiapi_list $x] 1]\
            -command {pdsend "pd midi-setapi $::pd_whichmidiapi"}
    }
    if {$midiapi_list_length > 0} {$mymenu add separator}

    if {$::windowingsystem ne "aqua"} {
        $mymenu add command -label [_ "Audio settings..."] \
            -command {pdsend "pd audio-properties"}
        $mymenu add command -label [_ "MIDI settings..."] \
            -command {pdsend "pd midi-properties"} 
        $mymenu add  separator
    }
    $mymenu add command -label [_ "Test Audio and MIDI..."] \
        -command {menu_doc_open doc/7.stuff/tools testtone.pd} 
    $mymenu add command -label [_ "Load Meter"] \
        -command {menu_doc_open doc/7.stuff/tools load-meter.pd} 
}

proc ::pd_menus::build_window_menu {mymenu mytoplevel} {
    variable accelerator
    if {$::windowingsystem eq "aqua"} {
        $mymenu add command -label [_ "Minimize"] -command {menu_minimize .} \
            -accelerator "$accelerator+M"
        $mymenu add command -label [_ "Zoom"] -command {menu_zoom .}
        $mymenu add  separator
    }
    $mymenu add command -label [_ "Parent Window"] \
        -command {pdsend "$::focused_window findparent"}
    $mymenu add command -label [_ "Pd window"] -command {menu_raise_pdwindow} \
        -accelerator "$accelerator+R"
    $mymenu add  separator
    if {$::windowingsystem eq "aqua"} {
        $mymenu add command -label [_ "Bring All to Front"] \
            -command {menu_bringalltofront}
        $mymenu add  separator
    }
}

proc ::pd_menus::build_help_menu {mymenu mytoplevel} {
    if {$::windowingsystem ne "aqua"} {
        $mymenu add command -label [_ "About Pd"] \
            -command {menu_doc_open doc/1.manual 1.introduction.txt} 
    }
    $mymenu add command -label [_ "HTML Manual..."] \
        -command {menu_doc_open doc/1.manual index.htm}
    $mymenu add command -label [_ "Browser..."] \
        -command {placeholder menu_helpbrowser \$help_top_directory} 
}

# ------------------------------------------------------------------------------
# update the menu entries for opening recent files
proc ::pd_menus::update_recentfiles_menu {} {
    variable menubar
    switch -- $::windowingsystem {
        "aqua" {::pd_menus::update_openrecent_menu_aqua .openrecent}
        "win32" {update_recentfiles_on_menu $menubar.file}
        "x11" {update_recentfiles_on_menu $menubar.file}
    }
}

proc ::pd_menus::clear_recentfiles_menu {} {
    set ::recentfiles_list {}
    ::pd_menus::update_recentfiles_menu
}

proc ::pd_menus::update_openrecent_menu_aqua {mymenu} {
    if {! [winfo exists $mymenu]} {menu $mymenu}
    $mymenu delete 0 end
    foreach filename $::recentfiles_list {
        puts "creating menu item for $filename"
        $mymenu add command -label [file tail $filename] \
            -command "open_file $filename"
    }
    $mymenu add  separator
    $mymenu add command -label [_ "Clear Menu"] \
        -command "::pd_menus::clear_recentfiles_menu"
}

# this expects to be run on the File menu, and to insert above the last separator
proc ::pd_menus::update_recentfiles_on_menu {mymenu} {
    set lastitem [$mymenu index end]
    set i 1
    while {[$mymenu type [expr $lastitem-$i]] ne "separator"} {incr i}
    set bottom_separator [expr $lastitem-$i]
    incr i
    while {[$mymenu type [expr $lastitem-$i]] ne "separator"} {incr i}
    set top_separator [expr $lastitem-$i]
    if {$top_separator < [expr $bottom_separator-1]} {
        $mymenu delete [expr $top_separator+1] [expr $bottom_separator-1]
    }
    set i 0
    foreach filename $::recentfiles_list {
        $mymenu insert [expr $top_separator+$i+1] command \
            -label [file tail $filename] -command "open_file $filename"
        incr i
    }
}

# ------------------------------------------------------------------------------
# menu building functions for Mac OS X/aqua

# for Mac OS X only
proc ::pd_menus::create_apple_menu {mymenu} {
    # TODO this should open a Pd patch called about.pd
    menu $mymenu.apple
    $mymenu.apple add command -label [_ "About Pd"] \
        -command {menu_doc_open doc/1.manual 1.introduction.txt} 
    $mymenu add cascade -label "Apple" -menu $mymenu.apple
    $mymenu.apple add  separator
    # starting in 8.4.14, this is created automatically
    set patchlevel [split [info patchlevel] .]
    if {[lindex $patchlevel 1] < 5 && [lindex $patchlevel 2] < 14} {
        $mymenu.apple add command -label [_ "Preferences..."] \
            -command {menu_preferences_dialog" -accelerator "Cmd+,}
    }
}

proc ::pd_menus::build_file_menu_aqua {mymenu} {
    variable accelerator
    $mymenu add command -label [_ "New"]       -accelerator "$accelerator+N"
    $mymenu add command -label [_ "Open"]      -accelerator "$accelerator+O"
    ::pd_menus::update_openrecent_menu_aqua .openrecent
    $mymenu add cascade -label [_ "Open Recent"] -menu .openrecent
    $mymenu add  separator
    $mymenu add command -label [_ "Close"]     -accelerator "$accelerator+W"
    $mymenu add command -label [_ "Save"]      -accelerator "$accelerator+S"
    $mymenu add command -label [_ "Save As..."] -accelerator "$accelerator+Shift+S"
    #$mymenu add command -label [_ "Save All"]
    #$mymenu add command -label [_ "Revert to Saved"]
    $mymenu add  separator
    $mymenu add command -label [_ "Message"]
    $mymenu add  separator
    $mymenu add command -label [_ "Print..."]   -accelerator "$accelerator+P"
}

# the "Edit", "Put", and "Find" menus do not have cross-platform differences

proc ::pd_menus::build_media_menu_aqua {mymenu} {
}

proc ::pd_menus::build_window_menu_aqua {mymenu} {
}

# the "Help" does not have cross-platform differences
 
# ------------------------------------------------------------------------------
# menu building functions for UNIX/X11

proc ::pd_menus::build_file_menu_x11 {mymenu} {
    variable accelerator
    $mymenu add command -label [_ "New"]        -accelerator "$accelerator+N"
    $mymenu add command -label [_ "Open"]       -accelerator "$accelerator+O"
    $mymenu add  separator
    $mymenu add command -label [_ "Save"]       -accelerator "$accelerator+S"
    $mymenu add command -label [_ "Save As..."] -accelerator "Shift+$accelerator+S"
    #    $mymenu add command -label "Revert"
    $mymenu add  separator
    $mymenu add command -label [_ "Message"]    -accelerator "$accelerator+M"
    $mymenu add command -label [_ "Print..."]   -accelerator "$accelerator+P"
    $mymenu add  separator
    # the recent files get inserted in here by update_recentfiles_on_menu
    $mymenu add  separator
    $mymenu add command -label [_ "Close"]      -accelerator "$accelerator+W"
    $mymenu add command -label [_ "Quit"]       -accelerator "$accelerator+Q" \
        -command {pdsend "pd verifyquit"}
}

# the "Edit", "Put", and "Find" menus do not have cross-platform differences

proc ::pd_menus::build_media_menu_x11 {mymenu} {
}

proc ::pd_menus::build_window_menu_x11 {mymenu} {
}

# the "Help" does not have cross-platform differences

# ------------------------------------------------------------------------------
# menu building functions for Windows/Win32

# for Windows only
proc ::pd_menus::create_system_menu {mymenu} {
    $mymenu add cascade -menu [menu $mymenu.system]
    # TODO add Close, Minimize, etc and whatever else is on the little menu
    # that is on the top left corner of the window frame
}

proc ::pd_menus::build_file_menu_win32 {mymenu} {
    variable accelerator
    $mymenu add command -label [_ "New"]      -accelerator "$accelerator+N"
    $mymenu add command -label [_ "Open"]     -accelerator "$accelerator+O"
    $mymenu add  separator
    $mymenu add command -label [_ "Save"]      -accelerator "$accelerator+S"
    $mymenu add command -label [_ "Save As..."] -accelerator "Shift+$accelerator+S"
    #    $mymenu add command -label "Revert"
    $mymenu add  separator
    $mymenu add command -label [_ "Message"]  -accelerator "$accelerator+M"
    $mymenu add command -label [_ "Print..."] -accelerator "$accelerator+P"
    $mymenu add  separator
    # the recent files get inserted in here by update_recentfiles_on_menu
    $mymenu add  separator
    $mymenu add command -label [_ "Close"]    -accelerator "$accelerator+W"
    $mymenu add command -label [_ "Quit"]     -accelerator "$accelerator+Q"\
        -command {pdsend "pd verifyquit"}
}

# the "Edit", "Put", and "Find" menus do not have cross-platform differences

proc ::pd_menus::build_media_menu_win32 {mymenu} {
}

proc ::pd_menus::build_window_menu_win32 {mymenu} {
}

# the "Help" does not have cross-platform differences