335. How to finetune interactive search in GtkTreeView - Part 4 - set custom compare function?

Problem

This is in response to the feedback by RaFko from Slovakia that "when you want to search for a treeview with markup text, you have to type whole markup, not only field text".

What we can do is to write our own custom compare function to be used by the interactive search. In the custom compare function, we strip away any markup tags.

In this example, we also do a preg_match instead of a direct match. What this means is that the interactive search now does a free-text search. The search column in this example is column 1. Try typing "21". It will match "Item 21". You do not need to type the full "Item 21" as shown below:

How to finetune interactive search in GtkTreeView - Part 4 - set custom compare function?


Solution


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   
82   
83   
84   
85   
86   
87   
88   
89   
90   
91   
92   
93   
94   
95   
96   
97   
101   
102   
103   
104   
106   
107   
108   
109   
110   
111   
112   
113   
114   
115   
116   
117   
118   
119   
120   
122   
123   
124   
125   
126   
127   
128   
129   
130   
131   
132   
133   
134   
135   
136   
137   
138   
139   
141   
142   
145   
146   
147   
148   
149   
150   
151   
152   
<?php
$window = new GtkWindow();
$window->set_size_request(400, 210);
$window->connect_simple('destroy', array('Gtk','main_quit'));
$window->add($vbox = new GtkVBox());

// display title
$title = new GtkLabel("Finetune interactive search in GtkTreeView\n".
"          Part 4 - set custom compare function");
$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));

$view = display_table($vbox, $data);

$view->set_enable_search(true);
$view->set_search_column(1); // note 1
$view->set_search_equal_func('compare_func'); // note 2

$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);
    }

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

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

    return $view;
}

// 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
    $val = $model->get_value($iter, $col_num);

    switch($col_num) {
        case 1:
            $cell->set_property('markup',
                "<span font_desc='Verdana 12'><b>$val</b></span>");
            break;

        case 2:
            if ($val<10) {
                $cell->set_property('markup',
                    "<span foreground='#FF0000'>$val</span>");
            } else {
                $cell->set_property('markup',
                    "<span foreground='#0000FF'>$val</span>");
            }
            break;

        case 3:
            $cell->set_property('text', '$'.number_format($val,2));
            break;
    }
    $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();
    if ($iter==null) return;
    $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 compare_func($model, $column, $key, $iter) {
    $val = $model->get_value($iter, $column); // note 3
    $val = strip_tags($val); // note 4
    if (preg_match("/$key/", $val)) { // note 5
        return false; // note 6
    } else {
        return true;
    }
}

?>

Output

As shown above.
 

Explanation

We make use of the code from How to display bold and colored fonts in GtkTreeView using pango markup language?

What's new here:

  1. Set column 1 as the search column.
  2. Set our own custom compare function.
  3. Get the value of the current cell at row $iter of column number $column.
  4. Let strip away any tags.
  5. Note that $key is the string being searched for. We use a preg_match here to do a free-text search. Interestingly, you can now use regular expression in the search string. For example, trying entering "4$" in the interactive search. This will match "Item 4" instead of "Item 42".
  6. Note in particular here that when a match is found, you return a false and not a true.

Related Links

Add comment


Security code
Refresh