tkxxs 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +8 -8
- data/CONTRIBUTORS +2 -1
- data/History.txt +10 -0
- data/Manifest.txt +1 -1
- data/{README.txt → README.rdoc} +36 -4
- data/ext/tkballoonhelp.rb +332 -332
- data/ext/tkballoonhelp_ORIGINAL.rb +208 -208
- data/lib/tkxxs.rb +710 -708
- data/lib/tkxxs/conf.rb +259 -259
- data/lib/tkxxs/tkxxs_classes.rb +1567 -1571
- data/lib/tkxxs/version.rb +1 -1
- data/rdoc_developers_hanna.bat +14 -14
- data/rdoc_users_hanna.bat +16 -16
- data/samples/big_example.rb +521 -521
- metadata +12 -13
- data/.gemtest +0 -0
data/lib/tkxxs/tkxxs_classes.rb
CHANGED
@@ -1,1571 +1,1567 @@
|
|
1
|
-
# encoding: Windows-1252 :encoding=Windows-1252:
|
2
|
-
# Copyright (c) Axel Friedrich 2010-2014
|
3
|
-
STDOUT.sync = true
|
4
|
-
STDERR.sync = true
|
5
|
-
|
6
|
-
##########################################################################
|
7
|
-
##########################################################################
|
8
|
-
class Hash
|
9
|
-
|
10
|
-
##################################################################
|
11
|
-
# Somewhat deeper dup
|
12
|
-
def dup2!( )
|
13
|
-
self.each_pair {|k,v|
|
14
|
-
self[k] = v.dup if v.class == String
|
15
|
-
}
|
16
|
-
self
|
17
|
-
end # dup2!
|
18
|
-
|
19
|
-
end # class Hash
|
20
|
-
|
21
|
-
##########################################################################
|
22
|
-
##########################################################################
|
23
|
-
# Creates dialogs for TKXXS
|
24
|
-
module TKXXS_CLASSES
|
25
|
-
|
26
|
-
##################################################################
|
27
|
-
# Purpose:
|
28
|
-
# Interpret last argument always as +Hash#, independent from the
|
29
|
-
# number of arguments:
|
30
|
-
# * Searches for the first argument which is a Hash, from right to
|
31
|
-
# left, starting with the last argument. (TODO: Why not simply
|
32
|
-
# detect if the last argument is a Hash?)
|
33
|
-
# * If Hash exists:
|
34
|
-
# ** Move the hash to the last element of args.
|
35
|
-
# ** Set the element, where the Hash comes from to nil, if it was
|
36
|
-
# not the last element.
|
37
|
-
# ** All other elements remain unchanged.
|
38
|
-
# * If no Hash exist:
|
39
|
-
# ** Sets the last element of args to {}
|
40
|
-
# ** All other elements remain unchanged.
|
41
|
-
#
|
42
|
-
# *Returns:* The rearranged args.
|
43
|
-
#
|
44
|
-
# Example:
|
45
|
-
# def m1( c=nil,b=nil,a=nil,h=nil )
|
46
|
-
# c, b, a, hash = args_1(c,b,a,h)
|
47
|
-
# p [c, b, a, hash]
|
48
|
-
#
|
49
|
-
# hash = {:a=>'aa',
|
50
|
-
# :b=>'bb',
|
51
|
-
# :c=>'cc'
|
52
|
-
# }.merge(hash)
|
53
|
-
#
|
54
|
-
# c = hash[:c] unless c
|
55
|
-
# b = hash[:b] unless b
|
56
|
-
# a = hash[:a] unless a
|
57
|
-
# nil
|
58
|
-
# end # m1
|
59
|
-
#
|
60
|
-
# m1(:c,:b,:a, {:h=>'hh'}) # => [:c, :b, :a, {:h=>"hh"}]
|
61
|
-
# m1(:c, {:h=>'hh'}) # => [:c, nil, nil, {:h=>"hh"}]
|
62
|
-
# m1(:c) # => [:c, nil, nil, {}]
|
63
|
-
# m1() # => [nil, nil, nil, {}]
|
64
|
-
#
|
65
|
-
def TKXXS_CLASSES.args_1( *args )
|
66
|
-
args = args.reverse
|
67
|
-
args.each_with_index {|arg, i|
|
68
|
-
if arg.class == Hash
|
69
|
-
args[i] = nil
|
70
|
-
args[0] = arg
|
71
|
-
break
|
72
|
-
end
|
73
|
-
}
|
74
|
-
args[0] =
|
75
|
-
unless args[0]
|
76
|
-
{}
|
77
|
-
else
|
78
|
-
## Marshal.load(Marshal.dump( args[0] ) ) # Doesn't work because of Proc
|
79
|
-
args[0].dup2! # args[0] must always be a Hash
|
80
|
-
end
|
81
|
-
args = args.reverse
|
82
|
-
args
|
83
|
-
end # TKXXS_CLASSES.args_1
|
84
|
-
|
85
|
-
##################################################################
|
86
|
-
# Set the value of new variables from the values of the hash.
|
87
|
-
#
|
88
|
-
# New variables are _always_: +question+ and +help+.
|
89
|
-
#
|
90
|
-
# 'hash' gets modified.
|
91
|
-
#
|
92
|
-
# On the left side of the equal sign: _always_ 'help, hash,
|
93
|
-
# question', thereafter the additional variables.
|
94
|
-
#
|
95
|
-
# Arguments of args_2: 'help,hash,question' and then the (optional)
|
96
|
-
# hash-keys, which shall be assigned to the additional variables.
|
97
|
-
# :help and :question can be put at arbitrary position, or omitted.
|
98
|
-
# hash _must_ have:
|
99
|
-
#
|
100
|
-
# If the variable +help+ and/or +question+ are already set (not
|
101
|
-
# equal +nil+), then they leave unchanged.
|
102
|
-
#
|
103
|
-
# EXAMPLE:
|
104
|
-
# help = 'h'
|
105
|
-
# hash = {
|
106
|
-
# :help=>'heeelp',
|
107
|
-
# :question=>'MyQuestion',
|
108
|
-
# :title=>'MyTitle',
|
109
|
-
# :SomethingElse=>'bla'
|
110
|
-
# }
|
111
|
-
# help, hash, question, title =
|
112
|
-
# TKXXS_CLASSES.args_2( help, hash, question, :title)
|
113
|
-
# # => ["h", {:SomethingElse=>"bla"}, "MyQuestion", "MyTitle"]
|
114
|
-
#
|
115
|
-
# * 'h' in +help+ _not_ overwritten, because +help+ was defined before;
|
116
|
-
# * Every key in +hash+ deleted, which corresponds to the args of +args_2+;
|
117
|
-
# * +question+ now set, because it was _not_ defined before;
|
118
|
-
# * +title+ set to <tt>hash[:title]</tt>
|
119
|
-
def TKXXS_CLASSES.args_2( help,hash,question,*keys )
|
120
|
-
hash.delete(:configSection)
|
121
|
-
q = hash.delete(:question)
|
122
|
-
question = q unless question
|
123
|
-
h = hash.delete(:help)
|
124
|
-
help = h unless help
|
125
|
-
|
126
|
-
values = [help,hash,question]
|
127
|
-
keys.each {|key|
|
128
|
-
val = hash.delete(key)
|
129
|
-
values << val
|
130
|
-
}
|
131
|
-
values[1] = hash
|
132
|
-
values
|
133
|
-
end # TKXXS_CLASSES.args_2
|
134
|
-
|
135
|
-
##########################################################################
|
136
|
-
##########################################################################
|
137
|
-
class TextW < TkText
|
138
|
-
include Tk::Tile
|
139
|
-
def initialize( parent, hash={} )
|
140
|
-
#---- Frame
|
141
|
-
@frame = Frame.new(parent) {padding "3 3 3 3"}.
|
142
|
-
pack(:fill=>:both, :expand=>true)
|
143
|
-
|
144
|
-
hash = Marshal.load(Marshal.dump( hash ) ) # Should work because never uses Proc
|
145
|
-
|
146
|
-
hash = {
|
147
|
-
:font=>'"Courier New" 10',
|
148
|
-
:wrap=>'word',
|
149
|
-
:bd=>'2'
|
150
|
-
}.merge(hash)
|
151
|
-
|
152
|
-
#---- self = TkText
|
153
|
-
super(@frame, hash)
|
154
|
-
|
155
|
-
#---- Scrollbars
|
156
|
-
sy = Scrollbar.new(@frame)
|
157
|
-
sy.pack('side'=>'right', 'fill'=>'y')
|
158
|
-
self.yscrollbar(sy)
|
159
|
-
|
160
|
-
sx = Scrollbar.new(@frame)
|
161
|
-
sx.pack('side'=>'bottom', 'fill'=>'x')
|
162
|
-
self.xscrollbar(sx)
|
163
|
-
|
164
|
-
self.pack('expand'=>'yes', 'fill'=>'both')
|
165
|
-
end # initialize
|
166
|
-
|
167
|
-
end # class TextW
|
168
|
-
|
169
|
-
##########################################################################
|
170
|
-
##########################################################################
|
171
|
-
class AskSingleLineD < Tk::Tile::Entry
|
172
|
-
include Tk::Tile
|
173
|
-
CONF = nil unless defined?(CONF)
|
174
|
-
attr_accessor :dialog # in: "self.dialog
|
175
|
-
|
176
|
-
##########################################################################
|
177
|
-
# See: TKXXS.ask_single_line
|
178
|
-
def initialize( question=nil, help=nil, hash=nil )
|
179
|
-
question, help, hash = TKXXS_CLASSES.args_1(question, help, hash)
|
180
|
-
|
181
|
-
# Must always include: :question, :help
|
182
|
-
hash = { # Default values
|
183
|
-
:question => "?",
|
184
|
-
:help => nil,
|
185
|
-
:
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
#
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
@
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
@dialog
|
204
|
-
@dialog.
|
205
|
-
@dialog.
|
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
|
-
@btn
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
@btn2
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
@frame.
|
261
|
-
@frame.
|
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
|
-
@dialog
|
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
|
-
self.
|
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
|
-
end #
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
:
|
449
|
-
:
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
#
|
458
|
-
#
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
@
|
466
|
-
|
467
|
-
|
468
|
-
@
|
469
|
-
|
470
|
-
#
|
471
|
-
|
472
|
-
@
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
@
|
497
|
-
grid(:sticky=>'ew')
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
@entry
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
@
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
@
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
when '
|
568
|
-
choices = aryWithChoices
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
|
596
|
-
|
597
|
-
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
617
|
-
|
618
|
-
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
##/
|
656
|
-
##/
|
657
|
-
##/
|
658
|
-
##/
|
659
|
-
##/
|
660
|
-
##/
|
661
|
-
##/
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
|
666
|
-
|
667
|
-
|
668
|
-
|
669
|
-
|
670
|
-
|
671
|
-
|
672
|
-
|
673
|
-
|
674
|
-
|
675
|
-
|
676
|
-
|
677
|
-
|
678
|
-
|
679
|
-
|
680
|
-
|
681
|
-
|
682
|
-
|
683
|
-
|
684
|
-
|
685
|
-
|
686
|
-
|
687
|
-
|
688
|
-
|
689
|
-
|
690
|
-
|
691
|
-
|
692
|
-
:
|
693
|
-
|
694
|
-
|
695
|
-
|
696
|
-
|
697
|
-
|
698
|
-
|
699
|
-
|
700
|
-
|
701
|
-
|
702
|
-
|
703
|
-
|
704
|
-
|
705
|
-
|
706
|
-
|
707
|
-
|
708
|
-
##/
|
709
|
-
|
710
|
-
|
711
|
-
|
712
|
-
|
713
|
-
|
714
|
-
|
715
|
-
|
716
|
-
|
717
|
-
|
718
|
-
|
719
|
-
|
720
|
-
|
721
|
-
|
722
|
-
|
723
|
-
|
724
|
-
|
725
|
-
|
726
|
-
|
727
|
-
|
728
|
-
|
729
|
-
|
730
|
-
|
731
|
-
|
732
|
-
|
733
|
-
|
734
|
-
|
735
|
-
|
736
|
-
|
737
|
-
|
738
|
-
unless
|
739
|
-
|
740
|
-
|
741
|
-
|
742
|
-
|
743
|
-
|
744
|
-
|
745
|
-
|
746
|
-
|
747
|
-
|
748
|
-
|
749
|
-
|
750
|
-
|
751
|
-
|
752
|
-
|
753
|
-
|
754
|
-
|
755
|
-
|
756
|
-
|
757
|
-
|
758
|
-
|
759
|
-
|
760
|
-
|
761
|
-
|
762
|
-
|
763
|
-
|
764
|
-
|
765
|
-
|
766
|
-
|
767
|
-
|
768
|
-
|
769
|
-
|
770
|
-
|
771
|
-
|
772
|
-
#---- Button-
|
773
|
-
@btn = Button.new(@frame, :text=>"
|
774
|
-
grid(:row=>2,:column=>0,:sticky=>'
|
775
|
-
@btn.command {
|
776
|
-
|
777
|
-
|
778
|
-
|
779
|
-
|
780
|
-
|
781
|
-
|
782
|
-
|
783
|
-
|
784
|
-
|
785
|
-
|
786
|
-
|
787
|
-
|
788
|
-
|
789
|
-
|
790
|
-
|
791
|
-
|
792
|
-
|
793
|
-
|
794
|
-
|
795
|
-
|
796
|
-
|
797
|
-
|
798
|
-
|
799
|
-
|
800
|
-
|
801
|
-
|
802
|
-
|
803
|
-
|
804
|
-
|
805
|
-
|
806
|
-
|
807
|
-
|
808
|
-
|
809
|
-
|
810
|
-
|
811
|
-
|
812
|
-
|
813
|
-
|
814
|
-
|
815
|
-
|
816
|
-
end #
|
817
|
-
|
818
|
-
|
819
|
-
|
820
|
-
|
821
|
-
|
822
|
-
|
823
|
-
|
824
|
-
|
825
|
-
|
826
|
-
|
827
|
-
|
828
|
-
|
829
|
-
|
830
|
-
|
831
|
-
|
832
|
-
|
833
|
-
|
834
|
-
|
835
|
-
|
836
|
-
|
837
|
-
|
838
|
-
|
839
|
-
|
840
|
-
|
841
|
-
|
842
|
-
|
843
|
-
|
844
|
-
|
845
|
-
|
846
|
-
|
847
|
-
|
848
|
-
|
849
|
-
|
850
|
-
|
851
|
-
|
852
|
-
|
853
|
-
|
854
|
-
|
855
|
-
|
856
|
-
|
857
|
-
|
858
|
-
|
859
|
-
|
860
|
-
|
861
|
-
#
|
862
|
-
#
|
863
|
-
#
|
864
|
-
#
|
865
|
-
#
|
866
|
-
#
|
867
|
-
#
|
868
|
-
#
|
869
|
-
#
|
870
|
-
#
|
871
|
-
#
|
872
|
-
#
|
873
|
-
# * <tt>:
|
874
|
-
#
|
875
|
-
#
|
876
|
-
|
877
|
-
|
878
|
-
|
879
|
-
|
880
|
-
|
881
|
-
|
882
|
-
|
883
|
-
|
884
|
-
|
885
|
-
|
886
|
-
|
887
|
-
|
888
|
-
|
889
|
-
|
890
|
-
|
891
|
-
|
892
|
-
|
893
|
-
|
894
|
-
|
895
|
-
|
896
|
-
|
897
|
-
|
898
|
-
|
899
|
-
|
900
|
-
|
901
|
-
|
902
|
-
|
903
|
-
|
904
|
-
|
905
|
-
|
906
|
-
|
907
|
-
@
|
908
|
-
|
909
|
-
|
910
|
-
|
911
|
-
|
912
|
-
|
913
|
-
|
914
|
-
Tk.
|
915
|
-
|
916
|
-
|
917
|
-
|
918
|
-
@dialog.
|
919
|
-
|
920
|
-
|
921
|
-
|
922
|
-
|
923
|
-
##/
|
924
|
-
##/
|
925
|
-
##/
|
926
|
-
##/
|
927
|
-
##/
|
928
|
-
##/
|
929
|
-
##/
|
930
|
-
##/
|
931
|
-
##/
|
932
|
-
|
933
|
-
|
934
|
-
|
935
|
-
|
936
|
-
|
937
|
-
|
938
|
-
|
939
|
-
|
940
|
-
#----
|
941
|
-
@
|
942
|
-
|
943
|
-
|
944
|
-
|
945
|
-
|
946
|
-
@
|
947
|
-
|
948
|
-
|
949
|
-
@
|
950
|
-
@
|
951
|
-
@
|
952
|
-
|
953
|
-
|
954
|
-
@
|
955
|
-
|
956
|
-
#----
|
957
|
-
@
|
958
|
-
|
959
|
-
|
960
|
-
|
961
|
-
@
|
962
|
-
|
963
|
-
|
964
|
-
@
|
965
|
-
@
|
966
|
-
|
967
|
-
|
968
|
-
|
969
|
-
|
970
|
-
|
971
|
-
|
972
|
-
|
973
|
-
#----
|
974
|
-
@
|
975
|
-
|
976
|
-
|
977
|
-
|
978
|
-
@
|
979
|
-
|
980
|
-
|
981
|
-
|
982
|
-
|
983
|
-
|
984
|
-
|
985
|
-
|
986
|
-
|
987
|
-
#
|
988
|
-
#
|
989
|
-
|
990
|
-
|
991
|
-
|
992
|
-
|
993
|
-
|
994
|
-
|
995
|
-
|
996
|
-
|
997
|
-
|
998
|
-
|
999
|
-
|
1000
|
-
|
1001
|
-
|
1002
|
-
|
1003
|
-
|
1004
|
-
|
1005
|
-
|
1006
|
-
|
1007
|
-
|
1008
|
-
|
1009
|
-
|
1010
|
-
|
1011
|
-
|
1012
|
-
|
1013
|
-
|
1014
|
-
|
1015
|
-
|
1016
|
-
|
1017
|
-
|
1018
|
-
|
1019
|
-
#
|
1020
|
-
#
|
1021
|
-
|
1022
|
-
|
1023
|
-
|
1024
|
-
|
1025
|
-
@
|
1026
|
-
|
1027
|
-
|
1028
|
-
|
1029
|
-
|
1030
|
-
|
1031
|
-
|
1032
|
-
|
1033
|
-
|
1034
|
-
|
1035
|
-
|
1036
|
-
|
1037
|
-
|
1038
|
-
|
1039
|
-
|
1040
|
-
|
1041
|
-
|
1042
|
-
|
1043
|
-
|
1044
|
-
##/
|
1045
|
-
##/
|
1046
|
-
##/
|
1047
|
-
##/
|
1048
|
-
##/
|
1049
|
-
##/
|
1050
|
-
##/
|
1051
|
-
##/
|
1052
|
-
##/
|
1053
|
-
##/
|
1054
|
-
##/
|
1055
|
-
##/
|
1056
|
-
|
1057
|
-
|
1058
|
-
|
1059
|
-
|
1060
|
-
|
1061
|
-
|
1062
|
-
|
1063
|
-
|
1064
|
-
|
1065
|
-
end #
|
1066
|
-
|
1067
|
-
def
|
1068
|
-
lbl = Label.new(@frame, :text=>"
|
1069
|
-
end #
|
1070
|
-
|
1071
|
-
def
|
1072
|
-
|
1073
|
-
end #
|
1074
|
-
|
1075
|
-
def
|
1076
|
-
|
1077
|
-
|
1078
|
-
|
1079
|
-
|
1080
|
-
|
1081
|
-
|
1082
|
-
|
1083
|
-
|
1084
|
-
|
1085
|
-
|
1086
|
-
|
1087
|
-
|
1088
|
-
|
1089
|
-
|
1090
|
-
|
1091
|
-
|
1092
|
-
|
1093
|
-
|
1094
|
-
|
1095
|
-
|
1096
|
-
|
1097
|
-
|
1098
|
-
|
1099
|
-
|
1100
|
-
|
1101
|
-
|
1102
|
-
|
1103
|
-
btn
|
1104
|
-
|
1105
|
-
|
1106
|
-
|
1107
|
-
|
1108
|
-
|
1109
|
-
|
1110
|
-
|
1111
|
-
|
1112
|
-
|
1113
|
-
|
1114
|
-
|
1115
|
-
|
1116
|
-
|
1117
|
-
when :
|
1118
|
-
validate_and_set_path(
|
1119
|
-
|
1120
|
-
|
1121
|
-
|
1122
|
-
|
1123
|
-
|
1124
|
-
|
1125
|
-
|
1126
|
-
|
1127
|
-
|
1128
|
-
btn
|
1129
|
-
|
1130
|
-
|
1131
|
-
|
1132
|
-
|
1133
|
-
|
1134
|
-
|
1135
|
-
|
1136
|
-
|
1137
|
-
|
1138
|
-
|
1139
|
-
|
1140
|
-
|
1141
|
-
|
1142
|
-
|
1143
|
-
|
1144
|
-
|
1145
|
-
|
1146
|
-
|
1147
|
-
|
1148
|
-
|
1149
|
-
|
1150
|
-
|
1151
|
-
|
1152
|
-
|
1153
|
-
btn
|
1154
|
-
|
1155
|
-
|
1156
|
-
|
1157
|
-
|
1158
|
-
|
1159
|
-
|
1160
|
-
|
1161
|
-
|
1162
|
-
|
1163
|
-
|
1164
|
-
|
1165
|
-
|
1166
|
-
when :
|
1167
|
-
validate_and_set_path(
|
1168
|
-
|
1169
|
-
|
1170
|
-
|
1171
|
-
|
1172
|
-
|
1173
|
-
|
1174
|
-
|
1175
|
-
|
1176
|
-
|
1177
|
-
btn
|
1178
|
-
|
1179
|
-
|
1180
|
-
|
1181
|
-
|
1182
|
-
|
1183
|
-
|
1184
|
-
|
1185
|
-
|
1186
|
-
|
1187
|
-
|
1188
|
-
|
1189
|
-
|
1190
|
-
|
1191
|
-
|
1192
|
-
|
1193
|
-
btn
|
1194
|
-
|
1195
|
-
|
1196
|
-
|
1197
|
-
btn
|
1198
|
-
|
1199
|
-
|
1200
|
-
|
1201
|
-
|
1202
|
-
|
1203
|
-
|
1204
|
-
|
1205
|
-
|
1206
|
-
|
1207
|
-
|
1208
|
-
|
1209
|
-
|
1210
|
-
|
1211
|
-
|
1212
|
-
|
1213
|
-
|
1214
|
-
|
1215
|
-
entry
|
1216
|
-
|
1217
|
-
|
1218
|
-
|
1219
|
-
|
1220
|
-
|
1221
|
-
|
1222
|
-
|
1223
|
-
|
1224
|
-
|
1225
|
-
|
1226
|
-
|
1227
|
-
|
1228
|
-
|
1229
|
-
|
1230
|
-
|
1231
|
-
btn
|
1232
|
-
|
1233
|
-
|
1234
|
-
|
1235
|
-
|
1236
|
-
|
1237
|
-
|
1238
|
-
|
1239
|
-
|
1240
|
-
|
1241
|
-
|
1242
|
-
|
1243
|
-
|
1244
|
-
|
1245
|
-
|
1246
|
-
|
1247
|
-
|
1248
|
-
|
1249
|
-
|
1250
|
-
|
1251
|
-
|
1252
|
-
|
1253
|
-
|
1254
|
-
|
1255
|
-
|
1256
|
-
@
|
1257
|
-
|
1258
|
-
|
1259
|
-
|
1260
|
-
@
|
1261
|
-
|
1262
|
-
|
1263
|
-
|
1264
|
-
|
1265
|
-
|
1266
|
-
|
1267
|
-
|
1268
|
-
|
1269
|
-
|
1270
|
-
|
1271
|
-
|
1272
|
-
|
1273
|
-
|
1274
|
-
|
1275
|
-
|
1276
|
-
|
1277
|
-
|
1278
|
-
|
1279
|
-
|
1280
|
-
|
1281
|
-
|
1282
|
-
|
1283
|
-
end #
|
1284
|
-
|
1285
|
-
def
|
1286
|
-
|
1287
|
-
|
1288
|
-
|
1289
|
-
|
1290
|
-
|
1291
|
-
|
1292
|
-
|
1293
|
-
|
1294
|
-
|
1295
|
-
|
1296
|
-
|
1297
|
-
|
1298
|
-
|
1299
|
-
|
1300
|
-
|
1301
|
-
|
1302
|
-
|
1303
|
-
|
1304
|
-
|
1305
|
-
|
1306
|
-
|
1307
|
-
|
1308
|
-
|
1309
|
-
|
1310
|
-
|
1311
|
-
|
1312
|
-
|
1313
|
-
|
1314
|
-
|
1315
|
-
@
|
1316
|
-
|
1317
|
-
|
1318
|
-
|
1319
|
-
|
1320
|
-
|
1321
|
-
@
|
1322
|
-
|
1323
|
-
|
1324
|
-
|
1325
|
-
|
1326
|
-
|
1327
|
-
|
1328
|
-
|
1329
|
-
|
1330
|
-
|
1331
|
-
|
1332
|
-
|
1333
|
-
|
1334
|
-
|
1335
|
-
|
1336
|
-
|
1337
|
-
|
1338
|
-
|
1339
|
-
|
1340
|
-
|
1341
|
-
nil
|
1342
|
-
|
1343
|
-
|
1344
|
-
|
1345
|
-
|
1346
|
-
|
1347
|
-
|
1348
|
-
paths
|
1349
|
-
|
1350
|
-
|
1351
|
-
|
1352
|
-
|
1353
|
-
|
1354
|
-
|
1355
|
-
|
1356
|
-
|
1357
|
-
|
1358
|
-
|
1359
|
-
|
1360
|
-
|
1361
|
-
|
1362
|
-
|
1363
|
-
|
1364
|
-
|
1365
|
-
|
1366
|
-
|
1367
|
-
|
1368
|
-
|
1369
|
-
|
1370
|
-
|
1371
|
-
|
1372
|
-
|
1373
|
-
|
1374
|
-
|
1375
|
-
|
1376
|
-
|
1377
|
-
|
1378
|
-
|
1379
|
-
|
1380
|
-
|
1381
|
-
|
1382
|
-
|
1383
|
-
|
1384
|
-
|
1385
|
-
|
1386
|
-
|
1387
|
-
|
1388
|
-
|
1389
|
-
|
1390
|
-
|
1391
|
-
|
1392
|
-
|
1393
|
-
|
1394
|
-
|
1395
|
-
|
1396
|
-
|
1397
|
-
|
1398
|
-
|
1399
|
-
|
1400
|
-
filesListStr.
|
1401
|
-
|
1402
|
-
|
1403
|
-
|
1404
|
-
|
1405
|
-
|
1406
|
-
|
1407
|
-
|
1408
|
-
|
1409
|
-
|
1410
|
-
|
1411
|
-
|
1412
|
-
|
1413
|
-
|
1414
|
-
|
1415
|
-
|
1416
|
-
|
1417
|
-
|
1418
|
-
|
1419
|
-
|
1420
|
-
hash
|
1421
|
-
|
1422
|
-
|
1423
|
-
|
1424
|
-
|
1425
|
-
|
1426
|
-
|
1427
|
-
|
1428
|
-
|
1429
|
-
|
1430
|
-
|
1431
|
-
|
1432
|
-
|
1433
|
-
|
1434
|
-
|
1435
|
-
|
1436
|
-
|
1437
|
-
|
1438
|
-
|
1439
|
-
|
1440
|
-
|
1441
|
-
|
1442
|
-
|
1443
|
-
|
1444
|
-
|
1445
|
-
|
1446
|
-
|
1447
|
-
|
1448
|
-
|
1449
|
-
|
1450
|
-
|
1451
|
-
|
1452
|
-
:
|
1453
|
-
|
1454
|
-
|
1455
|
-
|
1456
|
-
|
1457
|
-
|
1458
|
-
|
1459
|
-
|
1460
|
-
|
1461
|
-
|
1462
|
-
|
1463
|
-
|
1464
|
-
|
1465
|
-
|
1466
|
-
|
1467
|
-
|
1468
|
-
|
1469
|
-
|
1470
|
-
|
1471
|
-
|
1472
|
-
|
1473
|
-
|
1474
|
-
|
1475
|
-
|
1476
|
-
|
1477
|
-
|
1478
|
-
|
1479
|
-
|
1480
|
-
|
1481
|
-
|
1482
|
-
|
1483
|
-
|
1484
|
-
:
|
1485
|
-
|
1486
|
-
|
1487
|
-
|
1488
|
-
|
1489
|
-
|
1490
|
-
|
1491
|
-
|
1492
|
-
|
1493
|
-
|
1494
|
-
|
1495
|
-
|
1496
|
-
|
1497
|
-
|
1498
|
-
|
1499
|
-
|
1500
|
-
|
1501
|
-
|
1502
|
-
|
1503
|
-
|
1504
|
-
|
1505
|
-
|
1506
|
-
|
1507
|
-
|
1508
|
-
|
1509
|
-
|
1510
|
-
|
1511
|
-
|
1512
|
-
|
1513
|
-
|
1514
|
-
|
1515
|
-
|
1516
|
-
:
|
1517
|
-
:
|
1518
|
-
|
1519
|
-
|
1520
|
-
|
1521
|
-
|
1522
|
-
|
1523
|
-
|
1524
|
-
|
1525
|
-
|
1526
|
-
|
1527
|
-
|
1528
|
-
|
1529
|
-
|
1530
|
-
|
1531
|
-
|
1532
|
-
|
1533
|
-
|
1534
|
-
|
1535
|
-
|
1536
|
-
|
1537
|
-
|
1538
|
-
|
1539
|
-
|
1540
|
-
##/
|
1541
|
-
##/
|
1542
|
-
##/
|
1543
|
-
##/
|
1544
|
-
##/
|
1545
|
-
##/
|
1546
|
-
##/
|
1547
|
-
##/
|
1548
|
-
##/
|
1549
|
-
##/
|
1550
|
-
##/ :
|
1551
|
-
##/ :
|
1552
|
-
##/
|
1553
|
-
##/
|
1554
|
-
##/
|
1555
|
-
##/
|
1556
|
-
##/
|
1557
|
-
##/
|
1558
|
-
##/
|
1559
|
-
##/
|
1560
|
-
##/
|
1561
|
-
##/
|
1562
|
-
##/
|
1563
|
-
|
1564
|
-
|
1565
|
-
|
1566
|
-
|
1567
|
-
|
1568
|
-
|
1569
|
-
end # class
|
1570
|
-
|
1571
|
-
|
1
|
+
# encoding: Windows-1252 :encoding=Windows-1252:
|
2
|
+
# Copyright (c) Axel Friedrich 2010-2014
|
3
|
+
STDOUT.sync = true
|
4
|
+
STDERR.sync = true
|
5
|
+
|
6
|
+
##########################################################################
|
7
|
+
##########################################################################
|
8
|
+
class Hash
|
9
|
+
|
10
|
+
##################################################################
|
11
|
+
# Somewhat deeper dup
|
12
|
+
def dup2!( )
|
13
|
+
self.each_pair {|k,v|
|
14
|
+
self[k] = v.dup if v.class == String
|
15
|
+
}
|
16
|
+
self
|
17
|
+
end # dup2!
|
18
|
+
|
19
|
+
end # class Hash
|
20
|
+
|
21
|
+
##########################################################################
|
22
|
+
##########################################################################
|
23
|
+
# Creates dialogs for TKXXS
|
24
|
+
module TKXXS_CLASSES
|
25
|
+
|
26
|
+
##################################################################
|
27
|
+
# Purpose:
|
28
|
+
# Interpret last argument always as +Hash#, independent from the
|
29
|
+
# number of arguments:
|
30
|
+
# * Searches for the first argument which is a Hash, from right to
|
31
|
+
# left, starting with the last argument. (TODO: Why not simply
|
32
|
+
# detect if the last argument is a Hash?)
|
33
|
+
# * If Hash exists:
|
34
|
+
# ** Move the hash to the last element of args.
|
35
|
+
# ** Set the element, where the Hash comes from to nil, if it was
|
36
|
+
# not the last element.
|
37
|
+
# ** All other elements remain unchanged.
|
38
|
+
# * If no Hash exist:
|
39
|
+
# ** Sets the last element of args to {}
|
40
|
+
# ** All other elements remain unchanged.
|
41
|
+
#
|
42
|
+
# *Returns:* The rearranged args.
|
43
|
+
#
|
44
|
+
# Example:
|
45
|
+
# def m1( c=nil,b=nil,a=nil,h=nil )
|
46
|
+
# c, b, a, hash = args_1(c,b,a,h)
|
47
|
+
# p [c, b, a, hash]
|
48
|
+
#
|
49
|
+
# hash = {:a=>'aa',
|
50
|
+
# :b=>'bb',
|
51
|
+
# :c=>'cc'
|
52
|
+
# }.merge(hash)
|
53
|
+
#
|
54
|
+
# c = hash[:c] unless c
|
55
|
+
# b = hash[:b] unless b
|
56
|
+
# a = hash[:a] unless a
|
57
|
+
# nil
|
58
|
+
# end # m1
|
59
|
+
#
|
60
|
+
# m1(:c,:b,:a, {:h=>'hh'}) # => [:c, :b, :a, {:h=>"hh"}]
|
61
|
+
# m1(:c, {:h=>'hh'}) # => [:c, nil, nil, {:h=>"hh"}]
|
62
|
+
# m1(:c) # => [:c, nil, nil, {}]
|
63
|
+
# m1() # => [nil, nil, nil, {}]
|
64
|
+
#
|
65
|
+
def TKXXS_CLASSES.args_1( *args )
|
66
|
+
args = args.reverse
|
67
|
+
args.each_with_index {|arg, i|
|
68
|
+
if arg.class == Hash
|
69
|
+
args[i] = nil
|
70
|
+
args[0] = arg
|
71
|
+
break
|
72
|
+
end
|
73
|
+
}
|
74
|
+
args[0] =
|
75
|
+
unless args[0]
|
76
|
+
{}
|
77
|
+
else
|
78
|
+
## Marshal.load(Marshal.dump( args[0] ) ) # Doesn't work because of Proc
|
79
|
+
args[0].dup2! # args[0] must always be a Hash
|
80
|
+
end
|
81
|
+
args = args.reverse
|
82
|
+
args
|
83
|
+
end # TKXXS_CLASSES.args_1
|
84
|
+
|
85
|
+
##################################################################
|
86
|
+
# Set the value of new variables from the values of the hash.
|
87
|
+
#
|
88
|
+
# New variables are _always_: +question+ and +help+.
|
89
|
+
#
|
90
|
+
# 'hash' gets modified.
|
91
|
+
#
|
92
|
+
# On the left side of the equal sign: _always_ 'help, hash,
|
93
|
+
# question', thereafter the additional variables.
|
94
|
+
#
|
95
|
+
# Arguments of args_2: 'help,hash,question' and then the (optional)
|
96
|
+
# hash-keys, which shall be assigned to the additional variables.
|
97
|
+
# :help and :question can be put at arbitrary position, or omitted.
|
98
|
+
# hash _must_ have: :question.
|
99
|
+
#
|
100
|
+
# If the variable +help+ and/or +question+ are already set (not
|
101
|
+
# equal +nil+), then they leave unchanged.
|
102
|
+
#
|
103
|
+
# EXAMPLE:
|
104
|
+
# help = 'h'
|
105
|
+
# hash = {
|
106
|
+
# :help=>'heeelp',
|
107
|
+
# :question=>'MyQuestion',
|
108
|
+
# :title=>'MyTitle',
|
109
|
+
# :SomethingElse=>'bla'
|
110
|
+
# }
|
111
|
+
# help, hash, question, title =
|
112
|
+
# TKXXS_CLASSES.args_2( help, hash, question, :title)
|
113
|
+
# # => ["h", {:SomethingElse=>"bla"}, "MyQuestion", "MyTitle"]
|
114
|
+
#
|
115
|
+
# * 'h' in +help+ _not_ overwritten, because +help+ was defined before;
|
116
|
+
# * Every key in +hash+ deleted, which corresponds to the args of +args_2+;
|
117
|
+
# * +question+ now set, because it was _not_ defined before;
|
118
|
+
# * +title+ set to <tt>hash[:title]</tt>
|
119
|
+
def TKXXS_CLASSES.args_2( help,hash,question,*keys )
|
120
|
+
hash.delete(:configSection) if hash[:configSection]
|
121
|
+
q = hash.delete(:question)
|
122
|
+
question = q unless question
|
123
|
+
h = hash.delete(:help)
|
124
|
+
help = h unless help
|
125
|
+
|
126
|
+
values = [help,hash,question]
|
127
|
+
keys.each {|key|
|
128
|
+
val = hash.delete(key)
|
129
|
+
values << val
|
130
|
+
}
|
131
|
+
values[1] = hash
|
132
|
+
values
|
133
|
+
end # TKXXS_CLASSES.args_2
|
134
|
+
|
135
|
+
##########################################################################
|
136
|
+
##########################################################################
|
137
|
+
class TextW < TkText
|
138
|
+
include Tk::Tile
|
139
|
+
def initialize( parent, hash={} )
|
140
|
+
#---- Frame
|
141
|
+
@frame = Frame.new(parent) {padding "3 3 3 3"}.
|
142
|
+
pack(:fill=>:both, :expand=>true)
|
143
|
+
|
144
|
+
hash = Marshal.load(Marshal.dump( hash ) ) # Should work because never uses Proc
|
145
|
+
|
146
|
+
hash = {
|
147
|
+
:font=>'"Courier New" 10',
|
148
|
+
:wrap=>'word',
|
149
|
+
:bd=>'2'
|
150
|
+
}.merge(hash)
|
151
|
+
|
152
|
+
#---- self = TkText
|
153
|
+
super(@frame, hash)
|
154
|
+
|
155
|
+
#---- Scrollbars
|
156
|
+
sy = Scrollbar.new(@frame)
|
157
|
+
sy.pack('side'=>'right', 'fill'=>'y')
|
158
|
+
self.yscrollbar(sy)
|
159
|
+
|
160
|
+
sx = Scrollbar.new(@frame)
|
161
|
+
sx.pack('side'=>'bottom', 'fill'=>'x')
|
162
|
+
self.xscrollbar(sx)
|
163
|
+
|
164
|
+
self.pack('expand'=>'yes', 'fill'=>'both')
|
165
|
+
end # initialize
|
166
|
+
|
167
|
+
end # class TextW
|
168
|
+
|
169
|
+
##########################################################################
|
170
|
+
##########################################################################
|
171
|
+
class AskSingleLineD < Tk::Tile::Entry
|
172
|
+
include Tk::Tile
|
173
|
+
CONF = nil unless defined?(CONF)
|
174
|
+
attr_accessor :dialog # in: "self.dialog
|
175
|
+
|
176
|
+
##########################################################################
|
177
|
+
# See: TKXXS.ask_single_line
|
178
|
+
def initialize( question=nil, help=nil, hash=nil )
|
179
|
+
question, help, hash = TKXXS_CLASSES.args_1(question, help, hash)
|
180
|
+
|
181
|
+
# Must always include: :question, :help
|
182
|
+
hash = { # Default values
|
183
|
+
:question => "?",
|
184
|
+
:help => nil,
|
185
|
+
:defaultEntry => '',
|
186
|
+
}.merge(hash)
|
187
|
+
|
188
|
+
# Necessary, because hash[:configSection] is deleted later on
|
189
|
+
# with +args_2+.
|
190
|
+
CONF.section = hash[:configSection] if CONF && hash[:configSection]
|
191
|
+
|
192
|
+
help, hash, question, defaultEntry =
|
193
|
+
TKXXS_CLASSES.args_2(help,hash,question,:defaultEntry)
|
194
|
+
|
195
|
+
teardownDone = false
|
196
|
+
@ans = nil
|
197
|
+
@goOn = goOn = TkVariable.new
|
198
|
+
# Because you cannot use instance-vars in procs. TODO: smarter way?
|
199
|
+
|
200
|
+
Tk.update # Show any calling widget first to make it lower than @dialog
|
201
|
+
#---- Toplevel: @dialog
|
202
|
+
@dialog=Toplevel.new() {title "Please enter your answer..."}
|
203
|
+
@dialog.geometry = CONF[:dialogGeom] if CONF
|
204
|
+
@dialog.raise
|
205
|
+
@dialog.bind('Destroy') {
|
206
|
+
unless teardownDone
|
207
|
+
if CONF
|
208
|
+
CONF[:dialogGeom] = @dialog.geometry
|
209
|
+
end
|
210
|
+
goOn.value = '1' # !
|
211
|
+
teardownDone = true
|
212
|
+
end
|
213
|
+
}
|
214
|
+
|
215
|
+
#---- Frame
|
216
|
+
@frame = Frame.new(@dialog) {padding "3 3 3 3"}.
|
217
|
+
pack(:fill=>:both,:expand=>true)
|
218
|
+
|
219
|
+
#---- Label
|
220
|
+
@lbl = Label.new(@frame, :text=>question){
|
221
|
+
font $font if $font
|
222
|
+
wraplength '5i'
|
223
|
+
justify 'left'
|
224
|
+
}
|
225
|
+
@lbl.grid(:column=>0,:columnspan=>3,:sticky=>'ws')
|
226
|
+
|
227
|
+
#---- self = TkEntry
|
228
|
+
super(@frame, hash)
|
229
|
+
self.grid(:column=>0,:columnspan=>3,:sticky=>'ew')
|
230
|
+
self.insert(0, defaultEntry)
|
231
|
+
bind('Key-Return') {
|
232
|
+
ans_( self.get )
|
233
|
+
self.dialog.destroy
|
234
|
+
goOn.value = '1'
|
235
|
+
}
|
236
|
+
|
237
|
+
#---- Button-Cancel
|
238
|
+
@btn = Button.new(@frame, :text=>"Cancel").
|
239
|
+
grid(:column=>0,:row=>2)
|
240
|
+
@btn.command {
|
241
|
+
self.dialog.destroy
|
242
|
+
}
|
243
|
+
|
244
|
+
#---- Button-OK
|
245
|
+
@btn2 = Button.new(@frame, :text=>"OK").
|
246
|
+
grid(:column=>2,:row=>2)
|
247
|
+
@btn2.command {
|
248
|
+
ans_( self.get )
|
249
|
+
self.dialog.destroy
|
250
|
+
goOn.value = '1'
|
251
|
+
}
|
252
|
+
|
253
|
+
#---- Balloonhelp
|
254
|
+
if help
|
255
|
+
BalloonHelp.new(self, :text => help )
|
256
|
+
end
|
257
|
+
|
258
|
+
@frame.grid_columnconfigure([0,2],:weight=>0)
|
259
|
+
@frame.grid_columnconfigure(1,:weight=>1)
|
260
|
+
@frame.grid_rowconfigure(0,:weight=>1)
|
261
|
+
@frame.grid_rowconfigure([1,2],:weight=>0)
|
262
|
+
|
263
|
+
focus
|
264
|
+
update
|
265
|
+
end # initialize
|
266
|
+
|
267
|
+
##########################################################################
|
268
|
+
# *Returns*: (String or nil) The answer of the dialog. 'Cancel' returns nil.
|
269
|
+
def answer( )
|
270
|
+
@dialog.raise
|
271
|
+
@goOn.wait
|
272
|
+
@ans
|
273
|
+
end # ans
|
274
|
+
|
275
|
+
private
|
276
|
+
|
277
|
+
def private____________________( )
|
278
|
+
end # private____________________
|
279
|
+
|
280
|
+
def ans_( ans ) # TODO: Easier way?
|
281
|
+
@ans = ans.dup if ans
|
282
|
+
end # ans=
|
283
|
+
|
284
|
+
end # class AskSingleLineD
|
285
|
+
|
286
|
+
|
287
|
+
##########################################################################
|
288
|
+
##########################################################################
|
289
|
+
class AskMultiLineD < TkText
|
290
|
+
# class AskMultiLineD < TextW
|
291
|
+
include Tk::Tile
|
292
|
+
CONF = nil unless defined?(CONF)
|
293
|
+
attr_accessor :dialog # in: "self.dialog
|
294
|
+
|
295
|
+
##################################################################
|
296
|
+
# TODO: implement in TKXXS.
|
297
|
+
def initialize( question=nil, help=nil, hash=nil )
|
298
|
+
question, help, hash = TKXXS_CLASSES.args_1(question, help, hash)
|
299
|
+
|
300
|
+
# Must always include: :question, :help
|
301
|
+
hash = {
|
302
|
+
:question => "?",
|
303
|
+
:help => nil,
|
304
|
+
:title => "Please enter your answer..."
|
305
|
+
}.merge(hash)
|
306
|
+
|
307
|
+
# Necessary, because hash[:configSection] is deleted later on
|
308
|
+
# in args_2.
|
309
|
+
CONF.section = hash[:configSection] if CONF && hash[:configSection]
|
310
|
+
|
311
|
+
# help, hash, question, title =
|
312
|
+
# TKXXS_CLASSES.args_2( help, hash, question, :title)
|
313
|
+
help, hash, question, title =
|
314
|
+
TKXXS_CLASSES.args_2( help, hash, question, :title)
|
315
|
+
|
316
|
+
teardownDone = false
|
317
|
+
@ans = nil
|
318
|
+
@goOn = goOn = TkVariable.new
|
319
|
+
# Because you cannot use instance-vars in procs. TODO: smarter way?
|
320
|
+
|
321
|
+
Tk.update # Show any calling widget first to make it lower than @dialog
|
322
|
+
#---- Toplevel: @dialog
|
323
|
+
# @dialog=Toplevel.new() {title('title')}
|
324
|
+
@dialog=Toplevel.new() {title(title)}
|
325
|
+
@dialog.geometry = CONF[:dialogGeom] if CONF
|
326
|
+
@dialog.raise
|
327
|
+
@dialog.bind('Destroy') {
|
328
|
+
unless teardownDone
|
329
|
+
if CONF
|
330
|
+
CONF[:dialogGeom] = @dialog.geometry
|
331
|
+
end
|
332
|
+
goOn.value = '1' # !
|
333
|
+
teardownDone = true
|
334
|
+
end
|
335
|
+
}
|
336
|
+
|
337
|
+
#---- Frame
|
338
|
+
@frame = Frame.new(@dialog) {padding "3 3 3 3"}.
|
339
|
+
pack(:fill=>:both,:expand=>true)
|
340
|
+
|
341
|
+
#---- Label
|
342
|
+
@lbl = Label.new(@frame, :text=>question){
|
343
|
+
font $font if $font
|
344
|
+
wraplength '5i'
|
345
|
+
justify 'left'
|
346
|
+
}
|
347
|
+
@lbl.grid(:sticky=>'news')
|
348
|
+
|
349
|
+
#---- self = TextW
|
350
|
+
super(@frame, hash)
|
351
|
+
self.grid(:column=>0,:row=>1,:sticky=>'news')
|
352
|
+
@tagSel = TkTextTagSel.new(self)
|
353
|
+
bind('Control-Key-a') {
|
354
|
+
# From:
|
355
|
+
# "Using Control-a to select all text in a text widget : TCL",
|
356
|
+
# http://objectmix.com/tcl/35276-using-control-select-all-text-text-widget.html
|
357
|
+
@tagSel.add('0.0', :end)
|
358
|
+
Kernel.raise TkCallbackBreak
|
359
|
+
}
|
360
|
+
##/ bind('Key-Return') {
|
361
|
+
##/ ans_( self.get )
|
362
|
+
##/ self.dialog.destroy
|
363
|
+
##/ goOn.value = '1'
|
364
|
+
##/ }
|
365
|
+
|
366
|
+
#---- Scrollbars
|
367
|
+
sy = Scrollbar.new(@frame)
|
368
|
+
sy.grid(:column=>1,:row=>1,:sticky=>'ns')
|
369
|
+
self.yscrollbar(sy)
|
370
|
+
|
371
|
+
sx = Scrollbar.new(@frame)
|
372
|
+
sx.grid(:column=>0,:row=>2,:sticky=>'ew')
|
373
|
+
self.xscrollbar(sx)
|
374
|
+
|
375
|
+
#---- Button-Cancel
|
376
|
+
@btn = Button.new(@frame, :text=>"Cancel").
|
377
|
+
grid(:row=>3,:column=>0,:sticky=>'w')
|
378
|
+
@btn.command {
|
379
|
+
self.dialog.destroy
|
380
|
+
}
|
381
|
+
|
382
|
+
#---- Button-OK
|
383
|
+
@btn2 = Button.new(@frame, :text=>"OK").
|
384
|
+
grid(:row=>3,:column=>0,:sticky=>'e')
|
385
|
+
## grid(:row=>3,:column=>0,:sticky=>'e',:columnspan=>2)
|
386
|
+
@btn2.command {
|
387
|
+
got = self.get('0.0', 'end -1c')
|
388
|
+
ans_( got )
|
389
|
+
self.dialog.destroy
|
390
|
+
goOn.value = '1'
|
391
|
+
}
|
392
|
+
|
393
|
+
@frame.grid_columnconfigure(0, :weight=>1)
|
394
|
+
@frame.grid_columnconfigure(1, :weight=>0)
|
395
|
+
@frame.grid_rowconfigure([0,2,3], :weight=>0)
|
396
|
+
@frame.grid_rowconfigure(1, :weight=>1)
|
397
|
+
@lbl.bind('Configure'){
|
398
|
+
@lbl.wraplength = TkWinfo.width(@frame) - 40
|
399
|
+
}
|
400
|
+
|
401
|
+
#---- Balloonhelp
|
402
|
+
BalloonHelp.new(self,:text => help) if help
|
403
|
+
|
404
|
+
self.focus
|
405
|
+
##Tk.update
|
406
|
+
end # initialize
|
407
|
+
|
408
|
+
def answer( )
|
409
|
+
@dialog.raise
|
410
|
+
@goOn.wait
|
411
|
+
@ans
|
412
|
+
end # ans
|
413
|
+
|
414
|
+
private
|
415
|
+
|
416
|
+
def private____________________( )
|
417
|
+
end # private____________________
|
418
|
+
|
419
|
+
def ans_( ans ) # Is there an easier way?
|
420
|
+
@ans = ans.dup if ans
|
421
|
+
end # ans=
|
422
|
+
|
423
|
+
end # class AskMultiLineD
|
424
|
+
|
425
|
+
|
426
|
+
##########################################################################
|
427
|
+
##########################################################################
|
428
|
+
class SingleChoiceD < TkListbox
|
429
|
+
include Tk::Tile
|
430
|
+
CONF = nil unless defined?(CONF)
|
431
|
+
|
432
|
+
attr_accessor :dialog # in: "self.dialog
|
433
|
+
|
434
|
+
|
435
|
+
##########################################################################
|
436
|
+
# See: TKXXS::single_choice
|
437
|
+
def initialize( aryWithChoices, help=nil, hash=nil )
|
438
|
+
aryWithChoices, help, hash = TKXXS_CLASSES.args_1(aryWithChoices, help, hash)
|
439
|
+
|
440
|
+
@allChoices, @allClients, @allHelp = all_choices_clients_help(aryWithChoices)
|
441
|
+
|
442
|
+
# Must always include: :question, :help
|
443
|
+
hash = {
|
444
|
+
:question => "Choose one by single-click",
|
445
|
+
:help => nil,
|
446
|
+
:title => 'Choose one',
|
447
|
+
:bd=>'2', # TODO: noch ben�tigt?
|
448
|
+
:searchFieldHelp => "Enter RegExp for filter list here\n(not case sensitive)",
|
449
|
+
:returnChoiceAndClient=>false
|
450
|
+
}.merge(hash)
|
451
|
+
|
452
|
+
# Necessary, because hash[:configSection] is deleted later on
|
453
|
+
# in args_2.
|
454
|
+
|
455
|
+
CONF.section = hash[:configSection] if CONF && hash[:configSection]
|
456
|
+
|
457
|
+
# help, hash, question, title =
|
458
|
+
# TKXXS_CLASSES.args_2( help, hash, question, :title)
|
459
|
+
help,hash,question,title,searchFieldHelp,@returnChoiceAndClient=
|
460
|
+
TKXXS_CLASSES.args_2(help,hash,question,:title,:searchFieldHelp,:returnChoiceAndClient)
|
461
|
+
@allHelp ||= help
|
462
|
+
|
463
|
+
teardownDone = false
|
464
|
+
@ans = nil
|
465
|
+
@goOn = goOn = TkVariable.new
|
466
|
+
# Because you cannot use instance-vars in procs. TODO: smarter way?
|
467
|
+
|
468
|
+
@choices, @clients, @help = @allChoices, @allClients, @allHelp
|
469
|
+
|
470
|
+
Tk.update # Show any calling widget first to make it lower than @dialog
|
471
|
+
#---- Toplevel: @dialog
|
472
|
+
@dialog=Toplevel.new() {title(title)}
|
473
|
+
@dialog.geometry = CONF[:dialogGeom] if CONF
|
474
|
+
@dialog.bind('Destroy') {
|
475
|
+
unless teardownDone
|
476
|
+
goOn.value = '1' # !
|
477
|
+
$tkxxs_.delete(object_id)
|
478
|
+
if CONF
|
479
|
+
CONF[:dialogGeom] = @dialog.geometry
|
480
|
+
##/ CONF.save
|
481
|
+
end
|
482
|
+
teardownDone = true
|
483
|
+
end # unless
|
484
|
+
}
|
485
|
+
|
486
|
+
#---- Top Frame
|
487
|
+
@frame = Frame.new(@dialog) {padding "3 3 3 3"}.
|
488
|
+
pack(:fill=>:both,:expand=>true)
|
489
|
+
|
490
|
+
#---- Label
|
491
|
+
@lbl = Label.new(@frame, :text=>question).
|
492
|
+
grid(:sticky=>'ew')
|
493
|
+
|
494
|
+
#---- Search-Entry
|
495
|
+
filterRegex = nil
|
496
|
+
@entry = Entry.new(@frame) {|ent|
|
497
|
+
grid(:sticky=>'ew')
|
498
|
+
BalloonHelp.new(ent,:text => searchFieldHelp)
|
499
|
+
}
|
500
|
+
|
501
|
+
@entry.bind('Key-Return') { list_entry_chosen( goOn ) }
|
502
|
+
@entry.bind('Key-Down') { self.focus }
|
503
|
+
@entry.bind('Any-KeyRelease') {
|
504
|
+
entryVal = @entry.get
|
505
|
+
begin
|
506
|
+
filterRegex = Regexp.new(entryVal, Regexp::IGNORECASE)
|
507
|
+
populate_list( filterRegex )
|
508
|
+
rescue RegexpError
|
509
|
+
@lbl.text = "RegexpError"
|
510
|
+
else
|
511
|
+
@lbl.text = question
|
512
|
+
end
|
513
|
+
|
514
|
+
}
|
515
|
+
@entry.focus
|
516
|
+
|
517
|
+
# self = TkListbox
|
518
|
+
# TODO: Kein Weg, das interne Padding zu vergr��ern??
|
519
|
+
super(@frame, hash)
|
520
|
+
self.grid(:column=>0,:sticky=>'news')
|
521
|
+
self.bind('ButtonRelease-1') {
|
522
|
+
list_entry_chosen( goOn )
|
523
|
+
}
|
524
|
+
self.bind('Key-Return') {
|
525
|
+
list_entry_chosen( goOn )
|
526
|
+
}
|
527
|
+
|
528
|
+
#---- Scrollbar
|
529
|
+
scrollb = Scrollbar.new(@frame).grid(:column=>1,:row=>2,:sticky=>'ns')
|
530
|
+
self.yscrollbar(scrollb)
|
531
|
+
##self.width = 0 # Width as needed
|
532
|
+
##self.height = 30 # TODO: Make configurable
|
533
|
+
|
534
|
+
#---- Button-Cancel
|
535
|
+
@btn = Button.new(@frame, :text=>"Cancel").
|
536
|
+
grid(:row=>3,:column=>0,:sticky=>'w')
|
537
|
+
@btn.command {
|
538
|
+
self.destroy
|
539
|
+
}
|
540
|
+
|
541
|
+
@frame.grid_columnconfigure(0, :weight=>1)
|
542
|
+
@frame.grid_columnconfigure(1, :weight=>0)
|
543
|
+
@frame.grid_rowconfigure([0,1,3], :weight=>0)
|
544
|
+
@frame.grid_rowconfigure(2, :weight=>1)
|
545
|
+
|
546
|
+
#---- Balloonhelp
|
547
|
+
if @help
|
548
|
+
$tkxxs_[object_id] = {:listBalloonHelp=>@help.dup}
|
549
|
+
list_balloonhelp
|
550
|
+
end
|
551
|
+
|
552
|
+
populate_list(nil)
|
553
|
+
##/ insert(0, *allChoices)
|
554
|
+
##/ self.selection_set 0
|
555
|
+
update
|
556
|
+
end # initialize
|
557
|
+
|
558
|
+
def all_choices_clients_help( aryWithChoices, filterRegex=nil )
|
559
|
+
return [ [], [], nil] if !aryWithChoices || aryWithChoices.empty?
|
560
|
+
|
561
|
+
case aryWithChoices[0].class.to_s
|
562
|
+
when 'String'
|
563
|
+
choices = aryWithChoices
|
564
|
+
clients = choices.dup
|
565
|
+
## choicesAndClients = choices.zip(clients)
|
566
|
+
help = nil
|
567
|
+
when 'Array'
|
568
|
+
choices, clients, tmpHelp = aryWithChoices.transpose
|
569
|
+
## choicesAndClients = aryWithChoices
|
570
|
+
help = tmpHelp if tmpHelp
|
571
|
+
else
|
572
|
+
raise "ERROR: aryWithChoices[0].class must be 'String' or 'Array',\n" +
|
573
|
+
"but is #{ aryWithChoices[0].inspect }.class = #{ aryWithChoices[0].class } "
|
574
|
+
end
|
575
|
+
|
576
|
+
[choices, clients, help]
|
577
|
+
end # all_choices_clients_help
|
578
|
+
|
579
|
+
def populate_list( filterRegex=nil )
|
580
|
+
if filterRegex
|
581
|
+
@choices, @clients, help = filtered_choices_clients_help( filterRegex )
|
582
|
+
else
|
583
|
+
@choices, @clients, help = @allChoices, @allClients, @allHelp
|
584
|
+
end
|
585
|
+
|
586
|
+
self.delete(0, :end)
|
587
|
+
if @choices.empty?
|
588
|
+
self.insert(0, [] )
|
589
|
+
else
|
590
|
+
self.insert(0, *@choices)
|
591
|
+
self.selection_set(0)
|
592
|
+
self.activate(0)
|
593
|
+
## puts sprintf("--DEBUG: $tkxxs_: %1s ", $tkxxs_.inspect) #loe
|
594
|
+
## puts sprintf("--DEBUG: object_id: %1s ", object_id.inspect) #loe
|
595
|
+
## puts sprintf("--DEBUG: help: %1s ", help.inspect) #loe
|
596
|
+
$tkxxs_[object_id][:listBalloonHelp].replace(help) if help
|
597
|
+
end
|
598
|
+
end # populate_list
|
599
|
+
|
600
|
+
def filtered_choices_clients_help( filterRegex )
|
601
|
+
choices = []
|
602
|
+
clients = []
|
603
|
+
help = []
|
604
|
+
@allChoices.each_with_index {|ch, idx|
|
605
|
+
if ch[filterRegex]
|
606
|
+
choices << ch
|
607
|
+
clients << @allClients[idx]
|
608
|
+
help << @allHelp[idx] if @allHelp # ToDo: Gef�hrlich, falls nicht Array
|
609
|
+
end
|
610
|
+
}
|
611
|
+
help = nil if help.empty? # +2010-02-15 F�r pathchooser
|
612
|
+
|
613
|
+
[choices, clients, help]
|
614
|
+
end # filtered_choices_clients_help
|
615
|
+
|
616
|
+
def list_entry_chosen( goOn )
|
617
|
+
idx = self.curselection[0]
|
618
|
+
ans_( idx )
|
619
|
+
goOn.value = '1'
|
620
|
+
end # list_entry_chosen
|
621
|
+
|
622
|
+
def list_balloonhelp( )
|
623
|
+
bbb = BalloonHelp.new(self,
|
624
|
+
:command=>proc{|x,y,bhelp,parent|
|
625
|
+
idx = parent.nearest(y)
|
626
|
+
bhelp.text($tkxxs_[object_id][:listBalloonHelp][idx])
|
627
|
+
}
|
628
|
+
)
|
629
|
+
nil
|
630
|
+
end # list_balloonhelp
|
631
|
+
|
632
|
+
##################################################################
|
633
|
+
# *Returns:*
|
634
|
+
# The right side of +aryWithChoices+ if :returnChoiceAndClient == +false+ (default),
|
635
|
+
# both sides of +aryWithChoices+ if :returnChoiceAndClient == +true+,
|
636
|
+
# +nil+, if 'Cancel' was clicked.
|
637
|
+
def answer( )
|
638
|
+
@goOn.wait
|
639
|
+
self.dialog.destroy
|
640
|
+
idx = @ans
|
641
|
+
if @returnChoiceAndClient
|
642
|
+
@ans = [ @choices[idx], @clients[idx] ]
|
643
|
+
res = @ans
|
644
|
+
else
|
645
|
+
res = @ans ? @clients[idx] : nil
|
646
|
+
end
|
647
|
+
res
|
648
|
+
end # ans
|
649
|
+
|
650
|
+
##/ def answer( opts={:both=>false} )
|
651
|
+
##/ @goOn.wait
|
652
|
+
##/ self.dialog.destroy
|
653
|
+
##/ idx = @ans
|
654
|
+
##/ if opts[:both]
|
655
|
+
##/ @ans = [ @choices[idx], @clients[idx] ]
|
656
|
+
##/ res = @ans
|
657
|
+
##/ else
|
658
|
+
##/ res = @ans ? @clients[idx] : nil
|
659
|
+
##/ end
|
660
|
+
##/ res
|
661
|
+
##/ end # ans
|
662
|
+
|
663
|
+
private
|
664
|
+
|
665
|
+
def private____________________( )
|
666
|
+
end # private____________________
|
667
|
+
|
668
|
+
def ans_( ans ) # Is there an easier way?
|
669
|
+
@ans = ans if ans
|
670
|
+
end # ans=
|
671
|
+
|
672
|
+
end # class SingleChoiceD
|
673
|
+
|
674
|
+
##########################################################################
|
675
|
+
##########################################################################
|
676
|
+
# MultiChoiceD has no search box like SingleChoiceD, up to now.
|
677
|
+
class MultiChoiceD < TkListbox
|
678
|
+
include Tk::Tile
|
679
|
+
CONF = nil unless defined?(CONF)
|
680
|
+
attr_accessor :dialog # in: "self.dialog
|
681
|
+
|
682
|
+
##################################################################
|
683
|
+
# See: TKXXS::multi_choice
|
684
|
+
def initialize( aryWithChoices, help=nil, hash=nil )
|
685
|
+
aryWithChoices, help, hash = TKXXS_CLASSES.args_1(aryWithChoices, help, hash)
|
686
|
+
hash = {
|
687
|
+
:selectmode => :multiple,
|
688
|
+
:question => "Choose multiple:",
|
689
|
+
:help => nil,
|
690
|
+
:bd=>'2',
|
691
|
+
:title=>'Please choose multiple...',
|
692
|
+
:returnChoiceAndClient=>false
|
693
|
+
}.merge(hash)
|
694
|
+
|
695
|
+
# Necessary, because hash[:configSection] is deleted later on
|
696
|
+
# in args_2.
|
697
|
+
CONF.section = hash[:configSection] if CONF && hash[:configSection]
|
698
|
+
|
699
|
+
help,hash,question,title,searchFieldHelp,@returnChoiceAndClient=
|
700
|
+
TKXXS_CLASSES.args_2(help,hash,question,:title,:searchFieldHelp,:returnChoiceAndClient)
|
701
|
+
|
702
|
+
##/ question = hash[:question] unless question
|
703
|
+
##/ help = hash[:help] unless help
|
704
|
+
##/ CONF.section = hash[:configSection] if CONF
|
705
|
+
##/
|
706
|
+
##/ hash.delete :question
|
707
|
+
##/ hash.delete :help
|
708
|
+
##/ hash.delete :configSection
|
709
|
+
|
710
|
+
teardownDone = false
|
711
|
+
@ans = nil
|
712
|
+
@goOn = goOn = TkVariable.new
|
713
|
+
# Because you cannot use instance-vars in procs. TODO: smarter way?
|
714
|
+
case aryWithChoices[0].class.to_s
|
715
|
+
when 'String'
|
716
|
+
choices = aryWithChoices
|
717
|
+
clients = choices.dup
|
718
|
+
choicesAndClients = choices.zip(clients)
|
719
|
+
when 'Array'
|
720
|
+
choices, clients, tmpHelp = aryWithChoices.transpose
|
721
|
+
choicesAndClients = aryWithChoices
|
722
|
+
help = tmpHelp if tmpHelp
|
723
|
+
else
|
724
|
+
raise "ERROR: aryWithChoices[0].class must be 'String' or 'Array',\nbut is #{ aryWithChoices[0].inspect }.class = #{ aryWithChoices[0].class } "
|
725
|
+
end
|
726
|
+
|
727
|
+
Tk.update # Show any calling widget first to make it lower than @dialog
|
728
|
+
#---- Toplevel: @dialog
|
729
|
+
@dialog=Toplevel.new() {title(title)}
|
730
|
+
@dialog.geometry = CONF[:dialogGeom] if CONF
|
731
|
+
@dialog.bind('Destroy') {
|
732
|
+
unless teardownDone
|
733
|
+
goOn.value = '1' # !
|
734
|
+
if CONF
|
735
|
+
CONF[:dialogGeom] = @dialog.geometry
|
736
|
+
end
|
737
|
+
teardownDone = true
|
738
|
+
end # unless
|
739
|
+
}
|
740
|
+
|
741
|
+
#---- Frame
|
742
|
+
@frame = Frame.new(@dialog) {padding "3 3 3 3"}.
|
743
|
+
pack(:fill=>:both,:expand=>true)
|
744
|
+
|
745
|
+
#---- Label
|
746
|
+
@lbl = Label.new(@frame){
|
747
|
+
text question
|
748
|
+
font $font if $font
|
749
|
+
wraplength '400'
|
750
|
+
justify 'left'
|
751
|
+
}
|
752
|
+
@lbl.grid(:sticky=>'news')
|
753
|
+
|
754
|
+
#---- self = TkListbox
|
755
|
+
super(@frame, hash)
|
756
|
+
# TODO: Is there no way to increase the internal padding??
|
757
|
+
#self.pack(:side=>:left,:expand=>1,:fill=>:both)
|
758
|
+
self.grid(:sticky=>'news')
|
759
|
+
|
760
|
+
#---- Scrollbar
|
761
|
+
scrollb = Scrollbar.new(@frame).
|
762
|
+
grid(:row=>1,:column=>1,:sticky=>'news')
|
763
|
+
self.yscrollbar(scrollb)
|
764
|
+
|
765
|
+
#---- Button-Cancel
|
766
|
+
@btn = Button.new(@frame, :text=>"Cancel").
|
767
|
+
grid(:row=>2,:column=>0,:sticky=>'w')
|
768
|
+
@btn.command {
|
769
|
+
self.destroy
|
770
|
+
}
|
771
|
+
|
772
|
+
#---- Button-OK
|
773
|
+
@btn = Button.new(@frame, :text=>"OK").
|
774
|
+
grid(:row=>2,:column=>0,:sticky=>'e')
|
775
|
+
@btn.command {
|
776
|
+
ans = []
|
777
|
+
# TODO: Change to self.curselection like in SingleChoiceD
|
778
|
+
begin
|
779
|
+
choicesAndClients.each_with_index {|cc, idx|
|
780
|
+
ans << cc if self.selection_includes(idx)
|
781
|
+
}
|
782
|
+
rescue
|
783
|
+
ans = []
|
784
|
+
end
|
785
|
+
ans_( ans )
|
786
|
+
goOn.value = '1'
|
787
|
+
}
|
788
|
+
|
789
|
+
@frame.grid_columnconfigure(0, :weight=>1)
|
790
|
+
@frame.grid_columnconfigure(1, :weight=>0)
|
791
|
+
@frame.grid_rowconfigure([0,2], :weight=>0)
|
792
|
+
@frame.grid_rowconfigure(1, :weight=>1)
|
793
|
+
insert(0, *choices)
|
794
|
+
@lbl.bind('Configure'){
|
795
|
+
@lbl.wraplength = TkWinfo.width(@frame) - 40
|
796
|
+
}
|
797
|
+
|
798
|
+
#---- Balloonhelp
|
799
|
+
if help
|
800
|
+
BalloonHelp.new(self,
|
801
|
+
:command=>proc{|x,y,bhelp,parent|
|
802
|
+
idx = parent.nearest(y)
|
803
|
+
bhelp.text( help[idx] )
|
804
|
+
}
|
805
|
+
)
|
806
|
+
end
|
807
|
+
|
808
|
+
update
|
809
|
+
end # initialize
|
810
|
+
|
811
|
+
##/ def answer( )
|
812
|
+
##/ @goOn.wait
|
813
|
+
##/ self.dialog.destroy # includes grab("release")
|
814
|
+
##/ @ans = [] unless @ans
|
815
|
+
##/ @ans
|
816
|
+
##/ end # ans
|
817
|
+
##################################################################
|
818
|
+
# *Returns:*
|
819
|
+
# * An Array of the chosen right sides of +aryWithChoices+ if :returnChoiceAndClient == +false+ (default),
|
820
|
+
# * An Array of the chosen right and left sides of +aryWithChoices+ if :returnChoiceAndClient == +true+,
|
821
|
+
# * +nil+, if 'Cancel' was clicked.
|
822
|
+
#
|
823
|
+
def answer( )
|
824
|
+
@goOn.wait
|
825
|
+
self.dialog.destroy
|
826
|
+
@ans = [] unless @ans
|
827
|
+
|
828
|
+
if @returnChoiceAndClient
|
829
|
+
return @ans.transpose[0].zip(@ans.transpose[1]) # wg. help
|
830
|
+
else
|
831
|
+
return @ans.transpose[1]
|
832
|
+
end
|
833
|
+
end # ans
|
834
|
+
|
835
|
+
private
|
836
|
+
|
837
|
+
def private____________________( )
|
838
|
+
end # private____________________
|
839
|
+
|
840
|
+
def ans_( ans ) # Is there an easier way?
|
841
|
+
@ans = ans.dup if ans
|
842
|
+
ans
|
843
|
+
end # ans=
|
844
|
+
|
845
|
+
end # class MultiChoiceD
|
846
|
+
|
847
|
+
class FileAndDirChooser
|
848
|
+
include Tk::Tile
|
849
|
+
CONF = nil unless defined?(CONF)
|
850
|
+
|
851
|
+
attr_accessor :dialog # in: "self.dialog
|
852
|
+
|
853
|
+
##########################################################################
|
854
|
+
# This class provides the common functions for all dir- and file-choosers.
|
855
|
+
#
|
856
|
+
# To get this dialog explain, run the example and point the mouse
|
857
|
+
# at each button.
|
858
|
+
#
|
859
|
+
# *Params*:
|
860
|
+
# * +initialdir+ - (String, optional) Initial dir; default = +nil+
|
861
|
+
# -> Working dir at the time of calling this method.
|
862
|
+
# * +help+ - (String, optional) ; Text used in the BalloonHelp;
|
863
|
+
# default = +nil+ -> No help.
|
864
|
+
# * +hash+ - (Hash, optional)
|
865
|
+
# * <tt>:initialdir</tt> - Like above.
|
866
|
+
# * <tt>:help</tt> - Like above.
|
867
|
+
# * <tt>:mode</tt> - One of :choosedir, :openfile, :openfiles, :savefile.
|
868
|
+
# * <tt>:question</tt> - (String) Your question; +nil+ -> no question.
|
869
|
+
# * <tt>:title</tt> - (String) Title of the dialog window.
|
870
|
+
# * <tt>:defaultEntry</tt> - (String) Path, shown in the entry field.
|
871
|
+
# * <tt>:validate</tt> - +true+ or +false+; if true, a valid path
|
872
|
+
# must be chosen, canceling the dialog is not possible.
|
873
|
+
# * <tt>:configSection</tt> - (any String, Integer or Float or nil) Not
|
874
|
+
# important. Sets the section in the config-file, where for example the
|
875
|
+
# window size and position is stored.
|
876
|
+
def initialize( initialdir=nil,help=nil,hash=nil )
|
877
|
+
initialdir, help, hash =
|
878
|
+
TKXXS_CLASSES.args_1( initialdir,help,hash )
|
879
|
+
|
880
|
+
# Must always include: :question, :help
|
881
|
+
hash = {
|
882
|
+
:mode=>:openfiles,
|
883
|
+
:initialdir => Dir.pwd,
|
884
|
+
:question => "Please choose the desired files:",
|
885
|
+
:help => nil,
|
886
|
+
:title => 'Choose Files',
|
887
|
+
:defaultEntry => '',
|
888
|
+
:validate=>nil,
|
889
|
+
}.merge(hash)
|
890
|
+
|
891
|
+
# Necessary, because hash[:configSection] is deleted later on
|
892
|
+
# in args_2.
|
893
|
+
CONF.section = hash[:configSection] if CONF && hash[:configSection]
|
894
|
+
help,hash,question,initialdirH,mode,defaultEntry,@validate =
|
895
|
+
TKXXS_CLASSES.args_2(help,hash,question,:initialdir,:mode,:defaultEntry,:validate)
|
896
|
+
hash[:initialdir] = initialdir || initialdirH # Tja, etwas umstaendlich
|
897
|
+
|
898
|
+
@teardownDone = false
|
899
|
+
@mode = mode
|
900
|
+
@paths = nil
|
901
|
+
@ans = nil
|
902
|
+
@goOn = goOn = TkVariable.new
|
903
|
+
# Because you cannot use instance-vars in procs. TODO: smarter way?
|
904
|
+
old_section = CONF.section
|
905
|
+
CONF.section = nil # Following always stored in section=nil
|
906
|
+
@recent_dirs_size = CONF[:recentDirsSize]
|
907
|
+
@recent_files_size = CONF[:recentFilesSize]
|
908
|
+
CONF.section = old_section
|
909
|
+
|
910
|
+
|
911
|
+
Tk.update # Show any calling widget first to make it lower than @dialog
|
912
|
+
|
913
|
+
#---- Toplevel: @dialog
|
914
|
+
@dialog=Tk::Toplevel.new() {title(hash[:title])}
|
915
|
+
@dialog.geometry = CONF[:pathChooserGeom] if CONF
|
916
|
+
|
917
|
+
# This is neccessary for handling the close-button ('X') of the window
|
918
|
+
@dialog.protocol(:WM_DELETE_WINDOW) {validate_and_leave}
|
919
|
+
|
920
|
+
##/ @dialog.bind('Destroy') {
|
921
|
+
##/ unless teardownDone
|
922
|
+
##/ goOn.value = '1' # !
|
923
|
+
##/ $tkxxs_.delete(object_id)
|
924
|
+
##/ if CONF
|
925
|
+
##/ CONF[:pathChooserGeom] = @dialog.geometry
|
926
|
+
##/ CONF.section = nil
|
927
|
+
##/ ##/ CONF.save
|
928
|
+
##/ end
|
929
|
+
##/ teardownDone = true
|
930
|
+
##/ end # unless
|
931
|
+
##/ }
|
932
|
+
|
933
|
+
#---- Top Frame
|
934
|
+
@frame = Frame.new(@dialog) {padding "3 3 3 3"}.
|
935
|
+
pack(:fill=>:both,:expand=>true)
|
936
|
+
|
937
|
+
#---- Label (question)
|
938
|
+
@qLbl = question_lbl(question,help).grid(:columnspan=>6,:sticky=>'nsew')
|
939
|
+
|
940
|
+
#---- Labels ("Recent", "Favorites", ...)
|
941
|
+
@recLbl = recent_lbl.grid(:column=>0,:columnspan=>2,:row=>1)
|
942
|
+
@favLbl = favorite_lbl.grid(:column=>2,:columnspan=>2,:row=>1)
|
943
|
+
@pasteLbl = paste_lbl.grid(:column=>4,:row=>1)
|
944
|
+
|
945
|
+
#---- Buttons ("Dirs", "Files", "Browse", ...
|
946
|
+
@recDirsBtn = recent_dirs_btn(hash).grid(:row=>2,:column=>0)
|
947
|
+
@recFilesBtn = recent_files_btn.grid(:row=>2,:column=>1)
|
948
|
+
@favDirsBtn = favorite_dirs_btn(hash).grid(:row=>2,:column=>2)
|
949
|
+
@favFilesBtn = favorite_files_btn.grid(:row=>2,:column=>3)
|
950
|
+
@pasteBtn = paste_btn.grid(:row=>2,:column=>4)
|
951
|
+
@browseBtn = browse_btn(hash).grid(:row=>2,:column=>5)
|
952
|
+
|
953
|
+
#---- Entry (Path)
|
954
|
+
@entry = entry(defaultEntry).grid(:row=>3,:column=>0,:columnspan=>6,:sticky=>'ew')
|
955
|
+
|
956
|
+
#---- Buttons ("Cancel", "OK")
|
957
|
+
@cancelBtn = cancel_btn.grid(:row=>4,:column=>0)
|
958
|
+
@okBtn = ok_btn.grid(:row=>4,:column=>5)
|
959
|
+
|
960
|
+
#---- CheckButton ("Add to Favorites")
|
961
|
+
@favChkLbl = favorite_chk_label.grid(:row=>4,:column=>1,:columnspan=>2,:sticky=>'e')
|
962
|
+
@favDirsChkVal = nil
|
963
|
+
@favDirsChk = add2favorites_dirs_chk.grid(:row=>4,:column=>3,:columnspan=>1)
|
964
|
+
@favFilesChkVal = nil
|
965
|
+
unless @mode == :choosedir
|
966
|
+
@favFilesChk = add2favorites_files_chk.
|
967
|
+
grid(:row=>4,:column=>4,:columnspan=>1,:sticky=>'w')
|
968
|
+
end
|
969
|
+
|
970
|
+
#---- Text (showing other path formats)
|
971
|
+
@txt = text2.grid(:row=>5,:column=>0,:columnspan=>6,:sticky=>'sew')
|
972
|
+
|
973
|
+
#---- grid_configure
|
974
|
+
@frame.grid_columnconfigure([0,1,2,3,4,5], :weight=>1)
|
975
|
+
@frame.grid_rowconfigure([0,1,2,3,4], :weight=>0)
|
976
|
+
@frame.grid_rowconfigure([5], :weight=>1)
|
977
|
+
|
978
|
+
@entry.focus
|
979
|
+
Tk.update
|
980
|
+
##/ @dialog.raise
|
981
|
+
end # initialize
|
982
|
+
|
983
|
+
##################################################################
|
984
|
+
# For FileAndDirChooser, @validate is simply true or false. If
|
985
|
+
# user presses "Cancel" or the close button ('X') in the upper
|
986
|
+
# right, this is detected and user is informed by a message box.
|
987
|
+
# Otherwise, all chosen paths already have been checked "inline",
|
988
|
+
# hence no further validation is neccessary.
|
989
|
+
def validate_and_leave( )
|
990
|
+
# path(s) is/are not valid.
|
991
|
+
# TODO: Missleading method name; better(?): handle_invalid_path
|
992
|
+
if @validate
|
993
|
+
# Retry or completly exit the application
|
994
|
+
ans = Tk.messageBox(:icon=>:warning, :type=>:retrycancel,
|
995
|
+
:title=>'Message',
|
996
|
+
:message=>"INVALID ANSWER\n\nCANCEL will completely EXIT the application!"
|
997
|
+
## :detail=>"blablabla\n"*50
|
998
|
+
)
|
999
|
+
if ans == 'cancel'
|
1000
|
+
Tk.exit
|
1001
|
+
exit # Todo: n�tig?
|
1002
|
+
end
|
1003
|
+
else
|
1004
|
+
set_paths(nil) # uses the invalid path
|
1005
|
+
end
|
1006
|
+
end # validate_and_leave
|
1007
|
+
|
1008
|
+
def set_paths( paths )
|
1009
|
+
@paths = paths
|
1010
|
+
favorites_and_recent(paths) if paths && !paths.empty?
|
1011
|
+
@goOn.value = '1'
|
1012
|
+
nil
|
1013
|
+
end # set_paths
|
1014
|
+
|
1015
|
+
##################################################################
|
1016
|
+
# When dialog is finished, always +set_paths+ must be called. For
|
1017
|
+
# 'Cancel', use set_paths(nil)
|
1018
|
+
#
|
1019
|
+
# *Returns*: (String or Array or nil) Path(s); +nil+, if 'Cancel'
|
1020
|
+
# was clicked.
|
1021
|
+
def answer( )
|
1022
|
+
@dialog.raise # Raise dialog above all other windows
|
1023
|
+
@goOn.wait # Force hold on of the dialog
|
1024
|
+
# Tear down
|
1025
|
+
unless @teardownDone # TODO: not neccessary anymore?
|
1026
|
+
$tkxxs_.delete(object_id)
|
1027
|
+
if CONF # Save settings (e.g., window size)
|
1028
|
+
CONF[:pathChooserGeom] = @dialog.geometry
|
1029
|
+
##/ CONF.save
|
1030
|
+
end
|
1031
|
+
@teardownDone = true
|
1032
|
+
##/ @goOn.value = '1'
|
1033
|
+
@dialog.destroy # Remove dialog window
|
1034
|
+
end # unless
|
1035
|
+
Tk.update
|
1036
|
+
|
1037
|
+
@paths # return the "answer"
|
1038
|
+
end # ans
|
1039
|
+
|
1040
|
+
##/ ##################################################################
|
1041
|
+
##/ # Saves CONF!
|
1042
|
+
##/ def dialog_destroy( )
|
1043
|
+
##/ unless @teardownDone
|
1044
|
+
##/ $tkxxs_.delete(object_id)
|
1045
|
+
##/ if CONF
|
1046
|
+
##/ CONF[:pathChooserGeom] = @dialog.geometry
|
1047
|
+
##/ CONF.section = nil
|
1048
|
+
##/ ##/ CONF.save
|
1049
|
+
##/ end
|
1050
|
+
##/ @teardownDone = true
|
1051
|
+
##/ ##/ @goOn.value = '1'
|
1052
|
+
##/ @dialog.destroy
|
1053
|
+
##/ end # unless
|
1054
|
+
##/ nil
|
1055
|
+
##/ end # dialog_destroy
|
1056
|
+
|
1057
|
+
def question_lbl( question, help)
|
1058
|
+
lbl = Label.new(@frame, :text=>question){|w|
|
1059
|
+
BalloonHelp.new(w,:text=>help)
|
1060
|
+
}
|
1061
|
+
end # question_lbl
|
1062
|
+
|
1063
|
+
def recent_lbl( )
|
1064
|
+
lbl = Label.new(@frame, :text=>"Recent")
|
1065
|
+
end # recent_lbl
|
1066
|
+
|
1067
|
+
def favorite_lbl( )
|
1068
|
+
lbl = Label.new(@frame, :text=>"Favorites")
|
1069
|
+
end # favorite_lbl
|
1070
|
+
|
1071
|
+
def paste_lbl( )
|
1072
|
+
Label.new(@frame, :text=>"Clipboard")
|
1073
|
+
end # paste_lbl
|
1074
|
+
|
1075
|
+
def recent_dirs_btn( hash )
|
1076
|
+
btn = Button.new(@frame, :text=>"Dirs"){|w|
|
1077
|
+
BalloonHelp.new(w,:text=>"Recent directories")
|
1078
|
+
}
|
1079
|
+
btn.command {
|
1080
|
+
if CONF
|
1081
|
+
dirs = CONF[:recentDirs] || []
|
1082
|
+
## dir = SingleChoiceD.new(dirs.sort).answer
|
1083
|
+
dir = SingleChoiceD.new(dirs).answer # unsorted
|
1084
|
+
if dir
|
1085
|
+
hash2 = hash.merge({:initialdir=>dir}).dup
|
1086
|
+
##/ filesStr = Tk.getOpenFile(hash2)
|
1087
|
+
##/ set_paths( tks_result_to_ary( filesStr ) )
|
1088
|
+
case @mode
|
1089
|
+
##/ when :choosedir; set_paths(dir)
|
1090
|
+
when :choosedir; validate_and_set_path( dir )
|
1091
|
+
when :openfiles; choose(hash2)
|
1092
|
+
when :openfile; choose(hash2)
|
1093
|
+
when :savefile; choose(hash2)
|
1094
|
+
else; raise
|
1095
|
+
end # case
|
1096
|
+
end
|
1097
|
+
end # if CONF
|
1098
|
+
}
|
1099
|
+
btn
|
1100
|
+
end # recent_dirs_btn
|
1101
|
+
|
1102
|
+
def recent_files_btn( )
|
1103
|
+
btn = Button.new(@frame, :text=>"Files"){|w|
|
1104
|
+
BalloonHelp.new(w,:text=>"Recent files")
|
1105
|
+
}
|
1106
|
+
btn.command {
|
1107
|
+
if CONF
|
1108
|
+
files = CONF[:recentFiles] || []
|
1109
|
+
## filePath = SingleChoiceD.new(files.sort).answer
|
1110
|
+
filePath = SingleChoiceD.new(files).answer # unsorted
|
1111
|
+
if filePath
|
1112
|
+
case @mode
|
1113
|
+
when :choosedir
|
1114
|
+
validate_and_set_path(File.dirname(filePath))
|
1115
|
+
when :openfiles
|
1116
|
+
validate_and_set_path( filePath )
|
1117
|
+
when :openfile, :savefile
|
1118
|
+
validate_and_set_path( filePath )
|
1119
|
+
else; raise
|
1120
|
+
end # case
|
1121
|
+
end
|
1122
|
+
end # if CONF
|
1123
|
+
}
|
1124
|
+
btn
|
1125
|
+
end # recent_files_btn
|
1126
|
+
|
1127
|
+
def favorite_dirs_btn( hash )
|
1128
|
+
btn = Button.new(@frame, :text=>"Dirs"){|w|
|
1129
|
+
BalloonHelp.new(w,:text=>"Favorite directories")
|
1130
|
+
}
|
1131
|
+
btn.command {
|
1132
|
+
if CONF
|
1133
|
+
favDirs = CONF[:favoriteDirs] || []
|
1134
|
+
dir = SingleChoiceD.new(favDirs.sort).answer
|
1135
|
+
if dir
|
1136
|
+
hash2 = hash.merge({:initialdir=>dir}).dup
|
1137
|
+
##/ filesStr = Tk.getOpenFile(hash2)
|
1138
|
+
##/ set_paths( tks_result_to_ary( filesStr ) )
|
1139
|
+
case @mode
|
1140
|
+
when :choosedir; validate_and_set_path( dir )
|
1141
|
+
when :openfiles; choose(hash2)
|
1142
|
+
when :openfile; choose(hash2)
|
1143
|
+
when :savefile; choose(hash2)
|
1144
|
+
else; raise
|
1145
|
+
end # case
|
1146
|
+
end
|
1147
|
+
end # if CONF
|
1148
|
+
}
|
1149
|
+
btn
|
1150
|
+
end # favorite_dirs_btn
|
1151
|
+
|
1152
|
+
def favorite_files_btn( )
|
1153
|
+
btn = Button.new(@frame, :text=>"Files"){|w|
|
1154
|
+
BalloonHelp.new(w,:text=>"Favorite files")
|
1155
|
+
}
|
1156
|
+
btn.command {
|
1157
|
+
if CONF
|
1158
|
+
favFiles = CONF[:favoriteFiles] || []
|
1159
|
+
filePath = SingleChoiceD.new(favFiles.sort).answer
|
1160
|
+
if filePath
|
1161
|
+
case @mode
|
1162
|
+
when :choosedir
|
1163
|
+
validate_and_set_path(File.dirname(filePath))
|
1164
|
+
when :openfiles
|
1165
|
+
validate_and_set_path( [filePath] )
|
1166
|
+
when :openfile, :savefile
|
1167
|
+
validate_and_set_path( filePath )
|
1168
|
+
else; raise
|
1169
|
+
end # case
|
1170
|
+
end
|
1171
|
+
end # if CONF
|
1172
|
+
}
|
1173
|
+
btn
|
1174
|
+
end # favorite_files_btn
|
1175
|
+
|
1176
|
+
def paste_btn( )
|
1177
|
+
btn = Button.new(@frame, :text=>"Paste"){|w|
|
1178
|
+
BalloonHelp.new(w,:text=>"Paste clipboard to entry field")
|
1179
|
+
}
|
1180
|
+
btn.command {
|
1181
|
+
@entry.delete(0, :end)
|
1182
|
+
begin
|
1183
|
+
clip = TkClipboard.get
|
1184
|
+
rescue => err # Clipboard no String
|
1185
|
+
clip = "Nothing usefull in clipboard"
|
1186
|
+
end
|
1187
|
+
@entry.insert(0, clip)
|
1188
|
+
}
|
1189
|
+
btn
|
1190
|
+
end # paste_btn
|
1191
|
+
|
1192
|
+
def browse_btn( hash )
|
1193
|
+
btn = Button.new(@frame, :text=>"Browse"){|w|
|
1194
|
+
BalloonHelp.new(w,:text=>"Search for directory or file manually")
|
1195
|
+
}
|
1196
|
+
entry = @entry
|
1197
|
+
btn.command {
|
1198
|
+
entryStr = @entry.get.strip.gsub('\\', '/').chomp('/')
|
1199
|
+
unless entryStr.empty?
|
1200
|
+
if File.directory?(entryStr)
|
1201
|
+
hash[:initialdir] = entryStr
|
1202
|
+
elsif File.file?(entryStr)
|
1203
|
+
hash[:initialdir] = File.dirname(entryStr)
|
1204
|
+
end
|
1205
|
+
end
|
1206
|
+
choose(hash)
|
1207
|
+
}
|
1208
|
+
end # browse_btn
|
1209
|
+
|
1210
|
+
def entry( defaultEntry )
|
1211
|
+
entry = Entry.new(@frame) {|ent|
|
1212
|
+
BalloonHelp.new(ent,:text=>"Type or paste a path here.")
|
1213
|
+
}
|
1214
|
+
entry.insert(0, defaultEntry)
|
1215
|
+
entry.bind('Key-Return') { use_entry(@entry.get) }
|
1216
|
+
end # entry
|
1217
|
+
|
1218
|
+
def cancel_btn( )
|
1219
|
+
btn = Button.new(@frame, :text=>"Cancel"){|w|
|
1220
|
+
BalloonHelp.new(w,:text=>"Do nothing")
|
1221
|
+
}
|
1222
|
+
btn.command {
|
1223
|
+
validate_and_leave
|
1224
|
+
###set_paths(nil)
|
1225
|
+
##/ dialog_destroy
|
1226
|
+
}
|
1227
|
+
btn
|
1228
|
+
end # cancel_btn
|
1229
|
+
|
1230
|
+
def ok_btn( )
|
1231
|
+
btn = Button.new(@frame, :text=>"Use entry"){|w|
|
1232
|
+
BalloonHelp.new(w,:text=>"Use path from entry box")
|
1233
|
+
}
|
1234
|
+
btn.command { use_entry(@entry.get) }
|
1235
|
+
end # ok_btn
|
1236
|
+
|
1237
|
+
def use_entry( str )
|
1238
|
+
str = str.strip.gsub('\\', '/').chomp('/')
|
1239
|
+
validate_and_set_path( str )
|
1240
|
+
end # use_entry
|
1241
|
+
|
1242
|
+
def validate_and_set_path( str )
|
1243
|
+
case @mode
|
1244
|
+
when :choosedir
|
1245
|
+
if File.directory?(str)
|
1246
|
+
set_paths(str)
|
1247
|
+
elsif File.file?(str)
|
1248
|
+
set_paths( File.dirname(str) )
|
1249
|
+
else
|
1250
|
+
@txt.replace('0.0', :end, %Q<"#{ str }" is no > +
|
1251
|
+
"valid directory, please choose a valid one!")
|
1252
|
+
@entry.focus
|
1253
|
+
end
|
1254
|
+
when :openfiles, :openfile
|
1255
|
+
if File.file?(str)
|
1256
|
+
@mode == :openfile ? set_paths(str) : set_paths([str])
|
1257
|
+
else
|
1258
|
+
@txt.replace('0.0', :end, %Q<"#{ str }" is no > +
|
1259
|
+
"valid file, please choose a valid one!")
|
1260
|
+
@entry.focus
|
1261
|
+
end
|
1262
|
+
when :savefile
|
1263
|
+
if @validate
|
1264
|
+
dir = File.dirname(str)
|
1265
|
+
if File.directory?(dir)
|
1266
|
+
set_paths(str)
|
1267
|
+
else
|
1268
|
+
@txt.replace('0.0', :end, %Q<"#{ dir }" is no > +
|
1269
|
+
"valid directory, please choose a valid one!")
|
1270
|
+
@entry.focus
|
1271
|
+
end
|
1272
|
+
else
|
1273
|
+
# if file does not exist: create file and dir
|
1274
|
+
set_paths(str)
|
1275
|
+
end
|
1276
|
+
else; raise
|
1277
|
+
end # case
|
1278
|
+
nil
|
1279
|
+
end # validate_and_set_path
|
1280
|
+
|
1281
|
+
def favorite_chk_label( )
|
1282
|
+
lbl = Label.new(@frame, :text=>"Add to favorites")
|
1283
|
+
end # favorite_chk_label
|
1284
|
+
|
1285
|
+
def add2favorites_dirs_chk( )
|
1286
|
+
favChkVar = TkVariable.new(0)
|
1287
|
+
favChk = CheckButton.new(@frame) {|w|
|
1288
|
+
text 'dirs'
|
1289
|
+
variable favChkVar
|
1290
|
+
##/ onvalue 'metric'; offvalue 'imperial'
|
1291
|
+
BalloonHelp.new(w,:text=>"If checked, the path(s) of files or directories you are going to choose will be added to the favorite directories.")
|
1292
|
+
}
|
1293
|
+
favChk.command { self.fav_dirs_chk_changed(favChkVar.value) }
|
1294
|
+
favChk
|
1295
|
+
end # add2favorites_dirs_chk
|
1296
|
+
|
1297
|
+
def add2favorites_files_chk( )
|
1298
|
+
favChkVar = TkVariable.new(0)
|
1299
|
+
favChk = CheckButton.new(@frame) {|w|
|
1300
|
+
text 'files'
|
1301
|
+
variable favChkVar
|
1302
|
+
##/ onvalue 'metric'; offvalue 'imperial'
|
1303
|
+
BalloonHelp.new(w,:text=>"If checked, the files(s) you are going to choose will be added to the favorite files.")
|
1304
|
+
}
|
1305
|
+
favChk.command { self.fav_files_chk_changed(favChkVar.value) }
|
1306
|
+
favChk
|
1307
|
+
end # add2favorites_files_chk
|
1308
|
+
|
1309
|
+
def fav_dirs_chk_changed( val )
|
1310
|
+
@favDirsChkVal = val == '1' ? true : false
|
1311
|
+
@favDirsChkVal
|
1312
|
+
end # fav_dirs_chk_changed
|
1313
|
+
|
1314
|
+
def fav_files_chk_changed( val )
|
1315
|
+
@favFilesChkVal = val == '1' ? true : false
|
1316
|
+
@favDirsChk.invoke if @favFilesChkVal && !@favDirsChkVal
|
1317
|
+
@favFilesChkVal
|
1318
|
+
end # fav_files_chk_changed
|
1319
|
+
|
1320
|
+
def text2( )
|
1321
|
+
txt = TkText.new(@frame)
|
1322
|
+
tagSel = TkTextTagSel.new(txt)
|
1323
|
+
txt.bind('Control-Key-a') {
|
1324
|
+
# From: "Using Control-a to select all text in a text widget : TCL", http://objectmix.com/tcl/35276-using-control-select-all-text-text-widget.html
|
1325
|
+
tagSel.add('0.0', :end)
|
1326
|
+
Kernel.raise TkCallbackBreak
|
1327
|
+
}
|
1328
|
+
txt
|
1329
|
+
end # text2
|
1330
|
+
|
1331
|
+
def favorites_and_recent( paths )
|
1332
|
+
fav_dirs(paths) if @favDirsChkVal # For any mode
|
1333
|
+
fav_files(paths) if @favFilesChkVal # For any mode but :choosedir; no @favFilesChk for chooseDirectory
|
1334
|
+
recent_dirs(paths)
|
1335
|
+
recent_files(paths) unless @mode == :choosedir
|
1336
|
+
CONF.save
|
1337
|
+
nil
|
1338
|
+
end # favorites_and_recents
|
1339
|
+
|
1340
|
+
def fav_dirs( paths )
|
1341
|
+
return nil unless paths
|
1342
|
+
paths = paths.dup
|
1343
|
+
favDirs = CONF[:favoriteDirs] || []
|
1344
|
+
paths = [paths] if paths.class == String
|
1345
|
+
paths.map! {|p|
|
1346
|
+
File.file?(p) ? File.dirname(p) : p
|
1347
|
+
}
|
1348
|
+
favDirs.concat(paths)
|
1349
|
+
favDirs.uniq!
|
1350
|
+
CONF[:favoriteDirs] = favDirs
|
1351
|
+
favDirs
|
1352
|
+
end # fav_dirs
|
1353
|
+
|
1354
|
+
def fav_files( paths )
|
1355
|
+
return nil unless paths
|
1356
|
+
paths = paths.dup
|
1357
|
+
favFiles = CONF[:favoriteFiles] || []
|
1358
|
+
paths = [paths] if paths.class == String
|
1359
|
+
favFiles.concat(paths)
|
1360
|
+
favFiles.uniq!
|
1361
|
+
CONF[:favoriteFiles] = favFiles
|
1362
|
+
favFiles
|
1363
|
+
end # fav_files
|
1364
|
+
|
1365
|
+
def recent_dirs( paths )
|
1366
|
+
return nil unless paths
|
1367
|
+
paths = paths.dup
|
1368
|
+
dirs = CONF[:recentDirs] || []
|
1369
|
+
paths = [paths] if paths.class == String
|
1370
|
+
paths.map! {|p|
|
1371
|
+
p = File.file?(p) ? File.dirname(p) : p
|
1372
|
+
p = File.directory?(p) ? p : nil
|
1373
|
+
p
|
1374
|
+
}
|
1375
|
+
paths.compact!
|
1376
|
+
dirs = paths + dirs
|
1377
|
+
dirs.uniq!
|
1378
|
+
CONF[:recentDirs] = dirs[0, @recent_dirs_size ]
|
1379
|
+
dirs
|
1380
|
+
end # recent_dirs
|
1381
|
+
|
1382
|
+
def recent_files( paths)
|
1383
|
+
return nil unless paths
|
1384
|
+
paths = paths.dup
|
1385
|
+
files = CONF[:recentFiles] || []
|
1386
|
+
paths = [paths] if paths.class == String
|
1387
|
+
files = paths + files
|
1388
|
+
files.uniq!
|
1389
|
+
CONF[:recentFiles] = files[0, @recent_files_size ]
|
1390
|
+
files
|
1391
|
+
end # recent_files
|
1392
|
+
|
1393
|
+
def tks_result_to_ary( filesListStr )
|
1394
|
+
paths = []
|
1395
|
+
if filesListStr[0,1]=='{'
|
1396
|
+
filesListStr.scan( / \{ ( [^\}]+ ) \} /x) {
|
1397
|
+
paths << $1
|
1398
|
+
}
|
1399
|
+
else
|
1400
|
+
paths = filesListStr.split(' ')
|
1401
|
+
end
|
1402
|
+
|
1403
|
+
paths
|
1404
|
+
end # tks_result_to_ary
|
1405
|
+
end # class FileAndDirChooser
|
1406
|
+
|
1407
|
+
##########################################################################
|
1408
|
+
##########################################################################
|
1409
|
+
class ChooseDirD < FileAndDirChooser
|
1410
|
+
|
1411
|
+
##################################################################
|
1412
|
+
# See: TKXXS.choose_dir
|
1413
|
+
def initialize( initialdir=nil,help=nil,hash=nil )
|
1414
|
+
initialdir, help, hash =
|
1415
|
+
TKXXS_CLASSES.args_1( initialdir,help,hash )
|
1416
|
+
hash = {
|
1417
|
+
:mode => :choosedir,
|
1418
|
+
:question=>"Please choose a directory",
|
1419
|
+
:title=>"Choose Directory",
|
1420
|
+
}.merge(hash)
|
1421
|
+
super(initialdir, help, hash)
|
1422
|
+
end
|
1423
|
+
|
1424
|
+
def choose( hash )
|
1425
|
+
dirStr = Tk.chooseDirectory( hash )
|
1426
|
+
path = dirStr.empty? ? nil : dirStr
|
1427
|
+
|
1428
|
+
if path
|
1429
|
+
set_paths( path )
|
1430
|
+
else
|
1431
|
+
validate_and_leave
|
1432
|
+
end
|
1433
|
+
nil
|
1434
|
+
end # choose
|
1435
|
+
|
1436
|
+
alias :path :answer
|
1437
|
+
end # class ChooseDirD
|
1438
|
+
|
1439
|
+
##########################################################################
|
1440
|
+
##########################################################################
|
1441
|
+
class OpenFilesD < FileAndDirChooser
|
1442
|
+
##################################################################
|
1443
|
+
# See: TKXXS.open_files
|
1444
|
+
def initialize( initialdir=nil,help=nil,hash=nil )
|
1445
|
+
initialdir, help, hash =
|
1446
|
+
TKXXS_CLASSES.args_1( initialdir,help,hash )
|
1447
|
+
hash = {
|
1448
|
+
:mode => :openfiles,
|
1449
|
+
:question=>"Please choose the desired files",
|
1450
|
+
:title=>"Open Files",
|
1451
|
+
:filetypes => [['All files','*']],
|
1452
|
+
:multiple=>true
|
1453
|
+
}.merge(hash)
|
1454
|
+
super(initialdir, help, hash)
|
1455
|
+
end
|
1456
|
+
|
1457
|
+
def choose( hash )
|
1458
|
+
filesStr = Tk.getOpenFile(hash)
|
1459
|
+
if filesStr
|
1460
|
+
set_paths( tks_result_to_ary( filesStr ) )
|
1461
|
+
else
|
1462
|
+
validate_and_leave
|
1463
|
+
end
|
1464
|
+
nil
|
1465
|
+
end # choose
|
1466
|
+
|
1467
|
+
alias :paths :answer
|
1468
|
+
end # class OpenFilesD
|
1469
|
+
|
1470
|
+
##########################################################################
|
1471
|
+
##########################################################################
|
1472
|
+
class OpenFileD < FileAndDirChooser
|
1473
|
+
|
1474
|
+
##################################################################
|
1475
|
+
# See: TKXXS.open_file
|
1476
|
+
def initialize( initialdir=nil,help=nil,hash=nil )
|
1477
|
+
initialdir, help, hash =
|
1478
|
+
TKXXS_CLASSES.args_1( initialdir,help,hash )
|
1479
|
+
hash = {
|
1480
|
+
:mode => :openfile,
|
1481
|
+
:question=>"Please choose the desired file",
|
1482
|
+
:title=>"Open File",
|
1483
|
+
:filetypes => [['All files','*']],
|
1484
|
+
:multiple=>false
|
1485
|
+
}.merge(hash)
|
1486
|
+
super(initialdir, help, hash)
|
1487
|
+
end
|
1488
|
+
|
1489
|
+
def choose( hash )
|
1490
|
+
fileStr = Tk.getOpenFile(hash)
|
1491
|
+
if fileStr
|
1492
|
+
set_paths( fileStr )
|
1493
|
+
else
|
1494
|
+
validate_and_leave
|
1495
|
+
end
|
1496
|
+
nil
|
1497
|
+
end # choose
|
1498
|
+
|
1499
|
+
alias :path :answer
|
1500
|
+
end # class OpenFilesD
|
1501
|
+
|
1502
|
+
##########################################################################
|
1503
|
+
##########################################################################
|
1504
|
+
class SaveFileD < FileAndDirChooser
|
1505
|
+
|
1506
|
+
##################################################################
|
1507
|
+
# See: TKXXS.save_file
|
1508
|
+
def initialize( initialdir=nil,help=nil,hash=nil )
|
1509
|
+
initialdir, help, hash =
|
1510
|
+
TKXXS_CLASSES.args_1( initialdir,help,hash )
|
1511
|
+
hash = {
|
1512
|
+
:mode => :savefile,
|
1513
|
+
:question=>"Please choose, where to save the file:",
|
1514
|
+
:title=>"Save File As...",
|
1515
|
+
:filetypes => [['All files','*']],
|
1516
|
+
:initialfile => 'Untitled', # Default filename, extension will be added automatically by filetypes-setting
|
1517
|
+
:defaultextension => nil,
|
1518
|
+
}.merge(hash)
|
1519
|
+
super(initialdir, help, hash)
|
1520
|
+
end
|
1521
|
+
|
1522
|
+
def choose( hash )
|
1523
|
+
fileStr = Tk.getSaveFile(hash)
|
1524
|
+
path = fileStr.empty? ? nil : fileStr
|
1525
|
+
if path
|
1526
|
+
set_paths( path )
|
1527
|
+
else
|
1528
|
+
validate_and_leave
|
1529
|
+
end
|
1530
|
+
nil
|
1531
|
+
end # choose
|
1532
|
+
|
1533
|
+
alias :path :answer
|
1534
|
+
end # class SaveFileD
|
1535
|
+
|
1536
|
+
##/ ##########################################################################
|
1537
|
+
##/ ##########################################################################
|
1538
|
+
##/ class OpenFileD < OpenFilesD
|
1539
|
+
##/
|
1540
|
+
##/ def initialize( initialdir=nil,help=nil,hash=nil )
|
1541
|
+
##/ initialdir, help, hash =
|
1542
|
+
##/ TKXXS_CLASSES.args_1( initialdir,help,hash )
|
1543
|
+
##/
|
1544
|
+
##/ # Must always include: :question, :help, :configSection
|
1545
|
+
##/ hash = {
|
1546
|
+
##/ :initialdir => Dir.pwd,
|
1547
|
+
##/ :question => "Please choose the desired file:",
|
1548
|
+
##/ :help => nil,
|
1549
|
+
##/ :configSection => nil,
|
1550
|
+
##/ :filetypes => [['All files','*']],
|
1551
|
+
##/ :multiple => 0,
|
1552
|
+
##/ }.merge(hash)
|
1553
|
+
##/
|
1554
|
+
##/ super(initialdir,help,hash)
|
1555
|
+
##/ end # initialize
|
1556
|
+
##/
|
1557
|
+
##/ def path( )
|
1558
|
+
##/ CONF.section = nil if CONF
|
1559
|
+
##/ @paths[0]
|
1560
|
+
##/ end # paths
|
1561
|
+
##/ alias :answer :path
|
1562
|
+
##/ end # class OpenFileD
|
1563
|
+
|
1564
|
+
|
1565
|
+
end # class
|
1566
|
+
|
1567
|
+
|