285. How to display keyboard accelerator in GtkTreeView with GtkCellRendererAccel - Part 2 - change hot keys on the fly?

Problem

In Part 1, we have showed you how to display keyboard accelerators in a treeview using the new widget GtkCellRendererAccel.

In this Part 2, we will show you how you can allow users to change the hot keys on the fly as shown below:

How to display keyboard accelerator in GtkTreeView with GtkCellRendererAccel - Part 2 - change hot keys on the fly?


Solution

To change the keyboard accelerators on-the-fly:

When you run the script, click on a cell that displays the keyboard accelerator. You will see the cell displays the text "New accelerator...". Type any hot key you like e.g. Alt-1, Ctrl-F10, etc. Note that if you press Ctrl, Alt or Shift key, you will hear a loud beep. Don't worry. That's not an error. It's PHP-GTK2 telling you that you have pressed those keys. (While trying to get this to work, the beep misled me into thinking that it's an error!!!)

Also, note that GtkCellRendererAccel by itself doesn't have an associated underlying data model to keep track of these keyboard accelerators. As such, you have keep track of the assignment of the keyboard accelerators manually yourself. In this example, I store them in the global arrays $accel_keys and $accel_keys_mods.

Important Note: This only works for PHP-GTK2 compliled with gtk+ v2.10 and above. If you are using an older version, for linux, you may follow the step-by-step instructions to recompile php-gtk2 with gtk+ v2.10. For windows, please refer to How to install php gtk2 on windows? You may also want to take a look here to see some of the new exciting PHP-GTK2 Functionalities.


Sample Code

1   
2   
3   
4   
5   
6   
7   
8   
9   
10   
11   
12   
13   
14   
15   
16   
17   
18   
19   
20   
21   
22   
23   
24   
25   
26   
27   
28   
29   
30   
31   
32   
33   
34   
35   
36   
37   
38   
39   
40   
41   
42   
43   
44   
45   
46   
47   
48   
49   
50   
51   
52   
53   
54   
55   
56   
57   
58   
59   
60   
61   
62   
63   
64   
65   
66   
67   
68   
69   
70   
71   
72   
73   
74   
75   
76   
77   
78   
79   
80   
81   
83   
84   
85   
86   
87   
88   
89   
90   
91   
92   
93   
94   
95   
96   
97   
98   
99   
100   
101   
102   
103   
104   
105   
106   
110   
114   
115   
116   
117   
118   
119   
120   
121   
122   
123   
124   
125   
126   
127   
128   
129   
130   
131   
132   
133   
134   
135   
136   
138   
139   
143   
144   
145   
<?php
$window = new GtkWindow();
$window->set_title($argv[0]);
$window->set_size_request(400, 240);
$window->connect_simple('destroy', array('Gtk','main_quit'));
$window->add($vbox = new GtkVBox());

// display title
$title = new GtkLabel("   Display keyboard accelerators in GtkTreeView\n".
"Part 2 - Change keyboard accelerators on the fly");
$title->modify_font(new PangoFontDescription("Times New Roman Italic 10"));
$title->modify_fg(Gtk::STATE_NORMAL, GdkColor::parse("#0000ff"));
$title->set_size_request(-1, 40);
$vbox->pack_start($title, 0, 0);
$vbox->pack_start(new GtkLabel(), 0, 0);

// the 2D table
$data = array(
array('row0', 'item 42', 2, 3.1),
array('row1', 'item 36', 20, 6.21),
array('row2', 'item 21', 8, 9.36),
array('row3', 'item 10', 11, 12.4),
array('row4', 'item 7', 5, 15.5),
array('row5', 'item 4', 17, 18.6),
array('row6', 'item 3', 20, 21.73));

display_table($vbox, $data);

$window->show_all();
Gtk::main();

function display_table($vbox, $data) {

    // Set up a scroll window
    $scrolled_win = new GtkScrolledWindow();
    $scrolled_win->set_policy( Gtk::POLICY_AUTOMATIC,
        Gtk::POLICY_AUTOMATIC);
    $vbox->pack_start($scrolled_win);

    // Creates the list store
    if (defined("GObject::TYPE_STRING")) {
        $model = new GtkListStore(GObject::TYPE_STRING, GObject::TYPE_STRING,
            GObject::TYPE_LONG, GObject::TYPE_DOUBLE);
    } else {
        $model = new GtkListStore(Gtk::TYPE_STRING, Gtk::TYPE_STRING,
            Gtk::TYPE_LONG, Gtk::TYPE_DOUBLE);
    }
    $field_header = array('Row #', 'Description', 'Qty', 'Price');
    $field_justification = array(0.0, 0.0, 0.5, 1.0);

    // Creates the view to display the list store
    $view = new GtkTreeView($model);
    $scrolled_win->add($view);

    // Creates the columns
    for ($col=0; $col<count($field_header); ++$col) {
        $cell_renderer = new GtkCellRendererText();
        $cell_renderer->set_property("xalign", $field_justification[$col]);
        $column = new GtkTreeViewColumn($field_header[$col],
            $cell_renderer, 'text', $col);
        $column->set_alignment($field_justification[$col]);
        $column->set_sort_column_id($col);

        // set the header font and color
        $label = new GtkLabel($field_header[$col]);
        $label->modify_font(new PangoFontDescription("Arial Bold"));
        $label->modify_fg(Gtk::STATE_NORMAL, GdkColor::parse("#0000FF"));
        $column->set_widget($label);
        $label->show();

        // setup self-defined function to display alternate row color
        $column->set_cell_data_func($cell_renderer, "format_col", $col);
        $view->append_column($column);
    }

    $selection = $view->get_selection();
    $selection->connect('changed', 'on_selection');

    $cell_renderer = new GtkCellRendererAccel();
    $cell_renderer->set_property('editable', true); // note 1
    $cell_renderer->connect('accel-edited', 'on_edit'); // note 2
    $column = new GtkTreeViewColumn();
    $column->pack_start($cell_renderer, false);
    $view->append_column($column);
    $column->set_cell_data_func($cell_renderer, "format_col", $col);

    // pupulates the data
    global $accel_keys, $accel_keys_mods; // note 3
    for ($row=0; $row<count($data); ++$row) {
        $values = array();
        for ($col=0; $col<count($data[$row]); ++$col) {
            $values[] = $data[$row][$col];
        }
        $model->append($values);

        $accel_keys[$row] = 97+$row; // note 4
        $accel_keys_mods[$row] = Gdk::MOD1_MASK; // note 4
    }

}

// self-defined function to format the price column
function format_col($column, $cell, $model, $iter, $col_num) {
    $path = $model->get_path($iter); // get the current path
    $row_num = $path[0]; // get the row number
    if ($col_num==4) {
        global $accel_keys, $accel_keys_mods;
        $cell->set_property("accel-key", $accel_keys[$row_num]); // note 5
        $cell->set_property("accel-mods", $accel_keys_mods[$row_num]); // note 6
    } elseif ($col_num==3) {
        $amt = $model->get_value($iter, 3);
        $cell->set_property('text', '$'.number_format($amt,2));
    }
    $row_color = ($row_num%2==1) ? '#dddddd' : '#ffffff';
    $cell->set_property('cell-background', $row_color);
}

// the function that is called when user selects a row
function on_selection($selection) {
    list($model, $iter) = $selection->get_selected();
    $desc = $model->get_value($iter, 1);
    $qty = $model->get_value($iter, 2);
    $price = $model->get_value($iter, 3);
    print "You have selected $desc: $qty ($price)\n";
}

function on_edit($cell_renderer, $path, $accel_key, $accel_mods,
    $hardware_keycode) {
    global $accel_keys, $accel_keys_mods;
    $accel_keys[$path] = $accel_key;
    $accel_keys_mods[$path] = $accel_mods;
}

?>

Output

As shown above.

 

Explanation

This example makes use of the base code from How to display keyboard accelerator in GtkTreeView with GtkCellRendererAccel - Part 1?.

What's new here:

  1. Set the cellrenderer to be editable.
  2. Register the signal 'accel-edited' on the cellrenderer.
  3. These two global variables $accel_keys and $accel_keys_mods will help us keep track of all the keyboard accelerator assigned to each row.
  4. Store the new key value and key modifier in the global variables.
  5. Set the key value of the keyboard accelerator.
  6. Set the modifier key of the keyboard accelerator.

Note

To make the code easier to understand, I have not included any validation checks e.g. if the new hot key is already been in use by another row.

Related Links

Add comment


Security code
Refresh