-
Notifications
You must be signed in to change notification settings - Fork 3
/
tag-browser.rkt
435 lines (390 loc) · 15.3 KB
/
tag-browser.rkt
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
#lang racket/base
; tag-browser.rkt
; browse taglist and images, modify tags if necessary
(require racket/class
racket/gui/base
racket/string
(only-in srfi/13 string-contains-ci)
"base.rkt"
"config.rkt"
"db.rkt"
"embed.rkt"
"files.rkt"
"thumbnails.rkt")
(provide browser-frame show-tag-browser)
(define browser-frame
(new frame%
[label "Ivy Tag Browser"]
[width 800]
[height 500]))
; set the icon for the frame
(unless macosx?
(void (send browser-frame set-icon logo-bmp)))
; begin menu bar definitions
(define browser-menu-bar
(new menu-bar%
[parent browser-frame]))
(define browser-menu-bar-file
(new menu%
[parent browser-menu-bar]
[label "&File"]))
(define browser-menu-bar-edit
(new menu%
[parent browser-menu-bar]
[label "&Edit"]))
(define browser-menu-bar-file-close
(new menu-item%
[parent browser-menu-bar-file]
[label "Close"]
[shortcut #\W]
[callback (λ (button evt)
(send browser-frame show #f))]))
(define (err-mbox)
(message-box "Ivy Tag Browser - Error"
"You must first select an item from the list."
#f
'(ok stop)))
(define browser-menu-bar-edit-rename
(new menu-item%
[parent browser-menu-bar-edit]
[label "Rename Tag"]
[help-string "Rename tag and refresh browser."]
[callback (λ (button evt)
(define sel (send tag-lbox get-selection))
(cond [(number? sel)
(define tag-label (send tag-lbox get-string sel))
(send dialog-tfield set-value tag-label)
(send dialog-tfield refresh)
(send dialog-tfield focus)
(send rename-dialog center 'both)
(send rename-dialog show #t)]
[else (err-mbox)]))]))
(define rename-dialog
(new dialog%
[label "Ivy Tag Browser - Rename"]
[width 400]
[height 100]))
(define dialog-hpanel
(new horizontal-panel% [parent rename-dialog]))
(define (rename-ok-callback tfield)
(define sel (send tag-lbox get-selection))
(define old-tag-label (send tag-lbox get-string sel))
; scrub the new tag label of any commas
(define new-tag-label (string-replace (send tfield get-value) "," ""))
(cond [(string-null? new-tag-label)
(message-box "Ivy Tag Browser - Error"
"The new tag must not be empty!"
#f
'(ok stop))]
[else
(printf "Changing tag label from ~v to ~v\n" old-tag-label new-tag-label)
; get the image list from the old tag
(define img-lst (map path->string (search-db-exact 'or (list old-tag-label))))
(for ([img (in-list img-lst)])
(add-tags! img (list new-tag-label))
(del-tags! img (list old-tag-label))
(when (embed-support? img)
(add-embed-tags! img (list new-tag-label))
(del-embed-tags! img (list old-tag-label))))
(send rename-dialog show #f)
(update-tag-browser)]))
(define dialog-tfield
(new text-field%
[parent dialog-hpanel]
[label ""]
[callback (λ (tfield evt)
(when (eq? (send evt get-event-type) 'text-field-enter)
(rename-ok-callback tfield)))]))
(define dialog-button
(new button%
[parent dialog-hpanel]
[label "Ok"]
[callback (λ (button evt)
(rename-ok-callback dialog-tfield))]))
(define browser-menu-bar-edit-delete
(new menu-item%
[parent browser-menu-bar-edit]
[label "Delete Tag"]
[help-string "Delete tag and refresh browser."]
[callback
(λ (button evt)
(define sel (send tag-lbox get-selection))
(cond [(number? sel)
(define tag-label (send tag-lbox get-string sel))
; make certain we want to delete this tag
(define ok-cancel
(message-box "Ivy Tag Browser - Delete Tag"
"Are you sure you want to delete this tag?"
#f
'(ok-cancel caution)))
(when (eq? ok-cancel 'ok)
(define imagelist
(map path->string (search-db-exact 'or (list tag-label))))
(for ([img (in-list imagelist)])
(del-tags! img (list tag-label))
(when (embed-support? img)
(del-embed-tags! img (list tag-label))))
(update-tag-browser))]
[else (err-mbox)]))]))
(define browser-menu-bar-separator
(new separator-menu-item%
[parent browser-menu-bar-edit]))
(define browser-menu-bar-edit-edit
(new menu-item%
[parent browser-menu-bar-edit]
[label "Edit Image Tags"]
[help-string "Edit the taglist of the selected image."]
[callback
(λ (button evt)
(define sel (send img-lbox get-selection))
(cond [(number? sel)
(define img-label (send img-lbox get-data sel))
; 15 the tallest any column can be
(define tag-grid (grid-list (image-taglist img-label) 15))
; remove any children vpanel might have
(remove-children edit-tags-check-hpanel (send edit-tags-check-hpanel get-children))
; loop over the tag sections
(for ([tag-section (in-list tag-grid)])
(define vpanel-section
(new vertical-panel%
[parent edit-tags-check-hpanel]
[alignment '(left top)]))
; add check boxes to the vpanel
(for ([tag (in-list tag-section)])
(new check-box%
[label tag]
[parent vpanel-section]
[value #t]
[callback
(λ (button evt)
(cond [(send button get-value)
(add-tags! img-label tag)
(when (embed-support? img-label)
(add-embed-tags! img-label tag))]
[else
(del-tags! img-label (list tag))
(when (embed-support? img-label)
(del-embed-tags! img-label tag))]))])))
(send edit-tags-dialog center 'both)
(send edit-tags-dialog show #t)]
[else (err-mbox)]))]))
(define edit-tags-dialog
(new dialog%
[label "Ivy Tag Browser - Edit Tags"]
[width 200]
[height 400]))
(define edit-tags-check-hpanel
(new horizontal-panel%
[parent edit-tags-dialog]))
(define edit-tags-new-hpanel
(new horizontal-panel%
[parent edit-tags-dialog]))
(define (edit-tags-callback lbox tfield)
(define sel (send lbox get-selection))
(define img (send lbox get-data sel))
(define tags (send tfield get-value))
; empty tag string means add no new tags
(unless (string-null? tags)
; turn the string of tag(s) into a list then sort it
(define tag-lst (sort (tfield->list tfield) string<?))
(add-tags! img tag-lst)
(when (embed-support? img)
(add-embed-tags! img tag-lst))
(send tfield set-value ""))
(send edit-tags-dialog show #f)
(update-tag-browser))
(define edit-tags-tfield
(new text-field%
[parent edit-tags-new-hpanel]
[label "New tag(s): "]
[callback (λ (tfield evt)
(when (eq? (send evt get-event-type) 'text-field-enter)
(edit-tags-callback img-lbox tfield)))]))
(define edit-tags-button
(new button%
[parent edit-tags-new-hpanel]
[label "Ok"]
[callback (λ (button evt)
(edit-tags-callback img-lbox edit-tags-tfield))]))
(define browser-menu-bar-copy-separator
(new separator-menu-item%
[parent browser-menu-bar-edit]))
(define browser-menu-bar-edit-copy-path
(new menu-item%
[parent browser-menu-bar-edit]
[label "Copy Selected Image Path"]
[shortcut-prefix (if macosx?
(list 'cmd 'shift)
(list 'ctl 'shift))]
[shortcut #\C]
[help-string "Copy the current image's path"]
[callback (λ (i e)
(define selected-path (send img-lbox get-string-selection))
(when selected-path
(send the-clipboard set-clipboard-string
selected-path
(current-seconds))))]))
; end menu bar definitions
; begin tag filtering/search definitions
(define use-regex? (hash-ref config-hash 'browse-regex?))
(define (filter-query tfield)
(or (send (send tfield get-editor) get-text)
(if use-regex?
".*"
"")))
(define tag-filter-layout
(new horizontal-panel%
[parent browser-frame]
[stretchable-height #f]))
(define (filter-tags filter-str regex)
(λ (tag)
(if use-regex?
(regexp-match filter-str tag)
(string-contains-ci tag filter-str))))
(define tag-filter-tfield
(new text-field%
[parent tag-filter-layout]
[label "Filter Tags"]
[callback (λ (tfield evt)
(update-tag-browser (filter-query tfield)))]))
(define tag-filter-regex-checkbox
(new check-box%
[parent tag-filter-layout]
[label "Regex"]
[value use-regex?]
[callback (λ (chk evt)
(set! use-regex? (not use-regex?))
(hash-set! config-hash 'browse-regex? use-regex?)
(save-config)
(update-tag-browser))]))
; end tag filtering/search definitions
(define browser-hpanel
(new horizontal-panel%
[parent browser-frame]))
(define tag-vpanel
(new vertical-panel%
[parent browser-hpanel]))
(define tag-lbox
(new list-box%
[label "Tag List"]
[parent tag-vpanel]
[style '(single vertical-label)]
[choices (list "")]
[callback (λ (lbox evt)
(define sel (send lbox get-selection))
(define tag-label (if sel (send lbox get-string sel) ""))
(define img-lst (map path->string (search-db-exact 'or (list tag-label))))
(send img-lbox set-label (format "Image List (~a)" (length img-lst)))
(send img-lbox clear)
(remove-children thumb-vpanel (send thumb-vpanel get-children))
; add paths to the image lbox, truncating if necessary
(send img-lbox set
(for/list ([img (in-list img-lst)])
(string-truncate img +label-max+)))
; add full path string data to the entry
(for ([img (in-list img-lst)]
[n (in-naturals)])
(send img-lbox set-data n img))
; double click to load the tag category
(when (eq? (send evt get-event-type) 'list-box-dclick)
(define img-path (string->path (send img-lbox get-data 0)))
(define-values (base name dir?) (split-path img-path))
(image-dir base)
; populate pfs with the images in the tag category
(define lst
(for/list ([n (in-range (send img-lbox get-number))])
(string->path (send img-lbox get-data n))))
(pfs lst)
(send (ivy-tag-tfield) set-default-background)
(image-path img-path)
(load-image img-path)))]))
(define img-vpanel
(new vertical-panel%
[parent browser-hpanel]))
(define img-lbox
(new list-box%
[label "Image List "]
[parent img-vpanel]
[style '(single vertical-label)]
[choices (list "")]
[callback
(λ (lbox evt)
(define sel (send lbox get-selection))
; if sel is #f, make img-str ""
(define img-str (if sel (send lbox get-string sel) ""))
; do nothing if the user clicks on the placeholder string
(unless (string-null? img-str)
(define img-path (string->path img-str))
; set new thumbnail data
; get the thumbnail path
(define thumb-path (path->md5 img-str))
; make certain the thumbnail exists
(unless (file-exists? thumb-path)
(generate-thumbnails (list img-str)))
(send thumb-bmp load-file thumb-path)
; remove old thumb-button
(remove-children thumb-vpanel (send thumb-vpanel get-children))
; generate new thumb-button
(new button%
[parent thumb-vpanel]
[label thumb-bmp]
[callback
(λ (button evt)
(define-values (base name dir?) (split-path img-path))
(image-dir base)
; populate pfs with the images in the tag category
(define lst
(for/list ([img (in-range (send lbox get-number))])
(string->path (send lbox get-data img))))
(pfs lst)
(send (ivy-tag-tfield) set-default-background)
(image-path img-path)
(load-image img-path))])))]))
(define thumb-vpanel
(new vertical-panel%
[parent browser-hpanel]
[alignment '(center center)]
[stretchable-width #f]))
(define thumb-bmp (make-object bitmap% 128 128))
(define updating-frame
(new frame%
[label "Ivy Tag Browser"]
[width 200]
[height 40]
[style '(float)]))
(define updating-message
(new message%
[parent updating-frame]
[label "Updating Tag Browser..."]))
(define (update-tag-browser [filter-str (filter-query tag-filter-tfield)])
(send updating-frame center 'both)
(send updating-frame show #t)
; remove the "" we put as a placeholder
(send tag-lbox clear)
(send img-lbox clear)
; remove old thumb-button
(remove-children thumb-vpanel (send thumb-vpanel get-children))
; get every tag in the database
(define tag-labels (sort
(filter (filter-tags filter-str use-regex?)
(table-column 'tags 'Tag_Label))
string<?))
; add them to the list-box, truncating if necessary
(send tag-lbox set
(for/list ([img (in-list tag-labels)])
(string-truncate img +label-max+)))
; set data for the unmodified label string
(for ([img (in-list tag-labels)]
[n (in-naturals)])
(send tag-lbox set-data n img))
(send updating-frame show #f))
(define (show-tag-browser)
(update-tag-browser)
(unless (send browser-frame is-shown?)
(send browser-frame center 'both)
(send tag-filter-tfield focus)
; select filter contents on-show
(let ([txt (send tag-filter-tfield get-editor)])
(send txt move-position 'home)
(send txt move-position 'end #t))
(send browser-frame show #t)))