1#!/usr/bin/env ruby
2# -*- coding: utf-8 -*-
3#
4# tcolor --
5# このスクリプトはRGB,HSB,CYM形式をサポートする
6# 簡易カラーエディタです。
7#
8# Copyright (C) 1998 Takaaki Tateishi(ttate@jaist.ac.jp)
9# last update: Thu Jun 18 06:32:35 JST 1998
10#
11
12# まずはtk.rbを読み込む。
13
14require "tk"
15
16
17# Tkによって変更される変数はTkVariableのインスタンスを使う。
18
19$colorSpace = TkVariable.new(:rgb)
20$master = nil
21$red = 65535
22$green = 0
23$blue = 0
24$color = "#ffff00000000"
25$updating = TkVariable.new(0)
26$autoUpdate = TkVariable.new(1)
27$name = TkVariable.new($color)
28$command = TkVariable.new("print(%%,\"\n\")")
29# $command = TkVariable.new("")
30$label1 = TkVariable.new("label1")
31$label2 = TkVariable.new("label2")
32$label3 = TkVariable.new("label3")
33
34
35# リソースデータベースの設定
36if (TkVarAccess.new('tcl_platform')['platform'] == 'unix')
37  TkOptionDB.add('*Entry.background', 'white')
38end
39
40
41# 各イベント用のメソッド
42
43def rgbToHsv(red,green,blue)
44
45  if ( red > green )
46    max = red
47    min = green
48  else
49    max = green
50    min = red
51  end
52
53  if ( blue > max )
54    max = blue
55  else
56    if ( blue < min )
57      min = blue
58    end
59  end
60
61  range = max - min
62
63  if ( max == 0 )
64    sat = 0.0
65  else
66    sat = (max-min)/max
67  end
68
69  if ( sat == 0 )
70    hue = 0.0
71  else
72    rc = (max-red)/range
73    gc = (max-green)/range
74    bc = (max-blue)/range
75    if ( red == max )
76      hue = 0.166667 * (bc - gc)
77    else
78      if ( green == max )
79	hue = 0.166667 * (2.0 + rc - bc)
80      else
81	hue = 0.166667 * (4.0 + gc - rc)
82      end
83    end
84    if ( hue < 0.0 )
85      hue = hue + 1.0
86    end
87  end
88
89  [hue,sat,max/65535]
90end
91
92
93def hsbToRgb(hue,sat,value)
94  v = 65535.0 * value
95  if( sat == 0 )
96    ans = [v,v,v]
97  else
98    hue = hue*6.0
99    if ( hue >= 6 )
100      hue = 0.0
101    end
102    i = hue.to_i
103    f = hue - i
104    p = 65535.0 * value * (1.0 - sat)
105    q = 65535.0 * value * (1.0 - (sat * f))
106    t = 65535.0 * value * (1.0 - (sat * (1.0 - f)))
107    case i
108    when 0
109      ans = [v,t,p]
110    when 1
111      ans = [q,v,p]
112    when 2
113      ans = [p,v,t]
114    when 3
115      ans = [p,q,v]
116    when 4
117      ans = [t,p,v]
118    when 5
119      ans = [v,p,q]
120    else
121      raise(eException,"i value #{i} is out of range")
122    end
123  end
124  return ans
125end
126
127
128def _null_binding
129  Module.new.instance_eval{binding}
130end
131private :_null_binding
132
133def doUpdate
134  newCmd = $command.to_s.gsub("%%","\"#{$color}\"")
135  eval(newCmd, _null_binding)
136end
137
138
139def tc_scaleChanged
140  if( $updating.to_i == 1 )
141    return
142  end
143
144  $master = :scale if $master == nil
145
146  scale1 = $root.middle.middle.scale1
147  scale2 = $root.middle.middle.scale2
148  scale3 = $root.middle.middle.scale3
149
150  case $colorSpace.value.intern
151  when :rgb
152    $red = (scale1.get * 65.535).to_i
153    $green = (scale2.get * 65.535).to_i
154    $blue = (scale3.get * 65.535).to_i
155  when :cmy
156    $red = (65535 - scale1.get * 65.535).to_i
157    $green = (65535 - scale2.get * 65.535).to_i
158    $blue = (65535 - scale3.get * 65.535).to_i
159  when :hsb
160    list = hsbToRgb(scale1.get / 1000.0,
161		    scale2.get / 1000.0,
162		    scale3.get / 1000.0)
163    $red = list[0]
164    $green = list[1]
165    $blue = list[2]
166  else
167    raise(Exception,"unknown colorSpace")
168  end
169  $color = format("#%04x%04x%04x",$red.to_i,$green.to_i,$blue.to_i)
170  $name.value = $color if $master == :scale
171  $root.middle.right.set_color($color)
172  if( $autoUpdate.to_i == 1 )
173    doUpdate
174  end
175  Tk.update(true)
176  $master = nil if $master == :scale
177end
178
179
180def tc_setScales
181  $updating.value = 1
182
183  scale1 = $root.middle.middle.scale1
184  scale2 = $root.middle.middle.scale2
185  scale3 = $root.middle.middle.scale3
186
187  case $colorSpace.value.intern
188  when :rgb
189    scale1.set($red / 65.535)
190    scale2.set($green / 65.535)
191    scale3.set($blue / 65.535)
192  when :cmy
193    scale1.set((65535 - $red) / 65.535)
194    scale2.set((65535 - $green) / 65.535)
195    scale3.set((65535 - $blue) / 65.535)
196  when :hsb
197    list = rgbToHsv($red,$green,$blue)
198    scale1.set( list[0] * 1000.0 )
199    scale2.set( list[1] * 1000.0 )
200    scale3.set( list[2] * 1000.0 )
201  else
202    raise(Exception,"unknown colorSpace")
203  end
204
205  $updating.value = 0
206end
207
208
209def tc_loadNamedColor(name)
210  $name.value = name
211  $master = :name if $master == nil
212  if name[0,1] != "#"
213    list = TkWinfo.rgb($root.middle.right.swatch,name)
214    $red = list[0]
215    $green = list[1]
216    $blue = list[2]
217  else
218    case name.length
219    when 4
220      fmt = /#(.{1})(.{1})(.{1})/
221      shift = 12
222    when 7
223      fmt = /#(.{2})(.{2})(.{2})/
224      shift = 8
225    when 10
226      fmt = /#(.{3})(.{3})(.{3})/
227      shift = 4
228    when 13
229      fmt = /#(.{4})(.{4})(.{4})/
230      shift = 0
231    else
232      raise(eException,"syntax error in color name \"#{name}\"")
233    end
234    name.scan(fmt){|strlist|
235      if strlist.length != 3
236	raise(eException,"syntax error in color name \"#{name}\"")
237      end
238      $red = strlist[0].hex
239      $green = strlist[1].hex
240      $blue = strlist[2].hex
241    }
242    $red = $red << shift
243    $green = $green << shift
244    $blue = $blue << shift
245  end
246
247  tc_setScales
248  $color = format("#%04x%04x%04x",$red,$green,$blue)
249  $root.middle.right.set_color($color)
250  if $autoUpdate.to_i == 1
251    doUpdate
252  end
253  Tk.update(true)
254  $master = nil if $master == :name
255end
256
257
258def changeColorSpace(space)
259  case space
260  when :rgb
261    $label1.value = "Red"
262    $label2.value = "Green"
263    $label3.value = "Blue"
264  when :cmy
265    $label1.value = "Cyan"
266    $label2.value = "Magenta"
267    $label3.value = "Yellow"
268  when :hsb
269    $label1.value = "Hue"
270    $label2.value = "Saturation"
271    $label3.value = "Brightness"
272  end
273  tc_setScales
274end
275
276
277
278
279
280# tcolor������������������
281
282class TkColorMenuFrame<TkFrame
283  def initialize(parent)
284    super(parent,
285	  "relief"=>"raised",
286	  "borderwidth"=>"2")
287
288    # File������������������������������
289    @file = TkMenubutton.new(self){|button|
290
291      # File���������������������
292      @file_menu = TkMenu.new(button){
293	add "radio",
294	  "label" => "RGB color space",
295	  "variable" => $colorSpace,
296	  "value" => :rgb,
297	  "underline" => "0",
298	  "command" => proc{changeColorSpace(:rgb)}
299	add "radio",
300	  "label" => "CMY color space",
301	  "variable" => $colorSpace,
302	  "value" => :cmy,
303	  "underline" => "0",
304	  "command" => proc{changeColorSpace(:cmy)}
305	add "radio",
306	  "label" => "HSB color space",
307	  "variable" => $colorSpace,
308	  "value" => :hsb,
309	  "underline" => "0",
310	  "command" => proc{changeColorSpace(:hsb)}
311	add "separator"
312	add "radio",
313	  "label" => "Automatic updates",
314	  "variable" => $autoUpdate,
315	  "value" => "1",
316	  "underline" => "0"
317	add "radio",
318	  "label" => "Manual updates",
319	  "variable" => $autoUpdate,
320	  "value" => "0",
321	  "underline" => "0"
322	add "separator"
323	add "command",
324	  "label" => "Exit program",
325	  "underline" => "0",
326	  "command" => proc{exit}
327      }
328
329      # File���������������File���������������������������
330      menu @file_menu
331
332      text "File"
333      underline "0"
334    }.pack("side"=>"left")
335
336    self
337  end
338end
339
340
341# ������������������������������������������
342class TkColorBotFrame<TkFrame
343  def initialize(parent)
344    super(parent,
345	  "relief"=> "raised",
346	  "borderwidth"=> 2)
347
348    @commandLabel = TkLabel.new(self,
349				"text"=> "Command:")
350    @command = TkEntry.new(self,
351			   "relief"=> "sunken",
352			   "borderwidth"=> "2",
353			   "textvariable"=> $command,
354			   "font"=> "-Adobe-Courier-Medium-R-Normal--*-120-*-*-*-*-*-*")
355    @update = TkButton.new(self,
356			   "text"=> "Update",
357			   "command"=> proc{doUpdate})
358    @commandLabel.pack("side"=>"left")
359    @update.pack("side"=>"right","pady"=>".1c","padx"=>".25c")
360    @command.pack("expand"=>"yes","fill"=>"x","ipadx"=>".25c")
361
362    self
363  end
364end
365
366
367# ������������������������
368class TkColorMiddleLeftFrame<TkFrame
369  def initialize(parent)
370    super(parent)
371
372    for i in ["/usr/local/lib/X11rgb.txt","/usr/lib/X11/rgb.txt",
373	"/X11/R5/lib/X11/rgb.txt","/X11/R4/lib/rgb/rgb.txt",
374	"/usr/openwin/lib/X11/rgb.txt"]
375      if !File.readable?(i)
376	next
377      end
378      f = File.open(i)
379      @scroll = TkScrollbar.new(self,
380				"orient"=>"vertical",
381				"relief"=>"sunken",
382				"borderwidth"=>"2")
383      @scroll.pack("side"=>"right","fill"=>"y")
384      @names = TkListbox.new(self,
385			     "width"=>"20",
386			     "height"=>"12",
387			     "yscrollcommand"=> proc{|first,last| @scroll.set first,last},
388			     "relief"=>"sunken",
389			     "borderwidth"=>"2",
390			     "exportselection"=>"false")
391      @scroll.command(proc{|*args| @names.yview(*args)})
392      @names.bind("Double-1",proc{
393		    tc_loadNamedColor(@names.get(@names.curselection))})
394      @names.pack("side"=>"left")
395      while (line = f.gets)
396	line.chop!
397	linelist = line.split(/[ \t]+/)
398	if linelist.length == 4
399	  @names.insert("end",linelist[3])
400	end
401      end
402      f.close
403      break
404    end
405
406    self
407  end
408end
409
410
411# ���������������������������
412class TkColorMiddleMiddleFrame<TkFrame
413  # @scale1,@scale2,@scale3������������������������������������������(������������)
414  attr_reader :scale1, :scale2, :scale3
415
416  def initialize(parent)
417    super(parent)
418
419    @f1 = TkFrame.new(self)
420    @f2 = TkFrame.new(self)
421    @f3 = TkFrame.new(self)
422    @f4 = TkFrame.new(self)
423
424    for f in [@f1,@f2,@f3]
425      f.pack("side"=>"top","expand"=>"yes")
426    end
427    @f4.pack("side"=>"top","expand"=>"yes","fill"=>"x")
428
429    @label1 = TkLabel.new(self,"textvariable"=>$label1)
430    @scale1 = TkScale.new(self,"from"=>"0","to"=>"1000","length"=>"6c",
431			  "orient"=>"horizontal",
432			  "command"=>proc{tc_scaleChanged})
433    @scale1.pack("side"=>"top","anchor"=>"w")
434    @label1.pack("side"=>"top","anchor"=>"w")
435
436    @label2 = TkLabel.new(self,"textvariable"=>$label2)
437    @scale2 = TkScale.new(self,"from"=>"0","to"=>"1000","length"=>"6c",
438			  "orient"=>"horizontal",
439			  "command"=>proc{tc_scaleChanged})
440    @scale2.pack("side"=>"top","anchor"=>"w")
441    @label2.pack("side"=>"top","anchor"=>"w")
442
443    @label3 = TkLabel.new(self,"textvariable"=>$label3)
444    @scale3 = TkScale.new(self,"from"=>"0","to"=>"1000","length"=>"6c",
445			  "orient"=>"horizontal",
446			  "command"=>proc{tc_scaleChanged})
447    @scale3.pack("side"=>"top","anchor"=>"w")
448    @label3.pack("side"=>"top","anchor"=>"w")
449
450    @nameLabel = TkLabel.new(self,"text"=>"Name:")
451    @name = TkEntry.new(self,"relief"=>"sunken","borderwidth"=>"2",
452			"textvariable"=>$name,"width"=>"10",
453			"font"=>"-Adobe-Courier-Medium-R-Normal--*-120-*-*-*-*-*-*")
454    @nameLabel.pack("side"=>"left")
455    @name.pack("side"=>"right", "expand"=>"1", "fill"=>"x")
456    @name.bind("Return",proc{tc_loadNamedColor $name.to_s})
457
458    self
459  end
460end
461
462
463class TkColorMiddleRightFrame<TkFrame
464  attr_reader :swatch
465
466  def initialize(parent)
467    super(parent)
468    @swatch = TkFrame.new(self, "width"=>"2c", "height"=>"5c",
469			  "background"=>$color)
470    @value = TkLabel.new(self,
471			 "text"=>$color,
472			 "width"=>"13",
473			 "font"=>"-Adobe-Courier-Medium-R-Normal--*-120-*-*-*-*-*-*")
474    @swatch.pack("side"=>"top","expand"=>"yes","fill"=>"both")
475    @value.pack("side"=>"bottom","pady"=>".25c")
476
477    self
478  end
479
480  def set_color(color)
481    @swatch["background"] = color
482    @value["text"] = color
483  end
484end
485
486
487
488# ���������������������
489class TkColorMiddleFrame<TkFrame
490  attr_reader :left, :middle, :right
491
492  def initialize(parent)
493    super(parent,
494	  "relief"=> "raised",
495	  "borderwidth"=> "2")
496
497    @left = TkColorMiddleLeftFrame.new(self)
498    @left.pack("side"=>"left","padx"=>".25c","pady"=>".25c")
499
500    @middle = TkColorMiddleMiddleFrame.new(self)
501    @middle.pack("side"=>"left","expand"=>"yes","fill"=>"y")
502
503    @right = TkColorMiddleRightFrame.new(self)
504    @right.pack("side"=>"left","padx"=>".25c","pady"=>".25c","anchor"=>"s")
505
506    self
507  end
508end
509
510
511class TkColor<TkRoot
512  attr_reader :menu, :bottom, :middle
513
514  def initialize(*args)
515    super(*args)
516    @menu = TkColorMenuFrame.new(self)
517    @menu.pack("side"=>"top", "fill"=>"x")
518
519    @bottom = TkColorBotFrame.new(self)
520    @bottom.pack("side"=>"bottom","fill"=>"x")
521
522    @middle = TkColorMiddleFrame.new(self)
523    @middle.pack("side"=>"top","fill"=>"both")
524
525    self
526  end
527end
528
529
530$root = TkColor.new
531
532# ������������������������������������������������
533changeColorSpace :rgb
534Tk.mainloop
535