Initial commit of a Gtk+ element engine for tile.
authorPat Thoyts <patthoyts@users.sourceforge.net>
Tue, 24 Apr 2007 23:53:41 +0000 (00:53 +0100)
committerPat Thoyts <patthoyts@users.sourceforge.net>
Tue, 24 Apr 2007 23:53:41 +0000 (00:53 +0100)
ttkGtkTheme.c [new file with mode: 0644]

diff --git a/ttkGtkTheme.c b/ttkGtkTheme.c
new file mode 100644 (file)
index 0000000..3410d7d
--- /dev/null
@@ -0,0 +1,343 @@
+
+// see google codesearch for MOZ_GTK_SUCCESS and ensure_checkbox_widget
+
+#include <gtk/gtk.h>
+#include <gdk/gdkx.h>
+#include <glib/gprintf.h>
+
+#include <tk.h>
+
+/* Are we part of tile, or Tk... */
+#ifdef BUILD_tile
+#include "tkTheme.h"
+#define TTK_VERSION PACKAGE_VERSION
+#else
+#include "ttkTheme.h"
+#endif
+
+#include <string.h>
+
+typedef enum WIDGET_TYPE {
+    WIDGET_GTK_WINDOW,
+    WIDGET_GTK_BUTTON,
+    WIDGET_GTK_CHECKBUTTON,
+    WIDGET_GTK_RADIOBUTTON,
+} WIDGET_TYPE;
+
+typedef struct ThemeData {
+    GtkWidget *widget[4];
+    GtkWidget *container[4];
+} ThemeData;
+
+static Ttk_StateTable button_statemap[] = 
+{
+    { GTK_STATE_INSENSITIVE, TTK_STATE_DISABLED, 0 },
+    { GTK_STATE_ACTIVE,      TTK_STATE_PRESSED, 0 },
+    //{ GTK_STATE_SELECTED,    TTK_STATE_FOCUS, 0 },
+    { GTK_STATE_PRELIGHT,      TTK_STATE_ACTIVE, 0 },
+    { GTK_STATE_NORMAL,      0, 0 }
+};
+
+static Ttk_StateTable button_shadowmap[] = {
+    { GTK_SHADOW_IN,  TTK_STATE_PRESSED, 0 },
+    { GTK_SHADOW_OUT, 0, 0 }
+};
+
+/* ---------------------------------------------------------------------- */
+/* From some mozilla code */
+
+static gint
+TSOffsetStyleGCArray(GdkGC** gcs, gint xorigin, gint yorigin)
+{
+    int i;
+    /* there are 5 gc's in each array, for each of the widget states */
+    for (i = 0; i < 5; ++i)
+        gdk_gc_set_ts_origin(gcs[i], xorigin, yorigin);
+    return 0;
+}
+
+static gint
+TSOffsetStyleGCs(GtkStyle* style, gint xorigin, gint yorigin)
+{
+    TSOffsetStyleGCArray(style->fg_gc, xorigin, yorigin);
+    TSOffsetStyleGCArray(style->bg_gc, xorigin, yorigin);
+    TSOffsetStyleGCArray(style->light_gc, xorigin, yorigin);
+    TSOffsetStyleGCArray(style->dark_gc, xorigin, yorigin);
+    TSOffsetStyleGCArray(style->mid_gc, xorigin, yorigin);
+    TSOffsetStyleGCArray(style->text_gc, xorigin, yorigin);
+    TSOffsetStyleGCArray(style->base_gc, xorigin, yorigin);
+    gdk_gc_set_ts_origin(style->black_gc, xorigin, yorigin);
+    gdk_gc_set_ts_origin(style->white_gc, xorigin, yorigin);
+    return 0;
+}
+
+/* ---------------------------------------------------------------------- 
+ * Gtk+ element
+ *
+ */
+
+typedef struct ElementInfo {
+    const char *elementName;
+    //GtkWidget*(*createWidget)();
+    int id;
+    Ttk_ElementSpec *elementSpec;
+} ElementInfo;
+
+typedef struct ElementData {
+    GtkWidget   *widget;
+    GtkStyle    *style;
+    ElementInfo *info;
+} ElementData;
+
+static ElementData *
+NewElementData(ElementInfo *infoPtr, ThemeData *themeData)
+{
+    ElementData *elementData = (ElementData *)ckalloc(sizeof(ElementData));
+    elementData->info = infoPtr;
+    elementData->widget = themeData->widget[infoPtr->id];
+#if 1
+    if (infoPtr->id == WIDGET_GTK_WINDOW) {
+        gtk_widget_realize(elementData->widget);
+        themeData->container[infoPtr->id] = NULL;
+    } else {
+        GtkWidget *w = gtk_window_new(GTK_WINDOW_POPUP);
+        themeData->container[infoPtr->id] = w;
+        gtk_widget_realize(w);
+        gtk_container_add(GTK_CONTAINER(w), elementData->widget);
+        gtk_widget_realize(elementData->widget);
+        elementData->style = w->style;
+    }
+#else
+    gtk_widget_ensure_style(elementData->widget);
+    elementData->style = gtk_rc_get_style(elementData->widget);
+#endif
+    return elementData;
+}
+
+static void
+DestroyElementData(void *clientData)
+{
+    ElementData *elementData = clientData;
+    gtk_widget_destroy(elementData->widget);
+    g_object_unref(G_OBJECT(elementData->style));
+    ckfree(clientData);
+}
+
+static void
+GenericElementGeometry(void *clientData, void *elementRecord, Tk_Window tkwin,
+                       int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
+{
+    ElementData *elementData = clientData;
+    gint size = 0, space = 0;
+
+    switch (elementData->info->id) {
+        case WIDGET_GTK_CHECKBUTTON:
+        case WIDGET_GTK_RADIOBUTTON: {
+            gtk_widget_style_get(elementData->widget,
+                                 "indicator_size", &size, 
+                                 "indicator_spacing", &space, NULL);
+            *widthPtr = size ; *heightPtr = size;
+            break;
+        }
+    }
+}
+
+static void
+GenericElementDraw(void *clientData, void *elementRecord, Tk_Window tkwin,
+                   Drawable d, Ttk_Box b, unsigned int state)
+{
+    ElementData *elementData = clientData;
+    GdkDisplay *gdkdisplay = 0;
+    GdkWindow *gdkdrawable = 0;
+    GtkStyle *style = 0;
+
+    gdkdisplay = gdk_x11_lookup_xdisplay(Tk_Display(tkwin));
+    if (gdkdisplay == NULL) {
+        gdkdisplay = gdk_display_get_default();
+    }
+    if (gdkdisplay == NULL) {
+        g_printf("GenericDrawElement: failed to get display\n");
+    }
+    gdkdrawable = gdk_window_foreign_new_for_display(gdkdisplay, Tk_WindowId(tkwin));
+    style = gtk_style_attach(elementData->style, gdkdrawable);
+    TSOffsetStyleGCs(style, b.x, b.y);
+    switch (elementData->info->id) {
+        case WIDGET_GTK_CHECKBUTTON: {
+           GtkShadowType gshadow = (GtkShadowType)Ttk_StateTableLookup(button_shadowmap, state);
+            //GtkShadowType gshadow = (state & TTK_STATE_SELECTED)?GTK_SHADOW_IN:GTK_SHADOW_OUT;
+            GtkStateType gstate = (GtkStateType)Ttk_StateTableLookup(button_statemap, state);
+            GTK_TOGGLE_BUTTON(elementData->widget)->active = (state & TTK_STATE_SELECTED);
+            if (state & TTK_STATE_FOCUS) {
+                GTK_WIDGET_SET_FLAGS(elementData->widget, GTK_HAS_FOCUS);
+            } else {
+                GTK_WIDGET_UNSET_FLAGS(elementData->widget, GTK_HAS_FOCUS);
+            }
+            gtk_paint_check(style, gdkdrawable, gstate, gshadow,
+                            NULL, elementData->widget, "cellcheck",
+                            b.x, b.y, b.width, b.height);
+            break;
+        }
+        case WIDGET_GTK_RADIOBUTTON: {
+           GtkShadowType gshadow = (GtkShadowType)Ttk_StateTableLookup(button_shadowmap, state);
+            //GtkShadowType gshadow = (state & TTK_STATE_SELECTED)?GTK_SHADOW_IN:GTK_SHADOW_OUT;
+           GtkStateType gstate = (GtkStateType)Ttk_StateTableLookup(button_statemap, state);
+            GTK_TOGGLE_BUTTON(elementData->widget)->active = (state & TTK_STATE_SELECTED);
+            if (state & TTK_STATE_FOCUS) {
+                GTK_WIDGET_SET_FLAGS(elementData->widget, GTK_HAS_FOCUS);
+            } else {
+                GTK_WIDGET_UNSET_FLAGS(elementData->widget, GTK_HAS_FOCUS);
+            }
+            gtk_paint_option(style, gdkdrawable, gstate, gshadow,
+                             0, elementData->widget, "radiobutton",
+                             b.x, b.y, b.width, b.height);
+            break;
+        }
+        case WIDGET_GTK_BUTTON: {
+            Ttk_Padding p = {-2, -2, -2, -2};
+            //GtkShadowType gshadow = (state & TTK_STATE_PRESSED) 
+            //    ? GTK_SHADOW_IN : GTK_SHADOW_OUT;
+           GtkShadowType gshadow = (GtkShadowType)Ttk_StateTableLookup(button_shadowmap, state);
+            GtkStateType gstate = (GtkStateType)Ttk_StateTableLookup(button_statemap, state);
+            if (state & TTK_STATE_FOCUS) {
+                GTK_WIDGET_SET_FLAGS(elementData->widget, GTK_HAS_FOCUS);
+            } else {
+                GTK_WIDGET_UNSET_FLAGS(elementData->widget, GTK_HAS_FOCUS);
+            }
+
+            gdk_window_set_back_pixmap(gdkdrawable, NULL, TRUE);
+            gdk_window_clear_area(gdkdrawable, b.x, b.y, b.width, b.height);
+            gtk_widget_set_state(elementData->widget, gstate);
+            gtk_paint_box(style, gdkdrawable, gstate, gshadow,
+                          0, elementData->widget, "button", 
+                          b.x, b.y, b.width, b.height);
+            Ttk_ExpandBox(b, p);
+            gtk_paint_focus(style, gdkdrawable, gstate,
+                            0, elementData->widget, "button", b.x, b.y, b.width, b.height);
+            break;
+        }
+    }
+    gtk_style_detach(style);
+}
+
+static Ttk_ElementSpec GtkElementSpec = 
+{
+    TK_STYLE_VERSION_2,
+    sizeof(NullElement),
+    TtkNullElementOptions,
+    GenericElementGeometry,
+    GenericElementDraw
+};
+
+/* ----------------------------------------------------------------------
+ * Create a Glib event source to link the Tk and Glib event loops 
+ */
+
+static int 
+EventProc(Tcl_Event *evPtr, int flags)
+{
+    if (!(flags & TCL_WINDOW_EVENTS)) {
+        return 0;
+    }
+    while (gtk_events_pending()) {
+        gtk_main_iteration();
+    }
+    return 1;
+}
+
+static void 
+SetupProc(ClientData clientData, int flags) {
+    Tcl_Time block_time = {0, 0};
+    if (!(flags & TCL_WINDOW_EVENTS)) {
+        return;
+    }
+    if (!gtk_events_pending()) {
+        block_time.usec = 10000;
+    }
+    Tcl_SetMaxBlockTime(&block_time);
+    return;
+}
+
+static void 
+CheckProc(ClientData clientData, int flags) {
+    if (!(flags & TCL_WINDOW_EVENTS)) {
+        return;
+    }
+    if (gtk_events_pending()) {
+        Tcl_Event *event = (Tcl_Event *)ckalloc(sizeof(Tcl_Event));
+        event->proc = EventProc;
+        Tcl_QueueEvent(event, TCL_QUEUE_TAIL);
+    }
+    return;
+}
+
+/* ---------------------------------------------------------------------- */
+
+static void
+DeleteThemeData(void *clientData)
+{
+    /* ThemeData *themeData = clientData; */
+    ckfree(clientData);
+}
+static ElementInfo ElementInfoTable[] = {
+    { "Button.button", WIDGET_GTK_BUTTON, &GtkElementSpec },
+    { "Checkbutton.indicator", WIDGET_GTK_CHECKBUTTON, &GtkElementSpec },
+    { "Radiobutton.indicator", WIDGET_GTK_RADIOBUTTON, &GtkElementSpec },
+    { 0, 0, 0 }
+};
+
+MODULE_SCOPE int
+TtkGtkTheme_Init(Tcl_Interp *interp)
+{
+    ThemeData *themeData;
+    ElementInfo *infoPtr = NULL;
+    Ttk_Theme theme = NULL, parent = NULL;
+
+    parent = Ttk_GetTheme(interp, "clam");
+    theme = Ttk_CreateTheme(interp, "gtk", parent);
+    
+    if (!theme) {
+        printf("failed to create gtk theme\n");
+        return TCL_ERROR;
+    }
+
+    // LoadGtkThemeProcs();
+    themeData = (ThemeData *)ckalloc(sizeof(ThemeData));
+    Ttk_RegisterCleanup(interp, themeData, DeleteThemeData);
+
+    /*
+     * Initialize the Gtk+ library
+     */
+
+    gtk_init_check(0, NULL);
+    
+    //themeData->widget[WIDGET_GTK_WINDOW] = gtk_invisible_new();
+    themeData->widget[WIDGET_GTK_WINDOW] = gtk_window_new(GTK_WINDOW_POPUP);
+    themeData->widget[WIDGET_GTK_BUTTON] = gtk_button_new_with_label("b");
+    themeData->widget[WIDGET_GTK_CHECKBUTTON] = gtk_check_button_new_with_label("c");
+    themeData->widget[WIDGET_GTK_RADIOBUTTON] = gtk_radio_button_new_with_label(NULL, "r");
+    
+    /*
+     * Register elements
+     */
+
+    for (infoPtr = ElementInfoTable; infoPtr->elementName != 0; ++infoPtr) {
+        ClientData clientData = NewElementData(infoPtr, themeData);
+        Ttk_RegisterElementSpec(theme, infoPtr->elementName,
+                                infoPtr->elementSpec, clientData);
+        Ttk_RegisterCleanup(interp, clientData, DestroyElementData);
+    }
+
+    /*
+     * Create an event source to service Gtk+ events as part of the Tk event system
+     */
+
+    Tcl_CreateEventSource(SetupProc, CheckProc, NULL);
+
+    /*
+     * Register layouts
+     */
+
+
+    Tcl_PkgProvide(interp, "ttk::theme::gtk", TTK_VERSION);
+    return TCL_OK;
+}