1#
2#  tkextlib/iwidgets/scrolledtext.rb
3#                               by Hidetoshi NAGAI (nagai@ai.kyutech.ac.jp)
4#
5
6require 'tk'
7require 'tk/text'
8require 'tkextlib/iwidgets.rb'
9
10module Tk
11  module Iwidgets
12    class Scrolledtext < Tk::Iwidgets::Scrolledwidget
13    end
14  end
15end
16
17class Tk::Iwidgets::Scrolledtext
18  TkCommandNames = ['::iwidgets::scrolledtext'.freeze].freeze
19  WidgetClassName = 'Scrolledtext'.freeze
20  WidgetClassNames[WidgetClassName] ||= self
21
22  def __strval_optkeys
23    super() << 'textbackground'
24  end
25  private :__strval_optkeys
26
27  def __font_optkeys
28    super() << 'textfont'
29  end
30  private :__font_optkeys
31
32  ################################
33
34  def initialize(*args)
35    super(*args)
36    @text = component_widget('text')
37  end
38
39  def method_missing(id, *args)
40    if @text.respond_to?(id)
41      @text.__send__(id, *args)
42    else
43      super(id, *args)
44    end
45  end
46
47  ################################
48
49  def child_site
50    window(tk_call(@path, 'childsite'))
51  end
52
53  def clear
54    tk_call(@path, 'clear')
55    self
56  end
57
58  def import(file, idx=nil)
59    if idx
60      tk_call(@path, 'import', file, index(idx))
61    else
62      tk_call(@path, 'import', file)
63    end
64    self
65  end
66
67  def export(file)
68    tk_call(@path, 'export', file)
69    self
70  end
71
72  #####################################
73
74  include TkTextTagConfig
75
76  def tagid(tag)
77    if tag.kind_of?(Tk::Itk::Component)
78      tag.name
79    else
80      super(tag)
81    end
82  end
83  private :tagid
84
85  def bbox(index)
86    list(tk_send('bbox', index))
87  end
88  def compare(idx1, op, idx2)
89    bool(tk_send_without_enc('compare', _get_eval_enc_str(idx1),
90                             op, _get_eval_enc_str(idx2)))
91  end
92
93  def debug
94    bool(tk_send_without_enc('debug'))
95  end
96  def debug=(boolean)
97    tk_send_without_enc('debug', boolean)
98    #self
99    boolean
100  end
101
102  def delete(first, last=None)
103    tk_send_without_enc('delete', first, last)
104    self
105  end
106
107  def dlineinfo(index)
108    list(tk_send_without_enc('dlineinfo', _get_eval_enc_str(index)))
109  end
110
111  def get(*index)
112    _fromUTF8(tk_send_without_enc('get', *index))
113  end
114  def get_displaychars(*index)
115    # Tk8.5 feature
116    get('-displaychars', *index)
117  end
118
119  def image_cget_tkstring(index, slot)
120    _fromUTF8(tk_send_without_enc('image', 'cget',
121                                  _get_eval_enc_str(index), "-#{slot.to_s}"))
122  end
123  def image_cget_strict(index, slot)
124    case slot.to_s
125    when 'text', 'label', 'show', 'data', 'file'
126      _fromUTF8(tk_send_without_enc('image', 'cget',
127                                    _get_eval_enc_str(index), "-#{slot}"))
128    else
129      tk_tcl2ruby(_fromUTF8(tk_send_without_enc('image', 'cget',
130                                                _get_eval_enc_str(index),
131                                                "-#{slot}")))
132    end
133  end
134  def image_cget(index, slot)
135    unless TkItemConfigMethod.__IGNORE_UNKNOWN_CONFIGURE_OPTION__
136      image_cget_strict(index, slot)
137    else
138      begin
139        image_cget_strict(index, slot)
140      rescue => e
141        begin
142          if current_image_configinfo.has_key?(slot.to_s)
143            # error on known option
144            fail e
145          else
146            # unknown option
147            nil
148          end
149        rescue
150          fail e  # tag error
151        end
152      end
153    end
154  end
155
156  def image_configure(index, slot, value=None)
157    if slot.kind_of? Hash
158      _fromUTF8(tk_send_without_enc('image', 'configure',
159                                    _get_eval_enc_str(index),
160                                    *hash_kv(slot, true)))
161    else
162      _fromUTF8(tk_send_without_enc('image', 'configure',
163                                    _get_eval_enc_str(index),
164                                    "-#{slot}",
165                                    _get_eval_enc_str(value)))
166    end
167    self
168  end
169
170  def image_configinfo(index, slot = nil)
171    if TkComm::GET_CONFIGINFO_AS_ARRAY
172      if slot
173        case slot.to_s
174        when 'text', 'label', 'show', 'data', 'file'
175          #conf = tk_split_simplelist(_fromUTF8(tk_send_without_enc('image', 'configure', _get_eval_enc_str(index), "-#{slot}")))
176          conf = tk_split_simplelist(tk_send_without_enc('image', 'configure', _get_eval_enc_str(index), "-#{slot}"), false, true)
177        else
178          #conf = tk_split_list(_fromUTF8(tk_send_without_enc('image', 'configure', _get_eval_enc_str(index), "-#{slot}")))
179          conf = tk_split_list(tk_send_without_enc('image', 'configure', _get_eval_enc_str(index), "-#{slot}"), 0, false, true)
180        end
181        conf[0] = conf[0][1..-1]
182        conf
183      else
184        #tk_split_simplelist(_fromUTF8(tk_send_without_enc('image', 'configure', _get_eval_enc_str(index)))).collect{|conflist|
185        #  conf = tk_split_simplelist(conflist)
186        tk_split_simplelist(tk_send_without_enc('image', 'configure', _get_eval_enc_str(index)), false, false).collect{|conflist|
187          conf = tk_split_simplelist(conflist, false, true)
188          conf[0] = conf[0][1..-1]
189          case conf[0]
190          when 'text', 'label', 'show', 'data', 'file'
191          else
192            if conf[3]
193              if conf[3].index('{')
194                conf[3] = tk_split_list(conf[3])
195              else
196                conf[3] = tk_tcl2ruby(conf[3])
197              end
198            end
199            if conf[4]
200              if conf[4].index('{')
201                conf[4] = tk_split_list(conf[4])
202              else
203                conf[4] = tk_tcl2ruby(conf[4])
204              end
205            end
206          end
207          conf[1] = conf[1][1..-1] if conf.size == 2 # alias info
208          conf
209        }
210      end
211    else # ! TkComm::GET_CONFIGINFO_AS_ARRAY
212      if slot
213        case slot.to_s
214        when 'text', 'label', 'show', 'data', 'file'
215          #conf = tk_split_simplelist(_fromUTF8(tk_send_without_enc('image', 'configure', _get_eval_enc_str(index), "-#{slot}")))
216          conf = tk_split_simplelist(tk_send_without_enc('image', 'configure', _get_eval_enc_str(index), "-#{slot}"), false, true)
217        else
218          #conf = tk_split_list(_fromUTF8(tk_send_without_enc('image', 'configure', _get_eval_enc_str(index), "-#{slot}")))
219          conf = tk_split_list(tk_send_without_enc('image', 'configure', _get_eval_enc_str(index), "-#{slot}"), 0, false, true)
220        end
221        key = conf.shift[1..-1]
222        { key => conf }
223      else
224        ret = {}
225        #tk_split_simplelist(_fromUTF8(tk_send_without_enc('image', 'configure', _get_eval_enc_str(index)))).each{|conflist|
226        #  conf = tk_split_simplelist(conflist)
227        tk_split_simplelist(tk_send_without_enc('image', 'configure', _get_eval_enc_str(index)), false, false).each{|conflist|
228          conf = tk_split_simplelist(conflist, false, true)
229          key = conf.shift[1..-1]
230          case key
231          when 'text', 'label', 'show', 'data', 'file'
232          else
233            if conf[2]
234              if conf[2].index('{')
235                conf[2] = tk_split_list(conf[2])
236              else
237                conf[2] = tk_tcl2ruby(conf[2])
238              end
239            end
240            if conf[3]
241              if conf[3].index('{')
242                conf[3] = tk_split_list(conf[3])
243              else
244                conf[3] = tk_tcl2ruby(conf[3])
245              end
246            end
247          end
248          if conf.size == 1
249            ret[key] = conf[0][1..-1]  # alias info
250          else
251            ret[key] = conf
252          end
253        }
254        ret
255      end
256    end
257  end
258
259  def current_image_configinfo(index, slot = nil)
260    if TkComm::GET_CONFIGINFO_AS_ARRAY
261      if slot
262        conf = image_configinfo(index, slot)
263        {conf[0] => conf[4]}
264      else
265        ret = {}
266        image_configinfo(index).each{|conf|
267          ret[conf[0]] = conf[4] if conf.size > 2
268        }
269        ret
270      end
271    else # ! TkComm::GET_CONFIGINFO_AS_ARRAY
272      ret = {}
273      image_configinfo(index, slot).each{|k, conf|
274        ret[k] = conf[-1] if conf.kind_of?(Array)
275      }
276      ret
277    end
278  end
279
280  def image_names
281    #tk_split_simplelist(_fromUTF8(tk_send_without_enc('image', 'names'))).collect{|elt|
282    tk_split_simplelist(tk_send_without_enc('image', 'names'), false, true).collect{|elt|
283      tagid2obj(elt)
284    }
285  end
286
287  def index(idx)
288    tk_send_without_enc('index', _get_eval_enc_str(idx))
289  end
290
291  def insert(index, *args)
292    tk_send('insert', index, *args)
293    self
294  end
295
296  def mark_names
297    #tk_split_simplelist(_fromUTF8(tk_send_without_enc('mark', 'names'))).collect{|elt|
298    tk_split_simplelist(tk_send_without_enc('mark', 'names'), false, true).collect{|elt|
299      tagid2obj(elt)
300    }
301  end
302
303  def mark_gravity(mark, direction=nil)
304    if direction
305      tk_send_without_enc('mark', 'gravity',
306                          _get_eval_enc_str(mark), direction)
307      self
308    else
309      tk_send_without_enc('mark', 'gravity', _get_eval_enc_str(mark))
310    end
311  end
312
313  def mark_set(mark, index)
314    tk_send_without_enc('mark', 'set', _get_eval_enc_str(mark),
315                        _get_eval_enc_str(index))
316    self
317  end
318  alias set_mark mark_set
319
320  def mark_unset(*marks)
321    tk_send_without_enc('mark', 'unset',
322                        *(marks.collect{|mark| _get_eval_enc_str(mark)}))
323    self
324  end
325  alias unset_mark mark_unset
326
327  def mark_next(index)
328    tagid2obj(_fromUTF8(tk_send_without_enc('mark', 'next',
329                                            _get_eval_enc_str(index))))
330  end
331  alias next_mark mark_next
332
333  def mark_previous(index)
334    tagid2obj(_fromUTF8(tk_send_without_enc('mark', 'previous',
335                                            _get_eval_enc_str(index))))
336  end
337  alias previous_mark mark_previous
338
339  def scan_mark(x, y)
340    tk_send_without_enc('scan', 'mark', x, y)
341    self
342  end
343  def scan_dragto(x, y)
344    tk_send_without_enc('scan', 'dragto', x, y)
345    self
346  end
347
348
349  def _ktext_length(txt)
350    if TkCore::WITH_ENCODING ### Ruby 1.9 !!!!!!!!!!!!!
351      return txt.length
352    end
353    ###########################
354
355    if $KCODE !~ /n/i
356      return txt.gsub(/[^\Wa-zA-Z_\d]/, ' ').length
357    end
358
359    # $KCODE == 'NONE'
360    if JAPANIZED_TK
361      tk_call_without_enc('kstring', 'length',
362                          _get_eval_enc_str(txt)).to_i
363    else
364      begin
365        tk_call_without_enc('encoding', 'convertto', 'ascii',
366                            _get_eval_enc_str(txt)).length
367      rescue StandardError, NameError
368        # sorry, I have no plan
369        txt.length
370      end
371    end
372  end
373  private :_ktext_length
374
375  def tksearch(*args)
376    # call 'search' subcommand of text widget
377    #   args ::= [<array_of_opts>] <pattern> <start_index> [<stop_index>]
378    # If <pattern> is regexp, then it must be a regular expression of Tcl
379    if args[0].kind_of?(Array)
380      opts = args.shift.collect{|opt| '-' + opt.to_s }
381    else
382      opts = []
383    end
384
385    opts << '--'
386
387    ret = tk_send('search', *(opts + args))
388    if ret == ""
389      nil
390    else
391      ret
392    end
393  end
394
395  def tksearch_with_count(*args)
396    # call 'search' subcommand of text widget
397    #   args ::= [<array_of_opts>] <var> <pattern> <start_index> [<stop_index>]
398    # If <pattern> is regexp, then it must be a regular expression of Tcl
399    if args[0].kind_of?(Array)
400      opts = args.shift.collect{|opt| '-' + opt.to_s }
401    else
402      opts = []
403    end
404
405    opts << '-count' << args.shift << '--'
406
407    ret = tk_send('search', *(opts + args))
408    if ret == ""
409      nil
410    else
411      ret
412    end
413  end
414
415  def search_with_length(pat,start,stop=None)
416    pat = pat.chr if pat.kind_of? Integer
417    if stop != None
418      return ["", 0] if compare(start,'>=',stop)
419      txt = get(start,stop)
420      if (pos = txt.index(pat))
421        match = $&
422        #pos = txt[0..(pos-1)].split('').length if pos > 0
423        pos = _ktext_length(txt[0..(pos-1)]) if pos > 0
424        if pat.kind_of? String
425          #return [index(start + " + #{pos} chars"), pat.split('').length]
426          return [index(start + " + #{pos} chars"),
427                  _ktext_length(pat), pat.dup]
428        else
429          #return [index(start + " + #{pos} chars"), $&.split('').length]
430          return [index(start + " + #{pos} chars"),
431                  _ktext_length(match), match]
432        end
433      else
434        return ["", 0]
435      end
436    else
437      txt = get(start,'end - 1 char')
438      if (pos = txt.index(pat))
439        match = $&
440        #pos = txt[0..(pos-1)].split('').length if pos > 0
441        pos = _ktext_length(txt[0..(pos-1)]) if pos > 0
442        if pat.kind_of? String
443          #return [index(start + " + #{pos} chars"), pat.split('').length]
444          return [index(start + " + #{pos} chars"),
445                  _ktext_length(pat), pat.dup]
446        else
447          #return [index(start + " + #{pos} chars"), $&.split('').length]
448          return [index(start + " + #{pos} chars"),
449                  _ktext_length(match), match]
450        end
451      else
452        txt = get('1.0','end - 1 char')
453        if (pos = txt.index(pat))
454          match = $&
455          #pos = txt[0..(pos-1)].split('').length if pos > 0
456          pos = _ktext_length(txt[0..(pos-1)]) if pos > 0
457          if pat.kind_of? String
458            #return [index("1.0 + #{pos} chars"), pat.split('').length]
459            return [index("1.0 + #{pos} chars"),
460                    _ktext_length(pat), pat.dup]
461          else
462            #return [index("1.0 + #{pos} chars"), $&.split('').length]
463            return [index("1.0 + #{pos} chars"), _ktext_length(match), match]
464          end
465        else
466          return ["", 0]
467        end
468      end
469    end
470  end
471
472  def search(pat,start,stop=None)
473    search_with_length(pat,start,stop)[0]
474  end
475
476  def rsearch_with_length(pat,start,stop=None)
477    pat = pat.chr if pat.kind_of? Integer
478    if stop != None
479      return ["", 0] if compare(start,'<=',stop)
480      txt = get(stop,start)
481      if (pos = txt.rindex(pat))
482        match = $&
483        #pos = txt[0..(pos-1)].split('').length if pos > 0
484        pos = _ktext_length(txt[0..(pos-1)]) if pos > 0
485        if pat.kind_of? String
486          #return [index(stop + " + #{pos} chars"), pat.split('').length]
487          return [index(stop + " + #{pos} chars"), _ktext_length(pat), pat.dup]
488        else
489          #return [index(stop + " + #{pos} chars"), $&.split('').length]
490          return [index(stop + " + #{pos} chars"), _ktext_length(match), match]
491        end
492      else
493        return ["", 0]
494      end
495    else
496      txt = get('1.0',start)
497      if (pos = txt.rindex(pat))
498        match = $&
499        #pos = txt[0..(pos-1)].split('').length if pos > 0
500        pos = _ktext_length(txt[0..(pos-1)]) if pos > 0
501        if pat.kind_of? String
502          #return [index("1.0 + #{pos} chars"), pat.split('').length]
503          return [index("1.0 + #{pos} chars"), _ktext_length(pat), pat.dup]
504        else
505          #return [index("1.0 + #{pos} chars"), $&.split('').length]
506          return [index("1.0 + #{pos} chars"), _ktext_length(match), match]
507        end
508      else
509        txt = get('1.0','end - 1 char')
510        if (pos = txt.rindex(pat))
511          match = $&
512          #pos = txt[0..(pos-1)].split('').length if pos > 0
513          pos = _ktext_length(txt[0..(pos-1)]) if pos > 0
514          if pat.kind_of? String
515            #return [index("1.0 + #{pos} chars"), pat.split('').length]
516            return [index("1.0 + #{pos} chars"), _ktext_length(pat), pat.dup]
517          else
518            #return [index("1.0 + #{pos} chars"), $&.split('').length]
519            return [index("1.0 + #{pos} chars"), _ktext_length(match), match]
520          end
521        else
522          return ["", 0]
523        end
524      end
525    end
526  end
527
528  def rsearch(pat,start,stop=None)
529    rsearch_with_length(pat,start,stop)[0]
530  end
531
532  def see(index)
533    tk_send_without_enc('see', index)
534    self
535  end
536
537  ###############################
538
539  def xview(*index)
540    if index.size == 0
541      list(tk_send_without_enc('xview'))
542    else
543      tk_send_without_enc('xview', *index)
544      self
545    end
546  end
547  def xview_moveto(*index)
548    xview('moveto', *index)
549  end
550  def xview_scroll(*index)
551    xview('scroll', *index)
552  end
553
554  def yview(*index)
555    if index.size == 0
556      list(tk_send_without_enc('yview'))
557    else
558      tk_send_without_enc('yview', *index)
559      self
560    end
561  end
562  def yview_moveto(*index)
563    yview('moveto', *index)
564  end
565  def yview_scroll(*index)
566    yview('scroll', *index)
567  end
568end
569