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