Skip to content
Snippets Groups Projects
dialog_prefs.tcl 17.4 KiB
Newer Older
# the find dialog panel is a bit unusual in that it is created directly by the
# Tcl 'pd-gui'. Most dialog panels are created by sending a message to 'pd',
# which then sends a message to 'pd-gui' to create the panel.

package provide dialog_prefs 0.1

#package require pd_bindings
#package require dialog_gui

namespace eval ::dialog_prefs:: {
#    namespace export pdtk_
}

# mytoplevel isn't used here, but is kept for compatibility with other dialog ok procs
proc ::dialog_prefs::ok {mytoplevel} {
    return # hash this out later
    variable find_history

    if {$findstring eq ""} {
        if {$::windowingsystem eq "aqua"} {bell}
        return
    }
    if {$find_in_window eq ".pdwindow"} {
        if {$::tcl_version < 8.5} {
            # TODO implement in 8.4 style, without -all
            set matches [.pdwindow.text search -nocase -- $findstring 0.0]
        } else {
            set matches [.pdwindow.text search -all -nocase -- $findstring 0.0]
        }
        .pdwindow.text tag delete sel
        if {[llength $matches] > 0} {
            foreach match $matches {
                .pdwindow.text tag add sel $match "$match wordend"
            }
            .pdwindow.text see [lindex $matches 0]
            lappend find_history $findstring
        }
    } else {
        if {$findstring eq $previous_findstring \
                && $wholeword_button == $previous_wholeword_button} {
            pdsend "$find_in_window findagain"
        } else {
            pdsend [concat $find_in_window find [pdtk_encodedialog $findstring] \
                        $wholeword_button]
            set previous_findstring $findstring
            set previous_wholeword_button $wholeword_button
            lappend find_history $findstring
        }
    }
    if {$::windowingsystem eq "aqua"} {
        # (Mac OS X) hide panel after success, but keep it if unsuccessful by
        # having the couldnotfind proc reopen it
        cancel $mytoplevel
    } else {
        # (GNOME/Windows) find panel should retain focus after a find
        # (yes, a bit of a kludge)
        after 100 "raise .find; focus .find.entry"
    }
}

# mytoplevel isn't used here, but is kept for compatibility with other dialog cancel procs
proc ::dialog_prefs::cancel {mytoplevel} {
    wm withdraw .prefs
}

# the find panel is opened from the menu and key bindings
proc ::dialog_prefs::open_prefs_dialog {mytoplevel} {
    if {[winfo exists .prefs]} {
        wm deiconify .prefs
        raise .prefs
        # obtain last known mouse coords and pop the menu there
        global pointer_x_global pointer_y_global
        wm geometry .prefs "+$pointer_x_global+$pointer_y_global"
    } else {
        create_dialog $mytoplevel
    }
    pd [concat pd audio-properties \;]
    pd [concat pd midi-properties \;]
    ::dialog_gui::create_gui_dialog .prefs.nb.gui
#    need to think about what should get focus with ttk::notebook
#    .prefs.entry selection range 0 end
}

proc ::dialog_prefs::dropdown_set {name value} {
    if {$::windowingsystem eq "aqua"} {
        set my_var [$name cget -textvariable]
        set $my_var $value
    } else {
        $name set "$value"
    }
}

proc ::dialog_prefs::dropdown_by_index {name variable values textvar} {
    ::dialog_prefs::dropdown $name $variable $values $textvar
}

proc ::dialog_prefs::dropdown {name variable values args} {
    set textvar $variable
    if {$args ne ""} {set textvar $args}
    if {$::windowingsystem eq "aqua"} {
        ttk::menubutton $name -menu $name.menu -direction flush \
            -textvariable $textvar -cursor boat
        menu $name.menu -cursor boat
        set i 0
        foreach label $values {
            set value $label
            if {$args ne ""} {set value $i}
            $name.menu add radiobutton -value $value -variable $variable \
                -label $label -command "set $textvar \"$label\""
            incr i
        }
    } else {
        # set combobox width to largest string
        set width 0
        foreach value $values {
            set len [string length $value]
            set width [expr $len > $width ? $len : $width]
        }
        ttk::combobox $name -state readonly -width $width \
            -style Prefs.TCombobox -values $values
        set command "get"
        if {$args ne ""} {
            set command "current"
            # the following cmd prevents a bug in the alsa midi api
            # from throwing an index that's out of range. but once
            # that bug gets fixed, this should be removed so that
            # it doesn't hide any future bugs
            set $variable [expr min([set $variable], [llength $values] - 1)]
        }
        bind $name <<ComboboxSelected>> "set $variable \
            [concat {[} $name $command {]} ]; after 0 {%W selection clear}"
        if {$command eq "get"} {set command "set"}
        $name $command [set $variable]
    }
}

proc ::dialog_prefs::set_color {array key op} {
    # not sure if this is necessary, but just in case...
    if {$op ne "write"} {return}
    set c [set ${array}($key)]
    set commands {}
    switch $key {
        box {set commands [list "itemconfigure \
            box&&(!msg)&&(!atom) -fill $c"] }
        text { set commands [list "itemconfigure \
                   (text&&(!box&&!iemgui)) -fill $c"]
               lappend commands "itemconfigure \
                   label&&graph -fill $c"
              # lappend commands "itemconfigure \
              #     (text&&(!label) -fill $c"
             if {[winfo exists .search.resultstext]} {
                 .search.resultstext configure -foreground $c
                 .search.navtext configure -foreground $c
             }
        }
        text_in_console { 
            if {[winfo exists .printout.frame.text]} {
                .printout.frame.text configure -foreground $c
        }
        canvas_color {set commands [list "configure -bg $c"]
            if {[winfo exists .search.resultstext]} {
                .search.resultstext configure -bg $c
                .search.navtext configure -bg $c
            }
            if {[winfo exists .printout.frame.text]} {
                .printout.frame.text configure -bg $c
            }
        }
        canvas_cursor {set commands [list "configure -insertbackground $c"]}
        highlighted_text_bg \
            {set commands [list "configure -selectbackground $c"]}
        highlighted_text {set commands [list "configure -selectforeground $c"]}
        msg_border {set commands [list "itemconfigure \
            msg&&box -stroke $c"]}
        msg {set commands [list "itemconfigure \
            msg&&box -fill $c"]}
        control_nlet {set commands [list "itemconfigure \
            ((inlet||outlet)&&control) -fill $c"]
        }
        iemgui_nlet {set commands [list "itemconfigure \
                (inlet&&iemgui)||(outlet&&iemgui) -stroke $c"]
        }
        signal_nlet {set commands [list "itemconfigure \
            inlet&&signal -fill $c"]
            lappend commands "itemconfigure \
                outlet&&signal -fill $c"
        }
        #outlet  {set commands [list "itemconfigure outlet -stroke $c"]}
        signal_cord {set commands [list "itemconfigure \
            all_cords&&signal -stroke $c"]
            lappend commands "itemconfigure \
            (outlet&&signal)||(inlet&&signal) -stroke $c"
        }
        control_cord {set commands [list "itemconfigure \
            all_cords&&control -stroke $c"]
            lappend commands "itemconfigure \
            ((inlet||outlet)&&control) -stroke $c"
        }
        selection {
            set commands [list "itemconfigure \
                selected&&text&&(!box&&!iemgui) -fill $c"]
            lappend commands "itemconfigure \
                selected&&(border&&(!iemgui)) -fill $c"
            lappend commands "itemconfigure \
                selected&&border&&iemgui -stroke $c"
        }
        box_border {set commands [list "itemconfigure \
            (box)&&(!iemgui) -stroke $c"]}
        iemgui_border {
            set commands [list "itemconfigure border&&iemgui -stroke $c"]}
        atom_box {set commands [list "itemconfigure \
            atom&&box -fill $c"]}
        atom_box_border {set commands [list "itemconfigure \
            atom&&box -stroke $c"]}
        graph_border {set commands [list "itemconfigure \
            graph&&(!label) -stroke $c"]}
        graph {set commands [list "itemconfigure graph&&(!label) -fill $c"]}
        dash_fill {
            set commands [list "itemconfigure broken&&box -fill $c"]
            if {[winfo exists .printout.frame.text]} {
                .printout.frame.text tag configure errorlink -background $c
            }
        }
        dash_outline {
            set commands [list "itemconfigure broken&&box -stroke $c"]
            if {[winfo exists .printout.frame.text]} {
                .printout.frame.text tag configure errorlink -foreground $c
            }
        }



        magic_glass_bg {set commands [list "itemconfigure \
            magicGlassBg -fill $c"]}
        magic_glass_bd {set commands [list "itemconfigure \
            magicGlassLine -fill $c"]}
        magic_glass_text {set commands [list "itemconfigure \
            magicGlassText -fill $c"]}
        link {
            if {[winfo exists .search.resultstext]} {
                .search.resultstext tag configure link -foreground $c
                .search.navtext tag configure link -foreground $c
                .search.f.advancedlabel configure -foreground $c
            }
        }
        default {}
    }
    if {$commands ne ""} {
        foreach w [winfo children .] {
            foreach child [winfo children $w] {
                if {$child ne "" && [winfo class $child] eq "PathCanvas"} {
                    if {$key eq "canvas_color" &&
                        [info exists [format ::%s(%s) $key $w]]} {
                            continue
                    }
                    foreach command $commands {
                        eval $child $command
                    }
                }
            }
        }
    }
    # hack! how do I avoid hard-coding the window names here?
    set mytoplevel .prefs.nb.gui.colors
    if {[winfo exists $mytoplevel.$key]} {
        ::dialog_prefs::set_swatchbutton $mytoplevel.$key ${array}($key)
    }
    
}

proc ::dialog_prefs::set_swatchbutton {name variable} {
    if {[set $variable] eq ""} {return}
    image create photo ::img::swatchbutton::$name
    set c [set $variable]
    set bd #000000
    set stupid_top_and_bottom \
        [list $bd $bd $bd $bd $bd $bd $bd $bd $bd $bd $bd $bd]
    set dumb \
        [list $bd $c $c $c $c $c $c $c $c $c $c $bd]
    ::img::swatchbutton::$name put [list $stupid_top_and_bottom \
        $dumb $dumb $dumb $dumb $dumb $dumb $dumb $dumb \
        $dumb $dumb $stupid_top_and_bottom] -to 0 0 12 12
    $name configure -image ::img::swatchbutton::$name
}

proc ::dialog_prefs::swatchmenu_nav {w dir} {
    set new [expr {[$w index active] + 7 * $dir}]
    if {$new > [$w index end] || $new < 0} then return
    $w activate $new
}

proc ::dialog_prefs::swatchbutton_colorchooser {name variable} {
    set col [tk_chooseColor -parent $name -initialcolor [set $variable]]
    if {$col ne ""} {
        set $variable $col
        # kludge since the dialog has static name for colors, we refer to it here
        # with partially modular approach that should hopefully prove universal
        set combobox [string trimright $name .link] 
        ::dialog_prefs::dropdown_set $combobox.presets.presets Custom
        ::pd_guiprefs::write_guipreset
    }
}

proc ::dialog_prefs::swatchbutton {name variable} {
    ttk::button $name \
        -command "::dialog_prefs::swatchbutton_colorchooser $name $variable"
}

# These are images used to build the menu for choosing
# colors. The images hang around in memory until you exit
# Pd, but they shouldn't take up too much space to matter
# (and this only gets called once).  If there's a simpler
# way to build a _straightforward_ _user-friendly_
# colorchooser that would be nice, but I couldn't figure
# one out.
proc ::dialog_prefs::get_colorswatches {} {
    # stolen from the Firefox colorchooser
    set colors {  \
        #ffffff #cfcccc #c0c0c0 #999999 #666666 #333333 #000000 \
        #ffcccc #ff6666 #ff0000 #cc0000 #990000 #660000 #330000 \
        #ffcc99 #ff9966 #ff9900 #ff6600 #cc6600 #993300 #663300 \
        #ffff99 #ffff66 #ffcc66 #ffcc33 #cc9933 #996633 #663333 \
        #ffffcc #ffff33 #ffff00 #ffcc00 #999900 #666600 #333300 \
        #99ff99 #66ff99 #33ff33 #33cc00 #009900 #006600 #003300 \
        #99ffff #33ffff #66cccc #00cccc #339999 #336666 #003333 \
        #ccffff #66ffff #33ccff #3366ff #3333ff #000099 #000066 \
        #ccccff #9999ff #6666cc #6633ff #6600cc #333399 #330099 \
        #ffccff #ff99ff #cc66cc #cc33cc #993399 #663366 #330033 \
    }
    if {[lsearch [image names] ::img::colorswatches::*] == -1} {
        foreach color $colors {
        image create photo ::img::colorswatches::$color
        ::img::colorswatches::$color put $color -to 0 0 16 16
        }
    }
    return $colors
}

proc ::dialog_prefs::help {notebook} {
    set pane [$notebook select]
    regsub {.*\.(.*)} $pane {\1} topic
    set file all_about_${topic}_settings.pd
    set dir [file join $::sys_libdir doc 5.reference]
    menu_doc_open $dir $file
}

proc ::dialog_prefs::dialog_bindings {mytoplevel dialogname} {
    variable modifier

    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"
    # these aren't supported in the dialog, so alert the user, then break so
    # that no other key bindings are run
    bind $mytoplevel <$::modifier-Key-s>       {bell; break}
    bind $mytoplevel <$::modifier-Shift-Key-S> {bell; break}
    bind $mytoplevel <$::modifier-Key-p>       {bell; break}

    wm protocol $mytoplevel WM_DELETE_WINDOW "dialog_${dialogname}::cancel $mytoplevel"
}

proc ::dialog_prefs::create_dialog {mytoplevel} {
    toplevel .prefs -class [winfo class .]
    wm title .prefs [_ "Pure Data Preferences"]
#    wm geometry .prefs =475x125+150+150
    wm group .prefs .
    wm resizable .prefs 0 0
    wm transient .prefs

    # obtain last known mouse coords and pop the menu there
    global pointer_x_global pointer_y_global
    if {$pointer_x_global == 0 && $pointer_y_global == 0} {
        set pointer_x_global [expr [winfo rootx .]+30]
        set pointer_y_global [expr [winfo rooty .]+30]
    }
    wm geometry .prefs "+$pointer_x_global+$pointer_y_global"

#    .prefs configure -menu $::dialog_menubar

# todo: check this on the mac and on windows
#    .prefs configure -padx 10 -pady 5
    ::dialog_prefs::dialog_bindings .prefs "prefs"
    bind .prefs <$::modifier-Key-f> break

    # Ttk style setup

    # Common settings
    ttk::style configure Prefs.TLabelframe -borderwidth 0
    # todo: don't hardcode font here
    ttk::style configure Prefs.TLabelframe.Label \
        -font "{DejaVu Sans} 9 bold"

    # for OSX swatchbutton
    if {$::windowingsystem eq "x11"} {
        # custom arrow image for ttk::combobox
        set ::prefs_arrowimg [image create photo -data \
            {R0lGODlhGQAVAMIGACEhIVBQUFpaWl1dXXBwcImJif///////yH+EUNyZWF0ZWQg
            d2l0aCBHSU1QACH5BAEKAAcALAAAAAAZABUAAAMleLrc/jDKSau9OOutC/ggUGRE
            CBCbAArcEQBBqwxybd94ru9QAgA7
        }]
        # ttk::style theme use clam
        ttk::style element create Prefs.downarrow image $::prefs_arrowimg
        ttk::style layout Prefs.TCombobox {
            Combobox.focus -sticky nsew -children {
                Combobox.field -sticky nswe -children {
                    Prefs.downarrow -side right -sticky ns
                    Combobox.padding -expand 1 -sticky nswe -children {
                        Combobox.textarea -sticky nswe
                    }
                }
            }
        }
        ttk::style layout PrefsColors.TMenubutton {
            Menubutton.border -sticky nswe -children {
                Menubutton.focus -sticky nswe -children {
                    Menubutton.padding -expand 1 -sticky we -children {
                        Menubutton.label -side left -sticky {}
                    }
                }
            }
        }
        ttk::style configure Prefs.TCombobox -padding 3
        ttk::style map Prefs.TCombobox \
            -fieldbackground {{readonly pressed} #c1c4c7 \
                              {readonly hover} #fafaf9 \
                              readonly #f5f5f4} \
            -foreground {{readonly focus} black}
        # this shouldn't be global, but I can't get it to work for just
        # Prefsdialog class
        option add *TCombobox*Listbox.selectBackground #4a90d9
        option add *TCombobox*Listbox.selectForeground white
    }

    ttk::notebook .prefs.nb
    .prefs.nb add [ttk::frame .prefs.nb.audio -padding 10] \
        -text "Audio" -sticky nsew
    .prefs.nb add [ttk::frame .prefs.nb.midi -padding 10] \
        -text "MIDI" -sticky nsew
    .prefs.nb add [ttk::frame .prefs.nb.gui -padding 10] \
        -text "GUI" -stick nsew
    pack .prefs.nb -fill both -expand 1

    ttk::frame .prefs.bottomframe -padding 10
    pack .prefs.bottomframe -side bottom -fill both  -expand 1
    if {$::windowingsystem ne "aqua"} {
    ttk::button .prefs.bottomframe.closebutton \
        -text "Close" -command "::dialog_prefs::cancel .prefs"
    pack .prefs.bottomframe.closebutton -side right
    }
    ttk::button .prefs.bottomframe.helpbutton \
        -text "Help" -command "::dialog_prefs::help .prefs.nb"
    pack .prefs.bottomframe.helpbutton -side left
}