1#
2#               tk/canvas.rb - Tk canvas classes
3#                       by Yukihiro Matsumoto <matz@caelum.co.jp>
4#
5require 'tk'
6require 'tk/canvastag'
7require 'tk/itemconfig'
8require 'tk/scrollable'
9
10module TkCanvasItemConfig
11  include TkItemConfigMethod
12
13  def __item_strval_optkeys(id)
14    # maybe need to override
15    super(id) + [
16      'fill', 'activefill', 'disabledfill',
17      'outline', 'activeoutline', 'disabledoutline'
18    ]
19  end
20  private :__item_strval_optkeys
21
22  def __item_methodcall_optkeys(id)
23    {'coords'=>'coords'}
24  end
25  private :__item_methodcall_optkeys
26
27  def __item_val2ruby_optkeys(id)  # { key=>proc, ... }
28    super(id).update('window'=>proc{|i, v| window(v)},
29                     'tags'=>proc{|i, v|
30                       simplelist(v).collect{|tag| TkcTag.id2obj(self, tag)}
31                     })
32  end
33  private :__item_val2ruby_optkeys
34
35  def __item_pathname(tagOrId)
36    if tagOrId.kind_of?(TkcItem) || tagOrId.kind_of?(TkcTag)
37      self.path + ';' + tagOrId.id.to_s
38    else
39      self.path + ';' + tagOrId.to_s
40    end
41  end
42  private :__item_pathname
43end
44
45class Tk::Canvas<TkWindow
46  include TkCanvasItemConfig
47  include Tk::Scrollable
48
49  TkCommandNames = ['canvas'.freeze].freeze
50  WidgetClassName = 'Canvas'.freeze
51  WidgetClassNames[WidgetClassName] ||= self
52
53  def __destroy_hook__
54    TkcItem::CItemID_TBL.delete(@path)
55  end
56
57  #def create_self(keys)
58  #  if keys and keys != None
59  #    tk_call_without_enc('canvas', @path, *hash_kv(keys, true))
60  #  else
61  #    tk_call_without_enc('canvas', @path)
62  #  end
63  #end
64  #private :create_self
65
66  def __numval_optkeys
67    super() + ['closeenough']
68  end
69  private :__numval_optkeys
70
71  def __boolval_optkeys
72    super() + ['confine']
73  end
74  private :__boolval_optkeys
75
76  def tagid(tag)
77    if tag.kind_of?(TkcItem) || tag.kind_of?(TkcTag)
78      tag.id
79    else
80      tag  # maybe an Array of configure paramters
81    end
82  end
83  private :tagid
84
85
86  # create a canvas item without creating a TkcItem object
87  def create(type, *args)
88    if type.kind_of?(Class) && type < TkcItem
89      # do nothing
90    elsif TkcItem.type2class(type.to_s)
91      type = TkcItem.type2class(type.to_s)
92    else
93      fail ArgumentError, "type must a subclass of TkcItem class, or a string in CItemTypeToClass"
94    end
95    type.create(self, *args)
96  end
97
98  def addtag(tag, mode, *args)
99    mode = mode.to_s
100    if args[0] && mode =~ /^(above|below|with(tag)?)$/
101      args[0] = tagid(args[0])
102    end
103    tk_send_without_enc('addtag', tagid(tag), mode, *args)
104    self
105  end
106  def addtag_above(tagOrId, target)
107    addtag(tagOrId, 'above', tagid(target))
108  end
109  def addtag_all(tagOrId)
110    addtag(tagOrId, 'all')
111  end
112  def addtag_below(tagOrId, target)
113    addtag(tagOrId, 'below', tagid(target))
114  end
115  def addtag_closest(tagOrId, x, y, halo=None, start=None)
116    addtag(tagOrId, 'closest', x, y, halo, start)
117  end
118  def addtag_enclosed(tagOrId, x1, y1, x2, y2)
119    addtag(tagOrId, 'enclosed', x1, y1, x2, y2)
120  end
121  def addtag_overlapping(tagOrId, x1, y1, x2, y2)
122    addtag(tagOrId, 'overlapping', x1, y1, x2, y2)
123  end
124  def addtag_withtag(tagOrId, tag)
125    addtag(tagOrId, 'withtag', tagid(tag))
126  end
127
128  def bbox(tagOrId, *tags)
129    list(tk_send_without_enc('bbox', tagid(tagOrId),
130                             *tags.collect{|t| tagid(t)}))
131  end
132
133  #def itembind(tag, context, cmd=Proc.new, *args)
134  #  _bind([path, "bind", tagid(tag)], context, cmd, *args)
135  #  self
136  #end
137  def itembind(tag, context, *args)
138    # if args[0].kind_of?(Proc) || args[0].kind_of?(Method)
139    if TkComm._callback_entry?(args[0]) || !block_given?
140      cmd = args.shift
141    else
142      cmd = Proc.new
143    end
144    _bind([path, "bind", tagid(tag)], context, cmd, *args)
145    self
146  end
147
148  #def itembind_append(tag, context, cmd=Proc.new, *args)
149  #  _bind_append([path, "bind", tagid(tag)], context, cmd, *args)
150  #  self
151  #end
152  def itembind_append(tag, context, *args)
153    # if args[0].kind_of?(Proc) || args[0].kind_of?(Method)
154    if TkComm._callback_entry?(args[0]) || !block_given?
155      cmd = args.shift
156    else
157      cmd = Proc.new
158    end
159    _bind_append([path, "bind", tagid(tag)], context, cmd, *args)
160    self
161  end
162
163  def itembind_remove(tag, context)
164    _bind_remove([path, "bind", tagid(tag)], context)
165    self
166  end
167
168  def itembindinfo(tag, context=nil)
169    _bindinfo([path, "bind", tagid(tag)], context)
170  end
171
172  def canvasx(screen_x, *args)
173    #tk_tcl2ruby(tk_send_without_enc('canvasx', screen_x, *args))
174    number(tk_send_without_enc('canvasx', screen_x, *args))
175  end
176  def canvasy(screen_y, *args)
177    #tk_tcl2ruby(tk_send_without_enc('canvasy', screen_y, *args))
178    number(tk_send_without_enc('canvasy', screen_y, *args))
179  end
180  alias canvas_x canvasx
181  alias canvas_y canvasy
182
183  def coords(tag, *args)
184    if args.empty?
185      tk_split_list(tk_send_without_enc('coords', tagid(tag)))
186    else
187      tk_send_without_enc('coords', tagid(tag), *(args.flatten))
188      self
189    end
190  end
191
192  def dchars(tag, first, last=None)
193    tk_send_without_enc('dchars', tagid(tag),
194                        _get_eval_enc_str(first), _get_eval_enc_str(last))
195    self
196  end
197
198  def delete(*args)
199    tbl = nil
200    TkcItem::CItemID_TBL.mutex.synchronize{
201      tbl = TkcItem::CItemID_TBL[self.path]
202    }
203    if tbl
204      args.each{|tag|
205        find('withtag', tag).each{|item|
206          if item.kind_of?(TkcItem)
207            TkcItem::CItemID_TBL.mutex.synchronize{
208              tbl.delete(item.id)
209            }
210          end
211        }
212      }
213    end
214    tk_send_without_enc('delete', *args.collect{|t| tagid(t)})
215    self
216  end
217  alias remove delete
218
219  def dtag(tag, tag_to_del=None)
220    tk_send_without_enc('dtag', tagid(tag), tagid(tag_to_del))
221    self
222  end
223  alias deltag dtag
224
225  def find(mode, *args)
226    list(tk_send_without_enc('find', mode, *args)).collect!{|id|
227      TkcItem.id2obj(self, id)
228    }
229  end
230  def find_above(target)
231    find('above', tagid(target))
232  end
233  def find_all
234    find('all')
235  end
236  def find_below(target)
237    find('below', tagid(target))
238  end
239  def find_closest(x, y, halo=None, start=None)
240    find('closest', x, y, halo, start)
241  end
242  def find_enclosed(x1, y1, x2, y2)
243    find('enclosed', x1, y1, x2, y2)
244  end
245  def find_overlapping(x1, y1, x2, y2)
246    find('overlapping', x1, y1, x2, y2)
247  end
248  def find_withtag(tag)
249    find('withtag', tag)
250  end
251
252  def itemfocus(tagOrId=nil)
253    if tagOrId
254      tk_send_without_enc('focus', tagid(tagOrId))
255      self
256    else
257      ret = tk_send_without_enc('focus')
258      if ret == ""
259        nil
260      else
261        TkcItem.id2obj(self, ret)
262      end
263    end
264  end
265
266  def gettags(tagOrId)
267    list(tk_send_without_enc('gettags', tagid(tagOrId))).collect{|tag|
268      TkcTag.id2obj(self, tag)
269    }
270  end
271
272  def icursor(tagOrId, index)
273    tk_send_without_enc('icursor', tagid(tagOrId), index)
274    self
275  end
276
277  def imove(tagOrId, idx, x, y)
278    tk_send_without_enc('imove', tagid(tagOrId), idx, x, y)
279    self
280  end
281  alias i_move imove
282
283  def index(tagOrId, idx)
284    number(tk_send_without_enc('index', tagid(tagOrId), idx))
285  end
286
287  def insert(tagOrId, index, string)
288    tk_send_without_enc('insert', tagid(tagOrId), index,
289                        _get_eval_enc_str(string))
290    self
291  end
292
293=begin
294  def itemcget(tagOrId, option)
295    case option.to_s
296    when 'dash', 'activedash', 'disableddash'
297      conf = tk_send_without_enc('itemcget', tagid(tagOrId), "-#{option}")
298      if conf =~ /^[0-9]/
299        list(conf)
300      else
301        conf
302      end
303    when 'text', 'label', 'show', 'data', 'file', 'maskdata', 'maskfile'
304      _fromUTF8(tk_send_without_enc('itemcget', tagid(tagOrId), "-#{option}"))
305    when 'font', 'kanjifont'
306      #fnt = tk_tcl2ruby(tk_send('itemcget', tagid(tagOrId), "-#{option}"))
307      fnt = tk_tcl2ruby(_fromUTF8(tk_send_with_enc('itemcget', tagid(tagOrId), '-font')))
308      unless fnt.kind_of?(TkFont)
309        fnt = tagfontobj(tagid(tagOrId), fnt)
310      end
311      if option.to_s == 'kanjifont' && JAPANIZED_TK && TK_VERSION =~ /^4\.*/
312        # obsolete; just for compatibility
313        fnt.kanji_font
314      else
315        fnt
316      end
317    else
318      tk_tcl2ruby(_fromUTF8(tk_send_without_enc('itemcget', tagid(tagOrId),
319                                                "-#{option}")))
320    end
321  end
322
323  def itemconfigure(tagOrId, key, value=None)
324    if key.kind_of? Hash
325      key = _symbolkey2str(key)
326      coords = key.delete('coords')
327      self.coords(tagOrId, coords) if coords
328
329      if ( key['font'] || key['kanjifont'] \
330          || key['latinfont'] || key['asciifont'] )
331        tagfont_configure(tagid(tagOrId), key.dup)
332      else
333        _fromUTF8(tk_send_without_enc('itemconfigure', tagid(tagOrId),
334                                      *hash_kv(key, true)))
335      end
336
337    else
338      if ( key == 'coords' || key == :coords )
339        self.coords(tagOrId, value)
340      elsif ( key == 'font' || key == :font ||
341              key == 'kanjifont' || key == :kanjifont ||
342              key == 'latinfont' || key == :latinfont ||
343              key == 'asciifont' || key == :asciifont )
344        if value == None
345          tagfontobj(tagid(tagOrId))
346        else
347          tagfont_configure(tagid(tagOrId), {key=>value})
348        end
349      else
350        _fromUTF8(tk_send_without_enc('itemconfigure', tagid(tagOrId),
351                                      "-#{key}", _get_eval_enc_str(value)))
352      end
353    end
354    self
355  end
356#  def itemconfigure(tagOrId, key, value=None)
357#    if key.kind_of? Hash
358#      tk_send 'itemconfigure', tagid(tagOrId), *hash_kv(key)
359#    else
360#      tk_send 'itemconfigure', tagid(tagOrId), "-#{key}", value
361#    end
362#  end
363#  def itemconfigure(tagOrId, keys)
364#    tk_send 'itemconfigure', tagid(tagOrId), *hash_kv(keys)
365#  end
366
367  def itemconfiginfo(tagOrId, key=nil)
368    if TkComm::GET_CONFIGINFO_AS_ARRAY
369      if key
370        case key.to_s
371        when 'coords'
372          return ['coords', '', '', '', self.coords(tagOrId)]
373        when 'dash', 'activedash', 'disableddash'
374          conf = tk_split_simplelist(tk_send_without_enc('itemconfigure', tagid(tagOrId), "-#{key}"))
375          if conf[3] && conf[3] =~ /^[0-9]/
376            conf[3] = list(conf[3])
377          end
378          if conf[4] && conf[4] =~ /^[0-9]/
379            conf[4] = list(conf[4])
380          end
381        when 'text', 'label', 'show', 'data', 'file', 'maskdata', 'maskfile'
382          conf = tk_split_simplelist(_fromUTF8(tk_send_without_enc('itemconfigure', tagid(tagOrId), "-#{key}")))
383        when 'font', 'kanjifont'
384          conf = tk_split_simplelist(_fromUTF8(tk_send_without_enc('itemconfigure', tagid(tagOrId),"-#{key}")))
385          conf[4] = tagfont_configinfo(tagid(tagOrId), conf[4])
386        else
387          conf = tk_split_list(_fromUTF8(tk_send_without_enc('itemconfigure', tagid(tagOrId), "-#{key}")))
388        end
389        conf[0] = conf[0][1..-1]
390        conf
391      else
392        ret = tk_split_simplelist(_fromUTF8(tk_send_without_enc('itemconfigure', tagid(tagOrId)))).collect{|conflist|
393          conf = tk_split_simplelist(conflist)
394          conf[0] = conf[0][1..-1]
395          case conf[0]
396          when 'text', 'label', 'show', 'data', 'file', 'maskdata', 'maskfile'
397          when 'dash', 'activedash', 'disableddash'
398            if conf[3] && conf[3] =~ /^[0-9]/
399              conf[3] = list(conf[3])
400            end
401            if conf[4] && conf[4] =~ /^[0-9]/
402              conf[4] = list(conf[4])
403            end
404          else
405            if conf[3]
406              if conf[3].index('{')
407                conf[3] = tk_split_list(conf[3])
408              else
409                conf[3] = tk_tcl2ruby(conf[3])
410              end
411            end
412            if conf[4]
413              if conf[4].index('{')
414                conf[4] = tk_split_list(conf[4])
415              else
416                conf[4] = tk_tcl2ruby(conf[4])
417              end
418            end
419          end
420          conf[1] = conf[1][1..-1] if conf.size == 2 # alias info
421          conf
422        }
423
424        fontconf = ret.assoc('font')
425        if fontconf
426          ret.delete_if{|item| item[0] == 'font' || item[0] == 'kanjifont'}
427          fontconf[4] = tagfont_configinfo(tagid(tagOrId), fontconf[4])
428          ret.push(fontconf)
429        end
430
431        ret << ['coords', '', '', '', self.coords(tagOrId)]
432      end
433    else # ! TkComm::GET_CONFIGINFO_AS_ARRAY
434      if key
435        case key.to_s
436        when 'coords'
437          {'coords' => ['', '', '', self.coords(tagOrId)]}
438        when 'dash', 'activedash', 'disableddash'
439          conf = tk_split_simplelist(tk_send_without_enc('itemconfigure',
440                                                         tagid(tagOrId),
441                                                         "-#{key}"))
442          if conf[3] && conf[3] =~ /^[0-9]/
443            conf[3] = list(conf[3])
444          end
445          if conf[4] && conf[4] =~ /^[0-9]/
446            conf[4] = list(conf[4])
447          end
448        when 'text', 'label', 'show', 'data', 'file', 'maskdata', 'maskfile'
449          conf = tk_split_simplelist(_fromUTF8(tk_send_without_enc('itemconfigure', tagid(tagOrId), "-#{key}")))
450        when 'font', 'kanjifont'
451          conf = tk_split_simplelist(_fromUTF8(tk_send_without_enc('itemconfigure', tagid(tagOrId),"-#{key}")))
452          conf[4] = tagfont_configinfo(tagid(tagOrId), conf[4])
453        else
454          conf = tk_split_list(_fromUTF8(tk_send_without_enc('itemconfigure', tagid(tagOrId), "-#{key}")))
455        end
456        key = conf.shift[1..-1]
457        { key => conf }
458      else
459        ret = {}
460        tk_split_simplelist(_fromUTF8(tk_send_without_enc('itemconfigure', tagid(tagOrId)))).each{|conflist|
461          conf = tk_split_simplelist(conflist)
462          key = conf.shift[1..-1]
463          case key
464          when 'text', 'label', 'show', 'data', 'file', 'maskdata', 'maskfile'
465          when 'dash', 'activedash', 'disableddash'
466            if conf[2] && conf[2] =~ /^[0-9]/
467              conf[2] = list(conf[2])
468            end
469            if conf[3] && conf[3] =~ /^[0-9]/
470              conf[3] = list(conf[3])
471            end
472          else
473            if conf[2]
474              if conf[2].index('{')
475                conf[2] = tk_split_list(conf[2])
476              else
477                conf[2] = tk_tcl2ruby(conf[2])
478              end
479            end
480            if conf[3]
481              if conf[3].index('{')
482                conf[3] = tk_split_list(conf[3])
483              else
484                conf[3] = tk_tcl2ruby(conf[3])
485              end
486            end
487          end
488          if conf.size == 1
489            ret[key] = conf[0][1..-1]  # alias info
490          else
491            ret[key] = conf
492          end
493        }
494
495        fontconf = ret['font']
496        if fontconf
497          ret.delete('font')
498          ret.delete('kanjifont')
499          fontconf[3] = tagfont_configinfo(tagid(tagOrId), fontconf[3])
500          ret['font'] = fontconf
501        end
502
503        ret['coords'] = ['', '', '', self.coords(tagOrId)]
504
505        ret
506      end
507    end
508  end
509
510  def current_itemconfiginfo(tagOrId, key=nil)
511    if TkComm::GET_CONFIGINFO_AS_ARRAY
512      if key
513        conf = itemconfiginfo(tagOrId, key)
514        {conf[0] => conf[4]}
515      else
516        ret = {}
517        itemconfiginfo(tagOrId).each{|conf|
518          ret[conf[0]] = conf[4] if conf.size > 2
519        }
520        ret
521      end
522    else # ! TkComm::GET_CONFIGINFO_AS_ARRAY
523      ret = {}
524      itemconfiginfo(tagOrId, key).each{|k, conf|
525        ret[k] = conf[-1] if conf.kind_of?(Array)
526      }
527      ret
528    end
529  end
530=end
531
532  def lower(tag, below=nil)
533    if below
534      tk_send_without_enc('lower', tagid(tag), tagid(below))
535    else
536      tk_send_without_enc('lower', tagid(tag))
537    end
538    self
539  end
540
541  def move(tag, dx, dy)
542    tk_send_without_enc('move', tagid(tag), dx, dy)
543    self
544  end
545
546  def moveto(tag, x, y)
547    # Tcl/Tk 8.6 or later
548    tk_send_without_enc('moveto', tagid(tag), x, y)
549    self
550  end
551  alias move_to moveto
552
553  def postscript(keys)
554    tk_send("postscript", *hash_kv(keys))
555  end
556
557  def raise(tag, above=nil)
558    if above
559      tk_send_without_enc('raise', tagid(tag), tagid(above))
560    else
561      tk_send_without_enc('raise', tagid(tag))
562    end
563    self
564  end
565
566  def rchars(tag, first, last, str_or_coords)
567    # Tcl/Tk 8.6 or later
568    str_or_coords = str_or_coords.flatten if str_or_coords.kinad_of? Array
569    tk_send_without_enc('rchars', tagid(tag), first, last, str_or_coords)
570    self
571  end
572  alias replace_chars rchars
573  alias replace_coords rchars
574
575  def scale(tag, x, y, xs, ys)
576    tk_send_without_enc('scale', tagid(tag), x, y, xs, ys)
577    self
578  end
579
580  def scan_mark(x, y)
581    tk_send_without_enc('scan', 'mark', x, y)
582    self
583  end
584  def scan_dragto(x, y, gain=None)
585    tk_send_without_enc('scan', 'dragto', x, y, gain)
586    self
587  end
588
589  def select(mode, *args)
590    r = tk_send_without_enc('select', mode, *args)
591    (mode == 'item')? TkcItem.id2obj(self, r): self
592  end
593  def select_adjust(tagOrId, index)
594    select('adjust', tagid(tagOrId), index)
595  end
596  def select_clear
597    select('clear')
598  end
599  def select_from(tagOrId, index)
600    select('from', tagid(tagOrId), index)
601  end
602  def select_item
603    select('item')
604  end
605  def select_to(tagOrId, index)
606    select('to', tagid(tagOrId), index)
607  end
608
609  def itemtype(tag)
610    TkcItem.type2class(tk_send('type', tagid(tag)))
611  end
612
613  def create_itemobj_from_id(idnum)
614    id = TkcItem.id2obj(self, idnum.to_i)
615    return id if id.kind_of?(TkcItem)
616
617    typename = tk_send('type', id)
618    unless type = TkcItem.type2class(typename)
619      (itemclass = typename.dup)[0,1] = typename[0,1].upcase
620      type = TkcItem.const_set(itemclass, Class.new(TkcItem))
621      type.const_set("CItemTypeName", typename.freeze)
622      TkcItem::CItemTypeToClass[typename] = type
623    end
624
625    canvas = self
626    (obj = type.allocate).instance_eval{
627      @parent = @c = canvas
628      @path = canvas.path
629      @id = id
630      TkcItem::CItemID_TBL.mutex.synchronize{
631        TkcItem::CItemID_TBL[@path] = {} unless TkcItem::CItemID_TBL[@path]
632        TkcItem::CItemID_TBL[@path][@id] = self
633      }
634    }
635  end
636end
637
638#TkCanvas = Tk::Canvas unless Object.const_defined? :TkCanvas
639#Tk.__set_toplevel_aliases__(:Tk, Tk::Canvas, :TkCanvas)
640Tk.__set_loaded_toplevel_aliases__('tk/canvas.rb', :Tk, Tk::Canvas, :TkCanvas)
641
642
643class TkcItem<TkObject
644  extend Tk
645  include TkcTagAccess
646  extend TkItemFontOptkeys
647  extend TkItemConfigOptkeys
648
649  CItemTypeName = nil
650  CItemTypeToClass = {}
651
652  CItemID_TBL = TkCore::INTERP.create_table
653
654  TkCore::INTERP.init_ip_env{
655    CItemID_TBL.mutex.synchronize{ CItemID_TBL.clear }
656  }
657
658  def TkcItem.type2class(type)
659    CItemTypeToClass[type]
660  end
661
662  def TkcItem.id2obj(canvas, id)
663    cpath = canvas.path
664    CItemID_TBL.mutex.synchronize{
665      if CItemID_TBL[cpath]
666        CItemID_TBL[cpath][id]? CItemID_TBL[cpath][id]: id
667      else
668        id
669      end
670    }
671  end
672
673  ########################################
674  def self._parse_create_args(args)
675    fontkeys = {}
676    methodkeys = {}
677    if args[-1].kind_of? Hash
678      keys = _symbolkey2str(args.pop)
679      if args.size == 0
680        args = keys.delete('coords')
681        unless args.kind_of?(Array)
682          fail "coords parameter must be given by an Array"
683        end
684      end
685
686      #['font', 'kanjifont', 'latinfont', 'asciifont'].each{|key|
687      #  fontkeys[key] = keys.delete(key) if keys.key?(key)
688      #}
689      __item_font_optkeys(nil).each{|key|
690        fkey = key.to_s
691        fontkeys[fkey] = keys.delete(fkey) if keys.key?(fkey)
692
693        fkey = "kanji#{key}"
694        fontkeys[fkey] = keys.delete(fkey) if keys.key?(fkey)
695
696        fkey = "latin#{key}"
697        fontkeys[fkey] = keys.delete(fkey) if keys.key?(fkey)
698
699        fkey = "ascii#{key}"
700        fontkeys[fkey] = keys.delete(fkey) if keys.key?(fkey)
701      }
702
703      __item_optkey_aliases(nil).each{|alias_name, real_name|
704        alias_name = alias_name.to_s
705        if keys.has_key?(alias_name)
706          keys[real_name.to_s] = keys.delete(alias_name)
707        end
708      }
709
710      __item_methodcall_optkeys(nil).each{|key|
711        key = key.to_s
712        methodkeys[key] = keys.delete(key) if keys.key?(key)
713      }
714
715      __item_ruby2val_optkeys(nil).each{|key, method|
716        key = key.to_s
717        keys[key] = method.call(keys[key]) if keys.has_key?(key)
718      }
719
720      #args = args.flatten.concat(hash_kv(keys))
721      args = args.flatten.concat(itemconfig_hash_kv(nil, keys))
722    else
723      args = args.flatten
724    end
725
726    [args, fontkeys, methodkeys]
727  end
728  private_class_method :_parse_create_args
729
730  def self.create(canvas, *args)
731    unless self::CItemTypeName
732      fail RuntimeError, "#{self} is an abstract class"
733    end
734    args, fontkeys, methodkeys = _parse_create_args(args)
735    idnum = tk_call_without_enc(canvas.path, 'create',
736                                self::CItemTypeName, *args)
737    canvas.itemconfigure(idnum, fontkeys) unless fontkeys.empty?
738    canvas.itemconfigure(idnum, methodkeys) unless methodkeys.empty?
739    idnum.to_i  # 'canvas item id' is an integer number
740  end
741  ########################################
742
743  def initialize(parent, *args)
744    #unless parent.kind_of?(Tk::Canvas)
745    #  fail ArgumentError, "expect Tk::Canvas for 1st argument"
746    #end
747    @parent = @c = parent
748    @path = parent.path
749
750    @id = create_self(*args) # an integer number as 'canvas item id'
751    CItemID_TBL.mutex.synchronize{
752      CItemID_TBL[@path] = {} unless CItemID_TBL[@path]
753      CItemID_TBL[@path][@id] = self
754    }
755  end
756  def create_self(*args)
757    self.class.create(@c, *args) # return an integer number as 'canvas item id'
758  end
759  private :create_self
760
761  def id
762    @id
763  end
764
765  def exist?
766    if @c.find_withtag(@id)
767      true
768    else
769      false
770    end
771  end
772
773  def delete
774    @c.delete @id
775    CItemID_TBL.mutex.synchronize{
776      CItemID_TBL[@path].delete(@id) if CItemID_TBL[@path]
777    }
778    self
779  end
780  alias remove  delete
781  alias destroy delete
782end
783
784class TkcArc<TkcItem
785  CItemTypeName = 'arc'.freeze
786  CItemTypeToClass[CItemTypeName] = self
787end
788
789class TkcBitmap<TkcItem
790  CItemTypeName = 'bitmap'.freeze
791  CItemTypeToClass[CItemTypeName] = self
792end
793
794class TkcImage<TkcItem
795  CItemTypeName = 'image'.freeze
796  CItemTypeToClass[CItemTypeName] = self
797end
798
799class TkcLine<TkcItem
800  CItemTypeName = 'line'.freeze
801  CItemTypeToClass[CItemTypeName] = self
802end
803
804class TkcOval<TkcItem
805  CItemTypeName = 'oval'.freeze
806  CItemTypeToClass[CItemTypeName] = self
807end
808
809class TkcPolygon<TkcItem
810  CItemTypeName = 'polygon'.freeze
811  CItemTypeToClass[CItemTypeName] = self
812end
813
814class TkcRectangle<TkcItem
815  CItemTypeName = 'rectangle'.freeze
816  CItemTypeToClass[CItemTypeName] = self
817end
818
819class TkcText<TkcItem
820  CItemTypeName = 'text'.freeze
821  CItemTypeToClass[CItemTypeName] = self
822  def self.create(canvas, *args)
823    if args[-1].kind_of?(Hash)
824      keys = _symbolkey2str(args.pop)
825      txt = keys['text']
826      keys['text'] = _get_eval_enc_str(txt) if txt
827      args.push(keys)
828    end
829    super(canvas, *args)
830  end
831end
832
833class TkcWindow<TkcItem
834  CItemTypeName = 'window'.freeze
835  CItemTypeToClass[CItemTypeName] = self
836  def self.create(canvas, *args)
837    if args[-1].kind_of?(Hash)
838      keys = _symbolkey2str(args.pop)
839      win = keys['window']
840      # keys['window'] = win.epath if win.kind_of?(TkWindow)
841      keys['window'] = _epath(win) if win
842      args.push(keys)
843    end
844    super(canvas, *args)
845  end
846end
847