# 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