From: Pat Thoyts Date: Tue, 24 Apr 2007 23:53:41 +0000 (+0100) Subject: Initial commit of a Gtk+ element engine for tile. X-Git-Url: https://privyetmir.co.uk/gitweb.cgi?a=commitdiff_plain;h=0e65bfad47d7abf94d6f85cdac994395e18801b1;p=ttkGtkTheme Initial commit of a Gtk+ element engine for tile. --- 0e65bfad47d7abf94d6f85cdac994395e18801b1 diff --git a/ttkGtkTheme.c b/ttkGtkTheme.c new file mode 100644 index 0000000..3410d7d --- /dev/null +++ b/ttkGtkTheme.c @@ -0,0 +1,343 @@ + +// see google codesearch for MOZ_GTK_SUCCESS and ensure_checkbox_widget + +#include +#include +#include + +#include + +/* Are we part of tile, or Tk... */ +#ifdef BUILD_tile +#include "tkTheme.h" +#define TTK_VERSION PACKAGE_VERSION +#else +#include "ttkTheme.h" +#endif + +#include + +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; +}