File: tk/menuspec.rb

Overview
Module Structure
Code

Overview

Module Structure

  module: <Toplevel Module>
  module: TkMenuSpec#41
has properties
method: _create_menu / 5 #42
method: _use_menubar? / 1 #155
method: _create_menu_for_menubar / 1 #167
method: _create_menubutton / 4 #180
method: _get_cascade_menus / 1 #264

Code

   1  #
   2  # tk/menuspec.rb
   3  #                              Hidethoshi NAGAI (nagai@ai.kyutech.ac.jp)
   4  #
   5  # based on tkmenubar.rb :
   6  #   Copyright (C) 1998 maeda shugo. All rights reserved. 
   7  #   This file can be distributed under the terms of the Ruby.
   8  #
   9  # The format of the menu_spec is:
  10  #   [ menu_info, menu_info, ... ]
  11  #
  12  # And the format of the menu_info is:
  13  #   [
  14  #     [text, underline, configs], # menu button/entry (*1)
  15  #     [label, command, underline, accelerator, configs],   # command entry
  16  #     [label, TkVar_obj, underline, accelerator, configs], # checkbutton entry
  17  #     [label, [TkVar_obj, value], 
  18  #                        underline, accelerator, configs], # radiobutton entry
  19  #     [label, [[...menu_info...], [...menu_info...], ...], 
  20  #                        underline, accelerator, configs], # cascade entry (*2)
  21  #     '---', # separator
  22  #     ...
  23  #   ]
  24  #
  25  # underline, accelerator, and configs are optional pearameters. 
  26  # Hashes are OK instead of Arrays. Then the entry type ('command', 
  27  # 'checkbutton', 'radiobutton' or 'cascade') is given by 'type' key
  28  # (e.g. :type=>'cascade'). When type is 'cascade', an array of menu_info
  29  # is acceptable for 'menu' key (then, create sub-menu).
  30  #
  31  # NOTE: (*1)
  32  #   If you want to make special menus (*.help for UNIX, *.system for Win, 
  33  #   and *.apple for Mac), append 'menu_name'=>name (name is 'help' for UNIX, 
  34  #   'system' for Win, and 'apple' for Mac) option to the configs hash of 
  35  #   menu button/entry information.
  36  #
  37  # NOTE: (*2)
  38  #   If you want to configure a cascade menu, add :menu_config=>{...configs..}
  39  #   to the configs of the cascade entry.
  40 
  41  module TkMenuSpec
  42    def _create_menu(parent, menu_info, menu_name = nil, 
  43                     tearoff = false, default_opts = nil)
  44      if tearoff.kind_of?(Hash)
  45        default_opts = tearoff
  46        tearoff = false
  47      end
  48 
  49      if menu_name.kind_of?(Hash)
  50        default_opts = menu_name
  51        menu_name = nil
  52        tearoff = false
  53      end
  54 
  55      if default_opts.kind_of?(Hash)
  56        orig_opts = _symbolkey2str(default_opts)
  57      else
  58        orig_opts = {}
  59      end
  60 
  61      tearoff = orig_opts.delete('tearoff') if orig_opts.key?('tearoff')
  62 
  63      if menu_name
  64        #menu = Tk::Menu.new(parent, :widgetname=>menu_name, :tearoff=>tearoff)
  65        # --> use current TkMenu class
  66        menu = TkMenu.new(parent, :widgetname=>menu_name, :tearoff=>tearoff)
  67      else
  68        #menu = Tk::Menu.new(parent, :tearoff=>tearoff)
  69        # --> use current TkMenu class
  70        menu = TkMenu.new(parent, :tearoff=>tearoff)
  71      end
  72 
  73      for item_info in menu_info
  74        if item_info.kind_of?(Hash)
  75          options = orig_opts.dup
  76          options.update(_symbolkey2str(item_info))
  77          item_type = (options.delete('type') || 'command').to_s
  78          menu_name = options.delete('menu_name')
  79          menu_opts = orig_opts.dup
  80          menu_opts.update(_symbolkey2str(options.delete('menu_config') || {}))
  81          if item_type == 'cascade' && options['menu'].kind_of?(Array)
  82            # create cascade menu
  83            submenu = _create_menu(menu, options['menu'], menu_name, 
  84                                   tearoff, menu_opts)
  85            options['menu'] = submenu
  86          end
  87          menu.add(item_type, options)
  88 
  89        elsif item_info.kind_of?(Array)
  90          options = orig_opts.dup
  91 
  92          options['label'] = item_info[0] if item_info[0]
  93 
  94          case item_info[1]
  95          when TkVariable
  96            # checkbutton
  97            item_type = 'checkbutton'
  98            options['variable'] = item_info[1]
  99            options['onvalue']  = true
 100            options['offvalue'] = false
 101 
 102          when Array
 103            # radiobutton or cascade
 104            if item_info[1][0].kind_of?(TkVariable)
 105              # radiobutton
 106              item_type = 'radiobutton'
 107              options['variable'] = item_info[1][0]
 108              options['value'] = item_info[1][1] if item_info[1][1]
 109 
 110            else
 111              # cascade
 112              item_type = 'cascade'
 113              menu_opts = orig_opts.dup
 114              if item_info[4] && item_info[4].kind_of?(Hash)
 115                opts = _symbolkey2str(item_info[4])
 116                menu_name = opts.delete('menu_name')
 117                menu_config = opts.delete('menu_config') || {}
 118                menu_opts.update(_symbolkey2str(menu_config))
 119              end
 120              submenu = _create_menu(menu, item_info[1], menu_name, 
 121                                     tearoff, menu_opts)
 122              options['menu'] = submenu
 123            end
 124 
 125          else
 126            # command
 127            item_type = 'command'
 128            options['command'] = item_info[1] if item_info[1]
 129          end
 130 
 131          options['underline'] = item_info[2] if item_info[2]
 132          options['accelerator'] = item_info[3] if item_info[3]
 133          if item_info[4] && item_info[4].kind_of?(Hash)
 134            opts = _symbolkey2str(item_info[4])
 135            if item_type == 'cascade'
 136              opts.delete('menu_name')
 137              opts.delete('menu_config')
 138            end
 139            options.update(opts)
 140          end
 141          menu.add(item_type, options)
 142 
 143        elsif /^-+$/ =~ item_info
 144          menu.add('separator')
 145 
 146        else
 147          menu.add('command', 'label' => item_info)
 148        end
 149      end
 150 
 151      menu
 152    end
 153    private :_create_menu
 154 
 155    def _use_menubar?(parent)
 156      use_menubar = false
 157      if parent.kind_of?(Tk::Root) || parent.kind_of?(Tk::Toplevel)
 158        true 
 159      elsif parent.current_configinfo.has_key?('menu')
 160        true
 161      else
 162        false
 163      end
 164    end
 165    private :_use_menubar?
 166 
 167    def _create_menu_for_menubar(parent)
 168      #unless (mbar = parent.menu).kind_of?(TkMenu)
 169      # --> use current TkMenu class
 170      mbar = parent.menu
 171      unless mbar.kind_of?(Tk::Menu) || mbar.kind_of?(TkMenu)
 172        #mbar = Tk::Menu.new(parent, :tearoff=>false)
 173        mbar = TkMenu.new(parent, :tearoff=>false)
 174        parent.menu(mbar)
 175      end
 176      mbar
 177    end
 178    private :_create_menu_for_menubar
 179 
 180    def _create_menubutton(parent, menu_info, tearoff=false, default_opts = nil)
 181      btn_info = menu_info[0]
 182 
 183      if tearoff.kind_of?(Hash)
 184        default_opts = tearoff
 185        tearoff = false
 186      end
 187 
 188      if default_opts.kind_of?(Hash)
 189        keys = _symbolkey2str(default_opts)
 190      else
 191        keys = {}
 192      end
 193 
 194      tearoff = keys.delete('tearoff') if keys.key?('tearoff')
 195 
 196      if _use_menubar?(parent)
 197        # menubar by menu entries
 198        mbar = _create_menu_for_menubar(parent)
 199 
 200        menu_name = nil
 201 
 202        if btn_info.kind_of?(Hash)
 203          keys.update(_symbolkey2str(btn_info))
 204          menu_name = keys.delete('menu_name')
 205          keys['label'] = keys.delete('text') if keys.key?('text')
 206        elsif btn_info.kind_of?(Array)
 207          keys['label'] = btn_info[0] if btn_info[0]
 208          keys['underline'] = btn_info[1] if btn_info[1]
 209          if btn_info[2]&&btn_info[2].kind_of?(Hash)
 210            keys.update(_symbolkey2str(btn_info[2]))
 211            menu_name = keys.delete('menu_name')
 212          end
 213        else
 214          keys = {:label=>btn_info}
 215        end
 216 
 217        menu = _create_menu(mbar, menu_info[1..-1], menu_name, 
 218                            tearoff, default_opts)
 219        menu.tearoff(tearoff)
 220 
 221        keys['menu'] = menu
 222        mbar.add('cascade', keys)
 223 
 224        [mbar, menu]
 225 
 226      else
 227        # menubar by menubuttons
 228        #mbtn = Tk::Menubutton.new(parent)
 229        # --> use current TkMenubutton class
 230        mbtn = TkMenubutton.new(parent)
 231 
 232        menu_name = nil
 233 
 234        if btn_info.kind_of?(Hash)
 235          keys.update(_symbolkey2str(btn_info))
 236          menu_name = keys.delete('menu_name')
 237          keys['text'] = keys.delete('label') if keys.key?('label')
 238          mbtn.configure(keys)
 239        elsif btn_info.kind_of?(Array)
 240          mbtn.configure('text', btn_info[0]) if btn_info[0]
 241          mbtn.configure('underline', btn_info[1]) if btn_info[1]
 242          # mbtn.configure('accelerator', btn_info[2]) if btn_info[2]
 243          if btn_info[2]&&btn_info[2].kind_of?(Hash)
 244            keys.update(_symbolkey2str(btn_info[2]))
 245            menu_name = keys.delete('menu_name')
 246            mbtn.configure(keys)
 247          end
 248        else
 249          mbtn.configure('text', btn_info)
 250        end
 251 
 252        mbtn.pack('side' => 'left')
 253 
 254        menu = _create_menu(mbtn, menu_info[1..-1], menu_name, 
 255                            tearoff, default_opts)
 256      
 257        mbtn.menu(menu)
 258 
 259        [mbtn, menu]
 260      end
 261    end
 262    private :_create_menubutton
 263 
 264    def _get_cascade_menus(menu)
 265      menus = []
 266      (0..(menu.index('last'))).each{|idx|
 267        if menu.menutype(idx) == 'cascade'
 268          submenu = menu.entrycget(idx, 'menu')
 269          menus << [submenu, _get_cascade_menus(submenu)]
 270        end
 271      }
 272      menus
 273    end
 274    private :_get_cascade_menus
 275  end