Create  Edit  FrontPage  Index  Search  Changes  History  RSS  Login

tut-gtk2-treev-kbda

Keyboard Accelerator Renderers

treev-kbda-01.png

Gtk::CellRendererAccel widget displays textual representation of the keyboard accelerator keys. It allows you to dynamically edit the accelerator, hence changing its binding for the application. To edit the accelerator user must click on the "Accelerator" cell. The cell will then accept a new key combination user enters. The new key code will be added, along with any mask keys such as Ctrl, Alt, Shift or any combination of these. In fact the new key combination the user presses will be displayed.


BUG:
dialog-warning.png Currently, as of October 2012, there is a minorBUGin this area. Namely, one can not define more than a single modifier key in the model (if you wish to understand more about the bug, please search for the word BUG in the 'accelerators.rb' example program listing below).

Note however, that beside just mentioned minor rendering problem, the modifier keys are handled properly, and that even Gtk::CellRendererAccel handles them correctly, namely, if user enters, say, "<Shift+Ctrl+Alt>+X" combination into the editable renderer cell, we can, without any troubles, analyze and detect all the components of the combined keystroke when processing'accel_edited'signal:

renderer.signal_connect('accel_edited') do |w, path, accl_k, mask, hw_k|
  if (iter = treeview.model.get_iter(path))
    iter[MASK_COLUMN] = mask
    iter[VALUE_COLUMN] = accl_k
    puts "accel_k:#{accl_k}=[" +
         "#{Gdk::Keyval.to_name(accl_k)}] hw_k:#{hw_k} " +
         "Modifier keys=#{get_string_for_modifier_keys(mask)}"
  end

Where the deciphering of or'ed together modifiers is performed in 'get_string_for_modifier_keys' method. You can find all the modifiers in Gdk::Window#GdkModifierType.


Following is the example program:


accelerators.rb

#!/usr/bin/env ruby
require 'gtk2'

def setup_tree_view(treeview)
  # Create a new GtkCellRendererText, add it to the tree
  # view column and append the column to the tree view.
  renderer = Gtk::CellRendererText.new
  column = Gtk::TreeViewColumn.new("Action", renderer, "text" => ACTION_COLUMN)
  treeview.append_column(column)
  renderer = Gtk::CellRendererAccel.new
  column = Gtk::TreeViewColumn.new("Accelerator", renderer,
               { "accel_mods" => MASK_COLUMN, "accel_key" => VALUE_COLUMN }
  )
  renderer.editable = true
  treeview.append_column(column)

  renderer.signal_connect('accel_edited') do |w, path, accl_k, mask, hw_k|
    if (iter = treeview.model.get_iter(path))
      iter[MASK_COLUMN] = mask
      iter[VALUE_COLUMN] = accl_k
      puts "accel_k:#{accl_k}=[" +
           "#{Gdk::Keyval.to_name(accl_k)}] hw_k:#{hw_k} " +
           "Modifier keys=#{get_string_for_modifier_keys(mask)}"
    end
  end
end

def need_plus(arg)
  arg == '' ? '' : '+'
end
def get_string_for_modifier_keys(mask)
  mods = ""
  mods += "Shift"                     if mask & Gdk::Window::SHIFT_MASK != 0
  mods += "#{need_plus(mods)}Ctrl"    if mask & Gdk::Window::CONTROL_MASK != 0
  mods += "#{need_plus(mods)}Alt"     if mask & Gdk::Window::MOD1_MASK != 0
  mods += "#{need_plus(mods)}Mod2"    if mask & Gdk::Window::MOD2_MASK != 0
  mods += "#{need_plus(mods)}Meta"    if mask & Gdk::Window::META_MASK != 0
  mods += "#{need_plus(mods)}Button1" if mask & Gdk::Window::BUTTON1_MASK != 0
  mods += "#{need_plus(mods)}Super"   if mask & Gdk::Window::SUPER_MASK != 0
  mods == "" ? "" : "<"+mods+">"
end

class Accl
  attr_accessor :action, :mask, :value
  def initialize(a, m, v); @action, @mask, @value = a, m, v; end
end
ACTION_COLUMN = 0; MASK_COLUMN = 1; VALUE_COLUMN = 2
list = [
  Accl.new("Cut",   Gdk::Window::CONTROL_MASK, Gdk::Keyval::GDK_X),
  Accl.new("Copy",  Gdk::Window::CONTROL_MASK, Gdk::Keyval::GDK_C),
  Accl.new("Paste", Gdk::Window::CONTROL_MASK, Gdk::Keyval::GDK_V),
  Accl.new("New",   Gdk::Window::CONTROL_MASK, Gdk::Keyval::GDK_N),
  Accl.new("Open",  Gdk::Window::CONTROL_MASK, Gdk::Keyval::GDK_O),
  Accl.new("Print", Gdk::Window::CONTROL_MASK, Gdk::Keyval::GDK_P),
]

### -- There's a BUG in {{ Gtk::CellRendererAccel }}
### ------------------------------------------------
### All the above definitions of modifier keys when a "single" modifier is used
### work, however, if you added the line ad the end of this comment to the list 
### above, it would not be rendered properly because more modifier keys are OR'ed 
### together. Following would not work correctly:
### 
### Accl.new("Purge", Gdk::Window::CONTROL_MASK & Gdk::Window::MOD1_MASK, Gdk::Keyval::GDK_P),

treeview = Gtk::TreeView.new(store = Gtk::ListStore.new(String, Integer, Integer))
setup_tree_view(treeview)

# Add all of the products to the GtkListStore.
list.each_with_index do |e, i|
    iter = store.append
    iter[ACTION_COLUMN] = list[i].action
    iter[MASK_COLUMN]   = list[i].mask	# see: BUG in {{ Gtk::CellRendererAccel }} above
    iter[VALUE_COLUMN]  = list[i].value
    iter.next!
end

scrolled_win = Gtk::ScrolledWindow.new
scrolled_win.add(treeview)
scrolled_win.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC)
window = Gtk::Window.new("Accelerator Keys")
window.resizable = true
window.border_width = 10
window.signal_connect('destroy') { Gtk.main_quit }
window.set_size_request(250, 175)
window.add(scrolled_win)
window.show_all
Gtk.main
Last modified:2012/10/15 09:30:29
Keyword(s):
References:[tut-gtk] [tut-gtk2-treev] [Gtk::CellRendererAccel]