Stringscan

Check-in [b47e73ca88]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Update
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | trunk
Files: files | file ages | folders
SHA3-256:b47e73ca887cf0198f2743850f46722552f88dfc9cc9446d9364f0d622b4865f
User & Date: kevin 2019-12-07 02:02:13
Context
2019-12-07
02:02
Update Leaf check-in: b47e73ca88 user: kevin tags: trunk
2019-12-02
00:23
Add image check-in: 8a51ba2769 user: kevin tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to scriptlibs/softwareupdate/softwareupdate.tcl.

96
97
98
99
100
101
102


103
104
105
106
107
108
109
110
...
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
...
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
...
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
    #get the current installation path
    proc findCurrentInstallation {} {
	variable currentinstall
	variable appname 
	switch [tk windowingsystem] {

	    "aqua" {


	 set currentinstall
	    }
	    
	    "win32" {
		set currentinstall [file join $::env(APPDATA) ${appname}_App]  
	    }
	    
	    "x11" {
................................................................................

	wm withdraw .updateprogress
	update idletasks

	ttk::frame .updateprogress.f -padding 5
	pack .updateprogress.f -fill both -expand yes

	label .updateprogress.f.l -bg gray95 -image $icon -bd 0 -relief flat -highlightthickness 0 -width [image width $icon] -height [image height $icon]
	pack .updateprogress.f.l -side left -fill both -expand yes

	frame .updateprogress.f.r -bg gray95 -bd 0 -highlightcolor gray95
	pack .updateprogress.f.r -side right -fill both -expand yes
	
	
	ttk::label .updateprogress.f.r.t -text "Checking for updates..." -padding 5
	pack .updateprogress.f.r.t -side top -fill both -expand yes

	ttk::frame .updateprogress.f.r.f -padding 5
................................................................................

	.updateprogress.f.r.f.progress start

	ttk::button .updateprogress.f.r.f.b -text "Cancel" -command {destroy .updateprogress}

	pack .updateprogress.f.r.f.b -side bottom -fill both -expand no

	wm geometry .updateprogress 400x100
	wm resizable .updateprogress 0 0

	wm deiconify .updateprogress
	raise .updateprogress

	wm transient .updateprogress .

................................................................................
	    }
	}

	catch {destroy .downloadprogress}

    }


    proc setInstall {dirname} {
	variable currentinstall
	set currentinstall $dirname
    }
	

    namespace export *

}







>
>
|







 







|
|

|







 







<







 







<
<
<
<
<
<
<



96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
...
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
...
195
196
197
198
199
200
201

202
203
204
205
206
207
208
...
344
345
346
347
348
349
350







351
352
353
    #get the current installation path
    proc findCurrentInstallation {} {
	variable currentinstall
	variable appname 
	switch [tk windowingsystem] {

	    "aqua" {
		set approot [info nameofexecutable]
		set apppath [split $approot /]
		set currentinstall [join [lrange $apppath 0 [lsearch $apppath "*.app"]] / ]
	    }
	    
	    "win32" {
		set currentinstall [file join $::env(APPDATA) ${appname}_App]  
	    }
	    
	    "x11" {
................................................................................

	wm withdraw .updateprogress
	update idletasks

	ttk::frame .updateprogress.f -padding 5
	pack .updateprogress.f -fill both -expand yes

	ttk::label .updateprogress.f.l -image $icon  -padding 10
	pack .updateprogress.f.l -side left -fill both -expand yes 

	ttk::frame .updateprogress.f.r
	pack .updateprogress.f.r -side right -fill both -expand yes
	
	
	ttk::label .updateprogress.f.r.t -text "Checking for updates..." -padding 5
	pack .updateprogress.f.r.t -side top -fill both -expand yes

	ttk::frame .updateprogress.f.r.f -padding 5
................................................................................

	.updateprogress.f.r.f.progress start

	ttk::button .updateprogress.f.r.f.b -text "Cancel" -command {destroy .updateprogress}

	pack .updateprogress.f.r.f.b -side bottom -fill both -expand no


	wm resizable .updateprogress 0 0

	wm deiconify .updateprogress
	raise .updateprogress

	wm transient .updateprogress .

................................................................................
	    }
	}

	catch {destroy .downloadprogress}

    }








    namespace export *

}

Changes to stringscan.rb.

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
...
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
...
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
...
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
#Stringscan: grep-like tool, written in Ruby-Tk. (c) 2018 Kevin Walzer/WordTech Communications LLC. License: MIT license.
#encoding: UTF-8


require 'tk'
#find additional Tcl libraries
Tk::AUTO_PATH.list <<= File.dirname(__FILE__)
require_relative 'tkballoonhelp'  
require 'tkextlib/tile'
require 'find'
require 'mime/types'
require 'tkextlib/tkDND'

$platform = Tk.windowingsystem

if $platform == 'aqua'
    require 'tk/tk_mac'
end
if $platform == 'win32'
	TkPackage.require('dde')
	Tk.tk.call('dde servername Stringscan')
end
TkPackage.require('regproc')
TkPackage.require('machelp')
TkPackage.require('softwareupdate')
TkPackage.require('xplat')

if $platform == 'aqua'
    $accelkey = 'Command'
else 
    $accelkey = 'Control'
end

class StringscanApp

    #here we initialize our app class
    def initialize

        $dirname = ""
        $searchterm = ""
        $appname = 'Stringscan'
        $appversion = '2.0'
        Tk.tk_call('machelp::setAppName', $appname, $appversion)
        Tk.tk_call('softwareupdate::setAppName', $appname)
        Tk.tk_call('softwareupdate::setVersion', $appname, $appversion)

        bgerror = Tk.install_cmd(proc{
            |*args|
             Tk::messageBox :type => 'info', :message => args,
    :icon => 'error', :title => 'Error',
    :parent => $root
        })
        Tk.ip_eval("proc bgerror {args} {#{bgerror} $args}")

        if $platform == 'aqua'
            ##map ruby proc to "odoc" Apple Event
            setDir = Tk.install_cmd(proc{
                |*args|
                filename=(args[0]).delete('{}')
                begin
                    if File.directory?(filename)
                        $tbox.configure('state'=>'normal')
                        $tbox.delete('1.0', 'end')
                        $tbox.configure('state'=>'disabled')
................................................................................
                        $lbox.delete(0, 'end')
                        $dirname = filename
                        $direntry.value = filename
                    end
                rescue
                    raise
                end
            })    














            Tk.ip_eval("proc ::tk::mac::OpenDocument {args} {#{setDir} $args}")
            Tk.ip_eval("proc ::tk::mac::ShowHelp {} {#{Tk.install_cmd(proc{showHelp})}}")

			Tk.ip_eval("proc set_search_term {args} {#{set_search_term} $args}")
			Tk.ip_eval("proc set_search_dir {args} {#{set_search_dir} $args}")
            Tk.ip_eval("proc execute_search {} {#{Tk.install_cmd(proc{execute_search})}}")
        install = getInstall
        Tk.tk_call('softwareupdate::setInstall', install)
        end        

        drawgui
		if $platform == 'aqua' 
			Tk.tk_call('darkaqua::checkDarkMode')
    end  


    #core method; here we search for a string in text files within a directory and  display a list of matching files in the listbox 
    def stringgrep
        $root.update
        $file_list = []
        $grep_list = []
        $bottomlabel.configure('text' => "Searching for \"#{$searchterm}\" in #{$dirname}...")
        $lbox.delete(0, 'end')
................................................................................
                if result =~ /#{$searchterm}/ then
                    $lbox.insert('end', "#{i}")
                    f.close
                    end
            	end
            end
        end

        $grep_list = $lbox.get(0, 'end')
        num = $grep_list.count.to_s
        $bottomlabel.configure('text' => "Found #{num} matches for \"#{$searchterm}\" in #{$dirname}")
    rescue
      bgerr "Unable to locate any file matching the search term."
    end

def drawgui 
    begin
        Tk.ip_eval("console hide")
    rescue
      nil
    end

    #image data
    $folderdata = '#define xbm_folder_sans_32_width 32
#define xbm_folder_sans_32_height 32
static char xbm_folder_sans_32_bits[] = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0xFF, 0x03, 0x00, 0x00, 0xFF, 0x03, 0x00, 0x00,
  0x03, 0xFF, 0xFF, 0x3F, 0x03, 0xFF, 0xFF, 0x3F, 0x03, 0xFF, 0xFF, 0xFF,
  0x03, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0xC0,
  0x03, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0xC0,
  0x03, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0xC0,
  0x03, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0xC0,
  0x03, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0xC0,
  0x03, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0xC0,
  0x03, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0xC0,
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, };
'

    $glassdata = '#define xbm_search_32_width 32
#define xbm_search_32_height 32
static char xbm_search_32_bits[] = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00,
  0xC0, 0xFF, 0x00, 0x00, 0xF0, 0xE1, 0x03, 0x00, 0x70, 0x80, 0x07, 0x00,
  0x38, 0x00, 0x07, 0x00, 0x1C, 0x00, 0x06, 0x00, 0x1C, 0x00, 0x0E, 0x00,
  0x1C, 0x00, 0x0C, 0x00, 0x0C, 0x00, 0x0C, 0x00, 0x0C, 0x00, 0x0C, 0x00,
  0x0C, 0x00, 0x0C, 0x00, 0x1C, 0x00, 0x0E, 0x00, 0x18, 0x00, 0x0E, 0x00,
  0x38, 0x00, 0x07, 0x00, 0x70, 0x80, 0x0F, 0x00, 0xE0, 0xE3, 0x3F, 0x00,
  0xE0, 0xFF, 0x7F, 0x00, 0x00, 0xBF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x01,
  0x00, 0x00, 0xFE, 0x03, 0x00, 0x00, 0xFC, 0x07, 0x00, 0x00, 0xF8, 0x0F,
  0x00, 0x00, 0xF0, 0x1F, 0x00, 0x00, 0xE0, 0x3F, 0x00, 0x00, 0xC0, 0x3F,
  0x00, 0x00, 0x80, 0x1F, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x06,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, };
'

    $folderimage = TkBitmapImage.new(:data => $folderdata)
    $glassimage = TkBitmapImage.new(:data => $glassdata)

    $winicondata = 'R0lGODlhQABAAOf/AAABZAUCbwoIfAwPiBERkgkWnAQefYcAABIZnyEVoJIAC4UBPhscq2MLbBQesx4imiEhoSIhqDkcl54KABIxZC8ktS0nsSsqqh40dScsszAtpZsaAC0wmgBAlTQ4OjQykDg5NzE0tKgbNjc1vTg3pDk9v5knazpAqZswCEJIT0BExSRPrwJa0rYxQnM/noo6h0lMukxKx1BSX544f1ZTV1NVUgBj4ylZyMc2L0pQy789AGlLrw9j6jxayllbWFZUyzhd1EZaxTFhwVVWxjBg21BZxSdl1FxXwhto6VNZzjtj1H1TtlxczVVfzc5HSB1y7CRx4z1syK5SZmJh0jpt27laIGttan9iozJ26sRdJtxZAm1yfWBz21V50TWB81B72Eh+43d5dml6oc5lJ3V6fF59snV8hFOC5laE21aD7tRvEmiB0MtwP4CCf3+Ehm6Io8dyafhtAHyHl55+lliN8X2E1oSIkOt1ComLiIKIzpKIoPx3AIOOumuS8oyRk4qRq4WQ22aY9JGUkHqV3pKTn4eUyH2W1n2U8biMhP+CAHma75OatZmbmM2UPo6gsJ6gnf+OF5GnvpSk85ql2H+s+v+YIKWprKeppo+q9Iis/5mtv96jP6irvIyz5ayuq42y8f+jJ4u4/7K0sZ+44rG2uLC3v62146K3+f+sLai6x7W4yqu+1o7E/JzB/7u+u6TC5f+3M63A/77B1LTE1qjI/sPEzpvN//3APMPIysXIxLnH/bTL5OTDlqTR/8HN2svOyr7O/KvU/P/KP7PT/f7LYK7X/7vT/9zRpLXW+cPU5LXX9M/T1vTUU8LX/f7VRrfd/9XY1NPY4cvY/9TY79Ha+cDg9sfe9s3d7djc37/i/87e/P/fTcrh+v3gTcji/8Xk++Pfyt3g3Mrk//nflcjn/tHl+Nzj7PzlbNDn/9zm9eHm6Njq9+Pn/tfr/v/tVuLp8v3vX97t9Nru/+rs/eTu/N/x/ejw+O7z9vH08O71/vP4+/X49Pn7+Pj9//3//P///yH+EUNyZWF0ZWQgd2l0aCBHSU1QACH5BAEKAP8ALAAAAABAAEAAAAj+AP8JHEiwoMGDCBMqXMiwocOHECNKnEixosWLGDNq3Mixo0eCUJ6IHPmRIhRFmGIBk0aN3byX89hJi9XHiJE+p3QBA6Yr1ilFfZDY8AgAgAEjzfwpXcq0qapaTaPOo6ZLEpaMATTEUBIqX1N+YMMy/fUrqlmlxjKluUhgyhQqtPLx20d3X1iwdPHh4/crl96/gPHZXdqOHKUiRz4AkAhAg1sqw+TWnZyXLj9/uX7NDWwX79/Ch6f8CMA4hFsow/opBauPsr5+/fjpy3wX79x9gPnVIxcqiGgBjFW4xZJNn/HXsJP3M37vnj5cuJofv4ePevXOusm18p2EQPApTbD+WFvevLx58/1wLSuvDpt76cn33dvdqscUJt4jCsgBns545uedp889rqhnCRl4CMIIHmG4QQo25tEHhFv5QbQfeIGUMyByA5oH4D2WWPFILtCUaOIvorThBzav5UMOLRNOgcBiEA3wwxRFBHLNgPbYc489+QSZT489muPHIybKMgkgdeRRCCeuoGgFKT9+M0yMF9D4EAFJTBEEJRoSKeaY5rjhCjTYmHLIKcZoow01wEjCBR+i5IKHH+9UMwwRboWgpUMIuNUDJekIOaaYZf4SzjSK0OINOu20U8+k9bRzCheEuCKIHdbs2eefDAEQ6BRKfJLOoUIOaY8buSxKSTH+z3jzKKSRRooOMmnI4UobjiBjg1slgLoQABe4Veo7Pb4Tz7LLvqNsKZe4Ggyssc6KzrXoeJPNMGfYIYoPq/w6hQrCKgSAaW918g6l9dCzzjrpvLsOGdCoQ4ktvUz7zDPZZCPrv9nsO0wXjzCyhbgqkPYQACU8Nso767ArcSmihAMMK/jmS+2+/Qa8bzHFUCLGtyy4FQNwCws3BRSj2CPvy/K68Ys6odhicy8ab7zvMyAH04staCzYQRNT5ICyQwHEMFzLML98TRvQpIOvzTfnDHLPOP9MiRmPUED0DwNYyF94q+Qjz9lN+4JkMyD/THXWcGdNdShiXOLB1xU2ZCP+eF6sYk875ZRTazvytJOKJ+EME3AxOFPt+OOOl3FJDUdM0R1EXOJ4xiz2BO7556lUrPjiPkMOeb69lPFIDUXcN+OWTOBIxyyVfg56tMOQ843Hxfjc+Ns46xuM6jX4JmO5B4nqVhCB+LKO55F+Pgsj0MxKTr8f9x7M9tuDzHEvYjACgvEMIG8QABEIqogv5aBTDjfwc4NtMm3kkk4215Jzvcc7989vNt+gxRYEgQH7TMEC5ivIuYyliGigI37ws4Y1ImgGV2DjGe3IHzp017EOAlB/5OhEG9qwAgP6ySELnIIRBhENclzrGzDkRjWqIUFNXOIX1qBVBje4QRDqj4f+6HjGGwRRgzXEaAQJJAgAVEYFQ1xDhjOEoRRhWA0yWPAZhRsctrZ4rQyKEA8yGESMgoU0JhrCHDNMYxolOENHPMIV1/DG2QZHx8EFwwxE/IMhbuCWhDlEAEpbWSHSobtv0NAaNJwhIqthhku4IhnoqMfZ5kjHwiFDDoIIgxkWUQg+jetoCxkAf6aABT6koxpSLOQUYbgLMlxCFL7ghjzYNcmzoeMVmGzDFszBSU+ezCEEuFF4/kAPNRrTmK18hCce+Q1JUSqDyoiEGxghiBRcg5eFEFcO8qaQzDXBC3+ohyGNiUg2pnEXZsDDJS5hiVTMYhe7WEUqHJEgRiDOCpr+4CUftMlNhChPc5wQpw91WKsNjnMLYcADIxZKTUEoCA9WaIMnoPELODhhDn/ggVt+UICGAIABbinCGTjxDnNE46S1qIUsVMGJlj6lFtEwBz3MYA5CbMEKYchpGKxAgy2QggZvDAc4xoEDPdiAaEl43bAysDwvnOGpYIjqU5/6hapatapdEEIXtrrVKHi1C2sI6xUQ4YlfgOMcm5ACD4jGhAQk8R8pLEIQetCDINj1rnetK17zOle6+rUHNwjsC3hxjGOcoxvwGMMOiDaF8oUKXW6JrGQnG9khDAEGJ+AAB2BgWcq6ZQlsIMYtYCEMdzCjBZWbggPeKirOMoEJlr3rLAwwewISaJYEDyCABiAAgQtkwAEWyMAFIJBb25LgBLMdggm0sIdKwMIZ7tjEDO6z2oYIIAK85S0ChhuBDFjAAd5lAAMSgAAEEIAAAxBAUQIggAEQoLzdtYAFsCsBEcQhEaC4xTbgkQUXaKC6oQqAe8+LXgEYuCgIRnCoEmzgATRAB3uABCpKywwRVIAAby1JQhSghUQ4F7qbWECGNZyQCdw3v/utAokvAmEJU3gCK67IBDr8YXc0IsYV2cAe8HuLbmwCxxVpMSpugQIgU+QAWriDGops5CMf4ABNjrKUp0zlKlv5yljOMkMCAgA7'
	
    $winicon = TkPhotoImage.new(:data => $winicondata)
    #initialize variables for entry
    $dirname = TkVariable.new
    $searchterm = TkVariable.new
    #top window and frame
    Tk::TkDND::DND
    $root = TkRoot.new {
        title "Stringscan"
    }

    if $platform != 'aqua' 
        $root.iconphoto_default($winicon)
    end
        $root.withdraw
    #menu
    $menubar = TkMenu.new($root)
    TkOption.add '*tearOff', 0
................................................................................
        $appmenu.add :separator
    end
    $searchmenu = TkMenu.new($menubar)
    $menubar.add :cascade, :menu => $searchmenu, :label => 'Search'
    $searchmenu.add :command, :label => 'Choose Directory...', 'accelerator' => "#{$accelkey}+O", :command => proc{choosedir}
    $searchmenu.add :command, :label => 'Run Search', 'accelerator' => "#{$accelkey}+R", :command => proc{stringgrep}
    if $platform == 'win32'
        $searchmenu.add :command, :label => "Exit", :command=>proc{Tk.tk_call('regproc::makePitch; exit'}, 'accelerator' => "#{$accelkey}+Q"
    end
    $helpmenu = TkSysMenu_Help.new($menubar)
    $menubar.add :cascade, :menu => $helpmenu, :label => 'Help'
    if $platform != 'aqua'
        $helpmenu.add :command, :label=>'Stringscan Help', :command=>proc{
            Tk.tk_call('machelp::userhelp')
        }
................................................................................
    end
    $helpmenu.add :command, :label=>'Contact Code by Kevin', :command=>proc{
        Tk.tk_call('xplat::launch', 'mailto:kw@codebykevin.com?subject=Stringscan')
    }
    $helpmenu.add :command, :label=>'Web Site', :command=>proc{
        Tk.tk_call('xplat::launch', 'https://www.codebykevin.com/stringscan.html')
    }

    if $platform != 'aqua'
        $helpmenu.add :command, :label => 'About Stringscan', :command=> proc{aboutWindow}
        $helpmenu.add :command, :label=>'Check for Updates', :command=>proc{checkUpdate}
    end
    $root['menu'] = $menubar
    $mainframe = Tk::Tile::Frame.new($root).pack('side' => 'top','fill' => 'both','expand' => 'yes')
    #button frame and buttons
................................................................................
        relief 'sunken'
        wrap 'word'
        highlightcolor 'SlateGray3'
        state 'disabled'
    }.pack('side'=>'left','fill' => 'both', 'expand' => 'yes')
    $tscrollframe = Tk::Frame.new($bottomrightframe).pack('side'=>'right','fill' => 'y', 'expand' => 'no')
    $tscroll = Tk::Tile::Scrollbar.new($tscrollframe).pack('side'=>'right','fill' => 'y', 'expand' => 'no')
    $tbox.yscrollbar($tscroll)
    
    $bottomframe.add($bottomleftframe)
    $bottomframe.add($bottomrightframe)
    
    $labelframe = Tk::Tile::Frame.new($root).pack('side'=>'bottom', 'fill'=>'both', 'expand' => 'no')
    $bottomlabel = Tk::Tile::Label.new($labelframe) {text "No data displayed"}.pack('side' => 'left','fill' => 'both','expand' => 'no')
    $bottomsep = Tk::Tile::Separator.new($root) { orient 'horizontal' }.pack('side'=> 'bottom', 'fill'=>'both')
    $root.bind("#{$accelkey}-Q", proc{Tk.tk_call('regproc::makePitch; exit'})
    $root.bind("#{$accelkey}-q", proc{Tk.tk_call('regproc::makePitch; exit'})
    $root.bind("#{$accelkey}-O", proc{choosedir})
    $root.bind("#{$accelkey}-o", proc{choosedir})
    $root.bind("#{$accelkey}-R", proc{stringgrep})
    $root.bind("#{$accelkey}-r", proc{stringgrep})

    $root.update
    $root.deiconify

end


#select search directory
def choosedir
    $tbox.configure('state'=>'normal')
    $tbox.delete('1.0', 'end')
    $tbox.configure('state'=>'disabled')
    $lbox.delete(0, 'end')
    $dirname = Tk::chooseDirectory('initialdir'=>Dir.home, 'parent'=>$root)
    $direntry.value = $dirname
end

#read file for display in text widget
def highlighttext(file)
    $tbox.configure('state'=>'normal')
    $tbox.delete('1.0', 'end') 
    f = open(file, 'rb')
    while(!f.eof?)
        $tbox.insert('end', f.read(1000))
    end
    f.close
    $tbox.configure('state'=>'disabled')
    $tbox.tag_configure('search', :background=>'yellow')
    sethighlight
end

#set highlight color for search term
def sethighlight
    $tbox.configure('state'=>'normal')
    $tbox.tag_remove('search', '0.0', 'end')
    return if $searchterm == ""
    cur = '1.0'
    loop {
................................................................................
        cur, len = $tbox.search_with_length("#{$searchterm}", cur, 'end')
        break if cur == ""
        $tbox.tag_add('search', cur, "#{cur} + #{len} char")
        cur = $tbox.index("#{cur} + #{len} char")
    }
    $tbox.configure('state'=>'disabled')
end

#about window for app
def aboutWindow
    Tk::messageBox :type => 'ok',
    :message => 'Stringscan: Text Search Tool',
    :icon => 'info', :title => 'About Stringscan',
    :detail  => "Version 1.2\n(c) 2018 WordTech Communications LLC",
    :parent => $root
end

#check version of installed software
def checkUpdate 
    Tk.tk_call('softwareupdate::setIcon', $winicon)
    Tk.tk_call('softwareupdate::checkVersion', $appname, $appversion)
end


#scripting commands
def set_search_term (term)
	$searchterm=term
	return #searchterm
end

def set_search_dir (dir)
	$dirname = dir
	return $dirname
end

def execute_search
	stringgreb
end


#user help
def showHelp
    Tk.tk_call('machelp::userhelp')
end


#get current installation path on macOS
def getInstall
    return File.dirname(File.dirname(File.dirname(__FILE__)))
end
#end of app class
end

#run app
app = StringscanApp.new
Tk.mainloop

|

<
<






<

<

<




|
|





<





<

<


<







<


|




|
<
<
|







 







|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
<
>
|
|
|


<
>



|
<
>







 







<




|

<






<


|
|
|
|
|
|
|
|
|
|
|
|
|
|
<

|
|
|
|
|
|
|
|
|
|
|
|
|
|
<


<











<







 







|







 







<







 







|
<

|
<



|
|




<


<

<
<









<













<







 







<








<





<
<





<




<



<
<




<
<






<



<
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
...
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
...
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
...
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

#Stringscan: grep-like tool, written in Ruby-Tk. (c) 2019 Kevin Walzer/WordTech Communications LLC. License: MIT license.
#encoding: UTF-8


require 'tk'
#find additional Tcl libraries
Tk::AUTO_PATH.list <<= File.dirname(__FILE__)
require_relative 'tkballoonhelp'  
require 'tkextlib/tile'
require 'find'

require 'tkextlib/tkDND'

$platform = Tk.windowingsystem

if $platform == 'aqua'
    require 'tk/tk_mac'
end
if $platform == 'win32'
	require 'tk/winpkg'
	Tk::WinDDE.servername('Stringscan')
end
TkPackage.require('regproc')
TkPackage.require('machelp')
TkPackage.require('softwareupdate')
TkPackage.require('xplat')

if $platform == 'aqua'
    $accelkey = 'Command'
else 
    $accelkey = 'Control'
end

class StringscanApp

    #here we initialize our app class
    def initialize

        $dirname = ""
        $searchterm = ""
        $appname = 'Stringscan'
        $appversion = '2.0'
        Tk.tk_call('machelp::setAppName', $appname, $appversion)
        Tk.tk_call('softwareupdate::setAppName', $appname)
        Tk.tk_call('softwareupdate::setVersion', $appname, $appversion)

        bgerror = Tk.install_cmd(proc{
            |*args|
             Tk::messageBox :type => 'ok', :message => args,
    :icon => 'error', :title => 'Error',
    :parent => $root
        })
        Tk.ip_eval("proc bgerror {args} {#{bgerror} $args}")
		


        set_dir = Tk.install_cmd(proc{
                |*args|
                filename=(args[0]).delete('{}')
                begin
                    if File.directory?(filename)
                        $tbox.configure('state'=>'normal')
                        $tbox.delete('1.0', 'end')
                        $tbox.configure('state'=>'disabled')
................................................................................
                        $lbox.delete(0, 'end')
                        $dirname = filename
                        $direntry.value = filename
                    end
                rescue
                    raise
                end
            })   			
        set_search_term = Tk.install_cmd(proc{
			|*args|
				searchname=(args[0])
				$termentry.insert('end', searchname)
				$searchterm = searchname
				$termentry.value = searchname
			})											
        execute_search = Tk.install_cmd(proc{
			|*args|
				args = ""
				stringgrep
			})
				
        if $platform == 'aqua'
		  Tk.ip_eval("proc ::tk::mac::OpenDocument {args} {#{set_dir} $args}")

		end
		Tk.ip_eval("proc set_search_term {args} {#{set_search_term} $args}")
		Tk.ip_eval("proc set_search_dir {args} {#{set_dir} $args}")
        Tk.ip_eval("proc execute_search {}  {#{execute_search}}")
        install = getInstall
        Tk.tk_call('softwareupdate::setInstall', install)

      
        drawgui
		if $platform == 'aqua' 
			Tk.tk_call('darkaqua::checkDarkMode')
		end  

	end
    #core method; here we search for a string in text files within a directory and  display a list of matching files in the listbox 
    def stringgrep
        $root.update
        $file_list = []
        $grep_list = []
        $bottomlabel.configure('text' => "Searching for \"#{$searchterm}\" in #{$dirname}...")
        $lbox.delete(0, 'end')
................................................................................
                if result =~ /#{$searchterm}/ then
                    $lbox.insert('end', "#{i}")
                    f.close
                    end
            	end
            end
        end

        $grep_list = $lbox.get(0, 'end')
        num = $grep_list.count.to_s
        $bottomlabel.configure('text' => "Found #{num} matches for \"#{$searchterm}\" in #{$dirname}")
    rescue
      bgerror "Unable to locate any file matching the search term."		
    end

def drawgui 
    begin
        Tk.ip_eval("console hide")
    rescue
      nil
    end

    #image data
    $folderdata = '#define xbm_folder_sans_32_width 32
	#define xbm_folder_sans_32_height 32
	static char xbm_folder_sans_32_bits[] = {
	  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	  0x00, 0x00, 0x00, 0x00, 0xFF, 0x03, 0x00, 0x00, 0xFF, 0x03, 0x00, 0x00,
	  0x03, 0xFF, 0xFF, 0x3F, 0x03, 0xFF, 0xFF, 0x3F, 0x03, 0xFF, 0xFF, 0xFF,
	  0x03, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0xC0,
	  0x03, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0xC0,
	  0x03, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0xC0,
	  0x03, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0xC0,
	  0x03, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0xC0,
	  0x03, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0xC0,
	  0x03, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0xC0,
	  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, };
	'

    $glassdata = '#define xbm_search_32_width 32
	#define xbm_search_32_height 32
	static char xbm_search_32_bits[] = {
	  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00,
	  0xC0, 0xFF, 0x00, 0x00, 0xF0, 0xE1, 0x03, 0x00, 0x70, 0x80, 0x07, 0x00,
	  0x38, 0x00, 0x07, 0x00, 0x1C, 0x00, 0x06, 0x00, 0x1C, 0x00, 0x0E, 0x00,
	  0x1C, 0x00, 0x0C, 0x00, 0x0C, 0x00, 0x0C, 0x00, 0x0C, 0x00, 0x0C, 0x00,
	  0x0C, 0x00, 0x0C, 0x00, 0x1C, 0x00, 0x0E, 0x00, 0x18, 0x00, 0x0E, 0x00,
	  0x38, 0x00, 0x07, 0x00, 0x70, 0x80, 0x0F, 0x00, 0xE0, 0xE3, 0x3F, 0x00,
	  0xE0, 0xFF, 0x7F, 0x00, 0x00, 0xBF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x01,
	  0x00, 0x00, 0xFE, 0x03, 0x00, 0x00, 0xFC, 0x07, 0x00, 0x00, 0xF8, 0x0F,
	  0x00, 0x00, 0xF0, 0x1F, 0x00, 0x00, 0xE0, 0x3F, 0x00, 0x00, 0xC0, 0x3F,
	  0x00, 0x00, 0x80, 0x1F, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x06,
	  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, };
	'

    $folderimage = TkBitmapImage.new(:data => $folderdata)
    $glassimage = TkBitmapImage.new(:data => $glassdata)

    $winicondata = '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'
	
    $winicon = TkPhotoImage.new(:data => $winicondata)
    #initialize variables for entry
    $dirname = TkVariable.new
    $searchterm = TkVariable.new
    #top window and frame
    Tk::TkDND::DND
    $root = TkRoot.new {
        title "Stringscan"
    }

    if $platform != 'aqua' 
        $root.iconphoto_default($winicon)
    end
        $root.withdraw
    #menu
    $menubar = TkMenu.new($root)
    TkOption.add '*tearOff', 0
................................................................................
        $appmenu.add :separator
    end
    $searchmenu = TkMenu.new($menubar)
    $menubar.add :cascade, :menu => $searchmenu, :label => 'Search'
    $searchmenu.add :command, :label => 'Choose Directory...', 'accelerator' => "#{$accelkey}+O", :command => proc{choosedir}
    $searchmenu.add :command, :label => 'Run Search', 'accelerator' => "#{$accelkey}+R", :command => proc{stringgrep}
    if $platform == 'win32'
        $searchmenu.add :command, :label => "Exit", :command=>proc{Tk.tk_call('regproc::makePitch; exit')}, 'accelerator' => "#{$accelkey}+Q"
    end
    $helpmenu = TkSysMenu_Help.new($menubar)
    $menubar.add :cascade, :menu => $helpmenu, :label => 'Help'
    if $platform != 'aqua'
        $helpmenu.add :command, :label=>'Stringscan Help', :command=>proc{
            Tk.tk_call('machelp::userhelp')
        }
................................................................................
    end
    $helpmenu.add :command, :label=>'Contact Code by Kevin', :command=>proc{
        Tk.tk_call('xplat::launch', 'mailto:kw@codebykevin.com?subject=Stringscan')
    }
    $helpmenu.add :command, :label=>'Web Site', :command=>proc{
        Tk.tk_call('xplat::launch', 'https://www.codebykevin.com/stringscan.html')
    }

    if $platform != 'aqua'
        $helpmenu.add :command, :label => 'About Stringscan', :command=> proc{aboutWindow}
        $helpmenu.add :command, :label=>'Check for Updates', :command=>proc{checkUpdate}
    end
    $root['menu'] = $menubar
    $mainframe = Tk::Tile::Frame.new($root).pack('side' => 'top','fill' => 'both','expand' => 'yes')
    #button frame and buttons
................................................................................
        relief 'sunken'
        wrap 'word'
        highlightcolor 'SlateGray3'
        state 'disabled'
    }.pack('side'=>'left','fill' => 'both', 'expand' => 'yes')
    $tscrollframe = Tk::Frame.new($bottomrightframe).pack('side'=>'right','fill' => 'y', 'expand' => 'no')
    $tscroll = Tk::Tile::Scrollbar.new($tscrollframe).pack('side'=>'right','fill' => 'y', 'expand' => 'no')
    $tbox.yscrollbar($tscroll)   

    $bottomframe.add($bottomleftframe)
    $bottomframe.add($bottomrightframe)   

    $labelframe = Tk::Tile::Frame.new($root).pack('side'=>'bottom', 'fill'=>'both', 'expand' => 'no')
    $bottomlabel = Tk::Tile::Label.new($labelframe) {text "No data displayed"}.pack('side' => 'left','fill' => 'both','expand' => 'no')
    $bottomsep = Tk::Tile::Separator.new($root) { orient 'horizontal' }.pack('side'=> 'bottom', 'fill'=>'both')
    $root.bind("#{$accelkey}-Q", proc{Tk.tk_call('regproc::makePitch; exit')})
    $root.bind("#{$accelkey}-q", proc{Tk.tk_call('regproc::makePitch; exit')})
    $root.bind("#{$accelkey}-O", proc{choosedir})
    $root.bind("#{$accelkey}-o", proc{choosedir})
    $root.bind("#{$accelkey}-R", proc{stringgrep})
    $root.bind("#{$accelkey}-r", proc{stringgrep})

    $root.update
    $root.deiconify

end


#select search directory
def choosedir
    $tbox.configure('state'=>'normal')
    $tbox.delete('1.0', 'end')
    $tbox.configure('state'=>'disabled')
    $lbox.delete(0, 'end')
    $dirname = Tk::chooseDirectory('initialdir'=>Dir.home, 'parent'=>$root)
    $direntry.value = $dirname
end

#read file for display in text widget
def highlighttext(file)
    $tbox.configure('state'=>'normal')
    $tbox.delete('1.0', 'end') 
    f = open(file, 'rb')
    while(!f.eof?)
        $tbox.insert('end', f.read(1000))
    end
    f.close
    $tbox.configure('state'=>'disabled')
    $tbox.tag_configure('search', :background=>'yellow')
    sethighlight
end

#set highlight color for search term
def sethighlight
    $tbox.configure('state'=>'normal')
    $tbox.tag_remove('search', '0.0', 'end')
    return if $searchterm == ""
    cur = '1.0'
    loop {
................................................................................
        cur, len = $tbox.search_with_length("#{$searchterm}", cur, 'end')
        break if cur == ""
        $tbox.tag_add('search', cur, "#{cur} + #{len} char")
        cur = $tbox.index("#{cur} + #{len} char")
    }
    $tbox.configure('state'=>'disabled')
end

#about window for app
def aboutWindow
    Tk::messageBox :type => 'ok',
    :message => 'Stringscan: Text Search Tool',
    :icon => 'info', :title => 'About Stringscan',
    :detail  => "Version 1.2\n(c) 2018 WordTech Communications LLC",
    :parent => $root
end

#check version of installed software
def checkUpdate 
    Tk.tk_call('softwareupdate::setIcon', $winicon)
    Tk.tk_call('softwareupdate::checkVersion', $appname, $appversion)
end


#scripting commands
def set_search_term (term)
	$searchterm=term
	return #searchterm
end

def set_search_dir (dir)
	$dirname = dir
	return $dirname
end

def execute_search
	stringgreb
end


#user help
def showHelp
    Tk.tk_call('machelp::userhelp')
end


#get current installation path on macOS
def getInstall
    return File.dirname(File.dirname(File.dirname(__FILE__)))
end
#end of app class
end

#run app
app = StringscanApp.new
Tk.mainloop