ui/gtk/toolbar.c

Thu, 21 Nov 2024 13:17:56 +0100

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Thu, 21 Nov 2024 13:17:56 +0100
branch
newapi
changeset 389
d15eca5fd8b3
parent 367
1c3af8e31d8e
permissions
-rw-r--r--

implement image viewer context menus (GTK)

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright 2017 Olaf Wintermann. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *   1. Redistributions of source code must retain the above copyright
 *      notice, this list of conditions and the following disclaimer.
 *
 *   2. Redistributions in binary form must reproduce the above copyright
 *      notice, this list of conditions and the following disclaimer in the
 *      documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "toolbar.h"
#include "menu.h"
#include "button.h"
#include "icon.h"
#include "list.h"
#include <cx/mempool.h>
#include <cx/hash_map.h>
#include <cx/linked_list.h>
#include <cx/array_list.h>
#include "../common/context.h"


#if UI_GTK2 || UI_GTK3

GtkWidget* ui_create_toolbar(UiObject *obj) {
    GtkWidget *toolbar = gtk_toolbar_new();
#ifdef UI_GTK3
    gtk_style_context_add_class(
            gtk_widget_get_style_context(toolbar),
            GTK_STYLE_CLASS_PRIMARY_TOOLBAR);
#endif
    
    CxMap *items = uic_get_toolbar_items();
    CxList *left_defaults = uic_get_toolbar_defaults(UI_TOOLBAR_LEFT);
    CxList *center_defaults = uic_get_toolbar_defaults(UI_TOOLBAR_CENTER);
    CxList *right_defaults = uic_get_toolbar_defaults(UI_TOOLBAR_RIGHT);
    
    ui_toolbar_add_items(obj, toolbar, items, left_defaults);
    ui_toolbar_add_items(obj, toolbar, items, center_defaults);
    ui_toolbar_add_items(obj, toolbar, items, right_defaults);
    
    /*
    GtkToolbar *tb = GTK_TOOLBAR(toolbar);
    CxIterator i = cxListIterator(defaults);
    cx_foreach(char *, def, i) {
        UiToolItemI *item = cxMapGet(toolbar_items, def);
        if(item) {
            item->add_to(tb, item, obj);
        } else if(!strcmp(def, "@separator")) {
            gtk_toolbar_insert(tb, gtk_separator_tool_item_new(), -1);
        } else {
            fprintf(stderr, "UI Error: Unknown toolbar item: %s\n", def);
        }
    }
    */
    
    return toolbar;
}

static void create_item(UiObject *obj, GtkWidget *toolbar, UiToolbarItemI *i) {
    GtkToolbar *tb = GTK_TOOLBAR(toolbar);
    switch(i->type) {
        case UI_TOOLBAR_ITEM: {
            add_toolitem_widget(tb, (UiToolbarItem*)i, obj);
            break;
        }
        case UI_TOOLBAR_TOGGLEITEM: {
            add_toolitem_toggle_widget(tb, (UiToolbarToggleItem*)i, obj);
            break;
        }
        case UI_TOOLBAR_MENU: {
            add_toolitem_menu_widget(tb, (UiToolbarMenuItem*)i, obj);
            break;
        }
        default: fprintf(stderr, "toolbar item type unimplemented: %d\n", (int)i->type);
    }
}

void ui_toolbar_add_items(UiObject *obj, GtkWidget *toolbar, CxMap *items, CxList *defaults) {
    // add pre-configured items
    CxIterator i = cxListIterator(defaults);
    cx_foreach(char*, def, i) {
        UiToolbarItemI* item = uic_toolbar_get_item(def);
        if (!item) {
            fprintf(stderr, "unknown toolbar item: %s\n", def);
            continue;
        }
        create_item(obj, toolbar, item);
    }
}

static void set_toolbutton_icon(GtkToolItem *item, const char *icon_name) {
#if GTK_MAJOR_VERSION >= 3
        gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(item), icon_name);
#else
        UiIcon *icon = ui_icon(icon_name, 24);
        if(icon) {
            GdkPixbuf *pixbuf = ui_icon_pixbuf(icon);
            if(pixbuf) {
                GtkWidget *image = gtk_image_new_from_pixbuf(pixbuf);
                gtk_tool_button_set_icon_widget(GTK_TOOL_BUTTON(item), image);
            }
        }
#endif
}

void add_toolitem_widget(GtkToolbar *tb, UiToolbarItem *item, UiObject *obj) {
    GtkToolItem *button;
    if(item->args.stockid) {
#ifdef UI_GTK2
        button = gtk_tool_button_new_from_stock(item->args.stockid);
#else
        // TODO: gtk3 stock
        button = gtk_tool_button_new(NULL, item->args.label);
#endif
    } else {
        button = gtk_tool_button_new(NULL, item->args.label);
    }
    
    gtk_tool_item_set_homogeneous(button, FALSE);
    if(item->args.icon) {
        set_toolbutton_icon(button, item->args.icon);
    }
    gtk_tool_item_set_is_important(button, TRUE);
    
    ui_set_widget_ngroups(obj->ctx, GTK_WIDGET(button), item->args.groups, item->ngroups);
    
    if(item->args.onclick) {
        UiEventData *event = cxMalloc(
                obj->ctx->allocator,
                sizeof(UiEventData));
        event->obj = obj;
        event->callback = item->args.onclick;
        event->userdata = item->args.onclickdata;
        event->customdata = NULL;
        event->value = 0;
        
        g_signal_connect(
                button,
                "clicked",
                G_CALLBACK(ui_button_clicked),
                event);
    }
    
    gtk_toolbar_insert(tb, button, -1);
    
    /*
    if(item->groups) {
        uic_add_group_widget(obj->ctx, button, (ui_enablefunc)ui_set_enabled, item->groups);
    }
    */
}

void add_toolitem_toggle_widget(GtkToolbar *tb, UiToolbarToggleItem *item, UiObject *obj) {
    GtkToolItem *button;
    if(item->args.stockid) {
#ifdef UI_GTK2
        button = gtk_toggle_tool_button_new_from_stock(item->args.stockid);
#else
        button = gtk_toggle_tool_button_new_from_stock(item->args.stockid); // TODO: gtk3 stock
#endif
    } else {
        button = gtk_toggle_tool_button_new();
        gtk_tool_item_set_homogeneous(button, FALSE);
        if(item->args.label) {
            gtk_tool_button_set_label(GTK_TOOL_BUTTON(button), item->args.label);
        }
        if(item->args.icon) {
            set_toolbutton_icon(button, item->args.icon);
        }    
    }
    ui_set_widget_ngroups(obj->ctx, GTK_WIDGET(button), item->args.groups, item->ngroups);
    
    UiVar* var = uic_widget_var(obj->ctx, obj->ctx, NULL, item->args.varname, UI_VAR_INTEGER);
    if(var) {
        UiInteger *i = (UiInteger*)var->value;
	if(i) {
            i->get = ui_tool_toggle_button_get;
            i->set = ui_tool_toggle_button_set;
            i->obj = button;

            if(i->value != 0) {
                gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(button), TRUE);
            }
        }
    }
    
    UiVarEventData *event = cxMalloc(
                obj->ctx->allocator,
                sizeof(UiVarEventData));
    event->obj = obj;
    event->callback = item->args.onchange;
    event->userdata = item->args.onchangedata;
    event->var = var;

    g_signal_connect(
        button,
        "toggled",
        G_CALLBACK(ui_tool_button_toggled),
        event);
    
    // add item to toolbar
    gtk_toolbar_insert(tb, button, -1);
    
    /*
    if(item->groups) {
        uic_add_group_widget(obj->ctx, button, (ui_enablefunc)ui_set_enabled, item->groups);
    }
    */
}

void ui_tool_button_toggled(GtkToggleToolButton *widget, UiVarEventData *event) {
    UiEvent e;
    e.obj = event->obj;
    e.window = event->obj->window;
    e.document = event->obj->ctx->document;
    e.eventdata = NULL;
    e.intval = gtk_toggle_tool_button_get_active(widget);
    
    if(event->callback) {
        event->callback(&e, event->userdata);
    }
    
    UiVar *var = event->var;
    UiInteger *i = var ? var->value : NULL;
    
    if(i) {
        ui_notify_evt(i->observers, &e);
    }
}

int64_t ui_tool_toggle_button_get(UiInteger *integer) {
    integer->value = gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(integer->obj));
    return integer->value;
}

void ui_tool_toggle_button_set(UiInteger *integer, int64_t value) {
    gboolean s = value != 0 ? TRUE : FALSE;
    gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(integer->obj), s);
    integer->value = s;
}



typedef struct UiToolbarMenuWidget {
    GtkWidget *button;
    GtkMenu *menu;
} UiToolbarMenuWidget;

static void ui_toolbar_menubutton_clicked(GtkWidget *widget, UiToolbarMenuWidget *menu) {
    int x;
    gtk_menu_popup_at_widget(menu->menu, menu->button, GDK_GRAVITY_SOUTH_WEST, GDK_GRAVITY_NORTH_WEST, NULL);
}

static void ui_toolbar_menubutton_destroy(GtkWidget *widget, UiToolbarMenuWidget *menu) {
    free(menu);
}

void add_toolitem_menu_widget(GtkToolbar *tb, UiToolbarMenuItem *item, UiObject *obj) {
    GtkToolItem *button;
    if(item->args.stockid) {
#ifdef UI_GTK2
        button = gtk_tool_button_new_from_stock(item->args.stockid);
#else
        // TODO: gtk3 stock
        button = gtk_tool_button_new(NULL, item->args.label);
#endif
    } else {
        button = gtk_tool_button_new(NULL, item->args.label);
    }
    
    gtk_tool_item_set_homogeneous(button, FALSE);
    if(item->args.icon) {
        set_toolbutton_icon(button, item->args.icon);
    }
    gtk_tool_item_set_is_important(button, TRUE);
    
    gtk_toolbar_insert(tb, button, -1);
    
    // menu
    GtkWidget *menu_widget = gtk_menu_new();
    ui_add_menu_items(menu_widget, 0, &item->menu, obj); 
    gtk_widget_show_all(menu_widget);
    
    UiToolbarMenuWidget *tbmenu = malloc(sizeof(UiToolbarMenuWidget));
    tbmenu->button = GTK_WIDGET(button);
    tbmenu->menu = GTK_MENU(menu_widget);
    
    g_signal_connect(
                button,
                "clicked",
                G_CALLBACK(ui_toolbar_menubutton_clicked),
                tbmenu);
    
    g_signal_connect(
                button,
                "destroy",
                G_CALLBACK(ui_toolbar_menubutton_destroy),
                tbmenu);
}




// deprecated / unsupported
/*
void add_toolbar_combobox(GtkToolbar *tb, UiToolbarComboBox *cb, UiObject *obj) {
    UiModel *modelinfo = ui_model(obj->ctx, UI_STRING, "", -1);
    modelinfo->getvalue = cb->getvalue;
    UiListModel *model = ui_list_model_new(obj, cb->var, modelinfo);
    
    GtkWidget *combobox = ui_create_combobox(obj, model, cb->callback, cb->userdata);
    GtkToolItem *item = gtk_tool_item_new();
    gtk_container_add(GTK_CONTAINER(item), combobox);
    gtk_toolbar_insert(tb, item, -1);
}

void add_toolbar_combobox_nv(GtkToolbar *tb, UiToolbarComboBoxNV *cb, UiObject *obj) {
    UiVar *var = uic_create_var(obj->ctx, cb->listname, UI_VAR_LIST);
    if(var) {
        UiModel *modelinfo = ui_model(obj->ctx, UI_STRING, "", -1);
        modelinfo->getvalue = cb->getvalue;
        UiListModel *model = ui_list_model_new(obj, var, modelinfo);
        
        GtkWidget *combobox = ui_create_combobox(obj, model, cb->callback, cb->userdata);
        GtkToolItem *item = gtk_tool_item_new();
        gtk_container_add(GTK_CONTAINER(item), combobox);
        gtk_toolbar_insert(tb, item, -1);
    }
}
*/



#ifdef UI_GTK3

GtkWidget* ui_create_headerbar(UiObject *obj) {
    GtkWidget *headerbar = gtk_header_bar_new();
    
    CxMap *items = uic_get_toolbar_items();
    CxList *left_defaults = uic_get_toolbar_defaults(UI_TOOLBAR_LEFT);
    CxList *center_defaults = uic_get_toolbar_defaults(UI_TOOLBAR_CENTER);
    CxList *right_defaults = uic_get_toolbar_defaults(UI_TOOLBAR_RIGHT);
    
    ui_toolbar_headerbar_add_items(obj, headerbar, items, left_defaults);
    ui_toolbar_headerbar_add_items(obj, headerbar, items, center_defaults);
    ui_toolbar_headerbar_add_items(obj, headerbar, items, right_defaults);
    
    return headerbar;
}

static void hb_create_item(UiObject *obj, GtkWidget *toolbar, UiToolbarItemI *i) {
    GtkHeaderBar *tb = GTK_HEADER_BAR(toolbar);
    switch(i->type) {
        case UI_TOOLBAR_ITEM: {
            add_headerbar_item_widget(tb, (UiToolbarItem*)i, obj);
            break;
        }
        case UI_TOOLBAR_TOGGLEITEM: {
            add_headerbar_item_toggle_widget(tb, (UiToolbarToggleItem*)i, obj);
            break;
        }
        case UI_TOOLBAR_MENU: {
            add_headerbar_item_menu_widget(tb, (UiToolbarMenuItem*)i, obj);
            break;
        }
        default: fprintf(stderr, "toolbar item type unimplemented: %d\n", (int)i->type);
    }
}


void ui_toolbar_headerbar_add_items(UiObject *obj, GtkWidget *headerbar, CxMap *items, CxList *defaults) {
    // add pre-configured items
    CxIterator i = cxListIterator(defaults);
    cx_foreach(char*, def, i) {
        UiToolbarItemI* item = uic_toolbar_get_item(def);
        if (!item) {
            fprintf(stderr, "unknown toolbar item: %s\n", def);
            continue;
        }
        hb_create_item(obj, headerbar, item);
    }
}

void add_headerbar_item_widget(GtkHeaderBar *hb, UiToolbarItem *item, UiObject *obj) {
    GtkWidget *button = gtk_button_new_with_label(item->args.label);
    if(item->args.icon) {
        ui_button_set_icon_name(button, item->args.icon);
    }
    ui_set_widget_groups(obj->ctx, button, item->args.groups);
    
    gtk_header_bar_pack_start(hb, button);
    
}

void add_headerbar_item_toggle_widget(GtkHeaderBar *hb, UiToolbarToggleItem *item, UiObject *obj) {
    
}

void add_headerbar_item_menu_widget(GtkHeaderBar *hb, UiToolbarMenuItem *item, UiObject *obj) {
    
}

#endif /* UI_GTK3 */

#endif /* UI_GTK2 || UI_GTK3 */

mercurial