суббота, 8 ноября 2008 г.

"Правильная" обработка сигнала на активацию для GtkCellRendererToggle

Продолжаем бороться с GTK. На этот раз возникла необходимость отобразить дерево каталогов с возможностью выбора файлов/каталогов для скачивания примерно в таком виде:

На первый взгляд никаких проблем быть не должно: создаем GtkTreeViewColumn, в которую упаковываем GtkCellRendererToggle и GtkCellRendererText. Создали, упаковали - выглядит так, как и хотелось, но вот работает не так как надо. А именно, флажки переключаются даже тогда, когда пользователь щелкает по GtkCellRendererText с именем файла/директории. Подобное поведение может очень сильно раздражать, когда, например, пользователь ввиду слишком длинного списка с большим уровнем вложенности захочет просто кликнуть по имени файла, чтобы выделить всю строку и тем самым подсветить остальные колонки GtkTreeView, которых может быть довольно большое количество (размер, время модификации, тип файла и т. п.).

Как и в прошлый раз, для решения проблемы придется модифицировать внутренние структуры GTK, т. к. штатными средствами данную проблему похоже решить не удасться. Все, что нам нужно - это определить собственный обработчик сигнала для GtkCellRendererToggle. Пример того, как это можно сделать, представлен ниже. Все, что необходимо программисту - это вызвать функцию activate_cell_renderer_toggle_tree_mode(). После ее вызова все GtkCellRendererToggle'ы будут работать так, как требуется. Способ работает как с GTK, так и с Gtkmm.
#include <gtk/gtkcellrenderertoggle.h>


/// Модифицированный обработчик сигнала на активацию GtkCellRendererToggle.
static gint
gtk_cell_renderer_toggle_activate(      GtkCellRenderer* cell,
                                        GdkEvent* event,
                                        GtkWidget* widget,
                                        const gchar* path,
                                        GdkRectangle* background_area,
                                        GdkRectangle* cell_area,
                                        GtkCellRendererState flags)
{
    GtkCellRendererToggle* celltoggle = GTK_CELL_RENDERER_TOGGLE(cell);

    if(celltoggle->activatable)
    {
        if
        (
            !event ||
            event->type != GDK_BUTTON_PRESS ||
            (
                event->button.x >= cell_area->x &&
                event->button.x < cell_area->x + cell_area->width &&
                event->button.y >= cell_area->y &&
                event->button.y < cell_area->y + cell_area->height
            )
        )
        {
            g_signal_emit_by_name(cell, "toggled", path);
            return TRUE;
        }
    }

    return FALSE;
}


/// Активизирует "режим дерева" для GtkCellRendererToggle.
/// Модифицирует внутренние структуры GTK так, чтобы при упаковке
/// GtkCellRendererToggle в одну колонку с другими GtkCellRenderer'ами
/// GtkCellRendererToggle активизировался только в том случае, если
/// пользователь кликнул мышкой именно по нему, а не по любому
/// GtkCellRenderer'у данной колонки.
void
activate_cell_renderer_toggle_tree_mode(void)
{
    GtkCellRendererClass *cell_class;
    GtkCellRendererToggle *toggle_renderer;

    toggle_renderer = GTK_CELL_RENDERER_TOGGLE(gtk_cell_renderer_toggle_new());
    cell_class = GTK_CELL_RENDERER_CLASS(GTK_WIDGET_GET_CLASS(toggle_renderer));
    cell_class->activate = gtk_cell_renderer_toggle_activate;
    gtk_object_destroy(GTK_OBJECT(toggle_renderer));
}

1 комментарий:

Виталий комментирует...

Спасибо, мужик!
Очень интересно.