понедельник, 29 сентября 2008 г.

Назначение обработчика сигнала для заголовка GtkTreeViewColumn

Недавно потребовалось сделать так, чтобы при нажатии правой кнопкой мыши по заголовку колонки GtkTreeView возникало меню, с помощью которого можно было бы выбрать для отображения или скрыть отдельные колонки. Порывшись в документации к GTK, я обнаружил, что это не так то просто. А именно - нет какой-либо функции, которая могла бы возвратить виджет заголовка колонки, к которому я бы смог привязать обработчик сигнала на нажатие кнопки мыши.

Т. к. поиск в документации не дал никаких результатов, я начал искать обходные пути решения данной проблемы. Оказалось, что GTK позволяет назначить собственный виджет, который будет помещен в кнопку заголовка GtkTreeViewColumn. По умолчанию, если пользователь не назначит собственный виджет, вместо него используется обычный GtkLabel с именем колонки. Если же мы назначим заголовку собственный виджет, то сможем подняться от него вверх по дереву содержащих его контейнеров и получить нужную нам кнопку.

Поэтому, недолго думая, я написал следующую функцию, которая возвращает заветную кнопку заголовка колонки (функция написана для Gtkmm, но переписать ее под обычный GTK не составит никакого труда):
/// Возвращает кнопку, находящуюся в заголовке колонки GtkTreeView,
/// или NULL, если кнопку найти не удалось.
Gtk::Button* get_tree_view_column_header_button(Gtk::TreeViewColumn& column)
{
    Gtk::Widget* widget;

    if( !( widget = column.get_widget() ) )
    {
        // Если для заголовка не установлен никакой виджет, устанавливаем свой Gtk::Label,
        // чтобы по нему можно было выйти на остальные виджеты заголовка.
        Gtk::Label* header_label = Gtk::manage(new Gtk::Label(column.get_title()));
        column.set_widget(*header_label);
        header_label->show();

        widget = header_label->get_parent();
    }

    // Поднимаемся вверх по дереву контейнеров,
    // пока не наткнемся на Gtk::Button.
    while(widget && widget->get_name() != "GtkButton")
        widget = widget->get_parent();

    return dynamic_cast<Gtk::Button*>(widget);
}

Далее, получив кнопку для каждой колонки, можно привязать к ней любой обработчик сигнала:
// Назначаем обработчик нажатия кнопки мыши для заголовка колонки.
if( (button = get_tree_view_column_header_button(column)) )
{
    button->signal_button_press_event().connect(
        sigc::mem_fun(
            *this,
            &MyTree_view::on_column_header_button_press_event_callback
        ),
        false
    );
}

Примечание:

Конечно, это хак, и он использует внутренние структуры GTK, которые впоследствии (теоретически, хотя и очень врядли) могут измениться. Но, по-моему, такое решение гораздо лучше, чем вовсе ничего.

Данный метод будет работать только тогда, когда у колонки установлен атрибут "clickable" или для нее указана колонка GtkTreeModel, которая должна использоваться при сортировке (неявно устанавливает атрибут "clickable"). Вызывать функцию необходимо после добавления колонки в TreeView, т. к. виджеты заголовка колонки создаются именно в этот момент.

3 комментария:

Unknown комментирует...

А какую IDE используете вы ?

Dmitry Konishchev комментирует...

howto, я использую Vim, если его можно назвать IDE.

Анонимный комментирует...

Попробовал сделать, вроде работает :)