1# 2# Tile theme engin (tile widget set) support 3# by Hidetoshi NAGAI (nagai@ai.kyutech.ac.jp) 4# 5 6require 'tk' 7require 'tk/ttk_selector' 8 9# call setup script for general 'tkextlib' libraries 10require 'tkextlib/setup.rb' 11 12# library directory 13require 'tkextlib/tile/setup.rb' 14 15# load package 16# TkPackage.require('tile', '0.4') 17# TkPackage.require('tile', '0.6') 18# TkPackage.require('tile', '0.7') 19if Tk::TK_MAJOR_VERSION > 8 || 20 (Tk::TK_MAJOR_VERSION == 8 && Tk::TK_MINOR_VERSION >= 5) 21 begin 22 TkPackage.require('tile') # for compatibility (version check of 'tile') 23 rescue RuntimeError 24 # ignore, even if cannot find package 'tile' 25 end 26 pkgname = 'Ttk' 27else 28 pkgname = 'tile' 29end 30 31begin 32 verstr = TkPackage.require(pkgname) 33rescue RuntimeError 34 # define dummy methods 35 module Tk 36 module Tile 37 CANNOT_FIND_PACKAGE = true 38 def self.const_missing(sym) 39 TkPackage.require(PACKAGE_NAME) 40 end 41 def self.method_missing(*args) 42 TkPackage.require(PACKAGE_NAME) 43 end 44 end 45 end 46 Tk.__cannot_find_tk_package_for_widget_set__(:Ttk, pkgname) 47 if pkgname == 'Ttk' 48 verstr = Tk::TK_PATCHLEVEL # dummy 49 else 50 verstr = '0.7' # dummy 51 end 52end 53 54ver = verstr.split('.') 55if ver[0].to_i == 0 56 # Tile extension package 57 if ver[1].to_i <= 4 58 # version 0.4 or former 59 module Tk 60 module Tile 61 USE_TILE_NAMESPACE = true 62 USE_TTK_NAMESPACE = false 63 TILE_SPEC_VERSION_ID = 0 64 end 65 end 66 elsif ver[1].to_i <= 6 67 # version 0.5 -- version 0.6 68 module Tk 69 module Tile 70 USE_TILE_NAMESPACE = true 71 USE_TTK_NAMESPACE = true 72 TILE_SPEC_VERSION_ID = 5 73 end 74 end 75 elsif ver[1].to_i <= 7 76 module Tk 77 module Tile 78 USE_TILE_NAMESPACE = false 79 USE_TTK_NAMESPACE = true 80 TILE_SPEC_VERSION_ID = 7 81 end 82 end 83 else 84 # version 0.8 or later 85 module Tk 86 module Tile 87 USE_TILE_NAMESPACE = false 88 USE_TTK_NAMESPACE = true 89 TILE_SPEC_VERSION_ID = 8 90 end 91 end 92 end 93 94 module Tk::Tile 95 PACKAGE_NAME = 'tile'.freeze 96 end 97else 98 # Ttk package merged Tcl/Tk core (Tcl/Tk 8.5+) 99 module Tk 100 module Tile 101 USE_TILE_NAMESPACE = false 102 USE_TTK_NAMESPACE = true 103 TILE_SPEC_VERSION_ID = 8 104 105 PACKAGE_NAME = 'Ttk'.freeze 106 end 107 end 108end 109 110# autoload 111module Tk 112 module Tile 113 TkComm::TkExtlibAutoloadModule.unshift(self) 114 115 def self.package_name 116 PACKAGE_NAME 117 end 118 119 def self.package_version 120 begin 121 TkPackage.require(PACKAGE_NAME) 122 rescue 123 '' 124 end 125 end 126 127 def self.__Import_Tile_Widgets__! 128 warn 'Warning: "Tk::Tile::__Import_Tile_Widgets__!" is obsolete.' << 129 ' To control default widget set, use "Tk.default_widget_set = :Ttk"' 130 Tk.tk_call('namespace', 'import', '-force', 'ttk::*') 131 end 132 133 def self.__define_LoadImages_proc_for_compatibility__! 134 # Ttk 8.5 (Tile 0.8) lost 'LoadImages' utility procedure. 135 # So, some old scripts doen't work, because those scripts use the 136 # procedure to define local styles. 137 # Of course, rewriting such Tcl/Tk scripts isn't difficult for 138 # Tcl/Tk users. However, it may be troublesome for Ruby/Tk users 139 # who use such Tcl/Tk scripts as it is. 140 # This method may help Ruby/Tk users who don't want to modify old 141 # Tcl/Tk scripts for the latest version of Ttk (Tile) extension. 142 # This method defines a comaptible 'LoadImages' procedure on the 143 # Tcl/Tk interpreter working under Ruby/Tk. 144 # Please give attention to use this method. It may conflict with 145 # some definitions on Tcl/Tk scripts. 146 klass_name = self.name 147 proc_name = 'LoadImages' 148 if Tk::Tile::USE_TTK_NAMESPACE 149 ns_list = ['::tile'] 150 if Tk.info(:commands, "::ttk::#{proc_name}").empty? 151 ns_list << '::ttk' 152 end 153 else # Tk::Tile::USE_TILE_NAMESPACE 154 ns_list = ['::ttk'] 155 if Tk.info(:commands, "::tile::#{proc_name}").empty? 156 ns_list << '::tile' 157 end 158 end 159 160 ns_list.each{|ns| 161 cmd = "#{ns}::#{proc_name}" 162 unless Tk.info(:commands, cmd).empty? 163 #fail RuntimeError, "can't define '#{cmd}' command (already exist)" 164 165 # do nothing !!! 166 warn "Warning: can't define '#{cmd}' command (already exist)" if $DEBUG 167 next 168 end 169 TkNamespace.eval(ns){ 170 TkCore::INTERP.add_tk_procs(proc_name, 'imgdir {patterns {*.gif}}', 171 <<-'EOS') 172 foreach pattern $patterns { 173 foreach file [glob -directory $imgdir $pattern] { 174 set img [file tail [file rootname $file]] 175 if {![info exists images($img)]} { 176 set images($img) [image create photo -file $file] 177 } 178 } 179 } 180 return [array get images] 181 EOS 182 } 183 } 184 end 185 186 def self.load_images(imgdir, pat=nil) 187 if Tk::Tile::TILE_SPEC_VERSION_ID < 8 188 if Tk::Tile::USE_TTK_NAMESPACE 189 cmd = '::ttk::LoadImages' 190 else # Tk::Tile::USE_TILE_NAMESPACE 191 cmd = '::tile::LoadImages' 192 end 193 pat ||= TkComm::None 194 images = Hash[*TkComm.simplelist(Tk.tk_call(cmd, imgdir, pat))] 195 images.keys.each{|k| 196 images[k] = TkPhotoImage.new(:imagename=>images[k], 197 :without_creating=>true) 198 } 199 else ## TILE_SPEC_VERSION_ID >= 8 200 pat ||= '*.gif' 201 if pat.kind_of?(Array) 202 pat_list = pat 203 else 204 pat_list = [ pat ] 205 end 206 Dir.chdir(imgdir){ 207 pat_list.each{|pat| 208 Dir.glob(pat).each{|f| 209 img = File.basename(f, '.*') 210 unless TkComm.bool(Tk.info('exists', "images(#{img})")) 211 Tk.tk_call('set', "images(#{img})", 212 Tk.tk_call('image', 'create', 'photo', '-file', f)) 213 end 214 } 215 } 216 } 217 images = Hash[*TkComm.simplelist(Tk.tk_call('array', 'get', 'images'))] 218 images.keys.each{|k| 219 images[k] = TkPhotoImage.new(:imagename=>images[k], 220 :without_creating=>true) 221 } 222 end 223 224 images 225 end 226 227 def self.style(*args) 228 args.map!{|arg| TkComm._get_eval_string(arg)}.join('.') 229 end 230 231 def self.themes(glob_ptn = nil) 232 if TILE_SPEC_VERSION_ID < 8 && Tk.info(:commands, '::ttk::themes').empty? 233 fail RuntimeError, 'not support glob option' if glob_ptn 234 cmd = ['::tile::availableThemes'] 235 else 236 glob_ptn = '*' unless glob_ptn 237 cmd = ['::ttk::themes', glob_ptn] 238 end 239 240 begin 241 TkComm.simplelist(Tk.tk_call_without_enc(*cmd)) 242 rescue 243 TkComm.simplelist(Tk.tk_call('lsearch', '-all', '-inline', 244 Tk::Tile::Style.theme_names, 245 glob_ptn)) 246 end 247 end 248 249 def self.set_theme(theme) 250 if TILE_SPEC_VERSION_ID < 8 && Tk.info(:commands, '::ttk::setTheme').empty? 251 cmd = '::tile::setTheme' 252 else 253 cmd = '::ttk::setTheme' 254 end 255 256 begin 257 Tk.tk_call_without_enc(cmd, theme) 258 rescue 259 Tk::Tile::Style.theme_use(theme) 260 end 261 end 262 263 module KeyNav 264 if Tk::Tile::TILE_SPEC_VERSION_ID < 8 265 def self.enableMnemonics(w) 266 Tk.tk_call('::keynav::enableMnemonics', w) 267 end 268 def self.defaultButton(w) 269 Tk.tk_call('::keynav::defaultButton', w) 270 end 271 else # dummy 272 def self.enableMnemonics(w) 273 "" 274 end 275 def self.defaultButton(w) 276 "" 277 end 278 end 279 end 280 281 module Font 282 Default = 'TkDefaultFont' 283 Text = 'TkTextFont' 284 Heading = 'TkHeadingFont' 285 Caption = 'TkCaptionFont' 286 Tooltip = 'TkTooltipFont' 287 288 Fixed = 'TkFixedFont' 289 Menu = 'TkMenuFont' 290 SmallCaption = 'TkSmallCaptionFont' 291 Icon = 'TkIconFont' 292 293 TkFont::SYSTEM_FONT_NAMES.add [ 294 'TkDefaultFont', 'TkTextFont', 'TkHeadingFont', 295 'TkCaptionFont', 'TkTooltipFont', 'TkFixedFont', 296 'TkMenuFont', 'TkSmallCaptionFont', 'TkIconFont' 297 ] 298 end 299 300 module ParseStyleLayout 301 def _style_layout(lst) 302 ret = [] 303 until lst.empty? 304 sub = [lst.shift] 305 keys = {} 306 307 until lst.empty? 308 if lst[0][0] == ?- 309 k = lst.shift[1..-1] 310 children = lst.shift 311 children = _style_layout(children) if children.kind_of?(Array) 312 keys[k] = children 313 else 314 break 315 end 316 end 317 318 sub << keys unless keys.empty? 319 ret << sub 320 end 321 ret 322 end 323 private :_style_layout 324 end 325 326 module TileWidget 327 include Tk::Tile::ParseStyleLayout 328 329 def __val2ruby_optkeys # { key=>proc, ... } 330 # The method is used to convert a opt-value to a ruby's object. 331 # When get the value of the option "key", "proc.call(value)" is called. 332 super().update('style'=>proc{|v| _style_layout(list(v))}) 333 end 334 private :__val2ruby_optkeys 335 336 def ttk_instate(state, script=nil, &b) 337 if script 338 tk_send('instate', state, script) 339 elsif b 340 tk_send('instate', state, Proc.new(&b)) 341 else 342 bool(tk_send('instate', state)) 343 end 344 end 345 alias tile_instate ttk_instate 346 347 def ttk_state(state=nil) 348 if state 349 tk_send('state', state) 350 else 351 list(tk_send('state')) 352 end 353 end 354 alias tile_state ttk_state 355 356 def ttk_identify(x, y) 357 ret = tk_send_without_enc('identify', x, y) 358 (ret.empty?)? nil: ret 359 end 360 alias tile_identify ttk_identify 361 362 # remove instate/state/identify method 363 # to avoid the conflict with widget options 364 if Tk.const_defined?(:USE_OBSOLETE_TILE_STATE_METHOD) && Tk::USE_OBSOLETE_TILE_STATE_METHOD 365 alias instate ttk_instate 366 alias state ttk_state 367 alias identify ttk_identify 368 end 369 end 370 371 ###################################### 372 373 autoload :TButton, 'tkextlib/tile/tbutton' 374 autoload :Button, 'tkextlib/tile/tbutton' 375 376 autoload :TCheckButton, 'tkextlib/tile/tcheckbutton' 377 autoload :CheckButton, 'tkextlib/tile/tcheckbutton' 378 autoload :TCheckbutton, 'tkextlib/tile/tcheckbutton' 379 autoload :Checkbutton, 'tkextlib/tile/tcheckbutton' 380 381 autoload :Dialog, 'tkextlib/tile/dialog' 382 383 autoload :TEntry, 'tkextlib/tile/tentry' 384 autoload :Entry, 'tkextlib/tile/tentry' 385 386 autoload :TCombobox, 'tkextlib/tile/tcombobox' 387 autoload :Combobox, 'tkextlib/tile/tcombobox' 388 389 autoload :TFrame, 'tkextlib/tile/tframe' 390 autoload :Frame, 'tkextlib/tile/tframe' 391 392 autoload :TLabelframe, 'tkextlib/tile/tlabelframe' 393 autoload :Labelframe, 'tkextlib/tile/tlabelframe' 394 autoload :TLabelFrame, 'tkextlib/tile/tlabelframe' 395 autoload :LabelFrame, 'tkextlib/tile/tlabelframe' 396 397 autoload :TLabel, 'tkextlib/tile/tlabel' 398 autoload :Label, 'tkextlib/tile/tlabel' 399 400 autoload :TMenubutton, 'tkextlib/tile/tmenubutton' 401 autoload :Menubutton, 'tkextlib/tile/tmenubutton' 402 autoload :TMenuButton, 'tkextlib/tile/tmenubutton' 403 autoload :MenuButton, 'tkextlib/tile/tmenubutton' 404 405 autoload :TNotebook, 'tkextlib/tile/tnotebook' 406 autoload :Notebook, 'tkextlib/tile/tnotebook' 407 408 autoload :TPaned, 'tkextlib/tile/tpaned' 409 autoload :Paned, 'tkextlib/tile/tpaned' 410 autoload :PanedWindow, 'tkextlib/tile/tpaned' 411 autoload :Panedwindow, 'tkextlib/tile/tpaned' 412 413 autoload :TProgressbar, 'tkextlib/tile/tprogressbar' 414 autoload :Progressbar, 'tkextlib/tile/tprogressbar' 415 416 autoload :TRadioButton, 'tkextlib/tile/tradiobutton' 417 autoload :RadioButton, 'tkextlib/tile/tradiobutton' 418 autoload :TRadiobutton, 'tkextlib/tile/tradiobutton' 419 autoload :Radiobutton, 'tkextlib/tile/tradiobutton' 420 421 autoload :TScale, 'tkextlib/tile/tscale' 422 autoload :Scale, 'tkextlib/tile/tscale' 423 autoload :TProgress, 'tkextlib/tile/tscale' 424 autoload :Progress, 'tkextlib/tile/tscale' 425 426 autoload :TScrollbar, 'tkextlib/tile/tscrollbar' 427 autoload :Scrollbar, 'tkextlib/tile/tscrollbar' 428 autoload :XScrollbar, 'tkextlib/tile/tscrollbar' 429 autoload :YScrollbar, 'tkextlib/tile/tscrollbar' 430 431 autoload :TSeparator, 'tkextlib/tile/tseparator' 432 autoload :Separator, 'tkextlib/tile/tseparator' 433 434 autoload :TSpinbox, 'tkextlib/tile/tspinbox' 435 autoload :Spinbox, 'tkextlib/tile/tspinbox' 436 437 autoload :TSquare, 'tkextlib/tile/tsquare' 438 autoload :Square, 'tkextlib/tile/tsquare' 439 440 autoload :SizeGrip, 'tkextlib/tile/sizegrip' 441 autoload :Sizegrip, 'tkextlib/tile/sizegrip' 442 443 autoload :Treeview, 'tkextlib/tile/treeview' 444 445 autoload :Style, 'tkextlib/tile/style' 446 end 447end 448 449Ttk = Tk::Tile 450