aboutsummaryrefslogtreecommitdiff
path: root/pd/tcl/dialog_find.tcl
blob: 443bec3a89d2b14f112c09e6aa19725e17e738c2 (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
# 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_find 0.1

package require pd_bindings

namespace eval ::dialog_find:: {
    variable find_in_toplevel ".pdwindow"
    # store the state of the "Match whole word only" check box
    variable wholeword_button 0
    # if the search hasn't changed, then the Find button sends "findagain"
    variable previous_wholeword_button 0
    variable previous_findstring ""
    variable find_history {}
    variable history_position 0

    namespace export pdtk_couldnotfind
}

proc ::dialog_find::get_history {direction} {
    variable find_history
    variable history_position
    
    incr history_position $direction
    if {$history_position < 0} {set history_position 0}
    if {$history_position > [llength $find_history]} {
        set history_position [llength $find_history]
    }
    .find.entry delete 0 end
    .find.entry insert 0 [lindex $find_history end-[expr $history_position - 1]]
}

# mytoplevel isn't used here, but is kept for compatibility with other dialog ok procs
proc ::dialog_find::ok {mytoplevel} {
    variable find_in_window
    variable wholeword_button
    variable previous_wholeword_button
    variable previous_findstring
    variable find_history

    set findstring [.find.entry get]
    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_find::cancel {mytoplevel} {
    wm withdraw .find
}

proc ::dialog_find::set_window_to_search {mytoplevel} {
    variable find_in_window $mytoplevel
    if {[winfo exists .find.frame.targetlabel]} {
        if {$find_in_window eq ".find"} {
            set find_in_window [winfo toplevel [lindex [wm stackorder .] end-1]]
        }
            # this has funny side effects in tcl 8.4 ???
        if {$::tcl_version >= 8.5} {
            wm transient .find $find_in_window
        }
        .find.frame.targetlabel configure -text \
            [lookup_windowname $find_in_window]
    }
}

proc ::dialog_find::pdtk_couldnotfind {mytoplevel} {
    bell
    ::pdwindow::error [format [_ "Couldn't find '%s' in %s"] \
                   [.find.entry get] [lookup_windowname $mytoplevel] ]
    if {$::windowingsystem eq "aqua"} {open_find_dialog $mytoplevel}
}

# the find panel is opened from the menu and key bindings
proc ::dialog_find::open_find_dialog {mytoplevel} {
    if {[winfo exists .find]} {
        wm deiconify .find
        raise .find
    } else {
        create_dialog $mytoplevel
    }
    .find.entry selection range 0 end
}

proc ::dialog_find::create_dialog {mytoplevel} {
    toplevel .find -class DialogWindow
    wm title .find [_ "Find"]
    wm geometry .find =475x125+150+150
    wm group .find .
    wm resizable .find 0 0
    wm transient .find
    .find configure -menu $::dialog_menubar
    .find configure -padx 10 -pady 5
    ::pd_bindings::dialog_bindings .find "find"
    # sending these commands to the Find Dialog Panel should forward them to
    # the currently focused patch
    bind .find <$::modifier-Key-s> \
        {menu_send $::focused_window menusave; break}
    bind .find <$::modifier-Shift-Key-S> \
        {menu_send $::focused_window menusaveas; break}
    bind .find <$::modifier-Key-p> \
        {menu_print $::focused_window; break}
    
    frame .find.frame
    pack .find.frame -side top -fill x -pady 1
    label .find.frame.searchin -text [_ "Search in"]
    label .find.frame.targetlabel -text [_ "Pd window"]
    label .find.frame.for -text [_ "for:"]
    pack .find.frame.searchin .find.frame.targetlabel .find.frame.for -side left
    entry .find.entry -width 54 -font 18 -relief sunken \
        -highlightthickness 1 -highlightcolor blue
    pack .find.entry -side top -padx 10

    bind .find.entry <Up> "::dialog_find::get_history 1"
    bind .find.entry <Down> "::dialog_find::get_history -1"
    
    checkbutton .find.wholeword -variable ::dialog_find::wholeword_button \
        -text [_ "Match whole word only"] -anchor w
    pack .find.wholeword -side top -padx 30 -pady 3 -fill x
    
    frame .find.buttonframe -background yellow
    pack .find.buttonframe -side right -pady 3
    if {$::windowingsystem eq "win32"} {
        button .find.cancel -text [_ "Cancel"] -default normal -width 9 \
            -command "::dialog_find::cancel $mytoplevel"
        pack .find.cancel -side right -padx 6 -pady 3
    }
    button .find.button -text [_ "Find"] -default active -width 9 \
        -command "::dialog_find::ok $mytoplevel"
    pack .find.button -side right -padx 6 -pady 3
    if {$::windowingsystem eq "x11"} {
        button .find.close -text [_ "Close"] -default normal -width 9 \
            -command "::dialog_find::cancel $mytoplevel"
        pack .find.close -side right -padx 6 -pady 3
    }
    # on Mac OS X, the buttons shouldn't get Tab/keyboard focus
    if {$::windowingsystem eq "aqua"} {
        .find.wholeword configure -takefocus 0
        .find.button configure -takefocus 0
    }
    ::dialog_find::set_window_to_search $mytoplevel
    focus .find.entry
}