diff options
Diffstat (limited to 'iup/src/gtk')
30 files changed, 13949 insertions, 0 deletions
diff --git a/iup/src/gtk/iupgtk_button.c b/iup/src/gtk/iupgtk_button.c new file mode 100755 index 0000000..18be87c --- /dev/null +++ b/iup/src/gtk/iupgtk_button.c @@ -0,0 +1,477 @@ +/** \file + * \brief Button Control + * + * See Copyright Notice in "iup.h" + */ + +#include <gtk/gtk.h> + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <memory.h> +#include <stdarg.h> + +#include "iup.h" +#include "iupcbs.h" + +#include "iup_object.h" +#include "iup_layout.h" +#include "iup_attrib.h" +#include "iup_str.h" +#include "iup_image.h" +#include "iup_button.h" +#include "iup_drv.h" +#include "iup_drvfont.h" +#include "iup_image.h" +#include "iup_key.h" + +#include "iupgtk_drv.h" + + +#if !GTK_CHECK_VERSION(2, 6, 0) +static void gtk_button_set_image(GtkButton *button, GtkWidget *image) +{ +} +static GtkWidget* gtk_button_get_image(GtkButton *button) +{ + return NULL; +} +#endif + +void iupdrvButtonAddBorders(int *x, int *y) +{ +#ifdef WIN32 + int border_size = 2*5; +#else +#ifdef HILDON + int border_size = 2*7+1; /* borders are not symetric */ +#else + int border_size = 2*5+1; /* borders are not symetric */ +#endif +#endif + (*x) += border_size; + (*y) += border_size; +} + +static void gtk_button_children_callback(GtkWidget *widget, gpointer client_data) +{ + if (GTK_IS_LABEL(widget)) + { + GtkLabel **label = (GtkLabel**) client_data; + *label = (GtkLabel*)widget; + } +} + +static GtkLabel* gtkButtonGetLabel(Ihandle* ih) +{ + if (ih->data->type == IUP_BUTTON_TEXT) /* text only */ + return (GtkLabel*)gtk_bin_get_child((GtkBin*)ih->handle); + else if (ih->data->type == IUP_BUTTON_BOTH) /* both */ + { + /* when both is set, button contains an GtkAlignment, + that contains a GtkBox, that contains a label and an image */ + GtkContainer *container = (GtkContainer*)gtk_bin_get_child((GtkBin*)gtk_bin_get_child((GtkBin*)ih->handle)); + GtkLabel* label = NULL; + gtk_container_foreach(container, gtk_button_children_callback, &label); + return label; + } + return NULL; +} + +static int gtkButtonSetTitleAttrib(Ihandle* ih, const char* value) +{ + if (ih->data->type != IUP_BUTTON_IMAGE) /* text or both */ + { + GtkLabel* label = gtkButtonGetLabel(ih); + iupgtkSetMnemonicTitle(ih, label, value); + return 1; + } + + return 0; +} + +static int gtkButtonSetAlignmentAttrib(Ihandle* ih, const char* value) +{ + GtkButton* button = (GtkButton*)ih->handle; + PangoAlignment alignment; + float xalign, yalign; + char value1[30]="", value2[30]=""; + + iupStrToStrStr(value, value1, value2, ':'); + + if (iupStrEqualNoCase(value1, "ARIGHT")) + { + xalign = 1.0f; + alignment = PANGO_ALIGN_RIGHT; + } + else if (iupStrEqualNoCase(value1, "ACENTER")) + { + xalign = 0.5f; + alignment = PANGO_ALIGN_CENTER; + } + else /* "ALEFT" */ + { + xalign = 0; + alignment = PANGO_ALIGN_LEFT; + } + + if (iupStrEqualNoCase(value2, "ABOTTOM")) + yalign = 1.0f; + else if (iupStrEqualNoCase(value2, "ATOP")) + yalign = 0; + else /* ACENTER (default) */ + yalign = 0.5f; + + gtk_button_set_alignment(button, xalign, yalign); + + if (ih->data->type == IUP_BUTTON_TEXT && !GTK_IS_COLOR_BUTTON(ih->handle)) /* text only */ + { + PangoLayout* layout = gtk_label_get_layout(gtkButtonGetLabel(ih)); + if (layout) pango_layout_set_alignment(layout, alignment); + } + + return 1; +} + +static int gtkButtonSetPaddingAttrib(Ihandle* ih, const char* value) +{ + iupStrToIntInt(value, &ih->data->horiz_padding, &ih->data->vert_padding, 'x'); + if (ih->handle) + { + if (ih->data->type == IUP_BUTTON_TEXT) /* text only */ + { + GtkMisc* misc = (GtkMisc*)gtk_bin_get_child((GtkBin*)ih->handle); + gtk_misc_set_padding(misc, ih->data->horiz_padding, ih->data->vert_padding); + } + else + { + GtkAlignment* alignment = (GtkAlignment*)gtk_bin_get_child((GtkBin*)ih->handle); + gtk_alignment_set_padding(alignment, ih->data->vert_padding, ih->data->vert_padding, + ih->data->horiz_padding, ih->data->horiz_padding); + } + } + return 0; +} + +#ifdef WIN32 +static int gtkButtonSetBgColorAttrib(Ihandle* ih, const char* value) +{ + if (ih->data->type == IUP_BUTTON_TEXT && GTK_IS_COLOR_BUTTON(ih->handle)) + { + GdkColor color; + unsigned char r, g, b; + if (!iupStrToRGB(value, &r, &g, &b)) + return 0; + + iupgdkColorSet(&color, r, g, b); + gtk_color_button_set_color((GtkColorButton*)ih->handle, &color); + return 1; + } + + return iupdrvBaseSetBgColorAttrib(ih, value); +} +#endif + +static int gtkButtonSetFgColorAttrib(Ihandle* ih, const char* value) +{ + unsigned char r, g, b; + GtkLabel* label = gtkButtonGetLabel(ih); + if (!label) return 0; + + if (!iupStrToRGB(value, &r, &g, &b)) + return 0; + + iupgtkBaseSetFgColor((GtkWidget*)label, r, g, b); + + return 1; +} + +static int gtkButtonSetStandardFontAttrib(Ihandle* ih, const char* value) +{ + iupdrvSetStandardFontAttrib(ih, value); + + if (ih->handle) + { + GtkLabel* label = gtkButtonGetLabel(ih); + if (!label) return 1; + + gtk_widget_modify_font((GtkWidget*)label, (PangoFontDescription*)iupgtkGetPangoFontDescAttrib(ih)); + if (ih->data->type == IUP_BUTTON_TEXT && !GTK_IS_COLOR_BUTTON(ih->handle)) /* text only */ + iupgtkFontUpdatePangoLayout(ih, gtk_label_get_layout(label)); + } + return 1; +} + +static void gtkButtonSetPixbuf(Ihandle* ih, const char* name, int make_inactive) +{ + GtkButton* button = (GtkButton*)ih->handle; + GtkImage* image = (GtkImage*)gtk_button_get_image(button); + + if (name && image) + { + GdkPixbuf* pixbuf = iupImageGetImage(name, ih, make_inactive); + GdkPixbuf* old_pixbuf = gtk_image_get_pixbuf(image); + if (pixbuf != old_pixbuf) + gtk_image_set_from_pixbuf(image, pixbuf); + return; + } + + /* if not defined */ +#if GTK_CHECK_VERSION(2, 8, 0) + if (image) + gtk_image_clear(image); +#endif +} + +static int gtkButtonSetImageAttrib(Ihandle* ih, const char* value) +{ + if (ih->data->type != IUP_BUTTON_TEXT) /* image or both */ + { + if (iupdrvIsActive(ih)) + gtkButtonSetPixbuf(ih, value, 0); + else + { + if (!iupAttribGet(ih, "IMINACTIVE")) + { + /* if not active and IMINACTIVE is not defined + then automaticaly create one based on IMAGE */ + gtkButtonSetPixbuf(ih, value, 1); /* make_inactive */ + } + } + return 1; + } + else + return 0; +} + +static int gtkButtonSetImInactiveAttrib(Ihandle* ih, const char* value) +{ + if (ih->data->type != IUP_BUTTON_TEXT) /* image or both */ + { + if (!iupdrvIsActive(ih)) + { + if (value) + gtkButtonSetPixbuf(ih, value, 0); + else + { + /* if not defined then automaticaly create one based on IMAGE */ + char* name = iupAttribGet(ih, "IMAGE"); + gtkButtonSetPixbuf(ih, name, 1); /* make_inactive */ + } + } + return 1; + } + else + return 0; +} + +static int gtkButtonSetActiveAttrib(Ihandle* ih, const char* value) +{ + /* update the inactive image if necessary */ + if (ih->data->type != IUP_BUTTON_TEXT) /* image or both */ + { + if (!iupStrBoolean(value)) + { + char* name = iupAttribGet(ih, "IMINACTIVE"); + if (name) + gtkButtonSetPixbuf(ih, name, 0); + else + { + /* if not defined then automaticaly create one based on IMAGE */ + name = iupAttribGet(ih, "IMAGE"); + gtkButtonSetPixbuf(ih, name, 1); /* make_inactive */ + } + } + else + { + /* must restore the normal image */ + char* name = iupAttribGet(ih, "IMAGE"); + gtkButtonSetPixbuf(ih, name, 0); + } + } + + return iupBaseSetActiveAttrib(ih, value); +} + +static int gtkButtonSetFocusOnClickAttrib(Ihandle* ih, const char* value) +{ + if (iupStrBoolean(value)) + gtk_button_set_focus_on_click((GtkButton*)ih->handle, TRUE); + else + gtk_button_set_focus_on_click((GtkButton*)ih->handle, FALSE); + return 1; +} + +static gboolean gtkButtonEnterLeaveEvent(GtkWidget *widget, GdkEventCrossing *evt, Ihandle *ih) +{ + iupgtkEnterLeaveEvent(widget, evt, ih); + (void)widget; + + if (evt->type == GDK_ENTER_NOTIFY) + gtk_button_set_relief((GtkButton*)ih->handle, GTK_RELIEF_NORMAL); + else if (evt->type == GDK_LEAVE_NOTIFY) + gtk_button_set_relief((GtkButton*)ih->handle, GTK_RELIEF_NONE); + + return FALSE; +} + +static gboolean gtkButtonEvent(GtkWidget *widget, GdkEventButton *evt, Ihandle *ih) +{ + if (iupgtkButtonEvent(widget, evt, ih)==TRUE) + return TRUE; + + if (ih->data->type != IUP_BUTTON_TEXT) /* image or both */ + { + char* name = iupAttribGet(ih, "IMPRESS"); + if (name) + { + if (evt->type == GDK_BUTTON_PRESS) + gtkButtonSetPixbuf(ih, name, 0); + else + { + name = iupAttribGet(ih, "IMAGE"); + gtkButtonSetPixbuf(ih, name, 0); + } + } + } + + return FALSE; +} + +static void gtkButtonClicked(GtkButton *widget, Ihandle* ih) +{ + Icallback cb = IupGetCallback(ih, "ACTION"); + if (cb) + { + if (cb(ih) == IUP_CLOSE) + IupExitLoop(); + } + (void)widget; +} + +static int gtkButtonMapMethod(Ihandle* ih) +{ + int impress; + char* value; + + ih->handle = gtk_button_new(); + if (!ih->handle) + return IUP_ERROR; + + value = iupAttribGet(ih, "IMAGE"); + if (value) + { + gtk_button_set_image((GtkButton*)ih->handle, gtk_image_new()); + ih->data->type = IUP_BUTTON_IMAGE; + + value = iupAttribGet(ih, "TITLE"); + if (value) + { + GtkSettings* settings = gtk_widget_get_settings(ih->handle); + g_object_set(settings, "gtk-button-images", (int)TRUE, NULL); + + gtk_button_set_label((GtkButton*)ih->handle, value); + ih->data->type |= IUP_BUTTON_TEXT; + +#if GTK_CHECK_VERSION(2, 10, 0) + gtk_button_set_image_position((GtkButton*)ih->handle, ih->data->img_position); /* IUP and GTK have the same Ids */ +#endif + } + } + else + { + char* title = iupAttribGet(ih, "TITLE"); + if (!title) + { +#ifdef WIN32 + if (iupAttribGet(ih, "BGCOLOR")) + { + gtk_widget_destroy(ih->handle); + ih->handle = gtk_color_button_new(); + } + else + gtk_button_set_label((GtkButton*)ih->handle, ""); +#else + gtk_button_set_label((GtkButton*)ih->handle, ""); +#endif + } + else + gtk_button_set_label((GtkButton*)ih->handle, title); + ih->data->type = IUP_BUTTON_TEXT; + } + + /* add to the parent, all GTK controls must call this. */ + iupgtkBaseAddToParent(ih); + + if (!iupAttribGetBoolean(ih, "CANFOCUS")) + GTK_WIDGET_FLAGS(ih->handle) &= ~GTK_CAN_FOCUS; + + value = iupAttribGet(ih, "IMPRESS"); + impress = (ih->data->type & IUP_BUTTON_IMAGE && value)? 1: 0; + if (!impress && iupAttribGetBoolean(ih, "FLAT")) + { + gtk_button_set_relief((GtkButton*)ih->handle, GTK_RELIEF_NONE); + + g_signal_connect(G_OBJECT(ih->handle), "enter-notify-event", G_CALLBACK(gtkButtonEnterLeaveEvent), ih); + g_signal_connect(G_OBJECT(ih->handle), "leave-notify-event", G_CALLBACK(gtkButtonEnterLeaveEvent), ih); + } + else + { + if (impress && !iupAttribGetStr(ih, "IMPRESSBORDER")) + gtk_button_set_relief((GtkButton*)ih->handle, GTK_RELIEF_NONE); + else + gtk_button_set_relief((GtkButton*)ih->handle, GTK_RELIEF_NORMAL); + + g_signal_connect(G_OBJECT(ih->handle), "enter-notify-event", G_CALLBACK(iupgtkEnterLeaveEvent), ih); + g_signal_connect(G_OBJECT(ih->handle), "leave-notify-event", G_CALLBACK(iupgtkEnterLeaveEvent), ih); + } + + g_signal_connect(G_OBJECT(ih->handle), "focus-in-event", G_CALLBACK(iupgtkFocusInOutEvent), ih); + g_signal_connect(G_OBJECT(ih->handle), "focus-out-event", G_CALLBACK(iupgtkFocusInOutEvent), ih); + g_signal_connect(G_OBJECT(ih->handle), "key-press-event", G_CALLBACK(iupgtkKeyPressEvent), ih); + g_signal_connect(G_OBJECT(ih->handle), "show-help", G_CALLBACK(iupgtkShowHelp), ih); + + g_signal_connect(G_OBJECT(ih->handle), "clicked", G_CALLBACK(gtkButtonClicked), ih); + g_signal_connect(G_OBJECT(ih->handle), "button-press-event", G_CALLBACK(gtkButtonEvent), ih); + g_signal_connect(G_OBJECT(ih->handle), "button-release-event",G_CALLBACK(gtkButtonEvent), ih); + + gtk_widget_realize(ih->handle); + + return IUP_NOERROR; +} + +void iupdrvButtonInitClass(Iclass* ic) +{ + /* Driver Dependent Class functions */ + ic->Map = gtkButtonMapMethod; + + /* Driver Dependent Attribute functions */ + + /* Overwrite Common */ + iupClassRegisterAttribute(ic, "STANDARDFONT", NULL, gtkButtonSetStandardFontAttrib, IUPAF_SAMEASSYSTEM, "DEFAULTFONT", IUPAF_NOT_MAPPED); + + /* Overwrite Visual */ + iupClassRegisterAttribute(ic, "ACTIVE", iupBaseGetActiveAttrib, gtkButtonSetActiveAttrib, IUPAF_SAMEASSYSTEM, "YES", IUPAF_DEFAULT); + + /* Visual */ +#ifdef WIN32 + iupClassRegisterAttribute(ic, "BGCOLOR", NULL, gtkButtonSetBgColorAttrib, IUPAF_SAMEASSYSTEM, "DLGBGCOLOR", IUPAF_DEFAULT); +#else + iupClassRegisterAttribute(ic, "BGCOLOR", NULL, iupdrvBaseSetBgColorAttrib, IUPAF_SAMEASSYSTEM, "DLGBGCOLOR", IUPAF_DEFAULT); +#endif + + /* Special */ + iupClassRegisterAttribute(ic, "FGCOLOR", NULL, gtkButtonSetFgColorAttrib, IUPAF_SAMEASSYSTEM, "DLGFGCOLOR", IUPAF_DEFAULT); + iupClassRegisterAttribute(ic, "TITLE", NULL, gtkButtonSetTitleAttrib, NULL, NULL, IUPAF_NO_DEFAULTVALUE|IUPAF_NO_INHERIT); + + /* IupButton only */ + iupClassRegisterAttribute(ic, "ALIGNMENT", NULL, gtkButtonSetAlignmentAttrib, "ACENTER:ACENTER", NULL, IUPAF_NO_INHERIT); /* force new default value */ + iupClassRegisterAttribute(ic, "IMAGE", NULL, gtkButtonSetImageAttrib, NULL, NULL, IUPAF_NO_DEFAULTVALUE|IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "IMINACTIVE", NULL, gtkButtonSetImInactiveAttrib, NULL, NULL, IUPAF_NO_DEFAULTVALUE|IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "IMPRESS", NULL, NULL, NULL, NULL, IUPAF_NO_DEFAULTVALUE|IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "FOCUSONCLICK", NULL, gtkButtonSetFocusOnClickAttrib, IUPAF_SAMEASSYSTEM, "YES", IUPAF_NO_INHERIT); + + iupClassRegisterAttribute(ic, "PADDING", iupButtonGetPaddingAttrib, gtkButtonSetPaddingAttrib, IUPAF_SAMEASSYSTEM, "0x0", IUPAF_NOT_MAPPED); + iupClassRegisterAttribute(ic, "MARKUP", NULL, NULL, NULL, NULL, IUPAF_DEFAULT); +} diff --git a/iup/src/gtk/iupgtk_canvas.c b/iup/src/gtk/iupgtk_canvas.c new file mode 100755 index 0000000..daae4ad --- /dev/null +++ b/iup/src/gtk/iupgtk_canvas.c @@ -0,0 +1,624 @@ +/** \file + * \brief Canvas Control + * + * See Copyright Notice in "iup.h" + */ + +#include <gtk/gtk.h> + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <memory.h> +#include <stdarg.h> +#include <limits.h> + +#include "iup.h" +#include "iupcbs.h" + +#include "iup_object.h" +#include "iup_layout.h" +#include "iup_attrib.h" +#include "iup_dialog.h" +#include "iup_str.h" +#include "iup_drv.h" +#include "iup_drvinfo.h" +#include "iup_drvfont.h" +#include "iup_canvas.h" +#include "iup_key.h" + +#include "iupgtk_drv.h" + + +static int gtkCanvasScroll2Iup(GtkScrollType scroll, int vert) +{ + switch(scroll) + { + case GTK_SCROLL_STEP_UP: + return IUP_SBUP; + case GTK_SCROLL_STEP_DOWN: + return IUP_SBDN; + case GTK_SCROLL_PAGE_UP: + return IUP_SBPGUP; + case GTK_SCROLL_PAGE_DOWN: + return IUP_SBPGDN; + case GTK_SCROLL_STEP_LEFT: + return IUP_SBLEFT; + case GTK_SCROLL_STEP_RIGHT: + return IUP_SBRIGHT; + case GTK_SCROLL_PAGE_LEFT: + return IUP_SBPGLEFT; + case GTK_SCROLL_PAGE_RIGHT: + return IUP_SBPGRIGHT; + case GTK_SCROLL_STEP_BACKWARD: + return vert? IUP_SBUP: IUP_SBLEFT; + case GTK_SCROLL_STEP_FORWARD: + return vert? IUP_SBDN: IUP_SBRIGHT; + case GTK_SCROLL_PAGE_BACKWARD: + return vert? IUP_SBPGUP: IUP_SBPGLEFT; + case GTK_SCROLL_PAGE_FORWARD: + return vert? IUP_SBPGDN: IUP_SBPGRIGHT; + case GTK_SCROLL_JUMP: + case GTK_SCROLL_START: + case GTK_SCROLL_END: + return vert? IUP_SBPOSV: IUP_SBPOSH; + case GTK_SCROLL_NONE: + return -1; + } + + /* No IUP_SBDRAGV or IUP_SBDRAGH support in GTK */ + + return -1; +} + +static gboolean gtkCanvasHChangeValue(GtkRange *range, GtkScrollType scroll, double value, Ihandle *ih) +{ + double posx, posy; + IFniff cb; + + double xmin = iupAttribGetFloat(ih, "XMIN"); + double xmax = iupAttribGetFloat(ih, "XMAX"); + double dx = iupAttribGetFloat(ih, "DX"); + if (value < xmin) value = xmin; + if (value > xmax-dx) value = xmax-dx; + + posx = value; + ih->data->posx = (float)posx; + posy = ih->data->posy; + + cb = (IFniff)IupGetCallback(ih,"SCROLL_CB"); + if (cb) + { + int op = gtkCanvasScroll2Iup(scroll, 0); + if (op == -1) + return FALSE; + + cb(ih, op, (float)posx, (float)posy); + } + else + { + IFnff cb = (IFnff)IupGetCallback(ih,"ACTION"); + if (cb) + cb (ih, (float)posx, (float)posy); + } + + (void)range; + return FALSE; +} + +static gboolean gtkCanvasVChangeValue(GtkRange *range, GtkScrollType scroll, double value, Ihandle *ih) +{ + double posx, posy; + IFniff cb; + + double ymin = iupAttribGetFloat(ih, "YMIN"); + double ymax = iupAttribGetFloat(ih, "YMAX"); + double dy = iupAttribGetFloat(ih, "DY"); + if (value < ymin) value = ymin; + if (value > ymax-dy) value = ymax-dy; + + posy = value; + ih->data->posy = (float)posy; + posx = ih->data->posx; + + cb = (IFniff)IupGetCallback(ih,"SCROLL_CB"); + if (cb) + { + int op = gtkCanvasScroll2Iup(scroll, 1); + if (op == -1) + return FALSE; + + cb(ih, op, (float)posx, (float)posy); + } + else + { + IFnff cb = (IFnff)IupGetCallback(ih,"ACTION"); + if (cb) + cb (ih, (float)posx, (float)posy); + } + + (void)range; + return FALSE; +} + +static gboolean gtkCanvasScrollEvent(GtkWidget *widget, GdkEventScroll *evt, Ihandle *ih) +{ + IFnfiis wcb = (IFnfiis)IupGetCallback(ih, "WHEEL_CB"); + if (wcb) + { + int delta = evt->direction==GDK_SCROLL_UP||evt->direction==GDK_SCROLL_LEFT? 1: -1; + char status[IUPKEY_STATUS_SIZE] = IUPKEY_STATUS_INIT; + int button = evt->direction==GDK_SCROLL_UP||evt->direction==GDK_SCROLL_LEFT? 4: 5; + iupgtkButtonKeySetStatus(evt->state, button, status, 0); + + wcb(ih, (float)delta, (int)evt->x, (int)evt->y, status); + } + else + { + IFniff scb = (IFniff)IupGetCallback(ih,"SCROLL_CB"); + int delta = evt->direction==GDK_SCROLL_UP||evt->direction==GDK_SCROLL_LEFT? 1: -1; + + if (evt->direction==GDK_SCROLL_UP || evt->direction==GDK_SCROLL_DOWN) + { + float posy = ih->data->posy; + posy -= delta*iupAttribGetFloat(ih, "DY")/10.0f; + IupSetfAttribute(ih, "POSY", "%g", posy); + } + else + { + float posx = ih->data->posx; + posx -= delta*iupAttribGetFloat(ih, "DX")/10.0f; + IupSetfAttribute(ih, "POSX", "%g", posx); + } + + if (scb) + { + int scroll_gtk2iup[4] = {IUP_SBUP, IUP_SBDN, IUP_SBLEFT, IUP_SBRIGHT}; + int op = scroll_gtk2iup[evt->direction]; + scb(ih,op,ih->data->posx,ih->data->posy); + } + } + (void)widget; + return TRUE; +} + +static gboolean gtkCanvasButtonEvent(GtkWidget *widget, GdkEventButton *evt, Ihandle *ih) +{ + if (evt->type == GDK_BUTTON_PRESS) + { + /* Force focus on canvas click */ + if (iupAttribGetBoolean(ih, "CANFOCUS")) + gtk_widget_grab_focus(ih->handle); + } + + return iupgtkButtonEvent(widget, evt, ih); +} + +static int gtkCanvasSetBgColorAttrib(Ihandle* ih, const char* value); + +static gboolean gtkCanvasExposeEvent(GtkWidget *widget, GdkEventExpose *evt, Ihandle *ih) +{ + IFnff cb = (IFnff)IupGetCallback(ih,"ACTION"); + if (cb) + { + if (!iupAttribGet(ih, "_IUPGTK_NO_BGCOLOR")) + gtkCanvasSetBgColorAttrib(ih, iupAttribGetStr(ih, "BGCOLOR")); /* reset to update window attributes */ + + iupAttribSetStrf(ih, "CLIPRECT", "%d %d %d %d", evt->area.x, evt->area.y, evt->area.x+evt->area.width-1, evt->area.y+evt->area.height-1); + cb(ih,ih->data->posx,ih->data->posy); + iupAttribSetStr(ih, "CLIPRECT", NULL); + } + + (void)widget; + return TRUE; /* stop other handlers */ +} + +static gboolean gtkCanvasConfigureEvent(GtkWidget *widget, GdkEventConfigure *evt, Ihandle *ih) +{ + IFnii cb = (IFnii)IupGetCallback(ih,"RESIZE_CB"); + if (cb) + cb(ih,evt->width,evt->height); + + (void)widget; + return FALSE; +} + +static GtkScrolledWindow* gtkCanvasGetScrolledWindow(Ihandle* ih) +{ + return (GtkScrolledWindow*)iupAttribGet(ih, "_IUP_EXTRAPARENT"); +} + +static int gtkCanvasSetXAutoHideAttrib(Ihandle* ih, const char *value) +{ + GtkPolicyType vscrollbar_policy; + gtk_scrolled_window_get_policy(gtkCanvasGetScrolledWindow(ih), NULL, &vscrollbar_policy); + + if (ih->data->sb & IUP_SB_HORIZ) + { + GtkPolicyType hscrollbar_policy; + + if (iupStrBoolean(value)) + hscrollbar_policy = GTK_POLICY_AUTOMATIC; + else + hscrollbar_policy = GTK_POLICY_ALWAYS; + + gtk_scrolled_window_set_policy(gtkCanvasGetScrolledWindow(ih), hscrollbar_policy, vscrollbar_policy); + } + else + gtk_scrolled_window_set_policy(gtkCanvasGetScrolledWindow(ih), GTK_POLICY_NEVER, vscrollbar_policy); + + return 1; +} + +static int gtkCanvasSetYAutoHideAttrib(Ihandle* ih, const char *value) +{ + GtkPolicyType hscrollbar_policy; + gtk_scrolled_window_get_policy(gtkCanvasGetScrolledWindow(ih), &hscrollbar_policy, NULL); + + if (ih->data->sb & IUP_SB_VERT) + { + GtkPolicyType vscrollbar_policy; + + if (iupStrBoolean(value)) + vscrollbar_policy = GTK_POLICY_AUTOMATIC; + else + vscrollbar_policy = GTK_POLICY_ALWAYS; + + gtk_scrolled_window_set_policy(gtkCanvasGetScrolledWindow(ih), hscrollbar_policy, vscrollbar_policy); + } + else + gtk_scrolled_window_set_policy(gtkCanvasGetScrolledWindow(ih), hscrollbar_policy, GTK_POLICY_NEVER); + + return 1; +} + +static int gtkCanvasCheckScroll(double min, double max, double *page, double *pos) +{ + double old_pos = *pos; + double range = max-min; + if (*page > range) *page = range; + if (*page <= 0) *page = range/10.; + + if (*pos < min) *pos = min; + if (*pos > (max - *page)) *pos = max - *page; + + if (old_pos == *pos) + return 0; + else + return 1; +} + +static int gtkCanvasSetDXAttrib(Ihandle* ih, const char *value) +{ + if (ih->data->sb & IUP_SB_HORIZ) + { + double xmin, xmax, linex; + float dx; + int value_changed; + GtkAdjustment* sb_horiz = gtk_scrolled_window_get_hadjustment(gtkCanvasGetScrolledWindow(ih)); + if (!sb_horiz) return 1; + + if (!iupStrToFloat(value, &dx)) + return 1; + + xmin = iupAttribGetFloat(ih, "XMIN"); + xmax = iupAttribGetFloat(ih, "XMAX"); + + if (!iupAttribGet(ih,"LINEX")) + { + linex = dx/10; + if (linex==0) + linex = 1; + } + else + linex = iupAttribGetFloat(ih,"LINEX"); + + sb_horiz->lower = xmin; + sb_horiz->upper = xmax; + sb_horiz->step_increment = linex; + sb_horiz->page_size = dx; + + value_changed = gtkCanvasCheckScroll(xmin, xmax, &sb_horiz->page_size, &sb_horiz->value); + sb_horiz->page_increment = sb_horiz->page_size; + + gtk_adjustment_changed(sb_horiz); + + if (value_changed) + gtk_adjustment_value_changed(sb_horiz); + } + return 1; +} + +static int gtkCanvasSetDYAttrib(Ihandle* ih, const char *value) +{ + if (ih->data->sb & IUP_SB_VERT) + { + double ymin, ymax, liney; + float dy; + int value_changed; + GtkAdjustment* sb_vert = gtk_scrolled_window_get_vadjustment(gtkCanvasGetScrolledWindow(ih)); + if (!sb_vert) return 1; + + if (!iupStrToFloat(value, &dy)) + return 1; + + ymin = iupAttribGetFloat(ih, "YMIN"); + ymax = iupAttribGetFloat(ih, "YMAX"); + + if (!iupAttribGet(ih,"LINEY")) + { + liney = dy/10; + if (liney==0) + liney = 1; + } + else + liney = iupAttribGetFloat(ih,"LINEY"); + + sb_vert->lower = ymin; + sb_vert->upper = ymax; + sb_vert->step_increment = liney; + sb_vert->page_size = dy; + + value_changed = gtkCanvasCheckScroll(ymin, ymax, &sb_vert->page_size, &sb_vert->value); + sb_vert->page_increment = sb_vert->page_size; + + gtk_adjustment_changed(sb_vert); + + if (value_changed) + gtk_adjustment_value_changed(sb_vert); + } + return 1; +} + +static int gtkCanvasSetPosXAttrib(Ihandle* ih, const char *value) +{ + if (ih->data->sb & IUP_SB_HORIZ) + { + float posx, xmin, xmax, dx; + GtkAdjustment* sb_horiz = gtk_scrolled_window_get_hadjustment(gtkCanvasGetScrolledWindow(ih)); + if (!sb_horiz) return 1; + + if (!iupStrToFloat(value, &posx)) + return 1; + + xmin = iupAttribGetFloat(ih, "XMIN"); + xmax = iupAttribGetFloat(ih, "XMAX"); + dx = iupAttribGetFloat(ih, "DX"); + + if (posx < xmin) posx = xmin; + if (posx > (xmax - dx)) posx = xmax - dx; + ih->data->posx = posx; + + gtk_adjustment_set_value(sb_horiz, posx); + } + return 1; +} + +static int gtkCanvasSetPosYAttrib(Ihandle* ih, const char *value) +{ + if (ih->data->sb & IUP_SB_VERT) + { + float posy, ymin, ymax, dy; + GtkAdjustment* sb_vert = gtk_scrolled_window_get_vadjustment(gtkCanvasGetScrolledWindow(ih)); + if (!sb_vert) return 1; + + if (!iupStrToFloat(value, &posy)) + return 1; + + ymin = iupAttribGetFloat(ih, "YMIN"); + ymax = iupAttribGetFloat(ih, "YMAX"); + dy = iupAttribGetFloat(ih, "DY"); + + if (posy < ymin) posy = ymin; + if (posy > (ymax - dy)) posy = ymax - dy; + ih->data->posy = posy; + + gtk_adjustment_set_value(sb_vert, posy); + } + return 1; +} + +static int gtkCanvasSetBgColorAttrib(Ihandle* ih, const char* value) +{ + GtkScrolledWindow* scrolled_window = gtkCanvasGetScrolledWindow(ih); + unsigned char r, g, b; + + /* ignore given value, must use only from parent for the scrollbars */ + char* parent_value = iupBaseNativeParentGetBgColor(ih); + + if (iupStrToRGB(parent_value, &r, &g, &b)) + { + GtkWidget* sb; + + iupgtkBaseSetBgColor((GtkWidget*)scrolled_window, r, g, b); + +#if GTK_CHECK_VERSION(2, 8, 0) + sb = gtk_scrolled_window_get_hscrollbar(scrolled_window); + if (sb) iupgtkBaseSetBgColor(sb, r, g, b); + sb = gtk_scrolled_window_get_vscrollbar(scrolled_window); + if (sb) iupgtkBaseSetBgColor(sb, r, g, b); +#endif + } + + if (!IupGetCallback(ih, "ACTION")) + { + /* enable automatic double buffering */ + gtk_widget_set_double_buffered(ih->handle, TRUE); + gtk_widget_set_double_buffered((GtkWidget*)scrolled_window, TRUE); + return iupdrvBaseSetBgColorAttrib(ih, value); + } + else + { + /* disable automatic double buffering */ + gtk_widget_set_double_buffered(ih->handle, FALSE); + gtk_widget_set_double_buffered((GtkWidget*)scrolled_window, FALSE); + gdk_window_set_back_pixmap(ih->handle->window, NULL, FALSE); + iupAttribSetStr(ih, "_IUPGTK_NO_BGCOLOR", "1"); + return 1; + } +} + +static char* gtkCanvasGetDrawableAttrib(Ihandle* ih) +{ + return (char*)ih->handle->window; +} + +static void gtkCanvasDummyLogFunc(const gchar *log_domain, GLogLevelFlags log_level, const gchar *message, gpointer user_data) +{ + /* does nothing */ + (void)log_domain; + (void)log_level; + (void)message; + (void)user_data; +} + +static int gtkCanvasMapMethod(Ihandle* ih) +{ + GtkScrolledWindow* scrolled_window; + void* visual; + + if (!ih->parent) + return IUP_ERROR; + + ih->data->sb = iupBaseGetScrollbar(ih); + + visual = (void*)IupGetAttribute(ih, "VISUAL"); /* defined by the OpenGL Canvas in X11 or NULL */ + if (visual) + iupgtkPushVisualAndColormap(visual, (void*)iupAttribGet(ih, "COLORMAP")); + + ih->handle = gtk_drawing_area_new(); + + if (visual) + gtk_widget_pop_colormap(); + + if (!ih->handle) + return IUP_ERROR; + + scrolled_window = (GtkScrolledWindow*)gtk_scrolled_window_new(NULL, NULL); + if (!scrolled_window) + return IUP_ERROR; + + { + /* to avoid the "cannot add non scrollable widget" warning */ +#if GTK_CHECK_VERSION(2, 6, 0) + GLogFunc def_func = g_log_set_default_handler(gtkCanvasDummyLogFunc, NULL); +#endif + gtk_container_add((GtkContainer*)scrolled_window, ih->handle); +#if GTK_CHECK_VERSION(2, 6, 0) + g_log_set_default_handler(def_func, NULL); +#endif + } + + gtk_widget_show((GtkWidget*)scrolled_window); + + iupAttribSetStr(ih, "_IUP_EXTRAPARENT", (char*)scrolled_window); + + /* add to the parent, all GTK controls must call this. */ + iupgtkBaseAddToParent(ih); + + g_signal_connect(G_OBJECT(ih->handle), "focus-in-event", G_CALLBACK(iupgtkFocusInOutEvent), ih); + g_signal_connect(G_OBJECT(ih->handle), "focus-out-event", G_CALLBACK(iupgtkFocusInOutEvent), ih); + g_signal_connect(G_OBJECT(ih->handle), "key-press-event", G_CALLBACK(iupgtkKeyPressEvent), ih); + g_signal_connect(G_OBJECT(ih->handle), "key-release-event", G_CALLBACK(iupgtkKeyReleaseEvent), ih); + g_signal_connect(G_OBJECT(ih->handle), "enter-notify-event", G_CALLBACK(iupgtkEnterLeaveEvent), ih); + g_signal_connect(G_OBJECT(ih->handle), "leave-notify-event", G_CALLBACK(iupgtkEnterLeaveEvent), ih); + g_signal_connect(G_OBJECT(ih->handle), "show-help", G_CALLBACK(iupgtkShowHelp), ih); + + g_signal_connect(G_OBJECT(ih->handle), "expose-event", G_CALLBACK(gtkCanvasExposeEvent), ih); + g_signal_connect(G_OBJECT(ih->handle), "button-press-event", G_CALLBACK(gtkCanvasButtonEvent), ih); + g_signal_connect(G_OBJECT(ih->handle), "button-release-event",G_CALLBACK(gtkCanvasButtonEvent), ih); + g_signal_connect(G_OBJECT(ih->handle), "motion-notify-event",G_CALLBACK(iupgtkMotionNotifyEvent), ih); + g_signal_connect(G_OBJECT(ih->handle), "scroll-event",G_CALLBACK(gtkCanvasScrollEvent), ih); + +#if GTK_CHECK_VERSION(2, 8, 0) + g_signal_connect(G_OBJECT(gtk_scrolled_window_get_hscrollbar(scrolled_window)), "change-value",G_CALLBACK(gtkCanvasHChangeValue), ih); + g_signal_connect(G_OBJECT(gtk_scrolled_window_get_vscrollbar(scrolled_window)), "change-value",G_CALLBACK(gtkCanvasVChangeValue), ih); +#endif + + /* To receive mouse events on a drawing area, you will need to enable them. */ + gtk_widget_add_events(ih->handle, GDK_EXPOSURE_MASK| + GDK_POINTER_MOTION_MASK|GDK_BUTTON_MOTION_MASK| + GDK_BUTTON_PRESS_MASK|GDK_BUTTON_RELEASE_MASK| + GDK_KEY_PRESS_MASK|GDK_KEY_RELEASE_MASK| + GDK_ENTER_NOTIFY_MASK|GDK_LEAVE_NOTIFY_MASK| + GDK_FOCUS_CHANGE_MASK|GDK_STRUCTURE_MASK); + + /* To receive keyboard events, you will need to set the GTK_CAN_FOCUS flag on the drawing area. */ + if (ih->iclass->is_interactive) + { + if (iupAttribGetBoolean(ih, "CANFOCUS")) + GTK_WIDGET_FLAGS(ih->handle) |= GTK_CAN_FOCUS; + } + + if (iupAttribGetBoolean(ih, "BORDER")) + gtk_scrolled_window_set_shadow_type(scrolled_window, GTK_SHADOW_IN); + else + gtk_scrolled_window_set_shadow_type(scrolled_window, GTK_SHADOW_NONE); + + gtk_widget_realize((GtkWidget*)scrolled_window); + gtk_widget_realize(ih->handle); + + /* must be connected after realize or a RESIZE_CB will happen before MAP_CB + works only for the GtkDrawingArea. */ + g_signal_connect(G_OBJECT(ih->handle), "configure-event", G_CALLBACK(gtkCanvasConfigureEvent), ih); + + /* configure for DRAG&DROP */ + if (IupGetCallback(ih, "DROPFILES_CB")) + iupAttribSetStr(ih, "DRAGDROP", "YES"); + + /* update a mnemonic in a label if necessary */ + iupgtkUpdateMnemonic(ih); + + /* configure scrollbar */ + if (ih->data->sb) + { + GtkPolicyType hscrollbar_policy = GTK_POLICY_NEVER, vscrollbar_policy = GTK_POLICY_NEVER; + if (ih->data->sb & IUP_SB_HORIZ) + hscrollbar_policy = GTK_POLICY_AUTOMATIC; + if (ih->data->sb & IUP_SB_VERT) + vscrollbar_policy = GTK_POLICY_AUTOMATIC; + gtk_scrolled_window_set_policy(scrolled_window, hscrollbar_policy, vscrollbar_policy); + } + else + gtk_scrolled_window_set_policy(scrolled_window, GTK_POLICY_NEVER, GTK_POLICY_NEVER); + + /* force the update of BGCOLOR here, to let derived classes ignore it if ACTION is defined */ + gtkCanvasSetBgColorAttrib(ih, iupAttribGetStr(ih, "BGCOLOR")); + + return IUP_NOERROR; +} + +void iupdrvCanvasInitClass(Iclass* ic) +{ + /* Driver Dependent Class functions */ + ic->Map = gtkCanvasMapMethod; + + /* Driver Dependent Attribute functions */ + + /* Visual */ + iupClassRegisterAttribute(ic, "BGCOLOR", NULL, gtkCanvasSetBgColorAttrib, "255 255 255", NULL, IUPAF_DEFAULT); /* force new default value */ + + /* IupCanvas only */ + iupClassRegisterAttribute(ic, "DRAGDROP", NULL, iupgtkSetDragDropAttrib, NULL, NULL, IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "CURSOR", NULL, iupdrvBaseSetCursorAttrib, IUPAF_SAMEASSYSTEM, "ARROW", IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "DRAWSIZE", iupdrvBaseGetClientSizeAttrib, NULL, NULL, NULL, IUPAF_READONLY|IUPAF_NO_INHERIT); + + iupClassRegisterAttribute(ic, "DX", NULL, gtkCanvasSetDXAttrib, "0.1", NULL, IUPAF_NO_INHERIT); /* force new default value */ + iupClassRegisterAttribute(ic, "DY", NULL, gtkCanvasSetDYAttrib, "0.1", NULL, IUPAF_NO_INHERIT); /* force new default value */ + iupClassRegisterAttribute(ic, "POSX", iupCanvasGetPosXAttrib, gtkCanvasSetPosXAttrib, "0.0", NULL, IUPAF_NO_INHERIT); /* force new default value */ + iupClassRegisterAttribute(ic, "POSY", iupCanvasGetPosYAttrib, gtkCanvasSetPosYAttrib, "0.0", NULL, IUPAF_NO_INHERIT); /* force new default value */ + iupClassRegisterAttribute(ic, "XAUTOHIDE", NULL, gtkCanvasSetXAutoHideAttrib, "YES", NULL, IUPAF_DEFAULT); /* force new default value */ + iupClassRegisterAttribute(ic, "YAUTOHIDE", NULL, gtkCanvasSetYAutoHideAttrib, "YES", NULL, IUPAF_DEFAULT); /* force new default value */ + + iupClassRegisterAttribute(ic, "DRAWABLE", gtkCanvasGetDrawableAttrib, NULL, NULL, NULL, IUPAF_NO_STRING); + //iupClassRegisterAttribute(ic, "CD_GDK", NULL, NULL, IUPAF_SAMEASSYSTEM, "YES", IUPAF_NO_INHERIT); + + /* IupCanvas Windows or X only */ +#ifdef WIN32 + iupClassRegisterAttribute(ic, "HWND", iupgtkGetNativeWindowHandle, NULL, NULL, NULL, IUPAF_NO_STRING|IUPAF_NO_INHERIT); +#else + iupClassRegisterAttribute(ic, "XWINDOW", iupgtkGetNativeWindowHandle, NULL, NULL, NULL, IUPAF_NO_INHERIT|IUPAF_NO_STRING); + iupClassRegisterAttribute(ic, "XDISPLAY", (IattribGetFunc)iupdrvGetDisplay, NULL, NULL, NULL, IUPAF_READONLY|IUPAF_NOT_MAPPED|IUPAF_NO_INHERIT|IUPAF_NO_STRING); +#endif +} + diff --git a/iup/src/gtk/iupgtk_clipboard.c b/iup/src/gtk/iupgtk_clipboard.c new file mode 100755 index 0000000..f07a3e6 --- /dev/null +++ b/iup/src/gtk/iupgtk_clipboard.c @@ -0,0 +1,125 @@ +/** \file + * \brief Clipboard for the GTK Driver. + * + * See Copyright Notice in "iup.h" + */ + + +#include <stdio.h> +#include <stdlib.h> + +#include <gtk/gtk.h> + +#include "iup.h" + +#include "iup_object.h" +#include "iup_attrib.h" +#include "iup_str.h" +#include "iup_image.h" + +#include "iupgtk_drv.h" + + +static int gtkClipboardSetTextAttrib(Ihandle *ih, const char *value) +{ + GtkClipboard *clipboard = gtk_clipboard_get_for_display(gdk_display_get_default(), gdk_atom_intern("CLIPBOARD", FALSE)); + gtk_clipboard_set_text(clipboard, value, -1); + (void)ih; + return 0; +} + +static char* gtkClipboardGetTextAttrib(Ihandle *ih) +{ + GtkClipboard *clipboard = gtk_clipboard_get(gdk_atom_intern("CLIPBOARD", FALSE)); + (void)ih; + return iupgtkStrConvertFromUTF8(gtk_clipboard_wait_for_text(clipboard)); +} + +static int gtkClipboardSetImageAttrib(Ihandle *ih, const char *value) +{ +#if GTK_CHECK_VERSION(2, 6, 0) + GtkClipboard *clipboard = gtk_clipboard_get (gdk_atom_intern("CLIPBOARD", FALSE)); + GdkPixbuf *pixbuf = (GdkPixbuf*)iupImageGetImage(value, ih, 0); + if (pixbuf) + gtk_clipboard_set_image (clipboard, pixbuf); +#endif + return 0; +} + +static int gtkClipboardSetNativeImageAttrib(Ihandle *ih, const char *value) +{ +#if GTK_CHECK_VERSION(2, 6, 0) + GtkClipboard *clipboard; + (void)ih; + + if (!value) + return 0; + + clipboard = gtk_clipboard_get (gdk_atom_intern("CLIPBOARD", FALSE)); + + gtk_clipboard_set_image (clipboard, (GdkPixbuf*)value); +#endif + return 0; +} + +static char* gtkClipboardGetNativeImageAttrib(Ihandle *ih) +{ +#if GTK_CHECK_VERSION(2, 6, 0) + GtkClipboard *clipboard = gtk_clipboard_get (gdk_atom_intern("CLIPBOARD", FALSE)); + (void)ih; + return (char*)gtk_clipboard_wait_for_image (clipboard); +#else + return NULL; +#endif +} + +static char* gtkClipboardGetTextAvailableAttrib(Ihandle *ih) +{ + GtkClipboard *clipboard = gtk_clipboard_get (gdk_atom_intern("CLIPBOARD", FALSE)); + (void)ih; + if (gtk_clipboard_wait_is_text_available(clipboard)) + return "YES"; + else + return "NO"; +} + +static char* gtkClipboardGetImageAvailableAttrib(Ihandle *ih) +{ +#if GTK_CHECK_VERSION(2, 6, 0) + GtkClipboard *clipboard = gtk_clipboard_get (gdk_atom_intern("CLIPBOARD", FALSE)); + (void)ih; + if (gtk_clipboard_wait_is_image_available(clipboard)) + return "YES"; + else + return "NO"; +#else + return NULL; +#endif +} + +/******************************************************************************/ + +Ihandle* IupClipboard(void) +{ + return IupCreate("clipboard"); +} + +Iclass* iupClipboardGetClass(void) +{ + Iclass* ic = iupClassNew(NULL); + + ic->name = "clipboard"; + ic->format = NULL; /* no parameters */ + ic->nativetype = IUP_TYPECONTROL; + ic->childtype = IUP_CHILDNONE; + ic->is_interactive = 0; + + /* Attribute functions */ + iupClassRegisterAttribute(ic, "TEXT", gtkClipboardGetTextAttrib, gtkClipboardSetTextAttrib, NULL, NULL, IUPAF_NOT_MAPPED|IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "NATIVEIMAGE", gtkClipboardGetNativeImageAttrib, gtkClipboardSetNativeImageAttrib, NULL, NULL, IUPAF_NO_STRING|IUPAF_NOT_MAPPED|IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "IMAGE", NULL, gtkClipboardSetImageAttrib, NULL, NULL, IUPAF_WRITEONLY|IUPAF_NOT_MAPPED|IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "TEXTAVAILABLE", gtkClipboardGetTextAvailableAttrib, NULL, NULL, NULL, IUPAF_READONLY|IUPAF_NOT_MAPPED|IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "IMAGEAVAILABLE", gtkClipboardGetImageAvailableAttrib, NULL, NULL, NULL, IUPAF_READONLY|IUPAF_NOT_MAPPED|IUPAF_NO_INHERIT); + + return ic; +} diff --git a/iup/src/gtk/iupgtk_colordlg.c b/iup/src/gtk/iupgtk_colordlg.c new file mode 100755 index 0000000..9177f91 --- /dev/null +++ b/iup/src/gtk/iupgtk_colordlg.c @@ -0,0 +1,211 @@ +/** \file + * \brief IupColorDlg pre-defined dialog + * + * See Copyright Notice in "iup.h" + */ + +#include <gtk/gtk.h> + +#include <string.h> +#include <memory.h> +#include <stdio.h> + +#include "iup.h" + +#include "iup_object.h" +#include "iup_attrib.h" +#include "iup_str.h" +#include "iup_dialog.h" + +#include "iupgtk_drv.h" + + +static char* gtkColorDlgPaletteToString(const char* palette) +{ + char iup_str[20], *gtk_str, *palette_p; + char* str = iupStrGetMemory(300); + int off = 0, inc; + GdkColor color; + + gtk_str = iupStrDup(palette); + iupStrReplace(gtk_str, ':', 0); + + while (palette && *palette) + { + if (!gdk_color_parse (gtk_str, &color)) + return NULL; + + inc = sprintf(iup_str, "%d %d %d;", (int)iupCOLOR16TO8(color.red), (int)iupCOLOR16TO8(color.green), (int)iupCOLOR16TO8(color.blue)); + memcpy(str+off, iup_str, inc); + off += inc; + palette_p = strchr(palette, ':'); + if (palette_p) + { + palette_p++; + gtk_str += palette_p-palette; + } + palette = palette_p; + } + str[off-1] = 0; /* remove last separator */ + return str; +} + +static void gtkColorDlgGetPalette(Ihandle* ih, GtkColorSelection* colorsel) +{ + char *palette, *str; + + GtkSettings *settings = gtk_widget_get_settings(GTK_WIDGET(colorsel)); + g_object_get(settings, "gtk-color-palette", &palette, NULL); + + str = gtkColorDlgPaletteToString(palette); + if (str) iupAttribStoreStr(ih, "COLORTABLE", str); + g_free(palette); +} + +static char* gtkColorDlgStringToPalette(const char* str) +{ + char gtk_str[20]; + char* palette = iupStrGetMemory(200); + int off = 0; + unsigned char r, g, b; + + while (str && *str) + { + if (!iupStrToRGB(str, &r, &g, &b)) + return NULL; + + sprintf(gtk_str, "#%02X%02X%02X:", (int)r, (int)g, (int)b); + memcpy(palette+off, gtk_str, 8); + off += 8; + str = strchr(str, ';'); + if (str) str++; + } + palette[off-1] = 0; /* remove last separator */ + return palette; +} + +static void gtkColorDlgSetPalette(GtkColorSelection* colorsel, char* str) +{ + GtkSettings *settings = gtk_widget_get_settings(GTK_WIDGET(colorsel)); + gchar* palette = gtkColorDlgStringToPalette(str); + if (palette) + gtk_settings_set_string_property(settings, + "gtk-color-palette", + palette, + "gtk_color_selection_palette_to_string"); +} + +static int gtkColorDlgPopup(Ihandle* ih, int x, int y) +{ + InativeHandle* parent = iupDialogGetNativeParent(ih); + GtkColorSelectionDialog* dialog; + GtkColorSelection* colorsel; + GdkColor color; + char *value; + unsigned char r = 0, g = 0, b = 0, a = 255; + int response, ret; + + iupAttribSetInt(ih, "_IUPDLG_X", x); /* used in iupDialogUpdatePosition */ + iupAttribSetInt(ih, "_IUPDLG_Y", y); + + dialog = (GtkColorSelectionDialog*)gtk_color_selection_dialog_new(iupgtkStrConvertToUTF8(iupAttribGet(ih, "TITLE"))); + if (!dialog) + return IUP_ERROR; + + if (parent) + gtk_window_set_transient_for((GtkWindow*)dialog, (GtkWindow*)parent); + + ret = iupStrToRGBA(iupAttribGet(ih, "VALUE"), &r, &g, &b, &a); + + colorsel = (GtkColorSelection*)dialog->colorsel; + iupgdkColorSet(&color, r, g, b); + gtk_color_selection_set_current_color(colorsel, &color); + + value = iupAttribGetStr(ih, "ALPHA"); + if (value) + { + int alpha; + if (iupStrToInt(value, &alpha)) + { + if (alpha<0) alpha=0; + if (alpha>255) alpha=255; + gtk_color_selection_set_has_opacity_control(colorsel, TRUE); + gtk_color_selection_set_current_alpha(colorsel, iupCOLOR8TO16(alpha)); + } + } + else if (iupAttribGetBoolean(ih, "SHOWALPHA") || ret == 4) + { + gtk_color_selection_set_has_opacity_control(colorsel, TRUE); + gtk_color_selection_set_current_alpha(colorsel, iupCOLOR8TO16(a)); + } + else + gtk_color_selection_set_has_opacity_control(colorsel, FALSE); + + value = iupAttribGetStr(ih, "COLORTABLE"); + if (value) + { + gtk_color_selection_set_has_palette (colorsel, TRUE); + gtkColorDlgSetPalette(colorsel, value); + } + else if (iupAttribGetBoolean(ih, "SHOWCOLORTABLE")) + gtk_color_selection_set_has_palette (colorsel, TRUE); + else + gtk_color_selection_set_has_palette (colorsel, FALSE); + + if (IupGetCallback(ih, "HELP_CB")) + gtk_widget_show(dialog->help_button); + + /* initialize the widget */ + gtk_widget_realize(GTK_WIDGET(dialog)); + + ih->handle = GTK_WIDGET(dialog); + iupDialogUpdatePosition(ih); + ih->handle = NULL; + + do + { + response = gtk_dialog_run(GTK_DIALOG(dialog)); + + if (response == GTK_RESPONSE_HELP) + { + Icallback cb = IupGetCallback(ih, "HELP_CB"); + if (cb && cb(ih) == IUP_CLOSE) + response = GTK_RESPONSE_CANCEL; + } + } while (response == GTK_RESPONSE_HELP); + + if (response == GTK_RESPONSE_OK) + { + GdkColor color; + gtk_color_selection_get_current_color(colorsel, &color); + IupSetAttribute(ih, "STATUS", "1"); + + if (gtk_color_selection_get_has_opacity_control(colorsel)) + { + int alpha = gtk_color_selection_get_current_alpha(colorsel); + iupAttribSetInt(ih, "ALPHA", (int)iupCOLOR16TO8(alpha)); + iupAttribSetStrf(ih, "VALUE", "%d %d %d %d", (int)iupCOLOR16TO8(color.red), (int)iupCOLOR16TO8(color.green), (int)iupCOLOR16TO8(color.blue), (int)iupCOLOR16TO8(alpha)); + } + else + iupAttribSetStrf(ih, "VALUE", "%d %d %d", (int)iupCOLOR16TO8(color.red), (int)iupCOLOR16TO8(color.green), (int)iupCOLOR16TO8(color.blue)); + + if (gtk_color_selection_get_has_palette(colorsel)) + gtkColorDlgGetPalette(ih, colorsel); + } + else + { + iupAttribSetStr(ih, "ALPHA", NULL); + iupAttribSetStr(ih, "VALUE", NULL); + iupAttribSetStr(ih, "COLORTABLE", NULL); + iupAttribSetStr(ih, "STATUS", NULL); + } + + gtk_widget_destroy(GTK_WIDGET(dialog)); + + return IUP_NOERROR; +} + +void iupdrvColorDlgInitClass(Iclass* ic) +{ + ic->DlgPopup = gtkColorDlgPopup; +} diff --git a/iup/src/gtk/iupgtk_common.c b/iup/src/gtk/iupgtk_common.c new file mode 100755 index 0000000..40368f2 --- /dev/null +++ b/iup/src/gtk/iupgtk_common.c @@ -0,0 +1,830 @@ +/** \file + * \brief GTK Base Functions + * + * See Copyright Notice in "iup.h" + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <limits.h> + +#include <gtk/gtk.h> + +#include "iup.h" +#include "iupcbs.h" +#include "iupkey.h" + +#include "iup_object.h" +#include "iup_childtree.h" +#include "iup_key.h" +#include "iup_str.h" +#include "iup_class.h" +#include "iup_attrib.h" +#include "iup_focus.h" +#include "iup_key.h" +#include "iup_image.h" +#include "iup_drv.h" + +#include "iupgtk_drv.h" + + +/* WARNING: in GTK there are many controls that are not native windows, + so "->window" will NOT return a native window exclusive of that control, + in fact it can return a base native window shared by many controls. + IupCanvas is a special case that uses an exclusive native window. */ + +/* GTK only has abssolute positioning using a GtkFixed container, + so all elements returned by iupChildTreeGetNativeParentHandle should be a GtkFixed. + If not looks in the native parent. */ +static GtkFixed* gtkGetFixedParent(Ihandle* ih) +{ + GtkWidget* widget = iupChildTreeGetNativeParentHandle(ih); + while (widget && !GTK_IS_FIXED(widget)) + widget = gtk_widget_get_parent(widget); + return (GtkFixed*)widget; +} + +void iupgtkUpdateMnemonic(Ihandle* ih) +{ + GtkLabel* label = (GtkLabel*)iupAttribGet(ih, "_IUPGTK_LABELMNEMONIC"); + if (label) gtk_label_set_mnemonic_widget(label, ih->handle); +} + +void iupdrvActivate(Ihandle* ih) +{ + gtk_widget_activate(ih->handle); +} + +void iupdrvReparent(Ihandle* ih) +{ + GtkFixed* fixed = gtkGetFixedParent(ih); + GtkWidget* widget = (GtkWidget*)iupAttribGet(ih, "_IUP_EXTRAPARENT"); + if (!widget) widget = ih->handle; + gtk_widget_reparent(widget, (GtkWidget*)fixed); +} + +void iupgtkBaseAddToParent(Ihandle* ih) +{ + GtkFixed* fixed = gtkGetFixedParent(ih); + GtkWidget* widget = (GtkWidget*)iupAttribGet(ih, "_IUP_EXTRAPARENT"); + if (!widget) widget = ih->handle; + + gtk_fixed_put(fixed, widget, 0, 0); +} + +void iupdrvBaseLayoutUpdateMethod(Ihandle *ih) +{ + GtkFixed* fixed = gtkGetFixedParent(ih); + GtkWidget* widget = (GtkWidget*)iupAttribGet(ih, "_IUP_EXTRAPARENT"); + if (!widget) widget = ih->handle; + + gtk_fixed_move(fixed, widget, ih->x, ih->y); + gtk_widget_set_size_request(widget, ih->currentwidth, ih->currentheight); +} + +void iupdrvBaseUnMapMethod(Ihandle* ih) +{ + GtkWidget* widget = (GtkWidget*)iupAttribGet(ih, "_IUP_EXTRAPARENT"); + if (!widget) widget = ih->handle; + gtk_widget_unrealize(widget); + gtk_widget_destroy(widget); /* To match the call to gtk_*****_new */ +} + +void iupdrvDisplayUpdate(Ihandle *ih) +{ + /* Post a REDRAW */ + gtk_widget_queue_draw(ih->handle); +} + +void iupdrvDisplayRedraw(Ihandle *ih) +{ + GdkWindow* window = ih->handle->window; + /* Post a REDRAW */ + gtk_widget_queue_draw(ih->handle); + /* Force a REDRAW */ + if (window) + gdk_window_process_updates(window, FALSE); +} + +void iupdrvScreenToClient(Ihandle* ih, int *x, int *y) +{ + gint win_x = 0, win_y = 0; + GdkWindow* window = ih->handle->window; + if (window) + gdk_window_get_origin(window, &win_x, &win_y); + *x = *x - win_x; + *y = *y - win_y; +} + +gboolean iupgtkShowHelp(GtkWidget *widget, GtkWidgetHelpType *arg1, Ihandle *ih) +{ + Icallback cb; + (void)widget; + (void)arg1; + + cb = IupGetCallback(ih, "HELP_CB"); + if (cb && cb(ih) == IUP_CLOSE) + IupExitLoop(); + + return FALSE; +} + +gboolean iupgtkEnterLeaveEvent(GtkWidget *widget, GdkEventCrossing *evt, Ihandle *ih) +{ + Icallback cb = NULL; + (void)widget; + + if (evt->type == GDK_ENTER_NOTIFY) + cb = IupGetCallback(ih, "ENTERWINDOW_CB"); + else if (evt->type == GDK_LEAVE_NOTIFY) + cb = IupGetCallback(ih, "LEAVEWINDOW_CB"); + + if (cb) + cb(ih); + + return FALSE; +} + +int iupgtkSetMnemonicTitle(Ihandle* ih, GtkLabel* label, const char* value) +{ + char c = '_'; + char* str; + + if (!value) + value = ""; + + str = iupStrProcessMnemonic(value, &c, 1); /* replace & by c, the returned value of c is ignored in GTK */ + if (str != value) + { + gtk_label_set_text_with_mnemonic(label, iupgtkStrConvertToUTF8(str)); + free(str); + return 1; + } + else + { + if (iupAttribGetBoolean(ih, "MARKUP")) + gtk_label_set_markup(label, iupgtkStrConvertToUTF8(str)); + else + gtk_label_set_text(label, iupgtkStrConvertToUTF8(str)); + } + return 0; +} + +int iupdrvBaseSetZorderAttrib(Ihandle* ih, const char* value) +{ + if (iupdrvIsVisible(ih)) + { + GdkWindow* window = ih->handle->window; + if (iupStrEqualNoCase(value, "TOP")) + gdk_window_raise(window); + else + gdk_window_lower(window); + } + + return 0; +} + +void iupdrvSetVisible(Ihandle* ih, int visible) +{ + GtkWidget* container = (GtkWidget*)iupAttribGet(ih, "_IUP_EXTRAPARENT"); + if (visible) + { + if (container) gtk_widget_show(container); + gtk_widget_show(ih->handle); + } + else + { + if (container) gtk_widget_hide(container); + gtk_widget_hide(ih->handle); + } +} + +int iupdrvIsVisible(Ihandle* ih) +{ + if (GTK_WIDGET_VISIBLE(ih->handle)) + { + /* if marked as visible, since we use gtk_widget_hide and NOT gtk_widget_hide_all + must check its parents. */ + Ihandle* parent = ih->parent; + while (parent) + { + if (parent->iclass->nativetype != IUP_TYPEVOID) + { + if (!GTK_WIDGET_VISIBLE(parent->handle)) + return 0; + } + + parent = parent->parent; + } + return 1; + } + else + return 0; +} + +int iupdrvIsActive(Ihandle *ih) +{ + return (GTK_WIDGET_IS_SENSITIVE(ih->handle)); +} + +void iupdrvSetActive(Ihandle* ih, int enable) +{ + GtkWidget* container = (GtkWidget*)iupAttribGet(ih, "_IUP_EXTRAPARENT"); + if (container) gtk_widget_set_sensitive(container, enable); + gtk_widget_set_sensitive(ih->handle, enable); +} + +char* iupdrvBaseGetXAttrib(Ihandle *ih) +{ + GdkWindow* window = ih->handle->window; + GtkWidget* container = (GtkWidget*)iupAttribGet(ih, "_IUP_EXTRAPARENT"); + if (container) window = container->window; + + if (window) + { + char* str = iupStrGetMemory(20); + int x, y; + gdk_window_get_origin(window, &x, &y); + x += ih->handle->allocation.x; + sprintf(str, "%d", x); + return str; + } + else + return NULL; +} + +char* iupdrvBaseGetYAttrib(Ihandle *ih) +{ + GdkWindow* window = ih->handle->window; + GtkWidget* container = (GtkWidget*)iupAttribGet(ih, "_IUP_EXTRAPARENT"); + if (container) window = container->window; + + if (window) + { + char* str = iupStrGetMemory(20); + int x, y; + gdk_window_get_origin(window, &x, &y); + y += ih->handle->allocation.y; + sprintf(str, "%d", y); + return str; + } + else + return NULL; +} + +char* iupdrvBaseGetClientSizeAttrib(Ihandle *ih) +{ + char* str = iupStrGetMemory(20); + int w, h; + GdkWindow* window = ih->handle->window; + + if (window) + gdk_drawable_get_size(window, &w, &h); + else + return NULL; + + sprintf(str, "%dx%d", w, h); + return str; +} + +static GdkColor gtkDarkerColor(GdkColor *color) +{ + GdkColor dark_color = {0L,0,0,0}; + + dark_color.red = (color->red*9)/10; + dark_color.green = (color->green*9)/10; + dark_color.blue = (color->blue*9)/10; + + return dark_color; +} + +static guint16 gtkCROP16(int x) +{ + if (x > 65535) return 65535; + return (guint16)x; +} + +static GdkColor gtkLighterColor(GdkColor *color) +{ + GdkColor light_color = {0L,0,0,0}; + + light_color.red = gtkCROP16(((int)color->red*11)/10); + light_color.green = gtkCROP16(((int)color->green*11)/10); + light_color.blue = gtkCROP16(((int)color->blue*11)/10); + + return light_color; +} + +void iupgtkBaseSetBgColor(InativeHandle* handle, unsigned char r, unsigned char g, unsigned char b) +{ + GtkRcStyle *rc_style; + GdkColor color; + + iupgdkColorSet(&color, r, g, b); + + rc_style = gtk_widget_get_modifier_style(handle); + rc_style->base[GTK_STATE_NORMAL] = rc_style->bg[GTK_STATE_NORMAL] = rc_style->bg[GTK_STATE_INSENSITIVE] = color; + rc_style->bg[GTK_STATE_ACTIVE] = rc_style->base[GTK_STATE_ACTIVE] = gtkDarkerColor(&color); + rc_style->base[GTK_STATE_PRELIGHT] = rc_style->bg[GTK_STATE_PRELIGHT] = rc_style->base[GTK_STATE_INSENSITIVE] = gtkLighterColor(&color); + + rc_style->color_flags[GTK_STATE_NORMAL] |= GTK_RC_BASE | GTK_RC_BG; + rc_style->color_flags[GTK_STATE_ACTIVE] |= GTK_RC_BASE | GTK_RC_BG; + rc_style->color_flags[GTK_STATE_PRELIGHT] |= GTK_RC_BASE | GTK_RC_BG; + rc_style->color_flags[GTK_STATE_INSENSITIVE] |= GTK_RC_BASE | GTK_RC_BG; + + gtk_widget_modify_style(handle, rc_style); +} + +int iupdrvBaseSetBgColorAttrib(Ihandle* ih, const char* value) +{ + unsigned char r, g, b; + if (!iupStrToRGB(value, &r, &g, &b)) + return 0; + + iupgtkBaseSetBgColor(ih->handle, r, g, b); + + /* DO NOT NEED TO UPDATE GTK IMAGES SINCE THEY DO NOT DEPEND ON BGCOLOR */ + + return 1; +} + +void iupgtkBaseSetFgGdkColor(InativeHandle* handle, GdkColor *color) +{ + GtkRcStyle *rc_style; + + rc_style = gtk_widget_get_modifier_style(handle); + rc_style->fg[GTK_STATE_ACTIVE] = rc_style->fg[GTK_STATE_NORMAL] = rc_style->fg[GTK_STATE_PRELIGHT] = *color; + rc_style->text[GTK_STATE_ACTIVE] = rc_style->text[GTK_STATE_NORMAL] = rc_style->text[GTK_STATE_PRELIGHT] = *color; + rc_style->color_flags[GTK_STATE_NORMAL] |= GTK_RC_TEXT | GTK_RC_FG; + rc_style->color_flags[GTK_STATE_ACTIVE] |= GTK_RC_TEXT | GTK_RC_FG; + rc_style->color_flags[GTK_STATE_PRELIGHT] |= GTK_RC_TEXT | GTK_RC_FG; + + /* do not set at CHILD_CONTAINER */ + gtk_widget_modify_style(handle, rc_style); +} + +void iupgtkBaseSetFgColor(InativeHandle* handle, unsigned char r, unsigned char g, unsigned char b) +{ + GdkColor color; + iupgdkColorSet(&color, r, g, b); + iupgtkBaseSetFgGdkColor(handle, &color); +} + +int iupdrvBaseSetFgColorAttrib(Ihandle* ih, const char* value) +{ + unsigned char r, g, b; + if (!iupStrToRGB(value, &r, &g, &b)) + return 0; + + iupgtkBaseSetFgColor(ih->handle, r, g, b); + + return 1; +} + +static GdkCursor* gtkEmptyCursor(Ihandle* ih) +{ + /* creates an empty cursor */ + GdkColor cursor_color = {0L,0,0,0}; + char bitsnull[1] = {0x00}; + + GdkWindow* window = ih->handle->window; + GdkPixmap* pixmapnull = gdk_bitmap_create_from_data( + (GdkDrawable*)window, + bitsnull, + 1,1); + GdkCursor* cur = gdk_cursor_new_from_pixmap( + pixmapnull, + pixmapnull, + &cursor_color, + &cursor_color, + 0,0); + + g_object_unref(pixmapnull); + + return cur; +} + +static GdkCursor* gtkGetCursor(Ihandle* ih, const char* name) +{ + static struct { + const char* iupname; + int sysname; + } table[] = { + { "NONE", 0}, + { "NULL", 0}, + { "ARROW", GDK_LEFT_PTR}, + { "BUSY", GDK_WATCH}, + { "CROSS", GDK_CROSSHAIR}, + { "HAND", GDK_HAND2}, + { "HELP", GDK_QUESTION_ARROW}, + { "IUP", GDK_QUESTION_ARROW}, + { "MOVE", GDK_FLEUR}, + { "PEN", GDK_PENCIL}, + { "RESIZE_N", GDK_TOP_SIDE}, + { "RESIZE_S", GDK_BOTTOM_SIDE}, + { "RESIZE_NS", GDK_SB_V_DOUBLE_ARROW}, + { "RESIZE_W", GDK_LEFT_SIDE}, + { "RESIZE_E", GDK_RIGHT_SIDE}, + { "RESIZE_WE", GDK_SB_H_DOUBLE_ARROW}, + { "RESIZE_NE", GDK_TOP_RIGHT_CORNER}, + { "RESIZE_SE", GDK_BOTTOM_RIGHT_CORNER}, + { "RESIZE_NW", GDK_TOP_LEFT_CORNER}, + { "RESIZE_SW", GDK_BOTTOM_LEFT_CORNER}, + { "TEXT", GDK_XTERM}, + { "UPARROW", GDK_CENTER_PTR} + }; + + GdkCursor* cur; + char str[50]; + int i, count = sizeof(table)/sizeof(table[0]); + + /* check the cursor cache first (per control)*/ + sprintf(str, "_IUPGTK_CURSOR_%s", name); + cur = (GdkCursor*)iupAttribGet(ih, str); + if (cur) + return cur; + + /* check the pre-defined IUP names first */ + for (i = 0; i < count; i++) + { + if (iupStrEqualNoCase(name, table[i].iupname)) + { + if (table[i].sysname) + cur = gdk_cursor_new(table[i].sysname); + else + cur = gtkEmptyCursor(ih); + + break; + } + } + + if (i == count) + { + /* check for a name defined cursor */ + cur = iupImageGetCursor(name); + } + + /* save the cursor in cache */ + iupAttribSetStr(ih, str, (char*)cur); + + return cur; +} + +int iupdrvBaseSetCursorAttrib(Ihandle* ih, const char* value) +{ + GdkCursor* cur = gtkGetCursor(ih, value); + if (cur) + { + GdkWindow* window = ih->handle->window; + if (window) + gdk_window_set_cursor(window, cur); + return 1; + } + return 0; +} + +void iupgdkColorSet(GdkColor* color, unsigned char r, unsigned char g, unsigned char b) +{ + color->red = iupCOLOR8TO16(r); + color->green = iupCOLOR8TO16(g); + color->blue = iupCOLOR8TO16(b); + color->pixel = 0; +} + +static void gtkDragDataReceived(GtkWidget* w, GdkDragContext* context, int x, int y, + GtkSelectionData* seldata, guint info, guint time, Ihandle* ih) +{ + gchar **uris = NULL; + int i, count; + + IFnsiii cb = (IFnsiii)IupGetCallback(ih, "DROPFILES_CB"); + if (!cb) return; + +#if GTK_CHECK_VERSION(2, 6, 0) + uris = g_uri_list_extract_uris((char*)seldata->data); +#endif + + if (!uris) + return; + + count = 0; + while (uris[count]) + count++; + + for (i=0; i<count; i++) + { + char* filename = uris[i]; + if (iupStrEqualPartial(filename, "file://")) + { + filename += strlen("file://"); + if (filename[2] == ':') /* in Windows there is an extra '/' at the begining. */ + filename++; + } + if (cb(ih, filename, count-i-1, x, y) == IUP_IGNORE) + break; + } + + g_strfreev (uris); + (void)time; + (void)info; + (void)w; + (void)context; +} + +int iupgtkSetDragDropAttrib(Ihandle* ih, const char* value) +{ + if (iupStrBoolean(value)) + { + GtkTargetEntry dragtypes[] = { { "text/uri-list", 0, 0 } }; + gtk_drag_dest_set(ih->handle, GTK_DEST_DEFAULT_ALL, dragtypes, + sizeof(dragtypes) / sizeof(dragtypes[0]), GDK_ACTION_COPY); + g_signal_connect(G_OBJECT(ih->handle), "drag_data_received", G_CALLBACK(gtkDragDataReceived), ih); + } + else + { + gtk_drag_dest_unset(ih->handle); + } + return 1; +} + +int iupdrvGetScrollbarSize(void) +{ + static int size = 0; + + if (size == 0) + { + GtkRequisition requisition; + GtkWidget* win = gtk_window_new(GTK_WINDOW_TOPLEVEL); + GtkWidget* sb = gtk_vscrollbar_new(NULL); + gtk_container_add((GtkContainer*)win, sb); + gtk_widget_realize(win); + gtk_widget_size_request(sb, &requisition); + size = requisition.width; + gtk_widget_destroy(win); + } + + return size; +} + +void iupdrvDrawFocusRect(Ihandle* ih, void* _gc, int x, int y, int w, int h) +{ + GdkWindow* window = ih->handle->window; + GtkStyle *style = gtk_widget_get_style(ih->handle); + (void)_gc; + + gtk_paint_focus(style, window, GTK_WIDGET_STATE(ih->handle), NULL, ih->handle, NULL, x, y, w, h); +} + +void iupdrvBaseRegisterCommonAttrib(Iclass* ic) +{ +#ifdef WIN32 + iupClassRegisterAttribute(ic, "HFONT", iupgtkGetFontIdAttrib, NULL, NULL, NULL, IUPAF_NOT_MAPPED|IUPAF_NO_INHERIT|IUPAF_NO_STRING); +#else + iupClassRegisterAttribute(ic, "XFONTID", iupgtkGetFontIdAttrib, NULL, NULL, NULL, IUPAF_NOT_MAPPED|IUPAF_NO_INHERIT|IUPAF_NO_STRING); +#endif + iupClassRegisterAttribute(ic, "PANGOFONTDESC", iupgtkGetPangoFontDescAttrib, NULL, NULL, NULL, IUPAF_NOT_MAPPED|IUPAF_NO_INHERIT|IUPAF_NO_STRING); +} + +static int gtkStrIsAscii(const char* str) +{ + while(*str) + { + int c = *str; + if (c < 0) + return 0; + str++; + } + return 1; +} + +static char* gtkStrToUTF8(const char *str, const char* charset) +{ + return g_convert(str, -1, "UTF-8", charset, NULL, NULL, NULL); +} + +static char* gtkStrFromUTF8(const char *str, const char* charset) +{ + return g_convert(str, -1, charset, "UTF-8", NULL, NULL, NULL); +} + +static char* gktLastConvertUTF8 = NULL; + +void iupgtkReleaseConvertUTF8(void) +{ + if (gktLastConvertUTF8) + g_free(gktLastConvertUTF8); +} + +char* iupgtkStrConvertToUTF8(const char* str) /* From IUP to GTK */ +{ + if (!str || *str == 0) + return (char*)str; + + if (iupgtk_utf8autoconvert) /* this means str is in current locale */ + { + const char *charset = NULL; + if (g_get_charset(&charset)==TRUE) /* current locale is already UTF-8 */ + { + if (g_utf8_validate(str, -1, NULL)) + return (char*)str; + else + { + if (gktLastConvertUTF8) + g_free(gktLastConvertUTF8); + gktLastConvertUTF8 = gtkStrToUTF8(str, "ISO8859-1"); /* if string is not UTF-8, assume ISO8859-1 */ + if (!gktLastConvertUTF8) return (char*)str; + return gktLastConvertUTF8; + } + } + else + { + if (gtkStrIsAscii(str) || !charset) + return (char*)str; + else if (charset) + { + if (gktLastConvertUTF8) + g_free(gktLastConvertUTF8); + gktLastConvertUTF8 = gtkStrToUTF8(str, charset); + if (!gktLastConvertUTF8) return (char*)str; + return gktLastConvertUTF8; + } + } + } + return (char*)str; +} + +char* iupgtkStrConvertFromUTF8(const char* str) /* From GTK to IUP */ +{ + if (!str || *str == 0) + return (char*)str; + + if (iupgtk_utf8autoconvert) /* this means str is in current locale */ + { + const gchar *charset = NULL; + if (g_get_charset(&charset)==TRUE) /* current locale is already UTF-8 */ + { + if (g_utf8_validate(str, -1, NULL)) + return (char*)str; + else + { + if (gktLastConvertUTF8) + g_free(gktLastConvertUTF8); + gktLastConvertUTF8 = gtkStrFromUTF8(str, "ISO8859-1"); /* if string is not UTF-8, assume ISO8859-1 */ + if (!gktLastConvertUTF8) return (char*)str; + return gktLastConvertUTF8; + } + } + else + { + if (gtkStrIsAscii(str) || !charset) + return (char*)str; + else if (charset) + { + if (gktLastConvertUTF8) + g_free(gktLastConvertUTF8); + gktLastConvertUTF8 = gtkStrFromUTF8(str, charset); + if (!gktLastConvertUTF8) return (char*)str; + return gktLastConvertUTF8; + } + } + } + return (char*)str; +} + +static gboolean gtkGetFilenameCharset(const gchar **filename_charset) +{ + const gchar **charsets = NULL; + gboolean is_utf8 = FALSE; + +#if GTK_CHECK_VERSION(2, 6, 0) + is_utf8 = g_get_filename_charsets (&charsets); +#endif + + if (filename_charset && charsets) + *filename_charset = charsets[0]; + + return is_utf8; +} + +char* iupgtkStrConvertToFilename(const char* str) /* From IUP to Filename */ +{ + if (!str || *str == 0) + return (char*)str; + + if (iupgtk_utf8autoconvert) /* this means str is in current locale */ + return (char*)str; + else + { + const gchar *charset = NULL; + if (gtkGetFilenameCharset(&charset)==TRUE) /* current locale is already UTF-8 */ + { + if (g_utf8_validate(str, -1, NULL)) + return (char*)str; + else + { + if (gktLastConvertUTF8) + g_free(gktLastConvertUTF8); + gktLastConvertUTF8 = gtkStrFromUTF8(str, "ISO8859-1"); /* if string is not UTF-8, assume ISO8859-1 */ + if (!gktLastConvertUTF8) return (char*)str; + return gktLastConvertUTF8; + } + } + else + { + if (gtkStrIsAscii(str) || !charset) + return (char*)str; + else if (charset) + { + if (gktLastConvertUTF8) + g_free(gktLastConvertUTF8); + gktLastConvertUTF8 = gtkStrFromUTF8(str, charset); + if (!gktLastConvertUTF8) return (char*)str; + return gktLastConvertUTF8; + } + } + } + return (char*)str; +} + +char* iupgtkStrConvertFromFilename(const char* str) /* From Filename to IUP */ +{ + if (!str || *str == 0) + return (char*)str; + + if (iupgtk_utf8autoconvert) /* this means str is in current locale */ + return (char*)str; + else + { + const char *charset = NULL; + if (gtkGetFilenameCharset(&charset)==TRUE) /* current locale is already UTF-8 */ + { + if (g_utf8_validate(str, -1, NULL)) + return (char*)str; + else + { + if (gktLastConvertUTF8) + g_free(gktLastConvertUTF8); + gktLastConvertUTF8 = gtkStrToUTF8(str, "ISO8859-1"); /* if string is not UTF-8, assume ISO8859-1 */ + if (!gktLastConvertUTF8) return (char*)str; + return gktLastConvertUTF8; + } + } + else + { + if (gtkStrIsAscii(str) || !charset) + return (char*)str; + else if (charset) + { + if (gktLastConvertUTF8) + g_free(gktLastConvertUTF8); + gktLastConvertUTF8 = gtkStrToUTF8(str, charset); + if (!gktLastConvertUTF8) return (char*)str; + return gktLastConvertUTF8; + } + } + } + return (char*)str; +} + +gboolean iupgtkMotionNotifyEvent(GtkWidget *widget, GdkEventMotion *evt, Ihandle *ih) +{ + IFniis cb = (IFniis)IupGetCallback(ih,"MOTION_CB"); + if (cb) + { + char status[IUPKEY_STATUS_SIZE] = IUPKEY_STATUS_INIT; + iupgtkButtonKeySetStatus(evt->state, 0, status, 0); + cb(ih, (int)evt->x, (int)evt->y, status); + } + + (void)widget; + return FALSE; +} + +gboolean iupgtkButtonEvent(GtkWidget *widget, GdkEventButton *evt, Ihandle *ih) +{ + IFniiiis cb = (IFniiiis)IupGetCallback(ih,"BUTTON_CB"); + if (cb) + { + int doubleclick = 0, ret, press = 1; + int b = IUP_BUTTON1+(evt->button-1); + char status[IUPKEY_STATUS_SIZE] = IUPKEY_STATUS_INIT; + + if (evt->type == GDK_BUTTON_RELEASE) + press = 0; + + if (evt->type == GDK_2BUTTON_PRESS) + doubleclick = 1; + + iupgtkButtonKeySetStatus(evt->state, evt->button, status, doubleclick); + + ret = cb(ih, b, press, (int)evt->x, (int)evt->y, status); + if (ret==IUP_CLOSE) + IupExitLoop(); + else if (ret==IUP_IGNORE) + return TRUE; + } + + (void)widget; + return FALSE; +} diff --git a/iup/src/gtk/iupgtk_dialog.c b/iup/src/gtk/iupgtk_dialog.c new file mode 100755 index 0000000..46c0ce1 --- /dev/null +++ b/iup/src/gtk/iupgtk_dialog.c @@ -0,0 +1,1023 @@ +/** \file + * \brief IupDialog class + * + * See Copyright Notice in "iup.h" + */ + +#include <gtk/gtk.h> + +#ifdef HILDON +#include <hildon/hildon-program.h> +#endif + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <memory.h> +#include <stdarg.h> +#include <limits.h> +#include <time.h> + +#include "iup.h" +#include "iupcbs.h" + +#include "iup_class.h" +#include "iup_object.h" +#include "iup_layout.h" +#include "iup_dlglist.h" +#include "iup_attrib.h" +#include "iup_drv.h" +#include "iup_drvfont.h" +#include "iup_drvinfo.h" +#include "iup_focus.h" +#include "iup_str.h" +#define _IUPDLG_PRIVATE +#include "iup_dialog.h" +#include "iup_image.h" + +#include "iupgtk_drv.h" + + +static void gtkDialogSetMinMax(Ihandle* ih, int min_w, int min_h, int max_w, int max_h); + +/**************************************************************** + Utilities +****************************************************************/ + +int iupdrvDialogIsVisible(Ihandle* ih) +{ + return iupdrvIsVisible(ih); +} + +void iupdrvDialogUpdateSize(Ihandle* ih) +{ + int width, height; + gtk_window_get_size((GtkWindow*)ih->handle, &width, &height); + ih->currentwidth = width; + ih->currentheight = height; +} + +void iupdrvDialogGetSize(InativeHandle* handle, int *w, int *h) +{ + int width, height; + gtk_window_get_size((GtkWindow*)handle, &width, &height); + if (w) *w = width; + if (h) *h = height; +} + +void iupdrvDialogSetVisible(Ihandle* ih, int visible) +{ + if (visible) + gtk_widget_show(ih->handle); + else + gtk_widget_hide(ih->handle); +} + +void iupdrvDialogGetPosition(InativeHandle* handle, int *x, int *y) +{ + gtk_window_get_position((GtkWindow*)handle, x, y); +} + +void iupdrvDialogSetPosition(Ihandle *ih, int x, int y) +{ + gtk_window_move((GtkWindow*)ih->handle, x, y); +} + +static int gtkDialogGetMenuSize(Ihandle* ih) +{ +#ifdef HILDON + return 0; +#else + if (ih->data->menu) + return iupdrvMenuGetMenuBarSize(ih->data->menu); + else + return 0; +#endif +} + +static int gtkDialogGetWindowDecor(Ihandle* ih, int *win_border, int *win_caption) +{ + int x, y, frame_x, frame_y; + gdk_window_get_origin(ih->handle->window, &x, &y); + gdk_window_get_root_origin(ih->handle->window, &frame_x, &frame_y); + *win_border = x-frame_x; + *win_caption = y-frame_y-*win_border; + return 1; /* just for compatibility with iupdrvGetWindowDecor */ +} + +void iupdrvDialogGetDecoration(Ihandle* ih, int *border, int *caption, int *menu) +{ +#ifdef HILDON + /* In Hildon, borders have fixed dimensions, but are drawn as part + of the client area! */ + if (border) + *border = (iupAttribGetBoolean(ih, "HILDONWINDOW") && !iupAttribGetBoolean(ih, "FULLSCREEN")) ? 12 : 0; + if (caption) + *caption = 0; + if (menu) + *menu = 0; +#else + static int native_border = 0; + static int native_caption = 0; + + int has_caption = iupAttribGetBoolean(ih, "MAXBOX") || + iupAttribGetBoolean(ih, "MINBOX") || + iupAttribGetBoolean(ih, "MENUBOX") || + IupGetAttribute(ih, "TITLE"); /* must use IupGetAttribute to check from the native implementation */ + + int has_border = has_caption || + iupAttribGetBoolean(ih, "RESIZE") || + iupAttribGetBoolean(ih, "BORDER"); + + *menu = gtkDialogGetMenuSize(ih); + + if (ih->handle && iupdrvIsVisible(ih)) + { + int win_border, win_caption; + + if (gtkDialogGetWindowDecor(ih, &win_border, &win_caption)) + { +#ifdef WIN32 + if (*menu) + win_caption -= *menu; +#endif + + *border = 0; + if (has_border) + *border = win_border; + + *caption = 0; + if (has_caption) + *caption = win_caption; + + if (!native_border && *border) + native_border = win_border; + + if (!native_caption && *caption) + native_caption = win_caption; + } + } + + /* I could not set the size of the window including the decorations when the dialog is hidden */ + /* So we have to estimate the size of borders and caption when the dialog is hidden */ + + *border = 0; + if (has_border) + { + if (native_border) + *border = native_border; + else + *border = 5; + } + + *caption = 0; + if (has_caption) + { + if (native_caption) + *caption = native_caption; + else + *caption = 20; + } +#endif +} + +int iupdrvDialogSetPlacement(Ihandle* ih) +{ + char* placement; + int old_state = ih->data->show_state; + ih->data->show_state = IUP_SHOW; + + if (iupAttribGetBoolean(ih, "FULLSCREEN")) + { + gtk_window_fullscreen((GtkWindow*)ih->handle); + return 1; + } + + placement = iupAttribGet(ih, "PLACEMENT"); + if (!placement) + { + if (old_state == IUP_MAXIMIZE || old_state == IUP_MINIMIZE) + ih->data->show_state = IUP_RESTORE; + + gtk_window_unmaximize((GtkWindow*)ih->handle); + gtk_window_deiconify((GtkWindow*)ih->handle); + return 0; + } + + if (iupStrEqualNoCase(placement, "MINIMIZED")) + { + ih->data->show_state = IUP_MINIMIZE; + gtk_window_iconify((GtkWindow*)ih->handle); + } + else if (iupStrEqualNoCase(placement, "MAXIMIZED")) + { + ih->data->show_state = IUP_MAXIMIZE; + gtk_window_maximize((GtkWindow*)ih->handle); + } + else if (iupStrEqualNoCase(placement, "FULL")) + { + int width, height, x, y; + int border, caption, menu; + iupdrvDialogGetDecoration(ih, &border, &caption, &menu); + + /* position the decoration outside the screen */ + x = -(border); + y = -(border+caption+menu); + + /* the dialog client area will cover the task bar */ + iupdrvGetFullSize(&width, &height); + + height += menu; /* menu is inside the client area. */ + + /* set the new size and position */ + /* The resize evt will update the layout */ + gtk_window_move((GtkWindow*)ih->handle, x, y); + gtk_window_resize((GtkWindow*)ih->handle, width, height); + + if (old_state == IUP_MAXIMIZE || old_state == IUP_MINIMIZE) + ih->data->show_state = IUP_RESTORE; + } + + iupAttribSetStr(ih, "PLACEMENT", NULL); /* reset to NORMAL */ + + return 1; +} + + +/**************************************************************** + Callbacks and Events +****************************************************************/ + + +gboolean iupgtkDialogDeleteEvent(GtkWidget *widget, GdkEvent *evt, Ihandle *ih) +{ + Icallback cb; + (void)widget; + (void)evt; + + /* even when ACTIVE=NO the dialog gets this evt */ + if (!iupdrvIsActive(ih)) + return TRUE; + + cb = IupGetCallback(ih, "CLOSE_CB"); + if (cb) + { + int ret = cb(ih); + if (ret == IUP_IGNORE) + return TRUE; + if (ret == IUP_CLOSE) + IupExitLoop(); + } + + IupHide(ih); /* default: close the window */ + + return TRUE; /* do not propagate */ +} + +static gboolean gtkDialogConfigureEvent(GtkWidget *widget, GdkEventConfigure *evt, Ihandle *ih) +{ + int old_width, old_height, old_x, old_y; + gint x, y; + (void)widget; + +#ifndef HILDON + /* In hildon the menu is not a menubar */ + if (ih->data->menu && ih->data->menu->handle) + gtk_widget_set_size_request(ih->data->menu->handle, evt->width, -1); +#endif + + if (ih->data->ignore_resize) return FALSE; + + old_width = iupAttribGetInt(ih, "_IUPGTK_OLD_WIDTH"); + old_height = iupAttribGetInt(ih, "_IUPGTK_OLD_HEIGHT"); + + /* Check the size change, because configure is called also for position changes */ + if (evt->width != old_width || evt->height != old_height) + { + IFnii cb; + int border, caption, menu; + iupAttribSetInt(ih, "_IUPGTK_OLD_WIDTH", evt->width); + iupAttribSetInt(ih, "_IUPGTK_OLD_HEIGHT", evt->height); + + iupdrvDialogGetDecoration(ih, &border, &caption, &menu); + + /* update dialog size */ +#ifdef HILDON + /* In Hildon, the configure event contains the window size, not the client area size */ + ih->currentwidth = evt->width; + ih->currentheight = evt->height; +#else + ih->currentwidth = evt->width + 2*border; + ih->currentheight = evt->height + 2*border + caption; /* menu is inside the window client area */ +#endif + + cb = (IFnii)IupGetCallback(ih, "RESIZE_CB"); + if (!cb || cb(ih, evt->width, evt->height - menu)!=IUP_IGNORE) /* width and height here are for the client area */ + { + ih->data->ignore_resize = 1; + IupRefresh(ih); + ih->data->ignore_resize = 0; + } + } + + old_x = iupAttribGetInt(ih, "_IUPGTK_OLD_X"); + old_y = iupAttribGetInt(ih, "_IUPGTK_OLD_Y"); + gtk_window_get_position((GtkWindow*)ih->handle, &x, &y); /* ignore evt->x and evt->y because they are the clientpos and not X/Y */ + + /* Check the position change, because configure is called also for size changes */ + if (x != old_x || y != old_y) + { + IFnii cb; + iupAttribSetInt(ih, "_IUPGTK_OLD_X", x); + iupAttribSetInt(ih, "_IUPGTK_OLD_Y", y); + + cb = (IFnii)IupGetCallback(ih, "MOVE_CB"); + if (cb) + cb(ih, x, y); + } + + return FALSE; +} + +static gboolean gtkDialogWindowStateEvent(GtkWidget *widget, GdkEventWindowState *evt, Ihandle *ih) +{ + int state = -1; + (void)widget; + + if ((evt->changed_mask & GDK_WINDOW_STATE_MAXIMIZED) && /* if flag changed and */ + (evt->new_window_state & GDK_WINDOW_STATE_MAXIMIZED) && /* is now set */ + !(evt->new_window_state & GDK_WINDOW_STATE_WITHDRAWN)) /* is visible */ + state = IUP_MAXIMIZE; + else if ((evt->changed_mask & GDK_WINDOW_STATE_ICONIFIED) && + (evt->new_window_state & GDK_WINDOW_STATE_ICONIFIED) && + !(evt->new_window_state & GDK_WINDOW_STATE_WITHDRAWN)) + state = IUP_MINIMIZE; + else if ((evt->changed_mask & GDK_WINDOW_STATE_ICONIFIED) && + (evt->new_window_state & GDK_WINDOW_STATE_MAXIMIZED) && + !(evt->new_window_state & GDK_WINDOW_STATE_WITHDRAWN)) + state = IUP_MAXIMIZE; + else if (((evt->changed_mask & GDK_WINDOW_STATE_MAXIMIZED) && /* maximized changed */ + !(evt->new_window_state & GDK_WINDOW_STATE_MAXIMIZED) && /* not maximized */ + !(evt->new_window_state & GDK_WINDOW_STATE_WITHDRAWN) && /* is visible */ + !(evt->new_window_state & GDK_WINDOW_STATE_ICONIFIED)) /* not minimized */ + || /* OR */ + ((evt->changed_mask & GDK_WINDOW_STATE_ICONIFIED) && /* minimized changed */ + !(evt->new_window_state & GDK_WINDOW_STATE_ICONIFIED) && /* not minimized */ + !(evt->new_window_state & GDK_WINDOW_STATE_WITHDRAWN) && /* is visible */ + !(evt->new_window_state & GDK_WINDOW_STATE_MAXIMIZED))) /* not maximized */ + state = IUP_RESTORE; + + if (state < 0) + return FALSE; + + if (ih->data->show_state != state) + { + IFni cb; + ih->data->show_state = state; + + cb = (IFni)IupGetCallback(ih, "SHOW_CB"); + if (cb && cb(ih, state) == IUP_CLOSE) + IupExitLoop(); + } + + return FALSE; +} + +static gboolean gtkDialogChildDestroyEvent(GtkWidget *widget, Ihandle *ih) +{ + /* It seems that the documentation for this callback is not correct */ + /* The second parameter must be the user_data or it will fail. */ + (void)widget; + + /* If the IUP dialog was not destroyed, destroy it here. */ + if (iupObjectCheck(ih)) + IupDestroy(ih); + + /* this callback is usefull to destroy children dialogs when the parent is destroyed. */ + /* The application is responsable for destroying the children before this happen. */ + + return FALSE; +} + + +/**************************************************************** + Idialog Methods +****************************************************************/ + + +/* replace the common dialog SetChildrenPosition method because of + the menu that it is inside the dialog. */ +static void gtkDialogSetChildrenPositionMethod(Ihandle* ih, int x, int y) +{ + int menu_h = gtkDialogGetMenuSize(ih); + (void)x; + (void)y; + + /* Child coordinates are relative to client left-top corner. */ + iupBaseSetPosition(ih->firstchild, 0, menu_h); +} + +static void* gtkDialogGetInnerNativeContainerHandleMethod(Ihandle* ih, Ihandle* child) +{ + (void)child; + return (void*)gtk_bin_get_child((GtkBin*)ih->handle); +} + +static int gtkDialogMapMethod(Ihandle* ih) +{ + int decorations = 0; + int functions = 0; + InativeHandle* parent; + GtkWidget* fixed; + +#ifdef HILDON + if (iupAttribGetBoolean(ih, "HILDONWINDOW")) + { + HildonProgram *program = HILDON_PROGRAM(hildon_program_get_instance()); + ih->handle = hildon_window_new(); + if (ih->handle) + hildon_program_add_window(program, HILDON_WINDOW(ih->handle)); + } + else + { + iupAttribSetStr(ih, "DIALOGHINT", "YES"); /* otherwise not displayed correctly */ + ih->handle = gtk_window_new(GTK_WINDOW_TOPLEVEL); + } +#else + ih->handle = gtk_window_new(GTK_WINDOW_TOPLEVEL); +#endif + if (!ih->handle) + return IUP_ERROR; + + parent = iupDialogGetNativeParent(ih); + if (parent) + { + gtk_window_set_transient_for((GtkWindow*)ih->handle, (GtkWindow*)parent); + + /* manually remove child windows when parent is destroyed */ + g_signal_connect(G_OBJECT(parent), "destroy", G_CALLBACK(gtkDialogChildDestroyEvent), ih); + } + + g_signal_connect(G_OBJECT(ih->handle), "focus-in-event", G_CALLBACK(iupgtkFocusInOutEvent), ih); + g_signal_connect(G_OBJECT(ih->handle), "focus-out-event", G_CALLBACK(iupgtkFocusInOutEvent), ih); + g_signal_connect(G_OBJECT(ih->handle), "enter-notify-event", G_CALLBACK(iupgtkEnterLeaveEvent), ih); + g_signal_connect(G_OBJECT(ih->handle), "leave-notify-event", G_CALLBACK(iupgtkEnterLeaveEvent), ih); + g_signal_connect(G_OBJECT(ih->handle), "show-help", G_CALLBACK(iupgtkShowHelp), ih); + + /* The iupgtkKeyPressEvent of the control with the focus will propagate the key up to the dialog. */ + /* Inside iupgtkKeyPressEvent we test this to avoid duplicate calls. */ + g_signal_connect(G_OBJECT(ih->handle), "key-press-event", G_CALLBACK(iupgtkKeyPressEvent), ih); + + g_signal_connect(G_OBJECT(ih->handle), "configure-event", G_CALLBACK(gtkDialogConfigureEvent), ih); + g_signal_connect(G_OBJECT(ih->handle), "window-state-event", G_CALLBACK(gtkDialogWindowStateEvent), ih); + g_signal_connect(G_OBJECT(ih->handle), "delete-event", G_CALLBACK(iupgtkDialogDeleteEvent), ih); + + gtk_window_set_default_size((GtkWindow*)ih->handle, 100, 100); /* set this to avoid size calculation problems */ + + if (iupAttribGetBoolean(ih, "DIALOGHINT")) + gtk_window_set_type_hint(GTK_WINDOW(ih->handle), GDK_WINDOW_TYPE_HINT_DIALOG); + + /* the container that will receive the child element. */ + fixed = gtk_fixed_new(); + gtk_container_add((GtkContainer*)ih->handle, fixed); + gtk_widget_show(fixed); + + /* initialize the widget */ + gtk_widget_realize(ih->handle); + + if (iupAttribGetBoolean(ih, "DIALOGFRAME")) { + iupAttribSetStr(ih, "RESIZE", "NO"); + } + + if (!iupAttribGetBoolean(ih, "RESIZE")) { + iupAttribSetStr(ih, "MAXBOX", "NO"); /* Must also remove these, so RESIZE=NO can work */ + iupAttribSetStr(ih, "MINBOX", "NO"); + } + + if (IupGetAttribute(ih, "TITLE")) { /* must use IupGetAttribute to check from the native implementation */ + functions |= GDK_FUNC_MOVE; + decorations |= GDK_DECOR_TITLE; + } + + if (iupAttribGetBoolean(ih, "MENUBOX")) { + functions |= GDK_FUNC_CLOSE; + decorations |= GDK_DECOR_MENU; + } + + if (iupAttribGetBoolean(ih, "MINBOX")) { + functions |= GDK_FUNC_MINIMIZE; + decorations |= GDK_DECOR_MINIMIZE; + } + + if (iupAttribGetBoolean(ih, "MAXBOX")) { + functions |= GDK_FUNC_MAXIMIZE; + decorations |= GDK_DECOR_MAXIMIZE; + } + + if (iupAttribGetBoolean(ih, "RESIZE")) { + functions |= GDK_FUNC_RESIZE; + decorations |= GDK_DECOR_RESIZEH; + } + + if (iupAttribGetBoolean(ih, "BORDER")) + decorations |= GDK_DECOR_BORDER; + + if (decorations == 0) + gtk_window_set_decorated((GtkWindow*)ih->handle, FALSE); + else + { + GdkWindow* window = ih->handle->window; + if (window) + { + gdk_window_set_decorations(window, (GdkWMDecoration)decorations); + gdk_window_set_functions(window, (GdkWMFunction)functions); + } + } + + /* configure for DRAG&DROP */ + if (IupGetCallback(ih, "DROPFILES_CB")) + iupAttribSetStr(ih, "DRAGDROP", "YES"); + + { + /* Reset the DLGBGCOLOR global attribute + if it is the first time a dialog is created. + The value returned by gtk_style_new is not accurate. */ + GtkStyle* style = gtk_widget_get_style(ih->handle); + if (style && IupGetGlobal("_IUP_RESET_GLOBALCOLORS")) + { + iupgtkUpdateGlobalColors(style); + IupSetGlobal("_IUP_RESET_GLOBALCOLORS", NULL); + } + } + + /* configure the size range */ + gtkDialogSetMinMax(ih, 1, 1, 65535, 65535); /* MINSIZE and MAXSIZE default values */ + + /* Ignore VISIBLE before mapping */ + iupAttribSetStr(ih, "VISIBLE", NULL); + + return IUP_NOERROR; +} + +static void gtkDialogUnMapMethod(Ihandle* ih) +{ + GtkWidget* fixed; +#if GTK_CHECK_VERSION(2, 10, 0) + GtkStatusIcon* status_icon; +#endif + + if (ih->data->menu) + { + ih->data->menu->handle = NULL; /* the dialog will destroy the native menu */ + IupDestroy(ih->data->menu); + } + +#if GTK_CHECK_VERSION(2, 10, 0) + status_icon = (GtkStatusIcon*)iupAttribGet(ih, "_IUPDLG_STATUSICON"); + if (status_icon) + g_object_unref(status_icon); +#endif + + fixed = gtk_bin_get_child((GtkBin*)ih->handle); + gtk_widget_unrealize(fixed); + gtk_widget_destroy(fixed); + + gtk_widget_unrealize(ih->handle); /* To match the call to gtk_widget_realize */ + gtk_widget_destroy(ih->handle); /* To match the call to gtk_window_new */ +} + +static void gtkDialogLayoutUpdateMethod(Ihandle *ih) +{ + int border, caption, menu; + int width, height; + + if (ih->data->ignore_resize || + iupAttribGet(ih, "_IUPGTK_FS_STYLE")) + return; + + /* for dialogs the position is not updated here */ + ih->data->ignore_resize = 1; + + iupdrvDialogGetDecoration(ih, &border, &caption, &menu); + + /* set size excluding the border */ + width = ih->currentwidth - 2*border; + height = ih->currentheight - 2*border - caption; /* menu is inside the client area. */ + gtk_window_resize((GtkWindow*)ih->handle, width, height); + + if (!iupAttribGetBoolean(ih, "RESIZE")) + { + GdkGeometry geometry; + geometry.min_width = width; + geometry.min_height = height; + geometry.max_width = width; + geometry.max_height = height; + gtk_window_set_geometry_hints((GtkWindow*)ih->handle, ih->handle, + &geometry, (GdkWindowHints)(GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE)); + } + + ih->data->ignore_resize = 0; +} + + +/**************************************************************************** + Attributes +****************************************************************************/ + +static void gtkDialogSetMinMax(Ihandle* ih, int min_w, int min_h, int max_w, int max_h) +{ + /* The minmax size restricts the client area */ + GdkGeometry geometry; + int decorwidth = 0, decorheight = 0; + iupDialogGetDecorSize(ih, &decorwidth, &decorheight); + + geometry.min_width = 1; + if (min_w > decorwidth) + geometry.min_width = min_w-decorwidth; + + geometry.min_height = 1; + if (min_h > decorheight) + geometry.min_height = min_h-decorheight; + + geometry.max_width = 65535; + if (max_w > decorwidth && max_w > geometry.min_width) + geometry.max_width = max_w-decorwidth; + + geometry.max_height = 65535; + if (max_h > decorheight && max_w > geometry.min_height) + geometry.max_height = max_h-decorheight; + + gtk_window_set_geometry_hints((GtkWindow*)ih->handle, ih->handle, + &geometry, (GdkWindowHints)(GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE)); +} + +static int gtkDialogSetMinSizeAttrib(Ihandle* ih, const char* value) +{ + int min_w = 1, min_h = 1; /* MINSIZE default value */ + int max_w = 65535, max_h = 65535; /* MAXSIZE default value */ + iupStrToIntInt(value, &min_w, &min_h, 'x'); + + /* if MAXSIZE also set, must be also updated here */ + iupStrToIntInt(iupAttribGet(ih, "MAXSIZE"), &max_w, &max_h, 'x'); + + gtkDialogSetMinMax(ih, min_w, min_h, max_w, max_h); + return 1; +} + +static int gtkDialogSetMaxSizeAttrib(Ihandle* ih, const char* value) +{ + int min_w = 1, min_h = 1; /* MINSIZE default value */ + int max_w = 65535, max_h = 65535; /* MAXSIZE default value */ + iupStrToIntInt(value, &max_w, &max_h, 'x'); + + /* if MINSIZE also set, must be also updated here */ + iupStrToIntInt(iupAttribGet(ih, "MINSIZE"), &min_w, &min_h, 'x'); + + gtkDialogSetMinMax(ih, min_w, min_h, max_w, max_h); + return 1; +} + +static char* gtkDialogGetXAttrib(Ihandle *ih) +{ + char* str = iupStrGetMemory(20); + + gint x = 0; + gtk_window_get_position((GtkWindow*)ih->handle, &x, NULL); + + sprintf(str, "%d", x); + return str; +} + +static char* gtkDialogGetYAttrib(Ihandle *ih) +{ + char* str = iupStrGetMemory(20); + + gint y = 0; + gtk_window_get_position((GtkWindow*)ih->handle, NULL, &y); + + sprintf(str, "%d", y); + return str; +} + +static int gtkDialogSetTitleAttrib(Ihandle* ih, const char* value) +{ + if (!value) + value = ""; + gtk_window_set_title((GtkWindow*)ih->handle, iupgtkStrConvertToUTF8(value)); + return 0; +} + +static char* gtkDialogGetTitleAttrib(Ihandle* ih) +{ + const char* title = gtk_window_get_title((GtkWindow*)ih->handle); + + if (!title || title[0] == 0) + return NULL; + else + return iupStrGetMemoryCopy(iupgtkStrConvertFromUTF8(title)); +} + +static char* gtkDialogGetClientSizeAttrib(Ihandle *ih) +{ + char* str = iupStrGetMemory(20); + + int width, height; + gtk_window_get_size((GtkWindow*)ih->handle, &width, &height); + height -= gtkDialogGetMenuSize(ih); + + sprintf(str, "%dx%d", width, height); + return str; +} + +static int gtkDialogSetFullScreenAttrib(Ihandle* ih, const char* value) +{ + if (iupStrBoolean(value)) + { + if (!iupAttribGet(ih, "_IUPGTK_FS_STYLE")) + { + /* save the previous decoration attributes */ + /* during fullscreen these attributes can be consulted by the application */ + iupAttribStoreStr(ih, "_IUPGTK_FS_MAXBOX", iupAttribGet(ih, "MAXBOX")); + iupAttribStoreStr(ih, "_IUPGTK_FS_MINBOX", iupAttribGet(ih, "MINBOX")); + iupAttribStoreStr(ih, "_IUPGTK_FS_MENUBOX",iupAttribGet(ih, "MENUBOX")); + iupAttribStoreStr(ih, "_IUPGTK_FS_RESIZE", iupAttribGet(ih, "RESIZE")); + iupAttribStoreStr(ih, "_IUPGTK_FS_BORDER", iupAttribGet(ih, "BORDER")); + iupAttribStoreStr(ih, "_IUPGTK_FS_TITLE", IupGetAttribute(ih, "TITLE")); /* must use IupGetAttribute to check from the native implementation */ + + /* remove the decorations attributes */ + iupAttribSetStr(ih, "MAXBOX", "NO"); + iupAttribSetStr(ih, "MINBOX", "NO"); + iupAttribSetStr(ih, "MENUBOX", "NO"); + IupSetAttribute(ih, "TITLE", NULL); iupAttribSetStr(ih, "TITLE", NULL); /* remove from the hash table if we are during IupMap */ + iupAttribSetStr(ih, "RESIZE", "NO"); + iupAttribSetStr(ih, "BORDER", "NO"); + + if (iupdrvIsVisible(ih)) + gtk_window_fullscreen((GtkWindow*)ih->handle); + + iupAttribSetStr(ih, "_IUPGTK_FS_STYLE", "YES"); + } + } + else + { + char* fs_style = iupAttribGet(ih, "_IUPGTK_FS_STYLE"); + if (fs_style) + { + iupAttribSetStr(ih, "_IUPGTK_FS_STYLE", NULL); + + /* restore the decorations attributes */ + iupAttribStoreStr(ih, "MAXBOX", iupAttribGet(ih, "_IUPGTK_FS_MAXBOX")); + iupAttribStoreStr(ih, "MINBOX", iupAttribGet(ih, "_IUPGTK_FS_MINBOX")); + iupAttribStoreStr(ih, "MENUBOX",iupAttribGet(ih, "_IUPGTK_FS_MENUBOX")); + IupSetAttribute(ih, "TITLE", iupAttribGet(ih, "_IUPGTK_FS_TITLE")); /* must use IupSetAttribute to update the native implementation */ + iupAttribStoreStr(ih, "RESIZE", iupAttribGet(ih, "_IUPGTK_FS_RESIZE")); + iupAttribStoreStr(ih, "BORDER", iupAttribGet(ih, "_IUPGTK_FS_BORDER")); + + if (iupdrvIsVisible(ih)) + gtk_window_unfullscreen((GtkWindow*)ih->handle); + + /* remove auxiliar attributes */ + iupAttribSetStr(ih, "_IUPGTK_FS_MAXBOX", NULL); + iupAttribSetStr(ih, "_IUPGTK_FS_MINBOX", NULL); + iupAttribSetStr(ih, "_IUPGTK_FS_MENUBOX",NULL); + iupAttribSetStr(ih, "_IUPGTK_FS_RESIZE", NULL); + iupAttribSetStr(ih, "_IUPGTK_FS_BORDER", NULL); + iupAttribSetStr(ih, "_IUPGTK_FS_TITLE", NULL); + } + } + return 1; +} + +static int gtkDialogSetTopMostAttrib(Ihandle* ih, const char* value) +{ + if (iupStrBoolean(value)) + gtk_window_set_keep_above((GtkWindow*)ih->handle, TRUE); + else + gtk_window_set_keep_above((GtkWindow*)ih->handle, FALSE); + return 1; +} + +#if GTK_CHECK_VERSION(2, 12, 0) +static int gtkDialogSetOpacityAttrib(Ihandle *ih, const char *value) +{ + int opacity; + if (!iupStrToInt(value, &opacity)) + return 0; + + gtk_window_set_opacity((GtkWindow*)ih->handle, (double)opacity/255.0); + return 1; +} +#endif + +static int gtkDialogSetIconAttrib(Ihandle* ih, const char *value) +{ + if (!value) + gtk_window_set_icon((GtkWindow*)ih->handle, NULL); + else + { + GdkPixbuf* icon = (GdkPixbuf*)iupImageGetIcon(value); + if (icon) + gtk_window_set_icon((GtkWindow*)ih->handle, icon); + } + return 1; +} + +static int gtkDialogSetBackgroundAttrib(Ihandle* ih, const char* value) +{ + if (iupdrvBaseSetBgColorAttrib(ih, value)) + { + GtkStyle *style = gtk_widget_get_style(ih->handle); + if (style->bg_pixmap[GTK_STATE_NORMAL]) + { + style = gtk_style_copy(style); + style->bg_pixmap[GTK_STATE_NORMAL] = NULL; + gtk_widget_set_style(ih->handle, style); + } + return 1; + } + else + { + GdkPixbuf* pixbuf = iupImageGetImage(value, ih, 0); + if (pixbuf) + { + GdkPixmap* pixmap; + GtkStyle *style; + + gdk_pixbuf_render_pixmap_and_mask(pixbuf, &pixmap, NULL, 255); + + style = gtk_style_copy(gtk_widget_get_style(ih->handle)); + style->bg_pixmap[GTK_STATE_NORMAL] = pixmap; + gtk_widget_set_style(ih->handle, style); + + return 1; + } + } + + return 0; +} + +#if GTK_CHECK_VERSION(2, 10, 0) +static int gtkDialogTaskDoubleClick(int button) +{ + static int last_button = -1; + static GTimer* timer = NULL; + if (last_button == -1 || last_button != button) + { + last_button = button; + if (timer) + g_timer_destroy(timer); + timer = g_timer_new(); + return 0; + } + else + { + double seconds; + + if (!timer) /* just in case */ + return 0; + + seconds = g_timer_elapsed(timer, NULL); + if (seconds < 0.4) + { + /* reset state */ + g_timer_destroy(timer); + timer = NULL; + last_button = -1; + return 1; + } + else + { + g_timer_reset(timer); + return 0; + } + } +} + +static void gtkDialogTaskAction(GtkStatusIcon *status_icon, Ihandle *ih) +{ + /* from GTK source code it is called only when button==1 and pressed==1 */ + int button = 1; + int pressed = 1; + int dclick = gtkDialogTaskDoubleClick(button); + IFniii cb = (IFniii)IupGetCallback(ih, "TRAYCLICK_CB"); + if (cb && cb(ih, button, pressed, dclick) == IUP_CLOSE) + IupExitLoop(); + (void)status_icon; +} + +static void gtkDialogTaskPopupMenu(GtkStatusIcon *status_icon, guint gbutton, guint activate_time, Ihandle *ih) +{ + /* from GTK source code it is called only when button==3 and pressed==1 */ + int button = 3; + int pressed = 1; + int dclick = gtkDialogTaskDoubleClick(button); + IFniii cb = (IFniii)IupGetCallback(ih, "TRAYCLICK_CB"); + if (cb && cb(ih, button, pressed, dclick) == IUP_CLOSE) + IupExitLoop(); + (void)activate_time; + (void)gbutton; + (void)status_icon; +} + +static GtkStatusIcon* gtkDialogGetStatusIcon(Ihandle *ih) +{ + GtkStatusIcon* status_icon = (GtkStatusIcon*)iupAttribGet(ih, "_IUPDLG_STATUSICON"); + if (!status_icon) + { + status_icon = gtk_status_icon_new(); + + g_signal_connect(G_OBJECT(status_icon), "activate", G_CALLBACK(gtkDialogTaskAction), ih); + g_signal_connect(G_OBJECT(status_icon), "popup-menu", G_CALLBACK(gtkDialogTaskPopupMenu), ih); + + iupAttribSetStr(ih, "_IUPDLG_STATUSICON", (char*)status_icon); + } + return status_icon; +} + +static int gtkDialogSetTrayAttrib(Ihandle *ih, const char *value) +{ + GtkStatusIcon* status_icon = gtkDialogGetStatusIcon(ih); + gtk_status_icon_set_visible(status_icon, iupStrBoolean(value)); + return 1; +} + +static int gtkDialogSetTrayTipAttrib(Ihandle *ih, const char *value) +{ + GtkStatusIcon* status_icon = gtkDialogGetStatusIcon(ih); +#if GTK_CHECK_VERSION(2, 16, 0) + if (value) + { + gtk_status_icon_set_has_tooltip(status_icon, TRUE); + gtk_status_icon_set_tooltip_text(status_icon, value); + } + else + gtk_status_icon_set_has_tooltip(status_icon, FALSE); +#else + gtk_status_icon_set_tooltip(status_icon, value); +#endif + return 1; +} + +static int gtkDialogSetTrayImageAttrib(Ihandle *ih, const char *value) +{ + GtkStatusIcon* status_icon = gtkDialogGetStatusIcon(ih); + GdkPixbuf* icon = (GdkPixbuf*)iupImageGetIcon(value); + gtk_status_icon_set_from_pixbuf(status_icon, icon); + return 1; +} +#endif /* GTK_CHECK_VERSION(2, 10, 0) */ + +void iupdrvDialogInitClass(Iclass* ic) +{ + /* Driver Dependent Class methods */ + ic->Map = gtkDialogMapMethod; + ic->UnMap = gtkDialogUnMapMethod; + ic->LayoutUpdate = gtkDialogLayoutUpdateMethod; + ic->GetInnerNativeContainerHandle = gtkDialogGetInnerNativeContainerHandleMethod; + ic->SetChildrenPosition = gtkDialogSetChildrenPositionMethod; + + /* Callback Windows and GTK Only */ + iupClassRegisterCallback(ic, "TRAYCLICK_CB", "iii"); + + /* Driver Dependent Attribute functions */ + +#ifdef WIN32 + iupClassRegisterAttribute(ic, "HWND", iupgtkGetNativeWindowHandle, NULL, NULL, NULL, IUPAF_NO_STRING|IUPAF_NO_INHERIT); +#else + iupClassRegisterAttribute(ic, "XWINDOW", iupgtkGetNativeWindowHandle, NULL, NULL, NULL, IUPAF_NO_INHERIT|IUPAF_NO_STRING); +#endif + + /* Visual */ + iupClassRegisterAttribute(ic, "BGCOLOR", NULL, iupdrvBaseSetBgColorAttrib, "DLGBGCOLOR", NULL, IUPAF_DEFAULT); /* force new default value */ + + /* Overwrite Visual */ + iupClassRegisterAttribute(ic, "X", gtkDialogGetXAttrib, NULL, NULL, NULL, IUPAF_NO_DEFAULTVALUE|IUPAF_READONLY|IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "Y", gtkDialogGetYAttrib, NULL, NULL, NULL, IUPAF_NO_DEFAULTVALUE|IUPAF_READONLY|IUPAF_NO_INHERIT); + + /* Base Container */ + iupClassRegisterAttribute(ic, "CLIENTSIZE", gtkDialogGetClientSizeAttrib, NULL, NULL, NULL, IUPAF_NO_DEFAULTVALUE|IUPAF_READONLY|IUPAF_NO_INHERIT); + + /* Special */ + iupClassRegisterAttribute(ic, "TITLE", gtkDialogGetTitleAttrib, gtkDialogSetTitleAttrib, NULL, NULL, IUPAF_NO_DEFAULTVALUE|IUPAF_NO_INHERIT); + + /* IupDialog only */ + iupClassRegisterAttribute(ic, "BACKGROUND", NULL, gtkDialogSetBackgroundAttrib, IUPAF_SAMEASSYSTEM, "DLGBGCOLOR", IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "ICON", NULL, gtkDialogSetIconAttrib, NULL, NULL, IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "FULLSCREEN", NULL, gtkDialogSetFullScreenAttrib, NULL, NULL, IUPAF_WRITEONLY|IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "MINSIZE", NULL, gtkDialogSetMinSizeAttrib, IUPAF_SAMEASSYSTEM, "1x1", IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "MAXSIZE", NULL, gtkDialogSetMaxSizeAttrib, IUPAF_SAMEASSYSTEM, "65535x65535", IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "SAVEUNDER", NULL, NULL, NULL, NULL, IUPAF_READONLY|IUPAF_NO_INHERIT); /* saveunder not supported in GTK */ + + /* IupDialog Windows and GTK Only */ + iupClassRegisterAttribute(ic, "TOPMOST", NULL, gtkDialogSetTopMostAttrib, NULL, NULL, IUPAF_WRITEONLY|IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "DRAGDROP", NULL, iupgtkSetDragDropAttrib, NULL, NULL, IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "DIALOGHINT", NULL, NULL, NULL, NULL, IUPAF_NO_INHERIT); +#if GTK_CHECK_VERSION(2, 12, 0) + iupClassRegisterAttribute(ic, "OPACITY", NULL, gtkDialogSetOpacityAttrib, NULL, NULL, IUPAF_NO_INHERIT); +#endif +#if GTK_CHECK_VERSION(2, 10, 0) + iupClassRegisterAttribute(ic, "TRAY", NULL, gtkDialogSetTrayAttrib, NULL, NULL, IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "TRAYIMAGE", NULL, gtkDialogSetTrayImageAttrib, NULL, NULL, IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "TRAYTIP", NULL, gtkDialogSetTrayTipAttrib, NULL, NULL, IUPAF_NO_INHERIT); +#endif +} diff --git a/iup/src/gtk/iupgtk_drv.h b/iup/src/gtk/iupgtk_drv.h new file mode 100755 index 0000000..ade2a4a --- /dev/null +++ b/iup/src/gtk/iupgtk_drv.h @@ -0,0 +1,82 @@ +/** \file + * \brief GTK Driver + * + * See Copyright Notice in "iup.h" + */ + +#ifndef __IUPGTK_DRV_H +#define __IUPGTK_DRV_H + +#ifdef __cplusplus +extern "C" { +#endif + + +/* global variables, declared in iupgtk_globalattrib.c */ +extern int iupgtk_utf8autoconvert; + + +/* common */ +gboolean iupgtkEnterLeaveEvent(GtkWidget *widget, GdkEventCrossing *evt, Ihandle* ih); +gboolean iupgtkShowHelp(GtkWidget *widget, GtkWidgetHelpType *arg1, Ihandle* ih); +GtkFixed* iupgtkBaseGetFixed(Ihandle* ih); +void iupgtkBaseAddToParent(Ihandle* ih); +void iupgdkColorSet(GdkColor* color, unsigned char r, unsigned char g, unsigned char b); +int iupgtkSetDragDropAttrib(Ihandle* ih, const char* value); +int iupgtkSetMnemonicTitle(Ihandle* ih, GtkLabel* label, const char* value); +char* iupgtkStrConvertToUTF8(const char* str); +char* iupgtkStrConvertFromUTF8(const char* str); +void iupgtkReleaseConvertUTF8(void); +char* iupgtkStrConvertFromFilename(const char* str); +char* iupgtkStrConvertToFilename(const char* str); +void iupgtkUpdateMnemonic(Ihandle* ih); +gboolean iupgtkMotionNotifyEvent(GtkWidget *widget, GdkEventMotion *evt, Ihandle *ih); +gboolean iupgtkButtonEvent(GtkWidget *widget, GdkEventButton *evt, Ihandle *ih); +void iupgtkBaseSetBgColor(InativeHandle* handle, unsigned char r, unsigned char g, unsigned char b); +void iupgtkBaseSetFgColor(InativeHandle* handle, unsigned char r, unsigned char g, unsigned char b); +void iupgtkBaseSetFgGdkColor(InativeHandle* handle, GdkColor *color); + + +/* focus */ +gboolean iupgtkFocusInOutEvent(GtkWidget *widget, GdkEventFocus *evt, Ihandle* ih); + + +/* key */ +gboolean iupgtkKeyPressEvent(GtkWidget *widget, GdkEventKey *evt, Ihandle* ih); +gboolean iupgtkKeyReleaseEvent(GtkWidget *widget, GdkEventKey *evt, Ihandle* ih); +void iupgtkButtonKeySetStatus(guint state, unsigned int but, char* status, int doubleclick); +void iupgtkKeyEncode(int key, guint *keyval, guint *state); + + +/* font */ +char* iupgtkGetPangoFontDescAttrib(Ihandle *ih); +char* iupgtkGetFontIdAttrib(Ihandle *ih); +PangoFontDescription* iupgtkGetPangoFontDesc(const char* value); +char* iupgtkFindPangoFontDesc(PangoFontDescription* fontdesc); +void iupgtkFontUpdatePangoLayout(Ihandle* ih, PangoLayout* layout); +void iupgtkFontUpdateObjectPangoLayout(Ihandle* ih, gpointer object); + +/* There are PANGO_SCALE Pango units in one device unit. + For an output backend where a device unit is a pixel, + a size value of 10 * PANGO_SCALE gives 10 pixels. */ +#define IUPGTK_PANGOUNITS2PIXELS(_x) (((_x) + PANGO_SCALE/2) / PANGO_SCALE) +#define IUPGTK_PIXELS2PANGOUNITS(_x) ((_x) * PANGO_SCALE) + + +/* open */ +char* iupgtkGetNativeWindowHandle(Ihandle* ih); +void iupgtkPushVisualAndColormap(void* visual, void* colormap); +void* iupgtkGetNativeGraphicsContext(GtkWidget* widget); +void iupgtkReleaseNativeGraphicsContext(GtkWidget* widget, void* gc); +void iupgtkUpdateGlobalColors(GtkStyle* style); + + +/* dialog */ +gboolean iupgtkDialogDeleteEvent(GtkWidget *widget, GdkEvent *evt, Ihandle *ih); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/iup/src/gtk/iupgtk_filedlg.c b/iup/src/gtk/iupgtk_filedlg.c new file mode 100755 index 0000000..5426910 --- /dev/null +++ b/iup/src/gtk/iupgtk_filedlg.c @@ -0,0 +1,536 @@ +/** \file + * \brief IupFileDlg pre-defined dialog + * + * See Copyright Notice in "iup.h" + */ + +#include <gtk/gtk.h> + +#include <stdlib.h> +#include <string.h> +#include <memory.h> +#include <stdio.h> + +#include "iup.h" +#include "iupcbs.h" + +#include "iup_object.h" +#include "iup_attrib.h" +#include "iup_str.h" +#include "iup_drvinfo.h" +#include "iup_dialog.h" +#include "iup_strmessage.h" +#include "iup_array.h" +#include "iup_drvinfo.h" + +#include "iupgtk_drv.h" + + +static void iupStrRemoveChar(char* str, char c) +{ + char* p = str; + while (*str) + { + if (*str != c) + { + *p = *str; + p++; + } + + str++; + } + *p = 0; +} + +static void gtkFileDlgGetNextFilter(char** str, char** name, char** pattern) +{ + int len; + *name = *str; + + len = strlen(*name); + *pattern = (*name)+len+1; + + len = strlen(*pattern); + *str = (*pattern)+len+1; + + iupStrReplace(*pattern, ';', 0); /* remove other patterns */ +} + +static void gtkFileDlgGetMultipleFiles(Ihandle* ih, GSList* list) +{ + int len, cur_len, dir_len = -1; + char *filename, *all_names; + Iarray* names_array = iupArrayCreate(1024, 1); /* just set an initial size, but count is 0 */ + + while (list) + { + filename = (char*)list->data; + len = strlen(filename); + + if (dir_len == -1) + { + dir_len = len; + + while (dir_len && (filename[dir_len] != '/' && filename[dir_len] != '\\')) + dir_len--; + + cur_len = iupArrayCount(names_array); + all_names = iupArrayAdd(names_array, dir_len+1); + memcpy(all_names+cur_len, filename, dir_len); + all_names[cur_len+dir_len] = '|'; + + dir_len++; /* skip separator */ + } + len -= dir_len; /* remove directory */ + + cur_len = iupArrayCount(names_array); + all_names = iupArrayAdd(names_array, len+1); + memcpy(all_names+cur_len, filename+dir_len, len); + all_names[cur_len+len] = '|'; + + g_free(filename); + list = list->next; + } + + cur_len = iupArrayCount(names_array); + all_names = iupArrayInc(names_array); + all_names[cur_len+1] = 0; + + iupAttribStoreStr(ih, "VALUE", iupgtkStrConvertFromFilename(all_names)); + + iupArrayDestroy(names_array); +} + +#ifdef WIN32 +#include <gdk/gdkwin32.h> +#else +#include <gdk/gdkx.h> +#endif + +static void gtkFileDlgUpdatePreviewGLCanvas(Ihandle* ih) +{ + Ihandle* glcanvas = IupGetAttributeHandle(ih, "PREVIEWGLCANVAS"); + if (glcanvas) + { +#ifdef WIN32 + iupAttribSetStr(glcanvas, "HWND", iupAttribGet(ih, "HWND")); +#else + iupAttribSetStr(glcanvas, "XWINDOW", iupAttribGet(ih, "XWINDOW")); +#endif + glcanvas->iclass->Map(glcanvas); + } +} + +static void gtkFileDlgPreviewRealize(GtkWidget *widget, Ihandle *ih) +{ + iupAttribSetStr(ih, "PREVIEWDC", iupgtkGetNativeGraphicsContext(widget)); + iupAttribSetStr(ih, "WID", (char*)widget); + +#ifdef WIN32 + iupAttribSetStr(ih, "HWND", (char*)GDK_WINDOW_HWND(widget->window)); +#else + iupAttribSetStr(ih, "XWINDOW", (char*)GDK_WINDOW_XID(widget->window)); + iupAttribSetStr(ih, "XDISPLAY", (char*)iupdrvGetDisplay()); +#endif + gtkFileDlgUpdatePreviewGLCanvas(ih); +} + +static void gtkFileDlgRealize(GtkWidget *widget, Ihandle *ih) +{ + IFnss cb = (IFnss)IupGetCallback(ih, "FILE_CB"); + cb(ih, NULL, "INIT"); + + (void)widget; +} + +static gboolean gtkFileDlgPreviewConfigureEvent(GtkWidget *widget, GdkEventConfigure *evt, Ihandle *ih) +{ + iupAttribSetInt(ih, "PREVIEWWIDTH", evt->width); + iupAttribSetInt(ih, "PREVIEWHEIGHT", evt->height); + + (void)widget; + return FALSE; +} + +static gboolean gtkFileDlgPreviewExposeEvent(GtkWidget *widget, GdkEventExpose *evt, Ihandle *ih) +{ + GtkFileChooser *file_chooser = (GtkFileChooser*)iupAttribGet(ih, "_IUPDLG_FILE_CHOOSER"); + char *filename = gtk_file_chooser_get_preview_filename(file_chooser); + + IFnss cb = (IFnss)IupGetCallback(ih, "FILE_CB"); + if (iupdrvIsFile(filename)) + cb(ih, iupgtkStrConvertFromFilename(filename), "PAINT"); + else + cb(ih, NULL, "PAINT"); + + g_free (filename); + + (void)evt; + (void)widget; + return TRUE; /* stop other handlers */ +} + +static void gtkFileDlgUpdatePreview(GtkFileChooser *file_chooser, Ihandle* ih) +{ + char *filename = gtk_file_chooser_get_preview_filename(file_chooser); + + if (iupdrvIsFile(filename)) + { + IFnss cb = (IFnss)IupGetCallback(ih, "FILE_CB"); + cb(ih, iupgtkStrConvertFromFilename(filename), "SELECT"); + } + + g_free (filename); + + gtk_file_chooser_set_preview_widget_active(file_chooser, TRUE); +} + +static int gtkFileDlgPopup(Ihandle* ih, int x, int y) +{ + InativeHandle* parent = iupDialogGetNativeParent(ih); + GtkWidget* dialog; + GtkWidget* preview_canvas = NULL; + GtkFileChooserAction action; + IFnss file_cb; + char* value; + int response, filter_count = 0; + + iupAttribSetInt(ih, "_IUPDLG_X", x); /* used in iupDialogUpdatePosition */ + iupAttribSetInt(ih, "_IUPDLG_Y", y); + + value = iupAttribGetStr(ih, "DIALOGTYPE"); + if (iupStrEqualNoCase(value, "SAVE")) + action = GTK_FILE_CHOOSER_ACTION_SAVE; + else if (iupStrEqualNoCase(value, "DIR")) + action = GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER; + else + action = GTK_FILE_CHOOSER_ACTION_OPEN; + + value = iupAttribGet(ih, "TITLE"); + if (!value) + { + GtkStockItem item; + + if (action == GTK_FILE_CHOOSER_ACTION_SAVE) + value = GTK_STOCK_SAVE_AS; + else + value = GTK_STOCK_OPEN; + + gtk_stock_lookup(value, &item); + value = item.label; + + iupAttribStoreStr(ih, "TITLE", iupgtkStrConvertFromUTF8(value)); + value = iupAttribGet(ih, "TITLE"); + iupStrRemoveChar(value, '_'); + } + + dialog = gtk_file_chooser_dialog_new(iupgtkStrConvertToUTF8(value), (GtkWindow*)parent, action, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + NULL); + if (!dialog) + return IUP_ERROR; + + if (action == GTK_FILE_CHOOSER_ACTION_SAVE) + gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_SAVE, GTK_RESPONSE_OK); + else if (action == GTK_FILE_CHOOSER_ACTION_OPEN) + gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_OPEN, GTK_RESPONSE_OK); + else + gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_OK, GTK_RESPONSE_OK); + + if (IupGetCallback(ih, "HELP_CB")) + gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_HELP, GTK_RESPONSE_HELP); + +#if GTK_CHECK_VERSION(2, 6, 0) + if (iupAttribGetBoolean(ih, "SHOWHIDDEN")) + gtk_file_chooser_set_show_hidden(GTK_FILE_CHOOSER(dialog), TRUE); +#endif + + if (iupAttribGetBoolean(ih, "MULTIPLEFILES") && action == GTK_FILE_CHOOSER_ACTION_OPEN) + gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE); + +#if GTK_CHECK_VERSION(2, 8, 0) + if (!iupAttribGetBoolean(ih, "NOOVERWRITEPROMPT") && action == GTK_FILE_CHOOSER_ACTION_SAVE) + gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE); +#endif + + /* just check for the path inside FILE */ + value = iupAttribGet(ih, "FILE"); + if (value && (value[0] == '/' || value[1] == ':')) + { + char* dir = iupStrFileGetPath(value); + int len = strlen(dir); + iupAttribStoreStr(ih, "DIRECTORY", dir); + free(dir); + iupAttribStoreStr(ih, "FILE", value+len); + } + + value = iupAttribGet(ih, "DIRECTORY"); + if (value) + gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), iupgtkStrConvertToFilename(value)); + + value = iupAttribGet(ih, "FILE"); + if (value) + { + if (action == GTK_FILE_CHOOSER_ACTION_SAVE) + gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), iupgtkStrConvertToFilename(value)); + else + { + if (iupdrvIsFile(value)) /* check if file exists */ + gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog), iupgtkStrConvertToFilename(value)); + } + } + + value = iupAttribGet(ih, "EXTFILTER"); + if (value) + { + char *name, *pattern, *filters = iupStrDup(value), *p; + char atrib[30]; + int i; + int filter_index = iupAttribGetInt(ih, "FILTERUSED"); + if (!filter_index) + filter_index = 1; + + filter_count = iupStrReplace(filters, '|', 0) / 2; + + p = filters; + for (i=0; i<filter_count; i++) + { + GtkFileFilter *filter = gtk_file_filter_new(); + + gtkFileDlgGetNextFilter(&p, &name, &pattern); + + gtk_file_filter_set_name(filter, iupgtkStrConvertToUTF8(name)); + gtk_file_filter_add_pattern(filter, pattern); + gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter); + + sprintf(atrib, "_IUPDLG_FILTER%d", i+1); + iupAttribSetStr(ih, atrib, (char*)filter); + + if (i+1 == filter_index) + gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter); + } + + free(filters); + } + else + { + value = iupAttribGet(ih, "FILTER"); + if (value) + { + GtkFileFilter *filter = gtk_file_filter_new(); + char* info = iupAttribGet(ih, "FILTERINFO"); + if (!info) + info = value; + + gtk_file_filter_set_name(filter, iupgtkStrConvertToUTF8(info)); + gtk_file_filter_add_pattern(filter, value); + gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter); + } + } + + file_cb = (IFnss)IupGetCallback(ih, "FILE_CB"); + if (file_cb && action != GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER) + { + g_signal_connect(GTK_FILE_CHOOSER(dialog), "update-preview", G_CALLBACK(gtkFileDlgUpdatePreview), ih); + g_signal_connect(dialog, "realize", G_CALLBACK(gtkFileDlgRealize), ih); + + if (iupAttribGetBoolean(ih, "SHOWPREVIEW")) + { + GtkWidget* frame = gtk_frame_new(NULL); + gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_ETCHED_IN); + gtk_widget_set_size_request(frame, 180, 150); + + preview_canvas = gtk_drawing_area_new(); + gtk_widget_set_double_buffered(preview_canvas, FALSE); + gtk_container_add(GTK_CONTAINER(frame), preview_canvas); + gtk_widget_show(preview_canvas); + + g_signal_connect(preview_canvas, "configure-event", G_CALLBACK(gtkFileDlgPreviewConfigureEvent), ih); + g_signal_connect(preview_canvas, "expose-event", G_CALLBACK(gtkFileDlgPreviewExposeEvent), ih); + g_signal_connect(preview_canvas, "realize", G_CALLBACK(gtkFileDlgPreviewRealize), ih); + + iupAttribSetStr(ih, "_IUPDLG_FILE_CHOOSER", (char*)dialog); + + gtk_file_chooser_set_preview_widget(GTK_FILE_CHOOSER(dialog), frame); + } + } + + /* initialize the widget */ + gtk_widget_realize(GTK_WIDGET(dialog)); + + ih->handle = GTK_WIDGET(dialog); + iupDialogUpdatePosition(ih); + ih->handle = NULL; /* reset handle */ + + do + { + response = gtk_dialog_run(GTK_DIALOG(dialog)); + + if (response == GTK_RESPONSE_HELP) + { + Icallback cb = IupGetCallback(ih, "HELP_CB"); + if (cb && cb(ih) == IUP_CLOSE) + response = GTK_RESPONSE_CANCEL; + } + else if (response == GTK_RESPONSE_OK) + { + char *filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); + int file_exist = iupdrvIsFile(filename); + int dir_exist = iupdrvIsDirectory(filename); + g_free(filename); + + if (action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER) + { + if (!dir_exist) + { + iupStrMessageShowError(ih, "IUP_INVALIDDIR"); + response = GTK_RESPONSE_HELP; /* to leave the dialog open */ + continue; + } + } + else if (!iupAttribGetBoolean(ih, "MULTIPLEFILES")) + { + if (dir_exist) + { + iupStrMessageShowError(ih, "IUP_FILEISDIR"); + response = GTK_RESPONSE_HELP; /* to leave the dialog open */ + continue; + } + + if (!file_exist) /* if do not exist check ALLOWNEW */ + { + value = iupAttribGet(ih, "ALLOWNEW"); + if (!value) + { + if (action == GTK_FILE_CHOOSER_ACTION_SAVE) + value = "YES"; + else + value = "NO"; + } + + if (!iupStrBoolean(value)) + { + iupStrMessageShowError(ih, "IUP_FILENOTEXIST"); + response = GTK_RESPONSE_HELP; /* to leave the dialog open */ + continue; + } + } + + if (file_cb) + { + char *filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); + int ret = file_cb(ih, iupgtkStrConvertFromFilename(filename), "OK"); + g_free(filename); + + if (ret == IUP_IGNORE) + { + response = GTK_RESPONSE_HELP; /* to leave the dialog open */ + continue; + } + } + } + } + } while (response == GTK_RESPONSE_HELP); + + if (file_cb) + { + if (iupAttribGetBoolean(ih, "SHOWPREVIEW")) + iupgtkReleaseNativeGraphicsContext(preview_canvas, (void*)iupAttribGet(ih, "PREVIEWDC")); + + file_cb(ih, NULL, "FINISH"); + } + + if (response == GTK_RESPONSE_OK) + { + int file_exist, dir_exist; + + if (filter_count) + { + int i; + char atrib[30]; + GtkFileFilter* filter = gtk_file_chooser_get_filter(GTK_FILE_CHOOSER(dialog)); + + for (i=0; i<filter_count; i++) + { + sprintf(atrib, "_IUPDLG_FILTER%d", i+1); + if (filter == (GtkFileFilter*)iupAttribGet(ih, atrib)) + iupAttribSetInt(ih, "FILTERUSED", i+1); + } + } + + if (iupAttribGetBoolean(ih, "MULTIPLEFILES")) + { + GSList* file_list = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog)); + + if (file_list->next) /* if more than one file */ + gtkFileDlgGetMultipleFiles(ih, file_list); + else + { + char* filename = (char*)file_list->data; + iupAttribStoreStr(ih, "VALUE", iupgtkStrConvertFromFilename(filename)); + g_free(filename); + } + + g_slist_free(file_list); + file_exist = 1; + dir_exist = 0; + } + else + { + char *filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); + iupAttribStoreStr(ih, "VALUE", iupgtkStrConvertFromFilename(filename)); + file_exist = iupdrvIsFile(filename); + dir_exist = iupdrvIsDirectory(filename); + g_free(filename); + } + + if (dir_exist) + { + iupAttribSetStr(ih, "FILEEXIST", NULL); + iupAttribSetStr(ih, "STATUS", "0"); + } + else + { + if (file_exist) /* check if file exists */ + { + iupAttribSetStr(ih, "FILEEXIST", "YES"); + iupAttribSetStr(ih, "STATUS", "0"); + } + else + { + iupAttribSetStr(ih, "FILEEXIST", "NO"); + iupAttribSetStr(ih, "STATUS", "1"); + } + } + + if (action != GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER && !iupAttribGetBoolean(ih, "NOCHANGEDIR")) /* do change the current directory */ + { + /* GtkFileChooser does not change the current directory */ + char* dir = gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER(dialog)); + if (dir) iupdrvSetCurrentDirectory(dir); + g_free(dir); + } + } + else + { + iupAttribSetStr(ih, "FILTERUSED", NULL); + iupAttribSetStr(ih, "VALUE", NULL); + iupAttribSetStr(ih, "FILEEXIST", NULL); + iupAttribSetStr(ih, "STATUS", "-1"); + } + + gtk_widget_destroy(GTK_WIDGET(dialog)); + + return IUP_NOERROR; +} + +void iupdrvFileDlgInitClass(Iclass* ic) +{ + ic->DlgPopup = gtkFileDlgPopup; + + /* IupFileDialog Windows and GTK Only */ + iupClassRegisterAttribute(ic, "EXTFILTER", NULL, NULL, NULL, NULL, IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "FILTERINFO", NULL, NULL, NULL, NULL, IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "FILTERUSED", NULL, NULL, NULL, NULL, IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "MULTIPLEFILES", NULL, NULL, NULL, NULL, IUPAF_NO_INHERIT); +} diff --git a/iup/src/gtk/iupgtk_focus.c b/iup/src/gtk/iupgtk_focus.c new file mode 100755 index 0000000..fa596ba --- /dev/null +++ b/iup/src/gtk/iupgtk_focus.c @@ -0,0 +1,44 @@ +/** \file + * \brief GTK Focus + * + * See Copyright Notice in "iup.h" + */ + +#include <gtk/gtk.h> + +#include <stdio.h> + +#include "iup.h" + +#include "iup_object.h" +#include "iup_focus.h" +#include "iup_attrib.h" +#include "iup_drv.h" +#include "iup_assert.h" +#include "iup_drv.h" + +#include "iupgtk_drv.h" + + +void iupdrvSetFocus(Ihandle *ih) +{ + gtk_widget_grab_focus(ih->handle); +} + +gboolean iupgtkFocusInOutEvent(GtkWidget *widget, GdkEventFocus *evt, Ihandle *ih) +{ + (void)widget; + + if (evt->in) + { + /* even when ACTIVE=NO the dialog gets this evt */ + if (!iupdrvIsActive(ih)) + return TRUE; + + iupCallGetFocusCb(ih); + } + else + iupCallKillFocusCb(ih); + + return FALSE; +} diff --git a/iup/src/gtk/iupgtk_font.c b/iup/src/gtk/iupgtk_font.c new file mode 100755 index 0000000..f7ff348 --- /dev/null +++ b/iup/src/gtk/iupgtk_font.c @@ -0,0 +1,413 @@ +/** \file + * \brief GTK Font mapping + * + * See Copyright Notice in "iup.h" + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include <gtk/gtk.h> + +#include "iup.h" + +#include "iup_str.h" +#include "iup_attrib.h" +#include "iup_array.h" +#include "iup_object.h" +#include "iup_drv.h" +#include "iup_drvfont.h" +#include "iup_assert.h" + +#include "iupgtk_drv.h" + + +typedef struct _IgtkFont +{ + char standardfont[200]; + PangoFontDescription* fontdesc; + PangoAttribute* strikethrough; + PangoAttribute* underline; + PangoLayout* layout; + int charwidth, charheight; +} IgtkFont; + +static Iarray* gtk_fonts = NULL; +static PangoContext *gtk_fonts_context = NULL; + +static void gtkFontUpdate(IgtkFont* gtkfont) +{ + PangoAttrList *attrs; + + pango_layout_set_font_description(gtkfont->layout, gtkfont->fontdesc); + + attrs = pango_layout_get_attributes(gtkfont->layout); + if (!attrs) + { + attrs = pango_attr_list_new(); + pango_attr_list_insert(attrs, pango_attribute_copy(gtkfont->strikethrough)); + pango_attr_list_insert(attrs, pango_attribute_copy(gtkfont->underline)); + pango_layout_set_attributes(gtkfont->layout, attrs); + } + else + { + pango_attr_list_change(attrs, pango_attribute_copy(gtkfont->strikethrough)); + pango_attr_list_change(attrs, pango_attribute_copy(gtkfont->underline)); + } +} + +static IgtkFont* gtkFindFont(const char *standardfont) +{ + PangoFontMetrics* metrics; + PangoFontDescription* fontdesc; + int i, + is_underline = 0, + is_strikeout = 0, + count = iupArrayCount(gtk_fonts); + + IgtkFont* fonts = (IgtkFont*)iupArrayGetData(gtk_fonts); + + /* Check if the standardfont already exists in cache */ + for (i = 0; i < count; i++) + { + if (iupStrEqualNoCase(standardfont, fonts[i].standardfont)) + return &fonts[i]; + } + + /* not found, create a new one */ + { + int size = 0, is_pango = 0; + int is_bold = 0, + is_italic = 0; + char typeface[1024]; + const char* mapped_name; + + /* parse the old Windows format first */ + if (!iupFontParseWin(standardfont, typeface, &size, &is_bold, &is_italic, &is_underline, &is_strikeout)) + { + if (!iupFontParseX(standardfont, typeface, &size, &is_bold, &is_italic, &is_underline, &is_strikeout)) + { + if (!iupFontParsePango(standardfont, typeface, &size, &is_bold, &is_italic, &is_underline, &is_strikeout)) + return NULL; + else + is_pango = 1; + } + } + + mapped_name = iupFontGetPangoName(typeface); + if (mapped_name) + strcpy(typeface, mapped_name); + + if (is_pango && !is_underline && !is_strikeout && size>0) + fontdesc = pango_font_description_from_string(standardfont); + else + { + char new_standardfont[200]; + if (size<0) + { + double res = ((double)gdk_screen_get_width(gdk_screen_get_default()) / (double)gdk_screen_get_width_mm(gdk_screen_get_default())); /* pixels/mm */ + /* 1 point = 1/72 inch 1 inch = 25.4 mm */ + /* pixel = ((point/72)*25.4)*pixel/mm */ + size = (int)((-size/res)*2.83464567 + 0.5); /* from pixels to points */ + } + + sprintf(new_standardfont, "%s, %s%s%d", typeface, is_bold?"Bold ":"", is_italic?"Italic ":"", size); + + fontdesc = pango_font_description_from_string(new_standardfont); + } + } + + if (!fontdesc) + return NULL; + + /* create room in the array */ + fonts = (IgtkFont*)iupArrayInc(gtk_fonts); + + strcpy(fonts[i].standardfont, standardfont); + fonts[i].fontdesc = fontdesc; + fonts[i].strikethrough = pango_attr_strikethrough_new(is_strikeout? TRUE: FALSE); + fonts[i].underline = pango_attr_underline_new(is_underline? PANGO_UNDERLINE_SINGLE: PANGO_UNDERLINE_NONE); + fonts[i].layout = pango_layout_new(gtk_fonts_context); + + metrics = pango_context_get_metrics(gtk_fonts_context, fontdesc, pango_context_get_language(gtk_fonts_context)); + fonts[i].charheight = pango_font_metrics_get_ascent(metrics) + pango_font_metrics_get_descent(metrics); + fonts[i].charheight = IUPGTK_PANGOUNITS2PIXELS(fonts[i].charheight); + fonts[i].charwidth = pango_font_metrics_get_approximate_char_width(metrics); + fonts[i].charwidth = IUPGTK_PANGOUNITS2PIXELS(fonts[i].charwidth); + pango_font_metrics_unref(metrics); + + gtkFontUpdate(&(fonts[i])); + + return &fonts[i]; +} + +static PangoLayout* gtkFontGetWidgetPangoLayout(Ihandle *ih) +{ + int inherit; + char *def_value; + /* only check the native implementation */ + return (PangoLayout*)iupClassObjectGetAttribute(ih, "PANGOLAYOUT", &def_value, &inherit); +} + +static IgtkFont* gtkFontCreateNativeFont(Ihandle* ih, const char* value) +{ + IgtkFont *gtkfont = gtkFindFont(value); + if (!gtkfont) + { + iupERROR1("Failed to create Font: %s", value); + return NULL; + } + + iupAttribSetStr(ih, "_IUP_GTKFONT", (char*)gtkfont); + return gtkfont; +} + +static IgtkFont* gtkFontGet(Ihandle *ih) +{ + IgtkFont* gtkfont = (IgtkFont*)iupAttribGet(ih, "_IUP_GTKFONT"); + if (!gtkfont) + gtkfont = gtkFontCreateNativeFont(ih, iupGetFontAttrib(ih)); + return gtkfont; +} + +void iupgtkFontUpdatePangoLayout(Ihandle* ih, PangoLayout* layout) +{ + IgtkFont* gtkfont; + PangoAttrList *attrs; + + if (!layout) + return; + + gtkfont = gtkFontGet(ih); + if (!gtkfont) + return; + + attrs = pango_layout_get_attributes(layout); + if (!attrs) + { + attrs = pango_attr_list_new(); + pango_attr_list_insert(attrs, pango_attribute_copy(gtkfont->strikethrough)); + pango_attr_list_insert(attrs, pango_attribute_copy(gtkfont->underline)); + pango_layout_set_attributes(layout, attrs); + } + else + { + pango_attr_list_change(attrs, pango_attribute_copy(gtkfont->strikethrough)); + pango_attr_list_change(attrs, pango_attribute_copy(gtkfont->underline)); + } +} + +void iupgtkFontUpdateObjectPangoLayout(Ihandle* ih, gpointer object) +{ + PangoAttrList *attrs; + + IgtkFont* gtkfont = gtkFontGet(ih); + if (!gtkfont) + return; + + g_object_get(object, "attributes", &attrs, NULL); + if (!attrs) + { + attrs = pango_attr_list_new(); + pango_attr_list_insert(attrs, pango_attribute_copy(gtkfont->strikethrough)); + pango_attr_list_insert(attrs, pango_attribute_copy(gtkfont->underline)); + g_object_set(object, "attributes", attrs, NULL); + } + else + { + pango_attr_list_change(attrs, pango_attribute_copy(gtkfont->strikethrough)); + pango_attr_list_change(attrs, pango_attribute_copy(gtkfont->underline)); + } +} + +char* iupdrvGetSystemFont(void) +{ + static char systemfont[200] = ""; + GtkStyle* style; + GtkWidget* widget = gtk_invisible_new(); + gtk_widget_realize(widget); + style = gtk_widget_get_style(widget); + if (!style || !style->font_desc) + strcpy(systemfont, "Sans, 10"); + else + { + char* desc = pango_font_description_to_string(style->font_desc); + strcpy(systemfont, desc); + g_free(desc); + } + gtk_widget_unrealize(widget); + gtk_widget_destroy(widget); + return systemfont; +} + +char* iupgtkFindPangoFontDesc(PangoFontDescription* fontdesc) +{ + int i, count = iupArrayCount(gtk_fonts); + IgtkFont* fonts = (IgtkFont*)iupArrayGetData(gtk_fonts); + + /* Check if the standardfont already exists in cache */ + for (i = 0; i < count; i++) + { + if (pango_font_description_equal(fontdesc, fonts[i].fontdesc)) + return fonts[i].standardfont; + } + + return NULL; +} + +PangoFontDescription* iupgtkGetPangoFontDesc(const char* value) +{ + IgtkFont *gtkfont = gtkFindFont(value); + if (!gtkfont) + { + iupERROR1("Failed to create Font: %s", value); + return NULL; + } + return gtkfont->fontdesc; +} + +char* iupgtkGetPangoFontDescAttrib(Ihandle *ih) +{ + IgtkFont* gtkfont = gtkFontGet(ih); + if (!gtkfont) + return NULL; + else + return (char*)gtkfont->fontdesc; +} + +char* iupgtkGetFontIdAttrib(Ihandle *ih) +{ + IgtkFont* gtkfont = gtkFontGet(ih); + if (!gtkfont) + return NULL; + else + { + GdkFont* gdk_font = gdk_font_from_description(gtkfont->fontdesc); + return (char*)gdk_font_id(gdk_font); /* In UNIX will return an X Font ID, in Win32 will return an HFONT */ + } +} + +int iupdrvSetStandardFontAttrib(Ihandle* ih, const char* value) +{ + IgtkFont* gtkfont = gtkFontCreateNativeFont(ih, value); + if (!gtkfont) + return 1; + + /* If FONT is changed, must update the SIZE attribute */ + iupBaseUpdateSizeFromFont(ih); + + /* FONT attribute must be able to be set before mapping, + so the font is enable for size calculation. */ + if (ih->handle && (ih->iclass->nativetype != IUP_TYPEVOID)) + { + gtk_widget_modify_font(ih->handle, gtkfont->fontdesc); + iupgtkFontUpdatePangoLayout(ih, gtkFontGetWidgetPangoLayout(ih)); + } + + return 1; +} + +void iupdrvFontGetMultiLineStringSize(Ihandle* ih, const char* str, int *w, int *h) +{ + int max_w; + + IgtkFont* gtkfont = gtkFontGet(ih); + if (!gtkfont) + { + if (w) *w = 0; + if (h) *h = 0; + return; + } + + if (!str) + { + if (w) *w = 0; + if (h) *h = gtkfont->charheight * 1; + return; + } + + max_w = 0; + if (str[0]) + { + int dummy_h; + + pango_layout_set_attributes(gtkfont->layout, NULL); + + if (iupAttribGetBoolean(ih, "MARKUP")) + pango_layout_set_markup(gtkfont->layout, iupgtkStrConvertToUTF8(str), -1); + else + pango_layout_set_text(gtkfont->layout, iupgtkStrConvertToUTF8(str), -1); + + pango_layout_get_pixel_size(gtkfont->layout, &max_w, &dummy_h); + } + + if (w) *w = max_w; + if (h) *h = gtkfont->charheight * iupStrLineCount(str); +} + +int iupdrvFontGetStringWidth(Ihandle* ih, const char* str) +{ + IgtkFont* gtkfont; + int len, w; + char* line_end; + + if (!str || str[0]==0) + return 0; + + gtkfont = gtkFontGet(ih); + if (!gtkfont) + return 0; + + line_end = strchr(str, '\n'); + if (line_end) + len = line_end-str; + else + len = strlen(str); + + if (iupAttribGetBoolean(ih, "MARKUP")) + pango_layout_set_markup(gtkfont->layout, iupgtkStrConvertToUTF8(str), len); + else + pango_layout_set_text(gtkfont->layout, iupgtkStrConvertToUTF8(str), len); + + pango_layout_get_pixel_size(gtkfont->layout, &w, NULL); + return w; +} + +void iupdrvFontGetCharSize(Ihandle* ih, int *charwidth, int *charheight) +{ + IgtkFont* gtkfont = gtkFontGet(ih); + if (!gtkfont) + { + if (charwidth) *charwidth = 0; + if (charheight) *charheight = 0; + return; + } + + if (charheight) + *charheight = gtkfont->charheight; + + if (charwidth) + *charwidth = gtkfont->charwidth; +} + +void iupdrvFontInit(void) +{ + gtk_fonts = iupArrayCreate(50, sizeof(IgtkFont)); + gtk_fonts_context = gdk_pango_context_get(); + pango_context_set_language(gtk_fonts_context, gtk_get_default_language()); +} + +void iupdrvFontFinish(void) +{ + int i, count = iupArrayCount(gtk_fonts); + IgtkFont* fonts = (IgtkFont*)iupArrayGetData(gtk_fonts); + for (i = 0; i < count; i++) + { + pango_font_description_free(fonts[i].fontdesc); + pango_attribute_destroy(fonts[i].strikethrough); + pango_attribute_destroy(fonts[i].underline); + } + iupArrayDestroy(gtk_fonts); + g_object_unref(gtk_fonts_context); +} diff --git a/iup/src/gtk/iupgtk_fontdlg.c b/iup/src/gtk/iupgtk_fontdlg.c new file mode 100755 index 0000000..5769cbc --- /dev/null +++ b/iup/src/gtk/iupgtk_fontdlg.c @@ -0,0 +1,91 @@ +/** \file + * \brief IupFontDlg pre-defined dialog + * + * See Copyright Notice in "iup.h" + */ + +#include <gtk/gtk.h> + +#include <string.h> +#include <memory.h> + +#include "iup.h" + +#include "iup_object.h" +#include "iup_attrib.h" +#include "iup_str.h" +#include "iup_dialog.h" + +#include "iupgtk_drv.h" + + +static int gtkFontDlgPopup(Ihandle* ih, int x, int y) +{ + InativeHandle* parent = iupDialogGetNativeParent(ih); + GtkFontSelectionDialog* dialog; + int response; + char* preview_text; + + iupAttribSetInt(ih, "_IUPDLG_X", x); /* used in iupDialogUpdatePosition */ + iupAttribSetInt(ih, "_IUPDLG_Y", y); + + dialog = (GtkFontSelectionDialog*)gtk_font_selection_dialog_new(iupgtkStrConvertToUTF8(iupAttribGet(ih, "TITLE"))); + if (!dialog) + return IUP_ERROR; + + if (parent) + gtk_window_set_transient_for((GtkWindow*)dialog, (GtkWindow*)parent); + + gtk_font_selection_dialog_set_font_name(dialog, iupAttribGet(ih, "VALUE")); + + preview_text = iupAttribGet(ih, "PREVIEWTEXT"); + if (preview_text) + gtk_font_selection_dialog_set_preview_text(dialog, preview_text); + + if (IupGetCallback(ih, "HELP_CB")) + gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_HELP, GTK_RESPONSE_HELP); + + /* initialize the widget */ + gtk_widget_realize(GTK_WIDGET(dialog)); + + ih->handle = GTK_WIDGET(dialog); + iupDialogUpdatePosition(ih); + ih->handle = NULL; + + do + { + response = gtk_dialog_run(GTK_DIALOG(dialog)); + + if (response == GTK_RESPONSE_HELP) + { + Icallback cb = IupGetCallback(ih, "HELP_CB"); + if (cb && cb(ih) == IUP_CLOSE) + response = GTK_RESPONSE_CANCEL; + } + } while (response == GTK_RESPONSE_HELP); + + if (response == GTK_RESPONSE_OK) + { + char* fontname = gtk_font_selection_dialog_get_font_name(dialog); + iupAttribStoreStr(ih, "VALUE", fontname); + g_free(fontname); + iupAttribSetStr(ih, "STATUS", "1"); + } + else + { + iupAttribSetStr(ih, "VALUE", NULL); + iupAttribSetStr(ih, "STATUS", NULL); + } + + gtk_widget_destroy(GTK_WIDGET(dialog)); + + return IUP_NOERROR; +} + +void iupdrvFontDlgInitClass(Iclass* ic) +{ + ic->DlgPopup = gtkFontDlgPopup; + + /* IupFontDialog GTK Only */ + iupClassRegisterAttribute(ic, "PREVIEWTEXT", NULL, NULL, NULL, NULL, IUPAF_NO_INHERIT); +} diff --git a/iup/src/gtk/iupgtk_frame.c b/iup/src/gtk/iupgtk_frame.c new file mode 100755 index 0000000..022c6c7 --- /dev/null +++ b/iup/src/gtk/iupgtk_frame.c @@ -0,0 +1,155 @@ +/** \file + * \brief Frame Control + * + * See Copyright Notice in "iup.h" + */ + +#include <gtk/gtk.h> + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <memory.h> +#include <stdarg.h> + +#include "iup.h" + +#include "iup_object.h" +#include "iup_layout.h" +#include "iup_attrib.h" +#include "iup_str.h" +#include "iup_dialog.h" +#include "iup_drv.h" +#include "iup_drvfont.h" +#include "iup_stdcontrols.h" + +#include "iupgtk_drv.h" + + +void iupdrvFrameGetDecorOffset(Ihandle* ih, int *x, int *y) +{ + (void)ih; + *x = 0; + *y = 0; +} + +static char* gtkFrameGetTitleAttrib(Ihandle* ih) +{ + GtkFrame* frame = (GtkFrame*)ih->handle; + return iupStrGetMemoryCopy(iupgtkStrConvertFromUTF8(gtk_frame_get_label(frame))); +} + +static int gtkFrameSetTitleAttrib(Ihandle* ih, const char* value) +{ + GtkFrame* frame = (GtkFrame*)ih->handle; + gtk_frame_set_label(frame, iupgtkStrConvertToUTF8(value)); + return 0; +} + +static int gtkFrameSetBgColorAttrib(Ihandle* ih, const char* value) +{ + unsigned char r, g, b; + GtkWidget* label = gtk_frame_get_label_widget((GtkFrame*)ih->handle); + + if (!iupStrToRGB(value, &r, &g, &b)) + return 0; + + if (label) + iupgtkBaseSetBgColor(label, r, g, b); + + iupgtkBaseSetBgColor(ih->handle, r, g, b); + + return 1; +} + +static int gtkFrameSetFgColorAttrib(Ihandle* ih, const char* value) +{ + unsigned char r, g, b; + GtkWidget* label = gtk_frame_get_label_widget((GtkFrame*)ih->handle); + if (!label) return 0; + + if (!iupStrToRGB(value, &r, &g, &b)) + return 0; + + iupgtkBaseSetFgColor(label, r, g, b); + + return 1; +} + +static int gtkFrameSetStandardFontAttrib(Ihandle* ih, const char* value) +{ + iupdrvSetStandardFontAttrib(ih, value); + + if (ih->handle) + { + GtkWidget* label = gtk_frame_get_label_widget((GtkFrame*)ih->handle); + if (!label) return 1; + + gtk_widget_modify_font(label, (PangoFontDescription*)iupgtkGetPangoFontDescAttrib(ih)); + iupgtkFontUpdatePangoLayout(ih, gtk_label_get_layout((GtkLabel*)label)); + } + return 1; +} + +static void* gtkFrameGetInnerNativeContainerHandleMethod(Ihandle* ih, Ihandle* child) +{ + (void)child; + return (void*)gtk_bin_get_child((GtkBin*)ih->handle); +} + +static int gtkFrameMapMethod(Ihandle* ih) +{ + char *value, *title; + GtkWidget *fixed; + + if (!ih->parent) + return IUP_ERROR; + + title = iupAttribGet(ih, "TITLE"); + + ih->handle = gtk_frame_new(NULL); + if (!ih->handle) + return IUP_ERROR; + + if (title) + iupAttribSetStr(ih, "_IUPFRAME_HAS_TITLE", "1"); + else + { + value = iupAttribGetStr(ih, "SUNKEN"); + if (iupStrBoolean(value)) + gtk_frame_set_shadow_type((GtkFrame*)ih->handle, GTK_SHADOW_IN); + else + gtk_frame_set_shadow_type((GtkFrame*)ih->handle, GTK_SHADOW_ETCHED_IN); + } + + /* the container that will receive the child element. */ + fixed = gtk_fixed_new(); + gtk_container_add((GtkContainer*)ih->handle, fixed); + gtk_widget_show(fixed); + + /* Add to the parent, all GTK controls must call this. */ + iupgtkBaseAddToParent(ih); + + gtk_widget_realize(ih->handle); + + return IUP_NOERROR; +} + +void iupdrvFrameInitClass(Iclass* ic) +{ + /* Driver Dependent Class functions */ + ic->Map = gtkFrameMapMethod; + ic->GetInnerNativeContainerHandle = gtkFrameGetInnerNativeContainerHandleMethod; + + /* Driver Dependent Attribute functions */ + + /* Overwrite Common */ + iupClassRegisterAttribute(ic, "STANDARDFONT", NULL, gtkFrameSetStandardFontAttrib, IUPAF_SAMEASSYSTEM, "DEFAULTFONT", IUPAF_NOT_MAPPED); + + /* Visual */ + iupClassRegisterAttribute(ic, "BGCOLOR", NULL, gtkFrameSetBgColorAttrib, IUPAF_SAMEASSYSTEM, "DLGBGCOLOR", IUPAF_DEFAULT); + + /* Special */ + iupClassRegisterAttribute(ic, "FGCOLOR", NULL, gtkFrameSetFgColorAttrib, IUPAF_SAMEASSYSTEM, "DLGFGCOLOR", IUPAF_DEFAULT); + iupClassRegisterAttribute(ic, "TITLE", gtkFrameGetTitleAttrib, gtkFrameSetTitleAttrib, NULL, NULL, IUPAF_NO_DEFAULTVALUE|IUPAF_NO_INHERIT); +} diff --git a/iup/src/gtk/iupgtk_globalattrib.c b/iup/src/gtk/iupgtk_globalattrib.c new file mode 100755 index 0000000..eebe799 --- /dev/null +++ b/iup/src/gtk/iupgtk_globalattrib.c @@ -0,0 +1,211 @@ +/** \file + * \brief GTK Driver iupdrvSetGlobal + * + * See Copyright Notice in "iup.h" + */ + +#include <stdio.h> +#include <string.h> + +#include <gtk/gtk.h> + +#include "iup.h" + +#include "iup_object.h" +#include "iup_str.h" +#include "iup_drv.h" +#include "iup_drvinfo.h" +#include "iup_strmessage.h" + +#include "iupgtk_drv.h" + + +int iupgtk_utf8autoconvert = 1; + +static void gtkGlobalSendKey(int key, int press) +{ + Ihandle* focus; + gint nkeys = 0; + GdkKeymapKey *keys; + GdkEventKey evt; + memset(&evt, 0, sizeof(GdkEventKey)); + evt.send_event = TRUE; + + focus = IupGetFocus(); + if (!focus) + return; + evt.window = focus->handle->window; + + iupgtkKeyEncode(key, &evt.keyval, &evt.state); + if (!evt.keyval) + return; + + if (!gdk_keymap_get_entries_for_keyval(gdk_keymap_get_default(), evt.keyval, &keys, &nkeys)) + return; + evt.hardware_keycode = (guint16)keys[0].keycode; + evt.group = (guint8)keys[0].group; + + if (press & 0x01) + { + evt.type = GDK_KEY_PRESS; + gdk_display_put_event(gdk_display_get_default(), (GdkEvent*)&evt); + } + + if (press & 0x02) + { + evt.type = GDK_KEY_RELEASE; + gdk_display_put_event(gdk_display_get_default(), (GdkEvent*)&evt); + } +} + +int iupdrvSetGlobal(const char *name, const char *value) +{ + if (iupStrEqual(name, "LANGUAGE")) + { + iupStrMessageUpdateLanguage(value); + return 1; + } + if (iupStrEqual(name, "CURSORPOS")) + { +#if GTK_CHECK_VERSION(2, 8, 0) + int x, y; + if (iupStrToIntInt(value, &x, &y, 'x') == 2) + gdk_display_warp_pointer(gdk_display_get_default(), gdk_screen_get_default(), x, y); +#endif + return 0; + } + if (iupStrEqual(name, "UTF8AUTOCONVERT")) + { + if (!value || iupStrBoolean(value)) + iupgtk_utf8autoconvert = 1; + else + iupgtk_utf8autoconvert = 0; + return 0; + } + if (iupStrEqual(name, "KEYPRESS")) + { + int key; + if (iupStrToInt(value, &key)) + gtkGlobalSendKey(key, 0x01); + return 0; + } + if (iupStrEqual(name, "KEYRELEASE")) + { + int key; + if (iupStrToInt(value, &key)) + gtkGlobalSendKey(key, 0x02); + return 0; + } + if (iupStrEqual(name, "KEY")) + { + int key; + if (iupStrToInt(value, &key)) + gtkGlobalSendKey(key, 0x03); + return 0; + } + return 1; +} + +char *iupdrvGetGlobal(const char *name) +{ + if (iupStrEqual(name, "CURSORPOS")) + { + char *str = iupStrGetMemory(50); + int x, y; + iupdrvGetCursorPos(&x, &y); + sprintf(str, "%dx%d", (int)x, (int)y); + return str; + } + if (iupStrEqual(name, "SHIFTKEY")) + { + char key[5]; + iupdrvGetKeyState(key); + if (key[0] == 'S') + return "ON"; + else + return "OFF"; + } + if (iupStrEqual(name, "CONTROLKEY")) + { + char key[5]; + iupdrvGetKeyState(key); + if (key[1] == 'C') + return "ON"; + else + return "OFF"; + } + if (iupStrEqual(name, "MODKEYSTATE")) + { + char *str = iupStrGetMemory(5); + iupdrvGetKeyState(str); + return str; + } + if (iupStrEqual(name, "SCREENSIZE")) + { + char *str = iupStrGetMemory(50); + int w, h; + iupdrvGetScreenSize(&w, &h); + sprintf(str, "%dx%d", w, h); + return str; + } + if (iupStrEqual(name, "FULLSIZE")) + { + char *str = iupStrGetMemory(50); + int w, h; + iupdrvGetFullSize(&w, &h); + sprintf(str, "%dx%d", w, h); + return str; + } + if (iupStrEqual(name, "SCREENDEPTH")) + { + char *str = iupStrGetMemory(50); + int bpp = iupdrvGetScreenDepth(); + sprintf(str, "%d", bpp); + return str; + } + if (iupStrEqual(name, "VIRTUALSCREEN")) + { + char *str = iupStrGetMemory(50); + GdkScreen *screen = gdk_screen_get_default(); + GdkWindow *root = gdk_screen_get_root_window(gdk_screen_get_default()); + int x = 0; + int y = 0; + int w = gdk_screen_get_width(screen); + int h = gdk_screen_get_height(screen); + gdk_window_get_root_origin(root, &x, &y); + sprintf(str, "%d %d %d %d", x, y, w, h); + return str; + } + if (iupStrEqual(name, "MONITORSINFO")) + { + int i; + GdkScreen *screen = gdk_screen_get_default(); + int monitors_count = gdk_screen_get_n_monitors(screen); + char *str = iupStrGetMemory(monitors_count*50); + char* pstr = str; + GdkRectangle rect; + + for (i=0; i < monitors_count; i++) + { + gdk_screen_get_monitor_geometry(screen, i, &rect); + pstr += sprintf(pstr, "%d %d %d %d\n", rect.x, rect.y, rect.width, rect.height); + } + + return str; + } + if (iupStrEqual(name, "TRUECOLORCANVAS")) + { + if (gdk_visual_get_best_depth() > 8) + return "YES"; + else + return "NO"; + } + if (iupStrEqual(name, "UTF8AUTOCONVERT")) + { + if (iupgtk_utf8autoconvert) + return "YES"; + else + return "NO"; + } + return NULL; +} diff --git a/iup/src/gtk/iupgtk_help.c b/iup/src/gtk/iupgtk_help.c new file mode 100755 index 0000000..270677f --- /dev/null +++ b/iup/src/gtk/iupgtk_help.c @@ -0,0 +1,52 @@ +/** \file + * \brief GTK Driver IupHelp for non Windows systems + * + * See Copyright Notice in "iup.h" + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include <glib.h> + +#include "iup.h" + +#include "iup_str.h" + +int IupHelp(const char *url) +{ + GError *error = NULL; + gchar *argv[3]; + int ret; + char *browser = getenv("IUP_HELPAPP"); + if (!browser) + browser = IupGetGlobal("HELPAPP"); + + if (!browser) + { + char* system = IupGetGlobal("SYSTEM"); + if (iupStrEqualNoCase(system, "Linux") || + iupStrEqualNoCase(system, "FreeBSD")) + browser = "firefox"; + else if (iupStrEqualNoCase(system, "Darwin")) + browser = "safari"; + else if (iupStrEqualPartial(system, "CYGWIN")) + browser = "iexplore"; + else + browser = "netscape"; + } + + argv[0] = browser; + argv[1] = (gchar*)url; + argv[2] = NULL; + + ret = g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, &error); + + if (error) + g_error_free(error); + + if (!ret) + return -1; + return 1; +} diff --git a/iup/src/gtk/iupgtk_image.c b/iup/src/gtk/iupgtk_image.c new file mode 100755 index 0000000..b261974 --- /dev/null +++ b/iup/src/gtk/iupgtk_image.c @@ -0,0 +1,430 @@ +/** \file + * \brief Image Resource. + * + * See Copyright Notice in "iup.h" + */ + +#include <gtk/gtk.h> + +#include <stdio.h> +#include <stdlib.h> +#include <memory.h> + +#include "iup.h" + +#include "iup_object.h" +#include "iup_attrib.h" +#include "iup_str.h" +#include "iup_image.h" +#include "iup_drvinfo.h" + +#include "iupgtk_drv.h" + + +void iupdrvImageGetRawData(void* handle, unsigned char* imgdata) +{ + GdkPixbuf* pixbuf = (GdkPixbuf*)handle; + int w, h, y, x, bpp; + guchar *pixdata, *pixline_data; + int rowstride, channels, planesize; + unsigned char *r, *g, *b, *a; + + if (!iupdrvImageGetInfo(handle, &w, &h, &bpp)) + return; + + if (bpp==8) + return; + + pixdata = gdk_pixbuf_get_pixels(pixbuf); + rowstride = gdk_pixbuf_get_rowstride(pixbuf); + channels = gdk_pixbuf_get_n_channels(pixbuf); + + /* planes are separated in imgdata */ + planesize = w*h; + r = imgdata; + g = imgdata+planesize; + b = imgdata+2*planesize; + a = imgdata+3*planesize; + for (y=0; y<h; y++) + { + int lineoffset = (h-1 - y)*w; /* imgdata is bottom up */ + pixline_data = pixdata + y * rowstride; + for(x=0;x<w;x++) + { + int pos = x*channels; + r[lineoffset+x] = pixline_data[pos]; + g[lineoffset+x] = pixline_data[pos+1]; + b[lineoffset+x] = pixline_data[pos+2]; + + if (bpp == 32) + a[lineoffset+x] = pixline_data[pos+3]; + } + } +} + +void* iupdrvImageCreateImageRaw(int width, int height, int bpp, iupColor* colors, int colors_count, unsigned char *imgdata) +{ + GdkPixbuf* pixbuf; + guchar *pixdata, *pixline_data; + int rowstride, channels; + unsigned char *line_data; + int x, y, has_alpha = (bpp==32); + (void)colors_count; + + pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, has_alpha, 8, width, height); + if (!pixbuf) + return NULL; + + pixdata = gdk_pixbuf_get_pixels(pixbuf); + rowstride = gdk_pixbuf_get_rowstride(pixbuf); + channels = gdk_pixbuf_get_n_channels(pixbuf); + + /* GdkPixbuf is top-bottom */ + /* imgdata is bottom up */ + + if (bpp == 8) + { + for (y=0; y<height; y++) + { + pixline_data = pixdata + y * rowstride; + line_data = imgdata + (height-1 - y) * width; /* imgdata is bottom up */ + + for (x=0; x<width; x++) + { + unsigned char index = line_data[x]; + iupColor* c = &colors[index]; + guchar *r = &pixline_data[channels*x], + *g = r+1, + *b = g+1; + + *r = c->r; + *g = c->g; + *b = c->b; + } + } + } + else /* bpp == 32 or bpp == 24 */ + { + /* planes are separated in imgdata */ + int planesize = width*height; + unsigned char *r = imgdata, + *g = imgdata+planesize, + *b = imgdata+2*planesize, + *a = imgdata+3*planesize; + for (y=0; y<height; y++) + { + int lineoffset = (height-1 - y)*width; /* imgdata is bottom up */ + pixline_data = pixdata + y * rowstride; + for(x=0;x<width;x++) + { + int pos = x*channels; + pixline_data[pos] = r[lineoffset+x]; + pixline_data[pos+1] = g[lineoffset+x]; + pixline_data[pos+2] = b[lineoffset+x]; + + if (bpp == 32) + pixline_data[pos+3] = a[lineoffset+x]; + } + } + } + + return pixbuf; +} + +void* iupdrvImageCreateImage(Ihandle *ih, const char* bgcolor, int make_inactive) +{ + GdkPixbuf* pixbuf; + guchar *pixdata, *pixline_data; + int rowstride, channels; + unsigned char *imgdata, *line_data, bg_r=0, bg_g=0, bg_b=0; + int x, y, i, bpp, colors_count = 0, has_alpha = 0; + iupColor colors[256]; + + bpp = iupAttribGetInt(ih, "BPP"); + + if (bpp == 8) + has_alpha = iupImageInitColorTable(ih, colors, &colors_count); + else if (bpp == 32) + has_alpha = 1; + + pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, has_alpha, 8, ih->currentwidth, ih->currentheight); + if (!pixbuf) + return NULL; + + pixdata = gdk_pixbuf_get_pixels(pixbuf); + rowstride = gdk_pixbuf_get_rowstride(pixbuf); + channels = gdk_pixbuf_get_n_channels(pixbuf); + imgdata = (unsigned char*)iupAttribGetStr(ih, "WID"); + + if (make_inactive) + iupStrToRGB(bgcolor, &bg_r, &bg_g, &bg_b); + + if (bpp == 8) + { + if (make_inactive) + { + for (i=0;i<colors_count;i++) + { + if (colors[i].a == 0) + { + colors[i].r = bg_r; + colors[i].g = bg_g; + colors[i].b = bg_b; + colors[i].a = 255; + } + + iupImageColorMakeInactive(&(colors[i].r), &(colors[i].g), &(colors[i].b), + bg_r, bg_g, bg_b); + } + } + + for (y=0; y<ih->currentheight; y++) + { + pixline_data = pixdata + y * rowstride; + line_data = imgdata + y * ih->currentwidth; + + for (x=0; x<ih->currentwidth; x++) + { + unsigned char index = line_data[x]; + iupColor* c = &colors[index]; + guchar *r = &pixline_data[channels*x], + *g = r+1, + *b = g+1, + *a = b+1; + + *r = c->r; + *g = c->g; + *b = c->b; + + if (has_alpha) + *a = c->a; + } + } + } + else /* bpp == 32 or bpp == 24 */ + { + for (y=0; y<ih->currentheight; y++) + { + pixline_data = pixdata + y * rowstride; + line_data = imgdata + y * ih->currentwidth*channels; + + memcpy(pixline_data, line_data, ih->currentwidth*channels); + + if (make_inactive) + { + for (x=0; x<ih->currentwidth; x++) + { + guchar *r = &pixline_data[channels*x], + *g = r+1, + *b = g+1, + *a = b+1; + + if (has_alpha) + { + if (*a != 255) + { + *r = iupALPHABLEND(*r, bg_r, *a); + *g = iupALPHABLEND(*g, bg_g, *a); + *b = iupALPHABLEND(*b, bg_b, *a); + } + else + *a = 255; + } + + iupImageColorMakeInactive(r, g, b, + bg_r, bg_g, bg_b); + } + } + } + } + + return pixbuf; +} + +void* iupdrvImageCreateIcon(Ihandle *ih) +{ + return iupdrvImageCreateImage(ih, NULL, 0); +} + +void* iupdrvImageCreateCursor(Ihandle *ih) +{ + GdkCursor *cursor; + int hx, hy, bpp; + + hx=0; hy=0; + iupStrToIntInt(iupAttribGet(ih, "HOTSPOT"), &hx, &hy, ':'); + + bpp = iupAttribGetInt(ih, "BPP"); + + if (bpp == 8 && !iupAttribGet(ih, "3")) + { + GdkPixmap *source, *mask; + GdkColor fg, bg; + unsigned char r, g, b; + char *sbits, *mbits, *sb, *mb; + int y, x, line_size = (ih->currentwidth+7)/8; + int size_bytes = line_size*ih->currentheight; + unsigned char* imgdata = (unsigned char*)iupAttribGetStr(ih, "WID"); + + r = 255; g = 255; b = 255; + iupStrToRGB(iupAttribGet(ih, "1"), &r, &g, &b ); + iupgdkColorSet(&fg, r, g, b); + + r = 0; g = 0; b = 0; + iupStrToRGB(iupAttribGet(ih, "2"), &r, &g, &b ); + iupgdkColorSet(&bg, r, g, b); + + sbits = (char*)malloc(2*size_bytes); + if (!sbits) return NULL; + memset(sbits, 0, 2*size_bytes); + mbits = sbits + size_bytes; + + sb = sbits; + mb = mbits; + for (y=0; y<ih->currentheight; y++) + { + for (x=0; x<ih->currentwidth; x++) + { + int byte = x/8; + int bit = x%8; + int index = (int)imgdata[y*ih->currentwidth+x]; + /* index==0 is transparent */ + if (index == 1) + sb[byte] = (char)(sb[byte] | (1<<bit)); + if (index != 0) + mb[byte] = (char)(mb[byte] | (1<<bit)); + } + + sb += line_size; + mb += line_size; + } + + source = gdk_bitmap_create_from_data(NULL, sbits, ih->currentwidth, ih->currentheight); + mask = gdk_bitmap_create_from_data(NULL, mbits, ih->currentwidth, ih->currentheight); + + cursor = gdk_cursor_new_from_pixmap(source, mask, &fg, &bg, hx, hy); + + gdk_pixmap_unref(source); + gdk_pixmap_unref(mask); + free(sbits); + } + else + { + GdkPixbuf* pixbuf = iupdrvImageCreateImage(ih, NULL, 0); + cursor = gdk_cursor_new_from_pixbuf(gdk_display_get_default(), pixbuf, hx, hy); + g_object_unref(pixbuf); + } + + return cursor; +} + +void* iupdrvImageCreateMask(Ihandle *ih) +{ + int bpp; + GdkPixmap *mask; + char *bits, *sb; + int y, x, line_size = (ih->currentwidth+7)/8; + int size_bytes = line_size*ih->currentheight; + unsigned char* imgdata = (unsigned char*)iupAttribGetStr(ih, "WID"); + unsigned char colors[256]; + + bpp = iupAttribGetInt(ih, "BPP"); + if (bpp > 8) + return NULL; + + bits = (char*)malloc(size_bytes); + if (!bits) return NULL; + memset(bits, 0, size_bytes); + + iupImageInitNonBgColors(ih, colors); + + sb = bits; + for (y=0; y<ih->currentheight; y++) + { + for (x=0; x<ih->currentwidth; x++) + { + int byte = x/8; + int bit = x%8; + int index = (int)imgdata[y*ih->currentwidth+x]; + if (colors[index]) + sb[byte] = (char)(sb[byte] | (1<<bit)); + } + + sb += line_size; + } + + mask = gdk_bitmap_create_from_data(NULL, bits, ih->currentwidth, ih->currentheight); + + free(bits); + + return mask; +} + +void* iupdrvImageLoad(const char* name, int type) +{ + if (type == IUPIMAGE_CURSOR) +#if GTK_CHECK_VERSION(2, 8, 0) + return gdk_cursor_new_from_name(gdk_display_get_default(), name); +#else + return NULL; +#endif + else + { + GtkIconTheme *icon_theme; + GdkPixbuf *pixbuf = NULL; + + icon_theme = gtk_icon_theme_get_default(); + if (gtk_icon_theme_has_icon(icon_theme, name)) + { + GError *error = NULL; + pixbuf = gtk_icon_theme_load_icon(icon_theme, name, + 24, /* size */ + 0, /* flags */ + &error); + if (error) + g_error_free(error); + } + + if (!pixbuf) + { + GError *error = NULL; + pixbuf = gdk_pixbuf_new_from_file(name, &error); + if (error) + g_error_free(error); + } + + return pixbuf; + } +} + +int iupdrvImageGetInfo(void* handle, int *w, int *h, int *bpp) +{ + GdkPixbuf* pixbuf = (GdkPixbuf*)handle; + if (!GDK_IS_PIXBUF(pixbuf)) + { + if (w) *w = 0; + if (h) *h = 0; + if (bpp) *bpp = 0; + return 0; + } + if (w) *w = gdk_pixbuf_get_width(pixbuf); + if (h) *h = gdk_pixbuf_get_height(pixbuf); + if (bpp) *bpp = iupImageNormBpp(gdk_pixbuf_get_bits_per_sample(pixbuf)*gdk_pixbuf_get_n_channels(pixbuf)); + return 1; +} + +int iupdrvImageGetRawInfo(void* handle, int *w, int *h, int *bpp, iupColor* colors, int *colors_count) +{ + /* GdkPixbuf are only 24 bpp or 32 bpp */ + (void)colors; + (void)colors_count; + return iupdrvImageGetInfo(handle, w, h, bpp); +} + +void iupdrvImageDestroy(void* handle, int type) +{ + if (type == IUPIMAGE_CURSOR) + gdk_cursor_unref((GdkCursor*)handle); + else + g_object_unref(handle); +} diff --git a/iup/src/gtk/iupgtk_key.c b/iup/src/gtk/iupgtk_key.c new file mode 100755 index 0000000..5aec919 --- /dev/null +++ b/iup/src/gtk/iupgtk_key.c @@ -0,0 +1,422 @@ +/** \file + * \brief GTK Driver keyboard mapping + * + * See Copyright Notice in "iup.h" + */ + +#include <gtk/gtk.h> +#include <gdk/gdkkeysyms.h> + +#include <stdlib.h> +#include <stdio.h> + +#include "iup.h" +#include "iupcbs.h" +#include "iupkey.h" + +#include "iup_object.h" +#include "iup_key.h" + +#include "iupgtk_drv.h" + + +typedef struct _Igtk2iupkey +{ + guint gtkcode; + int iupcode; + int s_iupcode; + int c_iupcode; + int m_iupcode; + int y_iupcode; +} Igtk2iupkey; + +static Igtk2iupkey gtkkey_map[] = { + +{ GDK_Escape, K_ESC, K_sESC, K_cESC, K_mESC ,K_yESC }, +{ GDK_Pause, K_PAUSE, K_sPAUSE, K_cPAUSE, K_mPAUSE ,K_yPAUSE }, +{ GDK_Print, K_Print, K_sPrint, K_cPrint, K_mPrint ,K_yPrint }, +{ GDK_Menu, K_Menu, K_sMenu, K_cMenu, K_mMenu ,K_yMenu }, + +{ GDK_Home, K_HOME, K_sHOME, K_cHOME, K_mHOME ,K_yHOME }, +{ GDK_Up, K_UP, K_sUP, K_cUP, K_mUP ,K_yUP }, +{ GDK_Prior, K_PGUP, K_sPGUP, K_cPGUP, K_mPGUP ,K_yPGUP }, +{ GDK_Left, K_LEFT, K_sLEFT, K_cLEFT, K_mLEFT ,K_yLEFT }, +{ GDK_Begin, K_MIDDLE,K_sMIDDLE, K_cMIDDLE,K_mMIDDLE,K_yMIDDLE}, +{ GDK_Right, K_RIGHT, K_sRIGHT, K_cRIGHT, K_mRIGHT ,K_yRIGHT }, +{ GDK_End, K_END, K_sEND, K_cEND, K_mEND ,K_yEND }, +{ GDK_Down, K_DOWN, K_sDOWN, K_cDOWN, K_mDOWN ,K_yDOWN }, +{ GDK_Next, K_PGDN, K_sPGDN, K_cPGDN, K_mPGDN ,K_yPGDN }, +{ GDK_Insert, K_INS, K_sINS, K_cINS, K_mINS ,K_yINS }, +{ GDK_Delete, K_DEL, K_sDEL, K_cDEL, K_mDEL ,K_yDEL }, +{ GDK_space, K_SP, K_sSP, K_cSP, K_mSP ,K_ySP }, +{ GDK_Tab, K_TAB, K_sTAB, K_cTAB, K_mTAB ,K_yTAB }, +{ GDK_Return, K_CR, K_sCR, K_cCR, K_mCR ,K_yCR }, +{ GDK_BackSpace, K_BS, K_sBS, K_cBS, K_mBS ,K_yBS }, + +{ GDK_1, K_1, K_exclam, K_c1, K_m1, K_y1 }, +{ GDK_2, K_2, K_at, K_c2, K_m2, K_y2 }, +{ GDK_3, K_3, K_numbersign, K_c3, K_m3, K_y3 }, +{ GDK_4, K_4, K_dollar, K_c4, K_m4, K_y4 }, +{ GDK_5, K_5, K_percent, K_c5, K_m5, K_y5 }, +{ GDK_6, K_6, K_circum, K_c6, K_m6, K_y6 }, +{ GDK_7, K_7, K_ampersand, K_c7, K_m7, K_y7 }, +{ GDK_8, K_8, K_asterisk, K_c8, K_m8, K_y8 }, +{ GDK_9, K_9, K_parentleft, K_c9, K_m9, K_y9 }, +{ GDK_0, K_0, K_parentright, K_c0, K_m0, K_y0 }, + +/* Shift will be flaged so s_iupcode will contain the right code */ +{ GDK_exclam, K_1, K_exclam, K_c1, K_m1, K_y1 }, +{ GDK_at, K_2, K_at, K_c2, K_m2, K_y2 }, +{ GDK_numbersign, K_3, K_numbersign, K_c3, K_m3, K_y3 }, +{ GDK_dollar, K_4, K_dollar, K_c4, K_m4, K_y4 }, +{ GDK_percent, K_5, K_percent, K_c5, K_m5, K_y5 }, +{ GDK_dead_diaeresis, K_6, K_circum, K_c6, K_m6, K_y6 }, +{ GDK_ampersand, K_7, K_ampersand, K_c7, K_m7, K_y7 }, +{ GDK_asterisk, K_8, K_asterisk, K_c8, K_m8, K_y8 }, +{ GDK_parenleft, K_9, K_parentleft, K_c9, K_m9, K_y9 }, +{ GDK_parenright, K_0, K_parentright, K_c0, K_m0, K_y0 }, + +{ GDK_a, K_a, K_A, K_cA, K_mA, K_yA }, +{ GDK_b, K_b, K_B, K_cB, K_mB, K_yB }, +{ GDK_c, K_c, K_C, K_cC, K_mC, K_yC }, +{ GDK_d, K_d, K_D, K_cD, K_mD, K_yD }, +{ GDK_e, K_e, K_E, K_cE, K_mE, K_yE }, +{ GDK_f, K_f, K_F, K_cF, K_mF, K_yF }, +{ GDK_g, K_g, K_G, K_cG, K_mG, K_yG }, +{ GDK_h, K_h, K_H, K_cH, K_mH, K_yH }, +{ GDK_i, K_i, K_I, K_cI, K_mI, K_yI }, +{ GDK_j, K_j, K_J, K_cJ, K_mJ, K_yJ }, +{ GDK_k, K_k, K_K, K_cK, K_mK, K_yK }, +{ GDK_l, K_l, K_L, K_cL, K_mL, K_yL }, +{ GDK_m, K_m, K_M, K_cM, K_mM, K_yM }, +{ GDK_n, K_n, K_N, K_cN, K_mN, K_yN }, +{ GDK_o, K_o, K_O, K_cO, K_mO, K_yO }, +{ GDK_p, K_p, K_P, K_cP, K_mP, K_yP }, +{ GDK_q, K_q, K_Q, K_cQ, K_mQ, K_yQ }, +{ GDK_r, K_r, K_R, K_cR, K_mR, K_yR }, +{ GDK_s, K_s, K_S, K_cS, K_mS, K_yS }, +{ GDK_t, K_t, K_T, K_cT, K_mT, K_yT }, +{ GDK_u, K_u, K_U, K_cU, K_mU, K_yU }, +{ GDK_v, K_v, K_V, K_cV, K_mV, K_yV }, +{ GDK_w, K_w, K_W, K_cW, K_mW, K_yW }, +{ GDK_x, K_x, K_X, K_cX, K_mX, K_yX }, +{ GDK_y, K_y, K_Y, K_cY, K_mY, K_yY }, +{ GDK_z, K_z, K_Z, K_cZ, K_mZ, K_yZ }, + +/* Shift will be flaged so s_iupcode will contain the right code */ +{ GDK_A, K_a, K_A, K_cA, K_mA, K_yA }, +{ GDK_B, K_b, K_B, K_cB, K_mB, K_yB }, +{ GDK_C, K_c, K_C, K_cC, K_mC, K_yC }, +{ GDK_D, K_d, K_D, K_cD, K_mD, K_yD }, +{ GDK_E, K_e, K_E, K_cE, K_mE, K_yE }, +{ GDK_F, K_f, K_F, K_cF, K_mF, K_yF }, +{ GDK_G, K_g, K_G, K_cG, K_mG, K_yG }, +{ GDK_H, K_h, K_H, K_cH, K_mH, K_yH }, +{ GDK_I, K_i, K_I, K_cI, K_mI, K_yI }, +{ GDK_J, K_j, K_J, K_cJ, K_mJ, K_yJ }, +{ GDK_K, K_k, K_K, K_cK, K_mK, K_yK }, +{ GDK_L, K_l, K_L, K_cL, K_mL, K_yL }, +{ GDK_M, K_m, K_M, K_cM, K_mM, K_yM }, +{ GDK_N, K_n, K_N, K_cN, K_mN, K_yN }, +{ GDK_O, K_o, K_O, K_cO, K_mO, K_yO }, +{ GDK_P, K_p, K_P, K_cP, K_mP, K_yP }, +{ GDK_Q, K_q, K_Q, K_cQ, K_mQ, K_yQ }, +{ GDK_R, K_r, K_R, K_cR, K_mR, K_yR }, +{ GDK_S, K_s, K_S, K_cS, K_mS, K_yS }, +{ GDK_T, K_t, K_T, K_cT, K_mT, K_yT }, +{ GDK_U, K_u, K_U, K_cU, K_mU, K_yU }, +{ GDK_V, K_v, K_V, K_cV, K_mV, K_yV }, +{ GDK_W, K_w, K_W, K_cW, K_mW, K_yW }, +{ GDK_X, K_x, K_X, K_cX, K_mX, K_yX }, +{ GDK_Y, K_y, K_Y, K_cY, K_mY, K_yY }, +{ GDK_Z, K_z, K_Z, K_cZ, K_mZ, K_yZ }, + +{ GDK_F1, K_F1, K_sF1, K_cF1, K_mF1, K_yF1 }, +{ GDK_F2, K_F2, K_sF2, K_cF2, K_mF2, K_yF2 }, +{ GDK_F3, K_F3, K_sF3, K_cF3, K_mF3, K_yF3 }, +{ GDK_F4, K_F4, K_sF4, K_cF4, K_mF4, K_yF4 }, +{ GDK_F5, K_F5, K_sF5, K_cF5, K_mF5, K_yF5 }, +{ GDK_F6, K_F6, K_sF6, K_cF6, K_mF6, K_yF6 }, +{ GDK_F7, K_F7, K_sF7, K_cF7, K_mF7, K_yF7 }, +{ GDK_F8, K_F8, K_sF8, K_cF8, K_mF8, K_yF8 }, +{ GDK_F9, K_F9, K_sF9, K_cF9, K_mF9, K_yF9 }, +{ GDK_F10, K_F10, K_sF10, K_cF10, K_mF10, K_yF10 }, +{ GDK_F11, K_F11, K_sF11, K_cF11, K_mF11, K_yF11 }, +{ GDK_F12, K_F12, K_sF12, K_cF12, K_mF12, K_yF12 }, + +{ GDK_semicolon, K_semicolon, K_colon, K_cSemicolon, K_mSemicolon, K_ySemicolon }, +{ GDK_equal, K_equal, K_plus, K_cEqual, K_mEqual, K_yEqual }, +{ GDK_comma, K_comma, K_less, K_cComma, K_mComma, K_yComma }, +{ GDK_minus, K_minus, K_underscore, K_cMinus, K_mMinus, K_yMinus }, +{ GDK_period, K_period, K_greater, K_cPeriod, K_mPeriod, K_yPeriod }, +{ GDK_slash, K_slash, K_question, K_cSlash, K_mSlash, K_ySlash }, +{ GDK_grave, K_grave, K_tilde, 0, 0, 0 }, +{ GDK_bracketleft, K_bracketleft, K_braceleft, K_cBracketleft, K_mBracketleft, K_yBracketleft }, +{ GDK_backslash, K_backslash, K_bar, K_cBackslash, K_mBackslash, K_yBackslash }, +{ GDK_bracketright,K_bracketright, K_braceright, K_cBracketright,K_mBracketright,K_yBracketright }, +{ GDK_apostrophe, K_apostrophe, K_quotedbl, 0, 0, 0 }, + +/* Shift will be flaged so s_iupcode will contain the right code */ +{ GDK_colon, K_semicolon, K_colon, K_cSemicolon, K_mSemicolon, K_ySemicolon }, +{ GDK_plus, K_equal, K_plus, K_cEqual, K_mEqual, K_yEqual }, +{ GDK_less, K_comma, K_less, K_cComma, K_mComma, K_yComma }, +{ GDK_underscore, K_minus, K_underscore, K_cMinus, K_mMinus, K_yMinus }, +{ GDK_greater, K_period, K_greater, K_cPeriod, K_mPeriod, K_yPeriod }, +{ GDK_question, K_slash, K_question, K_cSlash, K_mSlash, K_ySlash }, +{ GDK_braceleft, K_bracketleft, K_braceleft, K_cBracketleft, K_mBracketleft, K_yBracketleft }, +{ GDK_bar, K_backslash, K_bar, K_cBackslash, K_mBackslash, K_yBackslash }, +{ GDK_braceright, K_bracketright, K_braceright, K_cBracketright,K_mBracketright,K_yBracketright }, +{ GDK_quotedbl, K_apostrophe, K_quotedbl, 0, 0, 0 }, + +{ GDK_KP_0, K_0, K_0, K_c0, K_m0, K_y0 }, +{ GDK_KP_1, K_1, K_1, K_c1, K_m1, K_y1 }, +{ GDK_KP_2, K_2, K_2, K_c2, K_m2, K_y2 }, +{ GDK_KP_3, K_3, K_3, K_c3, K_m3, K_y3 }, +{ GDK_KP_4, K_4, K_4, K_c4, K_m4, K_y4 }, +{ GDK_KP_5, K_5, K_5, K_c5, K_m5, K_y5 }, +{ GDK_KP_6, K_6, K_6, K_c6, K_m6, K_y6 }, +{ GDK_KP_7, K_7, K_7, K_c7, K_m7, K_y7 }, +{ GDK_KP_8, K_8, K_8, K_c8, K_m8, K_y8 }, +{ GDK_KP_9, K_9, K_9, K_c9, K_m9, K_y9 }, +{ GDK_KP_Multiply, K_asterisk, K_sAsterisk, K_cAsterisk, K_mAsterisk, K_yAsterisk }, +{ GDK_KP_Add, K_plus, K_sPlus, K_cPlus, K_mPlus, K_yPlus }, +{ GDK_KP_Subtract, K_minus, K_sMinus, K_cMinus, K_mMinus, K_yMinus }, +{ GDK_KP_Decimal, K_period, K_sPeriod, K_cPeriod, K_mPeriod, K_yPeriod }, +{ GDK_KP_Divide, K_slash, K_sSlash, K_cSlash, K_mSlash, K_ySlash }, +{ GDK_KP_Separator, K_comma, K_sComma, K_cComma, K_mComma, K_yComma }, + +{ GDK_ccedilla, K_ccedilla, K_Ccedilla, K_cCcedilla, K_mCcedilla, K_yCcedilla }, +{ GDK_Ccedilla, K_ccedilla, K_Ccedilla, K_cCcedilla, K_mCcedilla, K_yCcedilla }, + +{ GDK_dead_tilde, K_tilde, K_circum, 0, 0, 0 }, +{ GDK_dead_acute, K_acute, K_grave, 0, 0, 0 }, +{ GDK_dead_grave, K_grave, K_tilde, 0, 0, 0 }, +{ GDK_dead_circumflex, K_tilde, K_circum, 0, 0, 0 }, + +{ GDK_KP_F1, K_F1, K_sF1, K_cF1, K_mF1, K_yF1 }, +{ GDK_KP_F2, K_F2, K_sF2, K_cF2, K_mF2, K_yF2 }, +{ GDK_KP_F3, K_F3, K_sF3, K_cF3, K_mF3, K_yF3 }, +{ GDK_KP_F4, K_F4, K_sF4, K_cF4, K_mF4, K_yF4 }, +{ GDK_KP_Space, K_SP, K_sSP, K_cSP, K_mSP ,K_ySP }, +{ GDK_KP_Tab, K_TAB, K_sTAB, K_cTAB, K_mTAB ,K_yTAB }, +{ GDK_KP_Equal, K_equal, 0, K_cEqual, K_mEqual, K_yEqual }, + +{ GDK_KP_Enter, K_CR, K_sCR, K_cCR, K_mCR, K_yCR }, +{ GDK_KP_Home, K_HOME, K_sHOME, K_cHOME, K_mHOME, K_yHOME }, +{ GDK_KP_Up, K_UP, K_sUP, K_cUP, K_mUP, K_yUP }, +{ GDK_KP_Page_Up, K_PGUP, K_sPGUP, K_cPGUP, K_mPGUP, K_yPGUP }, +{ GDK_KP_Left, K_LEFT, K_sLEFT, K_cLEFT, K_mLEFT, K_yLEFT }, +{ GDK_KP_Begin, K_MIDDLE,K_sMIDDLE, K_cMIDDLE,K_mMIDDLE,K_yMIDDLE}, +{ GDK_KP_Right, K_RIGHT, K_sRIGHT, K_cRIGHT, K_mRIGHT, K_yRIGHT }, +{ GDK_KP_End, K_END, K_sEND, K_cEND, K_mEND, K_yEND }, +{ GDK_KP_Down, K_DOWN, K_sDOWN, K_cDOWN, K_mDOWN, K_yDOWN }, +{ GDK_KP_Page_Down, K_PGDN, K_sPGDN, K_cPGDN, K_mPGDN, K_yPGDN }, +{ GDK_KP_Insert, K_INS, K_sINS, K_cINS, K_mINS, K_yINS }, +{ GDK_KP_Delete, K_DEL, K_sDEL, K_cDEL, K_mDEL, K_yDEL } + +}; + +void iupgtkKeyEncode(int key, guint *keyval, guint *state) +{ + int i, iupcode = key & 0xFF; /* 0-255 interval */ + int count = sizeof(gtkkey_map)/sizeof(gtkkey_map[0]); + for (i = 0; i < count; i++) + { + Igtk2iupkey* key_map = &(gtkkey_map[i]); + if (key_map->iupcode == iupcode) + { + *keyval = key_map->gtkcode; + *state = 0; + + if (iupcode != key) + { + if (key_map->c_iupcode == key) + *state = GDK_CONTROL_MASK; + else if (key_map->m_iupcode == key) + *state = GDK_MOD1_MASK; + else if (key_map->y_iupcode == key) + *state = GDK_MOD4_MASK; + else if (key_map->s_iupcode == key) + *state = GDK_SHIFT_MASK; + } + return; + } + else if (key_map->s_iupcode == key) /* There are Shift keys bellow 256 */ + { + *keyval = key_map->gtkcode; + *state = GDK_SHIFT_MASK; + + if ((*keyval >= GDK_a) && + (*keyval <= GDK_z)) + { + /* remap to upper case */ + *keyval -= GDK_a-GDK_A; + } + return; + } + } +} + +static int gtkKeyMap2Iup(int state, int i) +{ + int code = 0; + if (state & GDK_CONTROL_MASK) /* Ctrl */ + code = gtkkey_map[i].c_iupcode; + else if (state & GDK_MOD1_MASK || + state & GDK_MOD5_MASK) /* Alt */ + code = gtkkey_map[i].m_iupcode; + else if (state & GDK_MOD4_MASK) /* Apple/Win */ + code = gtkkey_map[i].y_iupcode; + else if (state & GDK_LOCK_MASK) /* CapsLock */ + { + if ((state & GDK_SHIFT_MASK) || !iupKeyCanCaps(gtkkey_map[i].iupcode)) + return gtkkey_map[i].iupcode; + else + code = gtkkey_map[i].s_iupcode; + } + else if (state & GDK_SHIFT_MASK) /* Shift */ + code = gtkkey_map[i].s_iupcode; + else + return gtkkey_map[i].iupcode; + + if (!code) + code = gtkkey_map[i].iupcode; + + return code; +} + +static int gtkKeyDecode(GdkEventKey *evt) +{ + int i; + int count = sizeof(gtkkey_map)/sizeof(gtkkey_map[0]); + guint keyval = evt->keyval; + + if ((evt->state & GDK_MOD2_MASK) && /* NumLock */ + (keyval >= GDK_KP_Home) && + (keyval <= GDK_KP_Delete)) + { + /* remap to numeric keys */ + guint remap_numkey[] = {GDK_KP_7, GDK_KP_4, GDK_KP_8, GDK_KP_6, GDK_KP_2, GDK_KP_9, GDK_KP_3, GDK_KP_1, GDK_KP_5, GDK_KP_0, GDK_KP_Decimal}; + keyval = remap_numkey[keyval-GDK_KP_Home]; + } + + for (i = 0; i < count; i++) + { + if (gtkkey_map[i].gtkcode == keyval) + return gtkKeyMap2Iup(evt->state, i); + } + + return 0; +} + +gboolean iupgtkKeyPressEvent(GtkWidget *widget, GdkEventKey *evt, Ihandle *ih) +{ + int result; + int code = gtkKeyDecode(evt); + if (code == 0) + return FALSE; + + /* Avoid duplicate calls if a child of the dialog contains the focus. + GTK will call the callback for the child and for the dialog */ + if (ih->iclass->nativetype == IUP_TYPEDIALOG && ih != IupGetFocus()) + return FALSE; + + result = iupKeyCallKeyCb(ih, code); + if (result == IUP_CLOSE) + { + IupExitLoop(); + return FALSE; + } + if (result == IUP_IGNORE) + return TRUE; + + /* in the previous callback the dialog could be destroyed */ + if (iupObjectCheck(ih)) + { + /* this is called only for canvas */ + if (ih->iclass->nativetype == IUP_TYPECANVAS) + { + result = iupKeyCallKeyPressCb(ih, code, 1); + if (result == IUP_CLOSE) + { + IupExitLoop(); + return FALSE; + } + if (result == IUP_IGNORE) + return TRUE; + } + + if (!iupKeyProcessNavigation(ih, code, evt->state & GDK_SHIFT_MASK)) + return TRUE; + + /* compensate the show-help limitation. + * It is not called on F1, only on Shift+F1 and Ctrl+F1. */ + if (code == K_F1) + { + Icallback cb = IupGetCallback(ih, "HELP_CB"); + if (cb) + { + if (cb(ih) == IUP_CLOSE) + IupExitLoop(); + } + } + } + + (void)widget; + return FALSE; +} + +gboolean iupgtkKeyReleaseEvent(GtkWidget *widget, GdkEventKey *evt, Ihandle *ih) +{ + /* this is called only for canvas */ + int result; + int code = gtkKeyDecode(evt); + if (code == 0) + return FALSE; + + result = iupKeyCallKeyPressCb(ih, code, 0); + if (result == IUP_CLOSE) + { + IupExitLoop(); + return FALSE; + } + if (result == IUP_IGNORE) + return TRUE; + + (void)widget; + return FALSE; +} + +void iupgtkButtonKeySetStatus(guint state, unsigned int but, char* status, int doubleclick) +{ + if (state & GDK_SHIFT_MASK) + iupKEYSETSHIFT(status); + + if (state & GDK_CONTROL_MASK) + iupKEYSETCONTROL(status); + + if ((state & GDK_BUTTON1_MASK) || but==1) + iupKEYSETBUTTON1(status); + + if ((state & GDK_BUTTON2_MASK) || but==2) + iupKEYSETBUTTON2(status); + + if ((state & GDK_BUTTON3_MASK) || but==3) + iupKEYSETBUTTON3(status); + + if ((state & GDK_BUTTON4_MASK) || but==4) + iupKEYSETBUTTON4(status); + + if ((state & GDK_BUTTON5_MASK) || but==5) + iupKEYSETBUTTON5(status); + + if (state & GDK_MOD1_MASK || state & GDK_MOD5_MASK) /* Alt */ + iupKEYSETALT(status); + + if (state & GDK_MOD4_MASK) /* Apple/Win */ + iupKEYSETSYS(status); + + if (doubleclick) + iupKEYSETDOUBLE(status); +} + diff --git a/iup/src/gtk/iupgtk_label.c b/iup/src/gtk/iupgtk_label.c new file mode 100755 index 0000000..49d5c6d --- /dev/null +++ b/iup/src/gtk/iupgtk_label.c @@ -0,0 +1,318 @@ +/** \file + * \brief Label Control + * + * See Copyright Notice in "iup.h" + */ + +#include <gtk/gtk.h> + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <memory.h> +#include <stdarg.h> + +#include "iup.h" +#include "iupcbs.h" + +#include "iup_object.h" +#include "iup_layout.h" +#include "iup_attrib.h" +#include "iup_str.h" +#include "iup_image.h" +#include "iup_label.h" +#include "iup_drv.h" +#include "iup_image.h" +#include "iup_focus.h" + +#include "iupgtk_drv.h" + + +static int gtkLabelSetTitleAttrib(Ihandle* ih, const char* value) +{ + if (ih->data->type == IUP_LABEL_TEXT) + { + GtkLabel* label = (GtkLabel*)ih->handle; + if (iupgtkSetMnemonicTitle(ih, label, value)) + { + Ihandle* next = iupFocusNextInteractive(ih); + if (next) + { + if (next->handle) + gtk_label_set_mnemonic_widget(label, next->handle); + else + iupAttribSetStr(next, "_IUPGTK_LABELMNEMONIC", (char*)label); /* used by iupgtkUpdateMnemonic */ + } + } + return 1; + } + + return 0; +} + +static int gtkLabelSetWordWrapAttrib(Ihandle* ih, const char* value) +{ + if (ih->data->type == IUP_LABEL_TEXT) + { + GtkLabel* label = (GtkLabel*)ih->handle; + if (iupStrBoolean(value)) + gtk_label_set_line_wrap(label, TRUE); + else + gtk_label_set_line_wrap(label, FALSE); + return 1; + } + return 0; +} + +static int gtkLabelSetEllipsisAttrib(Ihandle* ih, const char* value) +{ + if (ih->data->type == IUP_LABEL_TEXT) + { +#if GTK_CHECK_VERSION(2, 6, 0) + GtkLabel* label = (GtkLabel*)ih->handle; + if (iupStrBoolean(value)) + gtk_label_set_ellipsize(label, PANGO_ELLIPSIZE_END); + else + gtk_label_set_ellipsize(label, PANGO_ELLIPSIZE_NONE); +#endif + return 1; + } + return 0; +} + +static int gtkLabelSetAlignmentAttrib(Ihandle* ih, const char* value) +{ + if (ih->data->type != IUP_LABEL_SEP_HORIZ && ih->data->type != IUP_LABEL_SEP_VERT) + { + GtkMisc* misc = (GtkMisc*)ih->handle; + PangoAlignment alignment; + float xalign, yalign; + char value1[30]="", value2[30]=""; + + iupStrToStrStr(value, value1, value2, ':'); + + if (iupStrEqualNoCase(value1, "ARIGHT")) + { + xalign = 1.0f; + alignment = PANGO_ALIGN_RIGHT; + } + else if (iupStrEqualNoCase(value1, "ACENTER")) + { + xalign = 0.5f; + alignment = PANGO_ALIGN_CENTER; + } + else /* "ALEFT" */ + { + xalign = 0; + alignment = PANGO_ALIGN_LEFT; + } + + if (iupStrEqualNoCase(value2, "ABOTTOM")) + yalign = 1.0f; + else if (iupStrEqualNoCase(value2, "ATOP")) + yalign = 0; + else /* ACENTER (default) */ + yalign = 0.5f; + + gtk_misc_set_alignment(misc, xalign, yalign); + + if (ih->data->type == IUP_LABEL_TEXT) + pango_layout_set_alignment(gtk_label_get_layout((GtkLabel*)ih->handle), alignment); + + return 1; + } + else + return 0; +} + +static int gtkLabelSetPaddingAttrib(Ihandle* ih, const char* value) +{ + iupStrToIntInt(value, &ih->data->horiz_padding, &ih->data->vert_padding, 'x'); + + if (ih->handle && ih->data->type != IUP_LABEL_SEP_HORIZ && ih->data->type != IUP_LABEL_SEP_VERT) + { + GtkMisc* misc = (GtkMisc*)ih->handle; + gtk_misc_set_padding(misc, ih->data->horiz_padding, ih->data->vert_padding); + } + return 0; +} + +static char* gtkLabelGetPangoLayoutAttrib(Ihandle* ih) +{ + if (ih->data->type == IUP_LABEL_TEXT) + return (char*)gtk_label_get_layout((GtkLabel*)ih->handle); + else + return NULL; +} + +static void gtkLabelSetPixbuf(Ihandle* ih, const char* name, int make_inactive) +{ + GtkImage* image_label = (GtkImage*)ih->handle; + + if (name) + { + GdkPixbuf* pixbuf = iupImageGetImage(name, ih, make_inactive); + GdkPixbuf* old_pixbuf = gtk_image_get_pixbuf(image_label); + if (pixbuf != old_pixbuf) + gtk_image_set_from_pixbuf(image_label, pixbuf); + return; + } + + /* if not defined */ +#if GTK_CHECK_VERSION(2, 8, 0) + gtk_image_clear(image_label); +#endif +} + +static int gtkLabelSetImageAttrib(Ihandle* ih, const char* value) +{ + if (ih->data->type == IUP_LABEL_IMAGE) + { + if (iupdrvIsActive(ih)) + gtkLabelSetPixbuf(ih, value, 0); + else + { + if (!iupAttribGet(ih, "IMINACTIVE")) + { + /* if not active and IMINACTIVE is not defined + then automaticaly create one based on IMAGE */ + gtkLabelSetPixbuf(ih, value, 1); /* make_inactive */ + } + } + return 1; + } + else + return 0; +} + +static int gtkLabelSetImInactiveAttrib(Ihandle* ih, const char* value) +{ + if (ih->data->type == IUP_LABEL_IMAGE) + { + if (!iupdrvIsActive(ih)) + { + if (value) + gtkLabelSetPixbuf(ih, value, 0); + else + { + /* if not defined then automaticaly create one based on IMAGE */ + char* name = iupAttribGet(ih, "IMAGE"); + gtkLabelSetPixbuf(ih, name, 1); /* make_inactive */ + } + } + return 1; + } + else + return 0; +} + +static int gtkLabelSetActiveAttrib(Ihandle* ih, const char* value) +{ + /* update the inactive image if necessary */ + if (ih->data->type == IUP_LABEL_IMAGE) + { + if (!iupStrBoolean(value)) + { + char* name = iupAttribGet(ih, "IMINACTIVE"); + if (name) + gtkLabelSetPixbuf(ih, name, 0); + else + { + /* if not defined then automaticaly create one based on IMAGE */ + name = iupAttribGet(ih, "IMAGE"); + gtkLabelSetPixbuf(ih, name, 1); /* make_inactive */ + } + } + else + { + /* must restore the normal image */ + char* name = iupAttribGet(ih, "IMAGE"); + gtkLabelSetPixbuf(ih, name, 0); + } + } + + return iupBaseSetActiveAttrib(ih, value); +} + +static int gtkLabelMapMethod(Ihandle* ih) +{ + char* value; + GtkWidget *label; + + value = iupAttribGet(ih, "SEPARATOR"); + if (value) + { + if (iupStrEqualNoCase(value, "HORIZONTAL")) + { + ih->data->type = IUP_LABEL_SEP_HORIZ; + label = gtk_hseparator_new(); + } + else /* "VERTICAL" */ + { + ih->data->type = IUP_LABEL_SEP_VERT; + label = gtk_vseparator_new(); + } + } + else + { + value = iupAttribGet(ih, "IMAGE"); + if (value) + { + ih->data->type = IUP_LABEL_IMAGE; + label = gtk_image_new(); + } + else + { + ih->data->type = IUP_LABEL_TEXT; + label = gtk_label_new(NULL); + } + } + + if (!label) + return IUP_ERROR; + + ih->handle = label; + + /* add to the parent, all GTK controls must call this. */ + iupgtkBaseAddToParent(ih); + + gtk_widget_realize(label); + + return IUP_NOERROR; +} + +void iupdrvLabelInitClass(Iclass* ic) +{ + /* Driver Dependent Class functions */ + ic->Map = gtkLabelMapMethod; + + /* Driver Dependent Attribute functions */ + + /* Common GTK only (when text is in a secondary element) */ + iupClassRegisterAttribute(ic, "PANGOLAYOUT", gtkLabelGetPangoLayoutAttrib, NULL, NULL, NULL, IUPAF_NO_INHERIT); + + /* Overwrite Visual */ + iupClassRegisterAttribute(ic, "ACTIVE", iupBaseGetActiveAttrib, gtkLabelSetActiveAttrib, IUPAF_SAMEASSYSTEM, "YES", IUPAF_DEFAULT); + + /* Visual */ + iupClassRegisterAttribute(ic, "BGCOLOR", NULL, iupdrvBaseSetBgColorAttrib, IUPAF_SAMEASSYSTEM, "DLGBGCOLOR", IUPAF_DEFAULT); + + /* Special */ + iupClassRegisterAttribute(ic, "FGCOLOR", NULL, iupdrvBaseSetFgColorAttrib, IUPAF_SAMEASSYSTEM, "DLGFGCOLOR", IUPAF_DEFAULT); + iupClassRegisterAttribute(ic, "TITLE", NULL, gtkLabelSetTitleAttrib, NULL, NULL, IUPAF_NO_DEFAULTVALUE|IUPAF_NO_INHERIT); + + /* IupLabel only */ + iupClassRegisterAttribute(ic, "ALIGNMENT", NULL, gtkLabelSetAlignmentAttrib, "ALEFT:ACENTER", NULL, IUPAF_NO_INHERIT); /* force new default value */ + iupClassRegisterAttribute(ic, "IMAGE", NULL, gtkLabelSetImageAttrib, NULL, NULL, IUPAF_NO_DEFAULTVALUE|IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "PADDING", iupLabelGetPaddingAttrib, gtkLabelSetPaddingAttrib, IUPAF_SAMEASSYSTEM, "0x0", IUPAF_NOT_MAPPED); + + /* IupLabel GTK and Motif only */ + iupClassRegisterAttribute(ic, "IMINACTIVE", NULL, gtkLabelSetImInactiveAttrib, NULL, NULL, IUPAF_NO_DEFAULTVALUE|IUPAF_NO_INHERIT); + + /* IupLabel Windows and GTK only */ + iupClassRegisterAttribute(ic, "WORDWRAP", NULL, gtkLabelSetWordWrapAttrib, NULL, NULL, IUPAF_DEFAULT); + iupClassRegisterAttribute(ic, "ELLIPSIS", NULL, gtkLabelSetEllipsisAttrib, NULL, NULL, IUPAF_DEFAULT); + + /* IupLabel GTK only */ + iupClassRegisterAttribute(ic, "MARKUP", NULL, NULL, NULL, NULL, IUPAF_DEFAULT); +} diff --git a/iup/src/gtk/iupgtk_list.c b/iup/src/gtk/iupgtk_list.c new file mode 100755 index 0000000..80f6cce --- /dev/null +++ b/iup/src/gtk/iupgtk_list.c @@ -0,0 +1,1439 @@ +/** \file + * \brief List Control + * + * See Copyright Notice in "iup.h" + */ + +#include <gtk/gtk.h> +#include <gdk/gdkkeysyms.h> + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <memory.h> +#include <stdarg.h> + +#include "iup.h" +#include "iupcbs.h" + +#include "iup_object.h" +#include "iup_layout.h" +#include "iup_attrib.h" +#include "iup_str.h" +#include "iup_drv.h" +#include "iup_drvfont.h" +#include "iup_mask.h" +#include "iup_key.h" +#include "iup_list.h" + +#include "iupgtk_drv.h" + + +static void gtkListSelectionChanged(GtkTreeSelection* selection, Ihandle* ih); +static void gtkListComboBoxChanged(GtkComboBox* widget, Ihandle* ih); + + +void iupdrvListAddItemSpace(Ihandle* ih, int *h) +{ + (void)ih; + *h += 3; +} + +void iupdrvListAddBorders(Ihandle* ih, int *x, int *y) +{ + int border_size = 2*5; + (*x) += border_size; + (*y) += border_size; + + if (ih->data->is_dropdown) + { +#ifdef HILDON + (*x) += 9; /* extra space for the dropdown button */ +#else + (*x) += 5; /* extra space for the dropdown button */ +#endif + + if (ih->data->has_editbox) + (*x) += 5; /* another extra space for the dropdown button */ + else + { + (*y) += 4; /* extra padding space */ + (*x) += 4; /* extra padding space */ + } + } + else + { + if (ih->data->has_editbox) + (*y) += 2*3; /* internal border between editbox and list */ + } +} + +static int gtkListConvertXYToPos(Ihandle* ih, int x, int y) +{ + if (!ih->data->is_dropdown) + { + GtkTreePath* path; + if (gtk_tree_view_get_dest_row_at_pos((GtkTreeView*)ih->handle, x, y, &path, NULL)) + { + int* indices = gtk_tree_path_get_indices(path); + int pos = indices[0]+1; /* IUP starts at 1 */ + gtk_tree_path_free (path); + return pos; + } + } + + return -1; +} + +static GtkTreeModel* gtkListGetModel(Ihandle* ih) +{ + if (ih->data->is_dropdown) + return gtk_combo_box_get_model((GtkComboBox*)ih->handle); + else + return gtk_tree_view_get_model((GtkTreeView*)ih->handle); +} + +int iupdrvListGetCount(Ihandle* ih) +{ + GtkTreeModel *model = gtkListGetModel(ih); + return gtk_tree_model_iter_n_children(model, NULL); +} + +void iupdrvListAppendItem(Ihandle* ih, const char* value) +{ + GtkTreeModel *model = gtkListGetModel(ih); + GtkTreeIter iter; + gtk_list_store_append(GTK_LIST_STORE(model), &iter); + gtk_list_store_set(GTK_LIST_STORE(model), &iter, 0, iupgtkStrConvertToUTF8(value), -1); +} + +void iupdrvListInsertItem(Ihandle* ih, int pos, const char* value) +{ + GtkTreeModel *model = gtkListGetModel(ih); + GtkTreeIter iter; + gtk_list_store_insert(GTK_LIST_STORE(model), &iter, pos); + gtk_list_store_set(GTK_LIST_STORE(model), &iter, 0, iupgtkStrConvertToUTF8(value), -1); +} + +void iupdrvListRemoveItem(Ihandle* ih, int pos) +{ + GtkTreeModel *model = gtkListGetModel(ih); + GtkTreeIter iter; + if (gtk_tree_model_iter_nth_child(model, &iter, NULL, pos)) + { + if (ih->data->is_dropdown && !ih->data->has_editbox) + { + /* must check if removing the current item */ + int curpos = gtk_combo_box_get_active((GtkComboBox*)ih->handle); + if (pos == curpos) + { + if (curpos > 0) curpos--; + else curpos++; + + gtk_combo_box_set_active((GtkComboBox*)ih->handle, curpos); + } + } + + gtk_list_store_remove(GTK_LIST_STORE(model), &iter); + } +} + +void iupdrvListRemoveAllItems(Ihandle* ih) +{ + GtkTreeModel *model = gtkListGetModel(ih); + gtk_list_store_clear(GTK_LIST_STORE(model)); +} + + +/*********************************************************************************/ + + +static int gtkListSetStandardFontAttrib(Ihandle* ih, const char* value) +{ + iupdrvSetStandardFontAttrib(ih, value); + + if (ih->handle) + { + if (ih->data->is_dropdown) + { + GtkCellRenderer* renderer = (GtkCellRenderer*)iupAttribGet(ih, "_IUPGTK_RENDERER"); + if (renderer) + { + g_object_set(G_OBJECT(renderer), "font-desc", (PangoFontDescription*)iupgtkGetPangoFontDescAttrib(ih), NULL); + iupgtkFontUpdateObjectPangoLayout(ih, G_OBJECT(renderer)); + } + } + + if (ih->data->has_editbox) + { + GtkEntry* entry = (GtkEntry*)iupAttribGet(ih, "_IUPGTK_ENTRY"); + gtk_widget_modify_font((GtkWidget*)entry, (PangoFontDescription*)iupgtkGetPangoFontDescAttrib(ih)); + iupgtkFontUpdatePangoLayout(ih, gtk_entry_get_layout(entry)); + } + } + return 1; +} + +static char* gtkListGetIdValueAttrib(Ihandle* ih, const char* name_id) +{ + int pos = iupListGetPos(ih, name_id); + if (pos != -1) + { + GtkTreeIter iter; + GtkTreeModel* model = gtkListGetModel(ih); + if (gtk_tree_model_iter_nth_child(model, &iter, NULL, pos)) + { + gchar *text = NULL; + gtk_tree_model_get(model, &iter, 0, &text, -1); + if (text) + { + char* ret_str = iupStrGetMemoryCopy(iupgtkStrConvertFromUTF8(text)); + g_free(text); + return ret_str; + } + } + } + return NULL; +} + +static int gtkListSetBgColorAttrib(Ihandle* ih, const char* value) +{ + unsigned char r, g, b; + + GtkScrolledWindow* scrolled_window = (GtkScrolledWindow*)iupAttribGet(ih, "_IUP_EXTRAPARENT"); + if (scrolled_window && !ih->data->is_dropdown) + { + /* ignore given value, must use only from parent for the scrollbars */ + char* parent_value = iupBaseNativeParentGetBgColor(ih); + + if (iupStrToRGB(parent_value, &r, &g, &b)) + { + GtkWidget* sb; + + if (!GTK_IS_SCROLLED_WINDOW(scrolled_window)) + scrolled_window = (GtkScrolledWindow*)iupAttribGet(ih, "_IUPGTK_SCROLLED_WINDOW"); + + iupgtkBaseSetBgColor((GtkWidget*)scrolled_window, r, g, b); + +#if GTK_CHECK_VERSION(2, 8, 0) + sb = gtk_scrolled_window_get_hscrollbar(scrolled_window); + if (sb) iupgtkBaseSetBgColor(sb, r, g, b); + + sb = gtk_scrolled_window_get_vscrollbar(scrolled_window); + if (sb) iupgtkBaseSetBgColor(sb, r, g, b); +#endif + } + } + + if (!iupStrToRGB(value, &r, &g, &b)) + return 0; + + if (ih->data->has_editbox) + { + GtkWidget* entry = (GtkWidget*)iupAttribGet(ih, "_IUPGTK_ENTRY"); + iupgtkBaseSetBgColor(entry, r, g, b); + } + + { + GtkCellRenderer* renderer = (GtkCellRenderer*)iupAttribGet(ih, "_IUPGTK_RENDERER"); + if (renderer) + { + GdkColor color; + iupgdkColorSet(&color, r, g, b); + g_object_set(G_OBJECT(renderer), "cell-background-gdk", &color, NULL); + } + } + + return iupdrvBaseSetBgColorAttrib(ih, value); +} + +static int gtkListSetFgColorAttrib(Ihandle* ih, const char* value) +{ + unsigned char r, g, b; + if (!iupStrToRGB(value, &r, &g, &b)) + return 0; + + iupgtkBaseSetFgColor(ih->handle, r, g, b); + + if (ih->data->has_editbox) + { + GtkWidget* entry = (GtkWidget*)iupAttribGet(ih, "_IUPGTK_ENTRY"); + iupgtkBaseSetFgColor(entry, r, g, b); + } + + { + GtkCellRenderer* renderer = (GtkCellRenderer*)iupAttribGet(ih, "_IUPGTK_RENDERER"); + if (renderer) + { + GdkColor color; + iupgdkColorSet(&color, r, g, b); + g_object_set(G_OBJECT(renderer), "foreground-gdk", &color, NULL); + } + } + + return 1; +} + +static char* gtkListGetValueAttrib(Ihandle* ih) +{ + if (ih->data->has_editbox) + { + GtkEntry* entry = (GtkEntry*)iupAttribGet(ih, "_IUPGTK_ENTRY"); + return iupStrGetMemoryCopy(iupgtkStrConvertFromUTF8(gtk_entry_get_text(entry))); + } + else + { + if (ih->data->is_dropdown) + { + int pos = gtk_combo_box_get_active((GtkComboBox*)ih->handle); + char* str = iupStrGetMemory(50); + sprintf(str, "%d", pos+1); /* IUP starts at 1 */ + return str; + } + else + { + GtkTreeSelection* selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(ih->handle)); + if (!ih->data->is_multiple) + { + GtkTreeIter iter; + GtkTreeModel* tree_model; + if (gtk_tree_selection_get_selected(selection, &tree_model, &iter)) + { + char* str; + GtkTreePath *path = gtk_tree_model_get_path(tree_model, &iter); + int* indices = gtk_tree_path_get_indices(path); + str = iupStrGetMemory(50); + sprintf(str, "%d", indices[0]+1); /* IUP starts at 1 */ + gtk_tree_path_free (path); + return str; + } + } + else + { + GList *il, *list = gtk_tree_selection_get_selected_rows(selection, NULL); + int count = iupdrvListGetCount(ih); + char* str = iupStrGetMemory(count+1); + memset(str, '-', count); + str[count]=0; + for (il=list; il; il=il->next) + { + GtkTreePath* path = (GtkTreePath*)il->data; + int* indices = gtk_tree_path_get_indices(path); + str[indices[0]] = '+'; + gtk_tree_path_free(path); + } + g_list_free(list); + return str; + } + } + } + + return NULL; +} + +static int gtkListSetValueAttrib(Ihandle* ih, const char* value) +{ + if (ih->data->has_editbox) + { + GtkEntry* entry = (GtkEntry*)iupAttribGet(ih, "_IUPGTK_ENTRY"); + if (!value) value = ""; + iupAttribSetStr(ih, "_IUPGTK_DISABLE_TEXT_CB", "1"); + gtk_entry_set_text(entry, iupgtkStrConvertToUTF8(value)); + iupAttribSetStr(ih, "_IUPGTK_DISABLE_TEXT_CB", NULL); + } + else + { + if (ih->data->is_dropdown) + { + int pos; + GtkTreeModel *model = gtkListGetModel(ih); + g_signal_handlers_block_by_func(G_OBJECT(ih->handle), G_CALLBACK(gtkListComboBoxChanged), ih); + if (iupStrToInt(value, &pos)==1 && + (pos>0 && pos<gtk_tree_model_iter_n_children(model, NULL))) + { + gtk_combo_box_set_active((GtkComboBox*)ih->handle, pos-1); /* IUP starts at 1 */ + iupAttribSetInt(ih, "_IUPLIST_OLDVALUE", pos); + } + else + { + gtk_combo_box_set_active((GtkComboBox*)ih->handle, -1); /* none */ + iupAttribSetStr(ih, "_IUPLIST_OLDVALUE", NULL); + } + g_signal_handlers_unblock_by_func(G_OBJECT(ih->handle), G_CALLBACK(gtkListComboBoxChanged), ih); + } + else + { + GtkTreeSelection* selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(ih->handle)); + if (!ih->data->is_multiple) + { + int pos; + g_signal_handlers_block_by_func(G_OBJECT(selection), G_CALLBACK(gtkListSelectionChanged), ih); + if (iupStrToInt(value, &pos)==1) + { + GtkTreePath* path = gtk_tree_path_new_from_indices(pos-1, -1); /* IUP starts at 1 */ + gtk_tree_selection_select_path(selection, path); + gtk_tree_path_free(path); + iupAttribSetInt(ih, "_IUPLIST_OLDVALUE", pos); + } + else + { + gtk_tree_selection_unselect_all(selection); + iupAttribSetStr(ih, "_IUPLIST_OLDVALUE", NULL); + } + g_signal_handlers_unblock_by_func(G_OBJECT(selection), G_CALLBACK(gtkListSelectionChanged), ih); + } + else + { + /* User has changed a multiple selection on a simple list. */ + int i, len, count; + + g_signal_handlers_block_by_func(G_OBJECT(selection), G_CALLBACK(gtkListSelectionChanged), ih); + + /* Clear all selections */ + gtk_tree_selection_unselect_all(selection); + + if (!value) + { + iupAttribSetStr(ih, "_IUPLIST_OLDVALUE", NULL); + return 0; + } + + len = strlen(value); + count = iupdrvListGetCount(ih); + if (len < count) + count = len; + + /* update selection list */ + for (i = 0; i<count; i++) + { + if (value[i]=='+') + { + GtkTreePath* path = gtk_tree_path_new_from_indices(i, -1); + gtk_tree_selection_select_path(selection, path); + gtk_tree_path_free(path); + } + } + iupAttribStoreStr(ih, "_IUPLIST_OLDVALUE", value); + g_signal_handlers_unblock_by_func(G_OBJECT(selection), G_CALLBACK(gtkListSelectionChanged), ih); + } + } + } + + return 0; +} + +static int gtkListSetShowDropdownAttrib(Ihandle* ih, const char* value) +{ + if (ih->data->is_dropdown) + { + if (iupStrBoolean(value)) + gtk_combo_box_popup((GtkComboBox*)ih->handle); + else + gtk_combo_box_popdown((GtkComboBox*)ih->handle); + } + return 0; +} + +static int gtkListSetTopItemAttrib(Ihandle* ih, const char* value) +{ + if (!ih->data->is_dropdown) + { + int pos = 1; + if (iupStrToInt(value, &pos)) + { + GtkTreePath* path = gtk_tree_path_new_from_indices(pos-1, -1); /* IUP starts at 1 */ + gtk_tree_view_scroll_to_cell((GtkTreeView*)ih->handle, path, NULL, FALSE, 0, 0); + gtk_tree_path_free(path); + } + } + return 0; +} + +static int gtkListSetSpacingAttrib(Ihandle* ih, const char* value) +{ + if (ih->data->is_dropdown) + return 0; + + if (!iupStrToInt(value, &ih->data->spacing)) + ih->data->spacing = 0; + + if (ih->handle) + { + GtkCellRenderer* renderer = (GtkCellRenderer*)iupAttribGet(ih, "_IUPGTK_RENDERER"); + if (renderer) + g_object_set(G_OBJECT(renderer), "xpad", ih->data->spacing, + "ypad", ih->data->spacing, + NULL); + return 0; + } + else + return 1; /* store until not mapped, when mapped will be set again */ +} + +static int gtkListSetPaddingAttrib(Ihandle* ih, const char* value) +{ + if (!ih->data->has_editbox) + return 0; + + iupStrToIntInt(value, &ih->data->horiz_padding, &ih->data->vert_padding, 'x'); + if (ih->handle) + { +#if GTK_CHECK_VERSION(2, 10, 0) + GtkEntry* entry; + GtkBorder border; + border.bottom = border.top = ih->data->vert_padding; + border.left = border.right = ih->data->horiz_padding; + entry = (GtkEntry*)iupAttribGet(ih, "_IUPGTK_ENTRY"); + gtk_entry_set_inner_border(entry, &border); +#endif + return 0; + } + else + return 1; /* store until not mapped, when mapped will be set again */ +} + +static int gtkListSetSelectionAttrib(Ihandle* ih, const char* value) +{ + int start=1, end=1; + GtkEntry* entry; + if (!ih->data->has_editbox) + return 0; + if (!value) + return 0; + + entry = (GtkEntry*)iupAttribGet(ih, "_IUPGTK_ENTRY"); + if (!value || iupStrEqualNoCase(value, "NONE")) + { + gtk_editable_select_region(GTK_EDITABLE(entry), 0, 0); + return 0; + } + + if (iupStrEqualNoCase(value, "ALL")) + { + gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1); + return 0; + } + + if (iupStrToIntInt(value, &start, &end, ':')!=2) + return 0; + + if(start<1 || end<1) + return 0; + + start--; /* IUP starts at 1 */ + end--; + + gtk_editable_select_region(GTK_EDITABLE(entry), start, end); + + return 0; +} + +static char* gtkListGetSelectionAttrib(Ihandle* ih) +{ + char *str; + int start, end; + GtkEntry* entry; + if (!ih->data->has_editbox) + return NULL; + + entry = (GtkEntry*)iupAttribGet(ih, "_IUPGTK_ENTRY"); + if (gtk_editable_get_selection_bounds(GTK_EDITABLE(entry), &start, &end)) + { + start++; /* IUP starts at 1 */ + end++; + str = iupStrGetMemory(100); + sprintf(str, "%d:%d", (int)start, (int)end); + return str; + } + + return NULL; +} + +static int gtkListSetSelectionPosAttrib(Ihandle* ih, const char* value) +{ + int start=0, end=0; + GtkEntry* entry; + if (!ih->data->has_editbox) + return 0; + if (!value) + return 0; + + entry = (GtkEntry*)iupAttribGet(ih, "_IUPGTK_ENTRY"); + if (!value || iupStrEqualNoCase(value, "NONE")) + { + gtk_editable_select_region(GTK_EDITABLE(entry), 0, 0); + return 0; + } + + if (iupStrEqualNoCase(value, "ALL")) + { + gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1); + return 0; + } + + if (iupStrToIntInt(value, &start, &end, ':')!=2) + return 0; + + if(start<0 || end<0) + return 0; + + gtk_editable_select_region(GTK_EDITABLE(entry), start, end); + + return 0; +} + +static char* gtkListGetSelectionPosAttrib(Ihandle* ih) +{ + int start, end; + char *str; + GtkEntry* entry; + if (!ih->data->has_editbox) + return NULL; + + entry = (GtkEntry*)iupAttribGet(ih, "_IUPGTK_ENTRY"); + if (gtk_editable_get_selection_bounds(GTK_EDITABLE(entry), &start, &end)) + { + str = iupStrGetMemory(100); + sprintf(str, "%d:%d", (int)start, (int)end); + return str; + } + + return NULL; +} + +static int gtkListSetSelectedTextAttrib(Ihandle* ih, const char* value) +{ + int start, end; + GtkEntry* entry; + if (!ih->data->has_editbox) + return 0; + if (!value) + return 0; + + entry = (GtkEntry*)iupAttribGet(ih, "_IUPGTK_ENTRY"); + if (gtk_editable_get_selection_bounds(GTK_EDITABLE(entry), &start, &end)) + { + /* disable callbacks */ + iupAttribSetStr(ih, "_IUPGTK_DISABLE_TEXT_CB", "1"); + gtk_editable_delete_selection(GTK_EDITABLE(entry)); + gtk_editable_insert_text(GTK_EDITABLE(entry), iupgtkStrConvertToUTF8(value), -1, &start); + iupAttribSetStr(ih, "_IUPGTK_DISABLE_TEXT_CB", NULL); + } + + return 0; +} + +static char* gtkListGetSelectedTextAttrib(Ihandle* ih) +{ + int start, end; + GtkEntry* entry; + if (!ih->data->has_editbox) + return NULL; + + entry = (GtkEntry*)iupAttribGet(ih, "_IUPGTK_ENTRY"); + if (gtk_editable_get_selection_bounds(GTK_EDITABLE(entry), &start, &end)) + { + char* selectedtext = gtk_editable_get_chars(GTK_EDITABLE(entry), start, end); + char* str = iupStrGetMemoryCopy(iupgtkStrConvertFromUTF8(selectedtext)); + g_free(selectedtext); + return str; + } + + return NULL; +} + +static int gtkListSetCaretAttrib(Ihandle* ih, const char* value) +{ + int pos = 1; + GtkEntry* entry; + if (!ih->data->has_editbox) + return 0; + if (!value) + return 0; + + sscanf(value,"%i",&pos); + pos--; /* IUP starts at 1 */ + if (pos < 0) pos = 0; + + entry = (GtkEntry*)iupAttribGet(ih, "_IUPGTK_ENTRY"); + gtk_editable_set_position(GTK_EDITABLE(entry), pos); + + return 0; +} + +static char* gtkListGetCaretAttrib(Ihandle* ih) +{ + if (ih->data->has_editbox) + { + char* str = iupStrGetMemory(50); + GtkEntry* entry = (GtkEntry*)iupAttribGet(ih, "_IUPGTK_ENTRY"); + int pos = gtk_editable_get_position(GTK_EDITABLE(entry)); + pos++; /* IUP starts at 1 */ + sprintf(str, "%d", (int)pos); + return str; + } + else + return NULL; +} + +static int gtkListSetCaretPosAttrib(Ihandle* ih, const char* value) +{ + int pos = 0; + GtkEntry* entry; + if (!ih->data->has_editbox) + return 0; + if (!value) + return 0; + + sscanf(value,"%i",&pos); + if (pos < 0) pos = 0; + + entry = (GtkEntry*)iupAttribGet(ih, "_IUPGTK_ENTRY"); + gtk_editable_set_position(GTK_EDITABLE(entry), pos); + + return 0; +} + +static char* gtkListGetCaretPosAttrib(Ihandle* ih) +{ + if (ih->data->has_editbox) + { + char* str = iupStrGetMemory(50); + GtkEntry* entry = (GtkEntry*)iupAttribGet(ih, "_IUPGTK_ENTRY"); + int pos = gtk_editable_get_position(GTK_EDITABLE(entry)); + sprintf(str, "%d", (int)pos); + return str; + } + else + return NULL; +} + +static int gtkListSetScrollToAttrib(Ihandle* ih, const char* value) +{ + int pos = 1; + GtkEntry* entry; + if (!ih->data->has_editbox) + return 0; + if (!value) + return 0; + + sscanf(value,"%i",&pos); + if (pos < 1) pos = 1; + pos--; /* return to GTK referece */ + + entry = (GtkEntry*)iupAttribGet(ih, "_IUPGTK_ENTRY"); + gtk_editable_set_position(GTK_EDITABLE(entry), pos); + + return 0; +} + +static int gtkListSetScrollToPosAttrib(Ihandle* ih, const char* value) +{ + int pos = 0; + GtkEntry* entry; + if (!ih->data->has_editbox) + return 0; + if (!value) + return 0; + + sscanf(value,"%i",&pos); + if (pos < 0) pos = 0; + + entry = (GtkEntry*)iupAttribGet(ih, "_IUPGTK_ENTRY"); + gtk_editable_set_position(GTK_EDITABLE(entry), pos); + + return 0; +} + +static int gtkListSetInsertAttrib(Ihandle* ih, const char* value) +{ + gint pos; + GtkEntry* entry; + if (!ih->data->has_editbox) + return 0; + if (!value) + return 0; + + iupAttribSetStr(ih, "_IUPGTK_DISABLE_TEXT_CB", "1"); /* disable callbacks */ + entry = (GtkEntry*)iupAttribGet(ih, "_IUPGTK_ENTRY"); + pos = gtk_editable_get_position(GTK_EDITABLE(entry)); + gtk_editable_insert_text(GTK_EDITABLE(entry), iupgtkStrConvertToUTF8(value), -1, &pos); + iupAttribSetStr(ih, "_IUPGTK_DISABLE_TEXT_CB", NULL); + + return 0; +} + +static int gtkListSetAppendAttrib(Ihandle* ih, const char* value) +{ + if (ih->data->has_editbox) + { + GtkEntry* entry = (GtkEntry*)iupAttribGet(ih, "_IUPGTK_ENTRY"); + gint pos = strlen(gtk_entry_get_text(entry))+1; + iupAttribSetStr(ih, "_IUPGTK_DISABLE_TEXT_CB", "1"); /* disable callbacks */ + gtk_editable_insert_text(GTK_EDITABLE(entry), iupgtkStrConvertToUTF8(value), -1, &pos); + iupAttribSetStr(ih, "_IUPGTK_DISABLE_TEXT_CB", NULL); + } + return 0; +} + +static int gtkListSetNCAttrib(Ihandle* ih, const char* value) +{ + if (!ih->data->has_editbox) + return 0; + + if (!iupStrToInt(value, &ih->data->nc)) + ih->data->nc = INT_MAX; + + if (ih->handle) + { + GtkEntry* entry = (GtkEntry*)iupAttribGet(ih, "_IUPGTK_ENTRY"); + gtk_entry_set_max_length(entry, ih->data->nc); + } + + return 0; +} + +static int gtkListSetClipboardAttrib(Ihandle *ih, const char *value) +{ + GtkEntry* entry; + if (!ih->data->has_editbox) + return 0; + + /* disable callbacks */ + iupAttribSetStr(ih, "_IUPGTK_DISABLE_TEXT_CB", "1"); + entry = (GtkEntry*)iupAttribGet(ih, "_IUPGTK_ENTRY"); + if (iupStrEqualNoCase(value, "COPY")) + gtk_editable_copy_clipboard(GTK_EDITABLE(entry)); + else if (iupStrEqualNoCase(value, "CUT")) + gtk_editable_cut_clipboard(GTK_EDITABLE(entry)); + else if (iupStrEqualNoCase(value, "PASTE")) + gtk_editable_paste_clipboard(GTK_EDITABLE(entry)); + else if (iupStrEqualNoCase(value, "CLEAR")) + gtk_editable_delete_selection(GTK_EDITABLE(entry)); + iupAttribSetStr(ih, "_IUPGTK_DISABLE_TEXT_CB", NULL); + return 0; +} + +static int gtkListSetReadOnlyAttrib(Ihandle* ih, const char* value) +{ + GtkEntry* entry; + if (!ih->data->has_editbox) + return 0; + entry = (GtkEntry*)iupAttribGet(ih, "_IUPGTK_ENTRY"); + gtk_editable_set_editable(GTK_EDITABLE(entry), !iupStrBoolean(value)); + return 0; +} + +static char* gtkListGetReadOnlyAttrib(Ihandle* ih) +{ + GtkEntry* entry; + if (!ih->data->has_editbox) + return NULL; + entry = (GtkEntry*)iupAttribGet(ih, "_IUPGTK_ENTRY"); + if (!gtk_editable_get_editable(GTK_EDITABLE(entry))) + return "YES"; + else + return "NO"; +} + + +/*********************************************************************************/ + + +static void gtkListEditMoveCursor(GtkWidget* entry, GtkMovementStep step, gint count, gboolean extend_selection, Ihandle* ih) +{ + int pos; + + IFniii cb = (IFniii)IupGetCallback(ih, "CARET_CB"); + if (!cb) return; + + pos = gtk_editable_get_position(GTK_EDITABLE(entry)); + + if (pos != ih->data->last_caret_pos) + { + ih->data->last_caret_pos = pos; + + cb(ih, 1, pos+1, pos); + } + + (void)step; + (void)count; + (void)extend_selection; +} + +static gboolean gtkListEditKeyPressEvent(GtkWidget* entry, GdkEventKey *evt, Ihandle *ih) +{ + if (iupgtkKeyPressEvent(entry, evt, ih) == TRUE) + return TRUE; + + if ((evt->keyval == GDK_Up || evt->keyval == GDK_KP_Up) || + (evt->keyval == GDK_Prior || evt->keyval == GDK_KP_Page_Up) || + (evt->keyval == GDK_Down || evt->keyval == GDK_KP_Down) || + (evt->keyval == GDK_Next || evt->keyval == GDK_KP_Page_Down)) + { + int pos = -1; + GtkTreeIter iter; + GtkTreeModel* model = NULL; + GtkTreeSelection* selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(ih->handle)); + if (gtk_tree_selection_get_selected(selection, &model, &iter)) + { + GtkTreePath *path = gtk_tree_model_get_path(model, &iter); + int* indices = gtk_tree_path_get_indices(path); + pos = indices[0]; + gtk_tree_path_free(path); + } + + if (pos == -1) + pos = 0; + else if (evt->keyval == GDK_Up || evt->keyval == GDK_KP_Up) + { + pos--; + if (pos < 0) pos = 0; + } + else if (evt->keyval == GDK_Prior || evt->keyval == GDK_KP_Page_Up) + { + pos -= 5; + if (pos < 0) pos = 0; + } + else if (evt->keyval == GDK_Down || evt->keyval == GDK_KP_Down) + { + int count = gtk_tree_model_iter_n_children(model, NULL); + pos++; + if (pos > count-1) pos = count-1; + } + else if (evt->keyval == GDK_Next || evt->keyval == GDK_KP_Page_Down) + { + int count = gtk_tree_model_iter_n_children(model, NULL); + pos += 5; + if (pos > count-1) pos = count-1; + } + + if (pos != -1) + { + GtkTreePath* path = gtk_tree_path_new_from_indices(pos, -1); + g_signal_handlers_block_by_func(G_OBJECT(selection), G_CALLBACK(gtkListSelectionChanged), ih); + gtk_tree_selection_select_path(selection, path); + g_signal_handlers_unblock_by_func(G_OBJECT(selection), G_CALLBACK(gtkListSelectionChanged), ih); + gtk_tree_path_free(path); + iupAttribSetInt(ih, "_IUPLIST_OLDVALUE", pos); + + if (!model) model = gtkListGetModel(ih); + + if (gtk_tree_model_iter_nth_child(model, &iter, NULL, pos)) + { + gchar *text = NULL; + gtk_tree_model_get(model, &iter, 0, &text, -1); + if (text) + { + gtk_entry_set_text((GtkEntry*)entry, text); + g_free(text); + } + } + + } + } + + return FALSE; +} + +static gboolean gtkListEditKeyReleaseEvent(GtkWidget *widget, GdkEventKey *evt, Ihandle *ih) +{ + gtkListEditMoveCursor(widget, 0, 0, 0, ih); + (void)evt; + return FALSE; +} + +static gboolean gtkListEditButtonEvent(GtkWidget *widget, GdkEventButton *evt, Ihandle *ih) +{ + gtkListEditMoveCursor(widget, 0, 0, 0, ih); + (void)evt; + return FALSE; +} + +static int gtkListCallEditCb(Ihandle* ih, GtkEditable *editable, const char* insert_value, int len, int start, int end) +{ + char *new_value, *value; + int ret = -1, key = 0; + + IFnis cb = (IFnis)IupGetCallback(ih, "EDIT_CB"); + if (!cb && !ih->data->mask) + return -1; /* continue */ + + value = iupStrGetMemoryCopy(iupgtkStrConvertFromUTF8(gtk_entry_get_text(GTK_ENTRY(editable)))); + + if (!insert_value) + { + new_value = iupStrDup(value); + if (end<0) end = strlen(value)+1; + iupStrRemove(new_value, start, end, 1); + } + else + { + if (!value) + new_value = iupStrDup(insert_value); + else + { + if (len < end-start) + { + new_value = iupStrDup(value); + new_value = iupStrInsert(new_value, insert_value, start, end); + } + else + new_value = iupStrInsert(value, insert_value, start, end); + } + } + + if (insert_value && insert_value[0]!=0 && insert_value[1]==0) + key = insert_value[0]; + + if (!new_value) + return -1; /* continue */ + + if (ih->data->nc && (int)strlen(new_value) > ih->data->nc) + { + if (new_value != value) free(new_value); + return 0; /* abort */ + } + + if (ih->data->mask && iupMaskCheck(ih->data->mask, new_value)==0) + { + if (new_value != value) free(new_value); + return 0; /* abort */ + } + + if (cb) + { + int cb_ret = cb(ih, key, (char*)new_value); + if (cb_ret==IUP_IGNORE) + ret = 0; /* abort */ + else if (cb_ret==IUP_CLOSE) + { + IupExitLoop(); + ret = 0; /* abort */ + } + else if (cb_ret!=0 && key!=0 && + cb_ret != IUP_DEFAULT && cb_ret != IUP_CONTINUE) + ret = cb_ret; /* abort and replace */ + } + + if (new_value != value) free(new_value); + return ret; /* continue */ +} + +static void gtkListEditDeleteText(GtkEditable *editable, int start, int end, Ihandle* ih) +{ + if (iupAttribGet(ih, "_IUPGTK_DISABLE_TEXT_CB")) + return; + + if (gtkListCallEditCb(ih, editable, NULL, 0, start, end)==0) + g_signal_stop_emission_by_name(editable, "delete_text"); +} + +static void gtkListEditInsertText(GtkEditable *editable, char *insert_value, int len, int *pos, Ihandle* ih) +{ + int ret; + + if (iupAttribGet(ih, "_IUPGTK_DISABLE_TEXT_CB")) + return; + + ret = gtkListCallEditCb(ih, editable, iupStrGetMemoryCopy(iupgtkStrConvertFromUTF8(insert_value)), len, *pos, *pos); + if (ret == 0) + g_signal_stop_emission_by_name(editable, "insert_text"); + else if (ret != -1) + { + insert_value[0] = (char)ret; /* replace key */ + + /* disable callbacks */ + iupAttribSetStr(ih, "_IUPGTK_DISABLE_TEXT_CB", "1"); + gtk_editable_insert_text(editable, insert_value, 1, pos); + iupAttribSetStr(ih, "_IUPGTK_DISABLE_TEXT_CB", NULL); + + g_signal_stop_emission_by_name(editable, "insert_text"); + } +} + +static void gtkListEditChanged(void* dummy, Ihandle* ih) +{ + if (iupAttribGet(ih, "_IUPGTK_DISABLE_TEXT_CB")) + return; + + iupBaseCallValueChangedCb(ih); + (void)dummy; +} + +static void gtkListComboBoxPopupShownChanged(GtkComboBox* widget, GParamSpec *pspec, Ihandle* ih) +{ + IFni cb = (IFni)IupGetCallback(ih, "DROPDOWN_CB"); + if (cb) + { + gboolean popup_shown; + g_object_get(widget, "popup-shown", &popup_shown, NULL); + cb(ih, popup_shown); + } + (void)pspec; +} + +static void gtkListComboBoxChanged(GtkComboBox* widget, Ihandle* ih) +{ + IFnsii cb = (IFnsii)IupGetCallback(ih, "ACTION"); + if (cb) + { + int pos = gtk_combo_box_get_active((GtkComboBox*)ih->handle); + pos++; /* IUP starts at 1 */ + iupListSingleCallActionCallback(ih, cb, pos); + } + + if (!ih->data->has_editbox) + iupBaseCallValueChangedCb(ih); + + (void)widget; +} + +static gboolean gtkListSimpleKeyPressEvent(GtkWidget *widget, GdkEventKey *evt, Ihandle *ih) +{ + if (iupgtkKeyPressEvent(widget, evt, ih) == TRUE) + return TRUE; + + if (evt->keyval == GDK_Return || evt->keyval == GDK_KP_Enter) + return TRUE; /* used to avoid the call to DBLCLICK_CB in the default processing */ + return FALSE; +} + +static void gtkListRowActivated(GtkTreeView *tree_view, GtkTreePath *path, GtkTreeViewColumn *column, Ihandle* ih) +{ + IFnis cb = (IFnis) IupGetCallback(ih, "DBLCLICK_CB"); + if (cb) + { + int* indices = gtk_tree_path_get_indices(path); + iupListSingleCallDblClickCallback(ih, cb, indices[0]+1); /* IUP starts at 1 */ + } + (void)column; + (void)tree_view; +} + +static void gtkListSelectionChanged(GtkTreeSelection* selection, Ihandle* ih) +{ + if (ih->data->has_editbox) + { + /* must manually update its contents */ + GtkTreeIter iter; + GtkTreeModel* tree_model; + if (gtk_tree_selection_get_selected(selection, &tree_model, &iter)) + { + GtkTreePath *path = gtk_tree_model_get_path(tree_model, &iter); + char* value = NULL; + gtk_tree_model_get(tree_model, &iter, 0, &value, -1); + if (value) + { + GtkEntry* entry = (GtkEntry*)iupAttribGet(ih, "_IUPGTK_ENTRY"); + gtk_entry_set_text(entry, value); + g_free(value); + } + gtk_tree_path_free(path); + } + } + + if (!ih->data->is_multiple) + { + IFnsii cb = (IFnsii)IupGetCallback(ih, "ACTION"); + if (cb) + { + GtkTreeSelection* selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(ih->handle)); + GtkTreeIter iter; + GtkTreeModel* tree_model; + if (gtk_tree_selection_get_selected(selection, &tree_model, &iter)) + { + GtkTreePath *path = gtk_tree_model_get_path(tree_model, &iter); + int* indices = gtk_tree_path_get_indices(path); + iupListSingleCallActionCallback(ih, cb, indices[0]+1); /* IUP starts at 1 */ + gtk_tree_path_free (path); + } + } + } + else + { + IFns multi_cb = (IFns)IupGetCallback(ih, "MULTISELECT_CB"); + IFnsii cb = (IFnsii) IupGetCallback(ih, "ACTION"); + if (multi_cb || cb) + { + GList *il, *list = gtk_tree_selection_get_selected_rows(selection, NULL); + int i, sel_count = g_list_length(list); + int* pos = malloc(sizeof(int)*sel_count); + for (il=list, i=0; il; il=il->next, i++) + { + GtkTreePath* path = (GtkTreePath*)il->data; + int* indices = gtk_tree_path_get_indices(path); + pos[i] = indices[0]; + gtk_tree_path_free(path); + } + g_list_free(list); + + iupListMultipleCallActionCallback(ih, cb, multi_cb, pos, sel_count); + free(pos); + } + } + + if (!ih->data->has_editbox) + iupBaseCallValueChangedCb(ih); +} + + +/*********************************************************************************/ + + +static int gtkListMapMethod(Ihandle* ih) +{ + GtkScrolledWindow* scrolled_window = NULL; + GtkListStore *store; + + store = gtk_list_store_new(1, G_TYPE_STRING); + + if (ih->data->is_dropdown) + { + GtkCellRenderer *renderer = NULL; + + if (ih->data->has_editbox) + ih->handle = gtk_combo_box_entry_new_with_model(GTK_TREE_MODEL(store), 0); + else + ih->handle = gtk_combo_box_new_with_model(GTK_TREE_MODEL(store)); + g_object_unref(store); + + if (!ih->handle) + return IUP_ERROR; + + g_object_set(G_OBJECT(ih->handle), "has-frame", TRUE, NULL); + + if (ih->data->has_editbox) + { + GtkWidget *entry; +#if GTK_CHECK_VERSION(2, 12, 0) + GList* list = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(ih->handle)); + renderer = list->data; + g_list_free(list); +#endif + + entry = gtk_bin_get_child(GTK_BIN(ih->handle)); + iupAttribSetStr(ih, "_IUPGTK_ENTRY", (char*)entry); + + g_signal_connect(G_OBJECT(entry), "focus-in-event", G_CALLBACK(iupgtkFocusInOutEvent), ih); + g_signal_connect(G_OBJECT(entry), "focus-out-event", G_CALLBACK(iupgtkFocusInOutEvent), ih); + g_signal_connect(G_OBJECT(entry), "enter-notify-event", G_CALLBACK(iupgtkEnterLeaveEvent), ih); + g_signal_connect(G_OBJECT(entry), "leave-notify-event", G_CALLBACK(iupgtkEnterLeaveEvent), ih); + g_signal_connect(G_OBJECT(entry), "show-help", G_CALLBACK(iupgtkShowHelp), ih); + g_signal_connect(G_OBJECT(entry), "key-press-event", G_CALLBACK(iupgtkKeyPressEvent), ih); + + g_signal_connect(G_OBJECT(entry), "delete-text", G_CALLBACK(gtkListEditDeleteText), ih); + g_signal_connect(G_OBJECT(entry), "insert-text", G_CALLBACK(gtkListEditInsertText), ih); + /* g_signal_connect(G_OBJECT(entry), "changed", G_CALLBACK(gtkListEditChanged), ih); */ + g_signal_connect_after(G_OBJECT(entry), "move-cursor", G_CALLBACK(gtkListEditMoveCursor), ih); /* only report some caret movements */ + g_signal_connect_after(G_OBJECT(entry), "key-release-event", G_CALLBACK(gtkListEditKeyReleaseEvent), ih); + g_signal_connect(G_OBJECT(entry), "button-press-event", G_CALLBACK(gtkListEditButtonEvent), ih); /* if connected "after" then it is ignored */ + g_signal_connect(G_OBJECT(entry), "button-release-event",G_CALLBACK(gtkListEditButtonEvent), ih); + + if (!iupAttribGetBoolean(ih, "CANFOCUS")) + GTK_WIDGET_FLAGS(ih->handle) &= ~GTK_CAN_FOCUS; + } + else + { + /* had to add an event box just to get get/killfocus,enter/leave events */ + GtkWidget *box = gtk_event_box_new(); + gtk_container_add((GtkContainer*)box, ih->handle); + iupAttribSetStr(ih, "_IUP_EXTRAPARENT", (char*)box); + + renderer = gtk_cell_renderer_text_new(); + gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(ih->handle), renderer, TRUE); + gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(ih->handle), renderer, "text", 0, NULL); + + g_signal_connect(G_OBJECT(box), "focus-in-event", G_CALLBACK(iupgtkFocusInOutEvent), ih); + g_signal_connect(G_OBJECT(box), "focus-out-event", G_CALLBACK(iupgtkFocusInOutEvent), ih); + g_signal_connect(G_OBJECT(box), "enter-notify-event", G_CALLBACK(iupgtkEnterLeaveEvent), ih); + g_signal_connect(G_OBJECT(box), "leave-notify-event", G_CALLBACK(iupgtkEnterLeaveEvent), ih); + g_signal_connect(G_OBJECT(ih->handle), "key-press-event", G_CALLBACK(iupgtkKeyPressEvent), ih); + g_signal_connect(G_OBJECT(ih->handle), "show-help", G_CALLBACK(iupgtkShowHelp), ih); + + if (!iupAttribGetBoolean(ih, "CANFOCUS")) + GTK_WIDGET_FLAGS(ih->handle) &= ~GTK_CAN_FOCUS; + else + GTK_WIDGET_FLAGS(box) |= GTK_CAN_FOCUS; + } + + g_signal_connect(ih->handle, "changed", G_CALLBACK(gtkListComboBoxChanged), ih); + g_signal_connect(ih->handle, "notify::popup-shown", G_CALLBACK(gtkListComboBoxPopupShownChanged), ih); + + if (renderer) + { + renderer->xpad = 0; + renderer->ypad = 0; + iupAttribSetStr(ih, "_IUPGTK_RENDERER", (char*)renderer); + } + } + else + { + GtkCellRenderer *renderer; + GtkTreeSelection* selection; + GtkTreeViewColumn *column; + GtkPolicyType scrollbar_policy; + + ih->handle = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store)); + g_object_unref(store); + + if (!ih->handle) + return IUP_ERROR; + + scrolled_window = (GtkScrolledWindow*)gtk_scrolled_window_new(NULL, NULL); + + if (ih->data->has_editbox) + { + GtkBox* vbox = (GtkBox*)gtk_vbox_new(FALSE, 0); + + GtkWidget *entry = gtk_entry_new(); + gtk_widget_show(entry); + gtk_box_pack_start(vbox, entry, FALSE, FALSE, 0); + iupAttribSetStr(ih, "_IUPGTK_ENTRY", (char*)entry); + + gtk_widget_show((GtkWidget*)vbox); + gtk_box_pack_end(vbox, (GtkWidget*)scrolled_window, TRUE, TRUE, 0); + iupAttribSetStr(ih, "_IUP_EXTRAPARENT", (char*)vbox); + iupAttribSetStr(ih, "_IUPGTK_SCROLLED_WINDOW", (char*)scrolled_window); + + GTK_WIDGET_FLAGS(ih->handle) &= ~GTK_CAN_FOCUS; /* focus goes only to the edit box */ + if (!iupAttribGetBoolean(ih, "CANFOCUS")) + GTK_WIDGET_FLAGS(entry) &= ~GTK_CAN_FOCUS; + + g_signal_connect(G_OBJECT(entry), "focus-in-event", G_CALLBACK(iupgtkFocusInOutEvent), ih); + g_signal_connect(G_OBJECT(entry), "focus-out-event", G_CALLBACK(iupgtkFocusInOutEvent), ih); + g_signal_connect(G_OBJECT(entry), "enter-notify-event", G_CALLBACK(iupgtkEnterLeaveEvent), ih); + g_signal_connect(G_OBJECT(entry), "leave-notify-event", G_CALLBACK(iupgtkEnterLeaveEvent), ih); + g_signal_connect(G_OBJECT(entry), "show-help", G_CALLBACK(iupgtkShowHelp), ih); + + g_signal_connect(G_OBJECT(entry), "delete-text", G_CALLBACK(gtkListEditDeleteText), ih); + g_signal_connect(G_OBJECT(entry), "insert-text", G_CALLBACK(gtkListEditInsertText), ih); + g_signal_connect(G_OBJECT(entry), "changed", G_CALLBACK(gtkListEditChanged), ih); + g_signal_connect_after(G_OBJECT(entry), "move-cursor", G_CALLBACK(gtkListEditMoveCursor), ih); /* only report some caret movements */ + g_signal_connect(G_OBJECT(entry), "key-press-event", G_CALLBACK(gtkListEditKeyPressEvent), ih); + g_signal_connect_after(G_OBJECT(entry), "key-release-event", G_CALLBACK(gtkListEditKeyReleaseEvent), ih); + g_signal_connect(G_OBJECT(entry), "button-press-event", G_CALLBACK(gtkListEditButtonEvent), ih); /* if connected "after" then it is ignored */ + g_signal_connect(G_OBJECT(entry), "button-release-event",G_CALLBACK(gtkListEditButtonEvent), ih); + } + else + { + iupAttribSetStr(ih, "_IUP_EXTRAPARENT", (char*)scrolled_window); + + if (!iupAttribGetBoolean(ih, "CANFOCUS")) + GTK_WIDGET_FLAGS(ih->handle) &= ~GTK_CAN_FOCUS; + + g_signal_connect(G_OBJECT(ih->handle), "focus-in-event", G_CALLBACK(iupgtkFocusInOutEvent), ih); + g_signal_connect(G_OBJECT(ih->handle), "focus-out-event", G_CALLBACK(iupgtkFocusInOutEvent), ih); + g_signal_connect(G_OBJECT(ih->handle), "enter-notify-event", G_CALLBACK(iupgtkEnterLeaveEvent), ih); + g_signal_connect(G_OBJECT(ih->handle), "leave-notify-event", G_CALLBACK(iupgtkEnterLeaveEvent), ih); + g_signal_connect(G_OBJECT(ih->handle), "key-press-event", G_CALLBACK(gtkListSimpleKeyPressEvent), ih); + g_signal_connect(G_OBJECT(ih->handle), "show-help", G_CALLBACK(iupgtkShowHelp), ih); + } + + column = gtk_tree_view_column_new(); + + renderer = gtk_cell_renderer_text_new(); + gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(column), renderer, TRUE); + gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(column), renderer, "text", 0, NULL); + + iupAttribSetStr(ih, "_IUPGTK_RENDERER", (char*)renderer); + g_object_set(G_OBJECT(renderer), "xpad", 0, NULL); + g_object_set(G_OBJECT(renderer), "ypad", 0, NULL); + + gtk_tree_view_append_column(GTK_TREE_VIEW(ih->handle), column); + gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(ih->handle), FALSE); + gtk_tree_view_set_enable_search(GTK_TREE_VIEW(ih->handle), FALSE); /* TODO: check "start-interactive-search" signal */ + + gtk_container_add((GtkContainer*)scrolled_window, ih->handle); + gtk_widget_show((GtkWidget*)scrolled_window); + gtk_scrolled_window_set_shadow_type(scrolled_window, GTK_SHADOW_IN); + + if (ih->data->sb) + { + if (iupAttribGetBoolean(ih, "AUTOHIDE")) + scrollbar_policy = GTK_POLICY_AUTOMATIC; + else + scrollbar_policy = GTK_POLICY_ALWAYS; + } + else + scrollbar_policy = GTK_POLICY_NEVER; + + gtk_scrolled_window_set_policy(scrolled_window, scrollbar_policy, scrollbar_policy); + + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(ih->handle)); + if (!ih->data->has_editbox && ih->data->is_multiple) + { + gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE); +#if GTK_CHECK_VERSION(2, 10, 0) + gtk_tree_view_set_rubber_banding(GTK_TREE_VIEW(ih->handle), TRUE); +#endif + } + else + gtk_tree_selection_set_mode(selection, GTK_SELECTION_BROWSE); + + g_signal_connect(G_OBJECT(selection), "changed", G_CALLBACK(gtkListSelectionChanged), ih); + g_signal_connect(G_OBJECT(ih->handle), "row-activated", G_CALLBACK(gtkListRowActivated), ih); + g_signal_connect(G_OBJECT(ih->handle), "motion-notify-event",G_CALLBACK(iupgtkMotionNotifyEvent), ih); + g_signal_connect(G_OBJECT(ih->handle), "button-press-event", G_CALLBACK(iupgtkButtonEvent), ih); + g_signal_connect(G_OBJECT(ih->handle), "button-release-event",G_CALLBACK(iupgtkButtonEvent), ih); + } + + if (iupAttribGetBoolean(ih, "SORT")) + gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(store), 0, GTK_SORT_ASCENDING); + + /* add to the parent, all GTK controls must call this. */ + iupgtkBaseAddToParent(ih); + + if (scrolled_window) + gtk_widget_realize((GtkWidget*)scrolled_window); + gtk_widget_realize(ih->handle); + + /* configure for DRAG&DROP */ + if (IupGetCallback(ih, "DROPFILES_CB")) + iupAttribSetStr(ih, "DRAGDROP", "YES"); + + IupSetCallback(ih, "_IUP_XY2POS_CB", (Icallback)gtkListConvertXYToPos); + + iupListSetInitialItems(ih); + + return IUP_NOERROR; +} + +void iupdrvListInitClass(Iclass* ic) +{ + /* Driver Dependent Class functions */ + ic->Map = gtkListMapMethod; + + /* Driver Dependent Attribute functions */ + + /* Overwrite Common */ + iupClassRegisterAttribute(ic, "STANDARDFONT", NULL, gtkListSetStandardFontAttrib, IUPAF_SAMEASSYSTEM, "DEFAULTFONT", IUPAF_NOT_MAPPED); + + /* Visual */ + iupClassRegisterAttribute(ic, "BGCOLOR", NULL, gtkListSetBgColorAttrib, IUPAF_SAMEASSYSTEM, "TXTBGCOLOR", IUPAF_DEFAULT); + + /* Special */ + iupClassRegisterAttribute(ic, "FGCOLOR", NULL, gtkListSetFgColorAttrib, IUPAF_SAMEASSYSTEM, "TXTFGCOLOR", IUPAF_DEFAULT); + + /* IupList only */ + iupClassRegisterAttributeId(ic, "IDVALUE", gtkListGetIdValueAttrib, iupListSetIdValueAttrib, IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "VALUE", gtkListGetValueAttrib, gtkListSetValueAttrib, NULL, NULL, IUPAF_NO_DEFAULTVALUE|IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "SHOWDROPDOWN", NULL, gtkListSetShowDropdownAttrib, NULL, NULL, IUPAF_WRITEONLY|IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "TOPITEM", NULL, gtkListSetTopItemAttrib, NULL, NULL, IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "DRAGDROP", NULL, iupgtkSetDragDropAttrib, NULL, NULL, IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "SPACING", iupListGetSpacingAttrib, gtkListSetSpacingAttrib, NULL, NULL, IUPAF_NOT_MAPPED); + + iupClassRegisterAttribute(ic, "PADDING", iupListGetPaddingAttrib, gtkListSetPaddingAttrib, IUPAF_SAMEASSYSTEM, "0x0", IUPAF_NOT_MAPPED); + iupClassRegisterAttribute(ic, "SELECTEDTEXT", gtkListGetSelectedTextAttrib, gtkListSetSelectedTextAttrib, NULL, NULL, IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "SELECTION", gtkListGetSelectionAttrib, gtkListSetSelectionAttrib, NULL, NULL, IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "SELECTIONPOS", gtkListGetSelectionPosAttrib, gtkListSetSelectionPosAttrib, NULL, NULL, IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "CARET", gtkListGetCaretAttrib, gtkListSetCaretAttrib, NULL, NULL, IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "CARETPOS", gtkListGetCaretPosAttrib, gtkListSetCaretPosAttrib, NULL, NULL, IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "INSERT", NULL, gtkListSetInsertAttrib, NULL, NULL, IUPAF_WRITEONLY|IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "APPEND", NULL, gtkListSetAppendAttrib, NULL, NULL, IUPAF_WRITEONLY|IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "READONLY", gtkListGetReadOnlyAttrib, gtkListSetReadOnlyAttrib, NULL, NULL, IUPAF_DEFAULT); + iupClassRegisterAttribute(ic, "NC", iupListGetNCAttrib, gtkListSetNCAttrib, NULL, NULL, IUPAF_NOT_MAPPED); + iupClassRegisterAttribute(ic, "CLIPBOARD", NULL, gtkListSetClipboardAttrib, NULL, NULL, IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "SCROLLTO", NULL, gtkListSetScrollToAttrib, NULL, NULL, IUPAF_WRITEONLY|IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "SCROLLTOPOS", NULL, gtkListSetScrollToPosAttrib, NULL, NULL, IUPAF_WRITEONLY|IUPAF_NO_INHERIT); +} diff --git a/iup/src/gtk/iupgtk_loop.c b/iup/src/gtk/iupgtk_loop.c new file mode 100755 index 0000000..e349a45 --- /dev/null +++ b/iup/src/gtk/iupgtk_loop.c @@ -0,0 +1,93 @@ +/** \file + * \brief GTK Message Loop + * + * See Copyright Notice in "iup.h" + */ + +#include <stdio.h> +#include <string.h> + +#include <gtk/gtk.h> + +#include "iup.h" +#include "iupcbs.h" + + +/* local variables */ +static IFidle gtk_idle_cb = NULL; +static guint gtk_idle_id; + +static gboolean gtkIdleFunc(gpointer data) +{ + (void)data; + if (gtk_idle_cb) + { + int ret = gtk_idle_cb(); + if (ret == IUP_CLOSE) + { + gtk_idle_cb = NULL; + IupExitLoop(); + return FALSE; /* removes the idle */ + } + if (ret == IUP_IGNORE) + { + gtk_idle_cb = NULL; + return FALSE; /* removes the idle */ + } + + return TRUE; /* keeps the idle */ + } + + return FALSE; /* removes the idle */ +} + +void iupdrvSetIdleFunction(Icallback f) +{ + if (gtk_idle_cb) + g_source_remove(gtk_idle_id); + + gtk_idle_cb = (IFidle)f; + + if (gtk_idle_cb) + gtk_idle_id = g_idle_add(gtkIdleFunc, NULL); +} + +void IupExitLoop(void) +{ + if (gtk_main_iteration_do(FALSE)==FALSE) + gtk_main_quit(); +} + +int IupMainLoopLevel(void) +{ + return gtk_main_level(); +} + +int IupMainLoop(void) +{ + gtk_main(); + return IUP_NOERROR; +} + +int IupLoopStep(void) +{ + if (gtk_main_iteration_do(FALSE)) + return IUP_CLOSE; + return IUP_DEFAULT; +} + +void IupFlush(void) +{ + IFidle old_gtk_idle_cb = NULL; + if (gtk_idle_cb) + { + old_gtk_idle_cb = gtk_idle_cb; + iupdrvSetIdleFunction(NULL); + } + + while (gtk_events_pending()) + gtk_main_iteration(); + + if (old_gtk_idle_cb) + iupdrvSetIdleFunction((Icallback)old_gtk_idle_cb); +} diff --git a/iup/src/gtk/iupgtk_menu.c b/iup/src/gtk/iupgtk_menu.c new file mode 100755 index 0000000..c12fbea --- /dev/null +++ b/iup/src/gtk/iupgtk_menu.c @@ -0,0 +1,525 @@ +/** \file + * \brief Menu Resources + * + * See Copyright Notice in "iup.h" + */ + +#include <gtk/gtk.h> +#include <gdk/gdkkeysyms.h> + +#ifdef HILDON +#include <hildon/hildon-window.h> +#endif + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <memory.h> +#include <stdarg.h> + +#include "iup.h" +#include "iupcbs.h" + +#include "iup_object.h" +#include "iup_childtree.h" +#include "iup_attrib.h" +#include "iup_dialog.h" +#include "iup_str.h" +#include "iup_label.h" +#include "iup_drv.h" +#include "iup_drvfont.h" +#include "iup_image.h" +#include "iup_menu.h" + +#include "iupgtk_drv.h" + + +typedef struct _ImenuPos +{ + int x, y; +} ImenuPos; + +static void gtkMenuPositionFunc(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, ImenuPos *menupos) +{ + *x = menupos->x; + *y = menupos->y; + *push_in = FALSE; + (void)menu; +} + +int iupdrvMenuPopup(Ihandle* ih, int x, int y) +{ + ImenuPos menupos; + menupos.x = x; + menupos.y = y; + gtk_menu_popup((GtkMenu*)ih->handle, NULL, NULL, (GtkMenuPositionFunc)gtkMenuPositionFunc, + (gpointer)&menupos, 0, gtk_get_current_event_time()); + gtk_main(); + return IUP_NOERROR; +} + +int iupdrvMenuGetMenuBarSize(Ihandle* ih) +{ + int ch; + iupdrvFontGetCharSize(ih, NULL, &ch); +#ifdef WIN32 + return 3 + ch + 3; +#else + return 4 + ch + 4; +#endif +} + +static void gtkItemUpdateImage(Ihandle* ih, const char* value, const char* image, const char* impress) +{ + GdkPixbuf* pixbuf; + + if (!impress || !iupStrBoolean(value)) + pixbuf = iupImageGetImage(image, ih, 0); + else + pixbuf = iupImageGetImage(impress, ih, 0); + + if (pixbuf) + { + GtkWidget* image_label = gtk_image_menu_item_get_image((GtkImageMenuItem*)ih->handle); + if (!image_label) + { + image_label = gtk_image_new(); + gtk_image_menu_item_set_image((GtkImageMenuItem*)ih->handle, image_label); + } + + if (pixbuf != gtk_image_get_pixbuf((GtkImage*)image_label)) + gtk_image_set_from_pixbuf((GtkImage*)image_label, pixbuf); + } + else + gtk_image_menu_item_set_image((GtkImageMenuItem*)ih->handle, NULL); +} + + +/*******************************************************************************************/ + + +static void gtkMenuMap(GtkWidget *widget, Ihandle* ih) +{ + Icallback cb = IupGetCallback(ih, "OPEN_CB"); + if (!cb && ih->parent) cb = (Icallback)IupGetCallback(ih->parent, "OPEN_CB"); /* check also in the Submenu */ + if (cb) cb(ih); + + (void)widget; +} + +static void gtkMenuUnMap(GtkWidget *widget, Ihandle* ih) +{ + Icallback cb = IupGetCallback(ih, "MENUCLOSE_CB"); + if (!cb && ih->parent) cb = (Icallback)IupGetCallback(ih->parent, "MENUCLOSE_CB"); /* check also in the Submenu */ + if (cb) cb(ih); + + (void)widget; +} + +static void gtkPopupMenuUnMap(GtkWidget *widget, Ihandle* ih) +{ + gtkMenuUnMap(widget, ih); + + /* quit the popup loop */ + gtk_main_quit(); +} + +static void gtkItemSelect(GtkWidget *widget, Ihandle* ih) +{ + Icallback cb = IupGetCallback(ih, "HIGHLIGHT_CB"); + if (cb) + cb(ih); + + cb = IupGetCallback(ih, "HELP_CB"); + if (cb) + gtk_menu_set_active((GtkMenu*)ih->parent->handle, IupGetChildPos(ih->parent, ih)); + + (void)widget; +} + +static void gtkItemActivate(GtkWidget *widget, Ihandle* ih) +{ + Icallback cb; + + if (GTK_IS_CHECK_MENU_ITEM(ih->handle) && !iupAttribGetBoolean(ih, "AUTOTOGGLE") && !iupAttribGetBoolean(ih->parent, "RADIO")) + { + /* GTK by default will do autotoggle */ + g_signal_handlers_block_by_func(G_OBJECT(ih->handle), G_CALLBACK(gtkItemActivate), ih); + gtk_check_menu_item_set_active((GtkCheckMenuItem*)ih->handle, !gtk_check_menu_item_get_active((GtkCheckMenuItem*)ih->handle)); + g_signal_handlers_unblock_by_func(G_OBJECT(ih->handle), G_CALLBACK(gtkItemActivate), ih); + } + + if (GTK_IS_IMAGE_MENU_ITEM(ih->handle)) + { + if (iupAttribGetBoolean(ih, "AUTOTOGGLE")) + { + if (iupAttribGetBoolean(ih, "VALUE")) + iupAttribSetStr(ih, "VALUE", "OFF"); + else + iupAttribSetStr(ih, "VALUE", "ON"); + + gtkItemUpdateImage(ih, iupAttribGet(ih, "VALUE"), iupAttribGet(ih, "IMAGE"), iupAttribGet(ih, "IMPRESS")); + } + } + + cb = IupGetCallback(ih, "ACTION"); + if (cb && cb(ih)==IUP_CLOSE) + IupExitLoop(); + + (void)widget; +} + +static gboolean gtkMenuKeyPressEvent(GtkWidget *widget, GdkEventKey *evt, Ihandle *ih) +{ + if (evt->keyval == GDK_F1) + { + Ihandle* child; + GtkWidget* active = gtk_menu_get_active((GtkMenu*)widget); + for (child=ih->firstchild; child; child=child->brother) + { + if (child->handle == active) + iupgtkShowHelp(NULL, NULL, child); + } + } + + (void)widget; + (void)evt; + return FALSE; +} + + +/*******************************************************************************************/ + + +static int gtkMenuMapMethod(Ihandle* ih) +{ + if (iupMenuIsMenuBar(ih)) + { + /* top level menu used for MENU attribute in IupDialog (a menu bar) */ +#ifdef HILDON + Ihandle *pih; + ih->handle = gtk_menu_new(); + if (!ih->handle) + return IUP_ERROR; + + pih = iupChildTreeGetNativeParent(ih); + hildon_window_set_menu(HILDON_WINDOW(pih->handle), GTK_MENU(ih->handle)); +#else + ih->handle = gtk_menu_bar_new(); + if (!ih->handle) + return IUP_ERROR; + + iupgtkBaseAddToParent(ih); +#endif + } + else + { + ih->handle = gtk_menu_new(); + if (!ih->handle) + return IUP_ERROR; + + if (ih->parent) + { + /* parent is a submenu */ + gtk_menu_item_set_submenu((GtkMenuItem*)ih->parent->handle, ih->handle); + + g_signal_connect(G_OBJECT(ih->handle), "map", G_CALLBACK(gtkMenuMap), ih); + g_signal_connect(G_OBJECT(ih->handle), "unmap", G_CALLBACK(gtkMenuUnMap), ih); + } + else + { + /* top level menu used for IupPopup */ + iupAttribSetStr(ih, "_IUPGTK_POPUP_MENU", "1"); + + g_signal_connect(G_OBJECT(ih->handle), "map", G_CALLBACK(gtkMenuMap), ih); + g_signal_connect(G_OBJECT(ih->handle), "unmap", G_CALLBACK(gtkPopupMenuUnMap), ih); + } + } + + gtk_widget_add_events(ih->handle, GDK_KEY_PRESS_MASK); + g_signal_connect(G_OBJECT(ih->handle), "key-press-event", G_CALLBACK(gtkMenuKeyPressEvent), ih); + + ih->serial = iupMenuGetChildId(ih); + gtk_widget_show(ih->handle); + + return IUP_NOERROR; +} + +void iupdrvMenuInitClass(Iclass* ic) +{ + /* Driver Dependent Class functions */ + ic->Map = gtkMenuMapMethod; + ic->UnMap = iupdrvBaseUnMapMethod; + + /* Used by iupdrvMenuGetMenuBarSize */ + iupClassRegisterAttribute(ic, "STANDARDFONT", NULL, NULL, IUPAF_SAMEASSYSTEM, "DEFAULTFONT", IUPAF_DEFAULT); /* use inheritance to retrieve standard fonts */ + + iupClassRegisterAttribute(ic, "BGCOLOR", NULL, iupdrvBaseSetBgColorAttrib, NULL, NULL, IUPAF_DEFAULT); +} + + +/*******************************************************************************************/ + +static int gtkItemSetTitleImageAttrib(Ihandle* ih, const char* value) +{ + if (GTK_IS_IMAGE_MENU_ITEM(ih->handle)) + { + gtkItemUpdateImage(ih, NULL, value, NULL); + return 1; + } + else + return 0; +} + +static int gtkItemSetImageAttrib(Ihandle* ih, const char* value) +{ + if (GTK_IS_IMAGE_MENU_ITEM(ih->handle)) + { + gtkItemUpdateImage(ih, iupAttribGet(ih, "VALUE"), value, iupAttribGet(ih, "IMPRESS")); + return 1; + } + else + return 0; +} + +static int gtkItemSetImpressAttrib(Ihandle* ih, const char* value) +{ + if (GTK_IS_IMAGE_MENU_ITEM(ih->handle)) + { + gtkItemUpdateImage(ih, iupAttribGet(ih, "VALUE"), iupAttribGet(ih, "IMAGE"), value); + return 1; + } + else + return 0; +} + +static int gtkItemSetTitleAttrib(Ihandle* ih, const char* value) +{ + char *str; + GtkWidget* label; + + if (!value) + { + str = " "; + value = str; + } + else + str = iupMenuProcessTitle(ih, value); + + label = gtk_bin_get_child((GtkBin*)ih->handle); + + iupgtkSetMnemonicTitle(ih, (GtkLabel*)label, str); + + if (str != value) free(str); + return 1; +} + +static int gtkItemSetValueAttrib(Ihandle* ih, const char* value) +{ + if (GTK_IS_CHECK_MENU_ITEM(ih->handle)) + { + if (iupAttribGetBoolean(ih->parent, "RADIO")) + value = "ON"; + + g_signal_handlers_block_by_func(G_OBJECT(ih->handle), G_CALLBACK(gtkItemActivate), ih); + gtk_check_menu_item_set_active((GtkCheckMenuItem*)ih->handle, iupStrBoolean(value)); + g_signal_handlers_unblock_by_func(G_OBJECT(ih->handle), G_CALLBACK(gtkItemActivate), ih); + return 0; + } + else if (GTK_IS_IMAGE_MENU_ITEM(ih->handle)) + { + gtkItemUpdateImage(ih, value, iupAttribGet(ih, "IMAGE"), iupAttribGet(ih, "IMPRESS")); + return 1; + } + else + return 0; +} + +static char* gtkItemGetValueAttrib(Ihandle* ih) +{ + if (GTK_IS_CHECK_MENU_ITEM(ih->handle)) + { + if (gtk_check_menu_item_get_active((GtkCheckMenuItem*)ih->handle)) + return "ON"; + else + return "OFF"; + } + else + return NULL; +} + +static int gtkItemMapMethod(Ihandle* ih) +{ + int pos; + + if (!ih->parent) + return IUP_ERROR; + +#ifndef HILDON + if (iupMenuIsMenuBar(ih->parent)) + ih->handle = gtk_menu_item_new_with_label(""); + else +#endif + { + if (iupAttribGet(ih, "IMAGE")||iupAttribGet(ih, "TITLEIMAGE")) + ih->handle = gtk_image_menu_item_new_with_label(""); + else if (iupAttribGetBoolean(ih->parent, "RADIO")) + { + GtkRadioMenuItem* last_tg = (GtkRadioMenuItem*)iupAttribGet(ih->parent, "_IUPGTK_LASTRADIOITEM"); + if (last_tg) + ih->handle = gtk_radio_menu_item_new_with_label_from_widget(last_tg, ""); + else + ih->handle = gtk_radio_menu_item_new_with_label(NULL, ""); + iupAttribSetStr(ih->parent, "_IUPGTK_LASTRADIOITEM", (char*)ih->handle); + } + else + { + char* hidemark = iupAttribGetStr(ih, "HIDEMARK"); + if (!hidemark && gtk_check_version(2, 14, 0) == NULL) + { + /* force HIDEMARK if VALUE is defined before Map, after GTK 2.14 */ + if (!iupAttribGet(ih, "VALUE")) + hidemark = "YES"; + } + + if (iupStrBoolean(hidemark)) + ih->handle = gtk_menu_item_new_with_label(""); + else + ih->handle = gtk_check_menu_item_new_with_label(""); + } + } + + if (!ih->handle) + return IUP_ERROR; + + ih->serial = iupMenuGetChildId(ih); + + g_signal_connect(G_OBJECT(ih->handle), "select", G_CALLBACK(gtkItemSelect), ih); + g_signal_connect(G_OBJECT(ih->handle), "activate", G_CALLBACK(gtkItemActivate), ih); + + pos = IupGetChildPos(ih->parent, ih); + gtk_menu_shell_insert((GtkMenuShell*)ih->parent->handle, ih->handle, pos); + gtk_widget_show(ih->handle); + + iupUpdateStandardFontAttrib(ih); + + return IUP_NOERROR; +} + +void iupdrvItemInitClass(Iclass* ic) +{ + /* Driver Dependent Class functions */ + ic->Map = gtkItemMapMethod; + ic->UnMap = iupdrvBaseUnMapMethod; + + /* Common */ + iupClassRegisterAttribute(ic, "STANDARDFONT", NULL, iupdrvSetStandardFontAttrib, IUPAF_SAMEASSYSTEM, "DEFAULTFONT", IUPAF_NOT_MAPPED); /* use inheritance to retrieve standard fonts */ + + /* Visual */ + iupClassRegisterAttribute(ic, "ACTIVE", iupBaseGetActiveAttrib, iupBaseSetActiveAttrib, IUPAF_SAMEASSYSTEM, "YES", IUPAF_DEFAULT); + iupClassRegisterAttribute(ic, "BGCOLOR", NULL, iupdrvBaseSetBgColorAttrib, IUPAF_SAMEASSYSTEM, "DLGBGCOLOR", IUPAF_DEFAULT); + + /* IupItem only */ + iupClassRegisterAttribute(ic, "VALUE", gtkItemGetValueAttrib, gtkItemSetValueAttrib, NULL, NULL, IUPAF_NO_DEFAULTVALUE|IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "TITLE", NULL, gtkItemSetTitleAttrib, NULL, NULL, IUPAF_NO_DEFAULTVALUE|IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "TITLEIMAGE", NULL, gtkItemSetTitleImageAttrib, NULL, NULL, IUPAF_NO_DEFAULTVALUE|IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "IMAGE", NULL, gtkItemSetImageAttrib, NULL, NULL, IUPAF_NO_DEFAULTVALUE|IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "IMPRESS", NULL, gtkItemSetImpressAttrib, NULL, NULL, IUPAF_NO_DEFAULTVALUE|IUPAF_NO_INHERIT); + + /* IupItem GTK and Motif only */ + iupClassRegisterAttribute(ic, "HIDEMARK", NULL, NULL, NULL, NULL, IUPAF_NOT_MAPPED); +} + + +/*******************************************************************************************/ + + +static int gtkSubmenuSetImageAttrib(Ihandle* ih, const char* value) +{ + if (GTK_IS_IMAGE_MENU_ITEM(ih->handle)) + { + gtkItemUpdateImage(ih, NULL, value, NULL); + return 1; + } + else + return 0; +} + +static int motSubmenuMapMethod(Ihandle* ih) +{ + int pos; + + if (!ih->parent) + return IUP_ERROR; + +#ifndef HILDON + if (iupMenuIsMenuBar(ih->parent)) + ih->handle = gtk_menu_item_new_with_label(""); + else +#endif + ih->handle = gtk_image_menu_item_new_with_label(""); + + if (!ih->handle) + return IUP_ERROR; + + ih->serial = iupMenuGetChildId(ih); + + pos = IupGetChildPos(ih->parent, ih); + gtk_menu_shell_insert((GtkMenuShell*)ih->parent->handle, ih->handle, pos); + gtk_widget_show(ih->handle); + + g_signal_connect(G_OBJECT(ih->handle), "select", G_CALLBACK(gtkItemSelect), ih); + + iupUpdateStandardFontAttrib(ih); + + return IUP_NOERROR; +} + +void iupdrvSubmenuInitClass(Iclass* ic) +{ + /* Driver Dependent Class functions */ + ic->Map = motSubmenuMapMethod; + ic->UnMap = iupdrvBaseUnMapMethod; + + /* Common */ + iupClassRegisterAttribute(ic, "STANDARDFONT", NULL, iupdrvSetStandardFontAttrib, IUPAF_SAMEASSYSTEM, "DEFAULTFONT", IUPAF_NOT_MAPPED); /* use inheritance to retrieve standard fonts */ + + /* Visual */ + iupClassRegisterAttribute(ic, "ACTIVE", iupBaseGetActiveAttrib, iupBaseSetActiveAttrib, IUPAF_SAMEASSYSTEM, "YES", IUPAF_DEFAULT); + iupClassRegisterAttribute(ic, "BGCOLOR", NULL, iupdrvBaseSetBgColorAttrib, IUPAF_SAMEASSYSTEM, "DLGBGCOLOR", IUPAF_DEFAULT); + + /* IupSubmenu only */ + iupClassRegisterAttribute(ic, "TITLE", NULL, gtkItemSetTitleAttrib, NULL, NULL, IUPAF_NO_DEFAULTVALUE|IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "IMAGE", NULL, gtkSubmenuSetImageAttrib, NULL, NULL, IUPAF_NO_DEFAULTVALUE|IUPAF_NO_INHERIT); +} + + +/*******************************************************************************************/ + + +static int gtkSeparatorMapMethod(Ihandle* ih) +{ + int pos; + + if (!ih->parent) + return IUP_ERROR; + + ih->handle = gtk_separator_menu_item_new(); + if (!ih->handle) + return IUP_ERROR; + + ih->serial = iupMenuGetChildId(ih); + + pos = IupGetChildPos(ih->parent, ih); + gtk_menu_shell_insert((GtkMenuShell*)ih->parent->handle, ih->handle, pos); + gtk_widget_show(ih->handle); + + return IUP_NOERROR; +} + +void iupdrvSeparatorInitClass(Iclass* ic) +{ + /* Driver Dependent Class functions */ + ic->Map = gtkSeparatorMapMethod; + ic->UnMap = iupdrvBaseUnMapMethod; +} diff --git a/iup/src/gtk/iupgtk_messagedlg.c b/iup/src/gtk/iupgtk_messagedlg.c new file mode 100755 index 0000000..a036af6 --- /dev/null +++ b/iup/src/gtk/iupgtk_messagedlg.c @@ -0,0 +1,128 @@ +/** \file + * \brief GTK IupMessageDlg pre-defined dialog + * + * See Copyright Notice in "iup.h" + */ + +#include <gtk/gtk.h> + +#include "iup.h" + +#include "iup_object.h" +#include "iup_attrib.h" +#include "iup_str.h" +#include "iup_dialog.h" + +#include "iupgtk_drv.h" + +/* Sometimes GTK decides to invert the buttons position because of the GNOME Guidelines. + To avoid that we define different Ids for the buttons. */ +#define IUP_RESPONSE_1 -100 +#define IUP_RESPONSE_2 -200 +#define IUP_RESPONSE_HELP -300 + +#ifndef GTK_MESSAGE_OTHER +#define GTK_MESSAGE_OTHER GTK_MESSAGE_INFO +#endif + +static int gtkMessageDlgPopup(Ihandle* ih, int x, int y) +{ + InativeHandle* parent = iupDialogGetNativeParent(ih); + GtkMessageType type = GTK_MESSAGE_OTHER; + GtkWidget* dialog; + char *icon, *buttons, *title; + int response, num_but = 2; + + iupAttribSetInt(ih, "_IUPDLG_X", x); /* used in iupDialogUpdatePosition */ + iupAttribSetInt(ih, "_IUPDLG_Y", y); + + icon = iupAttribGetStr(ih, "DIALOGTYPE"); + if (iupStrEqualNoCase(icon, "ERROR")) + type = GTK_MESSAGE_ERROR; + else if (iupStrEqualNoCase(icon, "WARNING")) + type = GTK_MESSAGE_WARNING; + else if (iupStrEqualNoCase(icon, "INFORMATION")) + type = GTK_MESSAGE_INFO; + else if (iupStrEqualNoCase(icon, "QUESTION")) + type = GTK_MESSAGE_QUESTION; + + dialog = gtk_message_dialog_new((GtkWindow*)parent, + 0, + type, + GTK_BUTTONS_NONE, + iupgtkStrConvertToUTF8(iupAttribGet(ih, "VALUE"))); + if (!dialog) + return IUP_ERROR; + + title = iupAttribGet(ih, "TITLE"); + if (title) + gtk_window_set_title(GTK_WINDOW(dialog), iupgtkStrConvertToUTF8(title)); + + buttons = iupAttribGetStr(ih, "BUTTONS"); + if (iupStrEqualNoCase(buttons, "OKCANCEL")) + { + gtk_dialog_add_button(GTK_DIALOG(dialog), + GTK_STOCK_OK, + IUP_RESPONSE_1); + gtk_dialog_add_button(GTK_DIALOG(dialog), + GTK_STOCK_CANCEL, + IUP_RESPONSE_2); + } + else if (iupStrEqualNoCase(buttons, "YESNO")) + { + gtk_dialog_add_button(GTK_DIALOG(dialog), + GTK_STOCK_YES, + IUP_RESPONSE_1); + gtk_dialog_add_button(GTK_DIALOG(dialog), + GTK_STOCK_NO, + IUP_RESPONSE_2); + } + else /* OK */ + { + gtk_dialog_add_button(GTK_DIALOG(dialog), + GTK_STOCK_OK, + IUP_RESPONSE_1); + num_but = 1; + } + + if (IupGetCallback(ih, "HELP_CB")) + gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_HELP, IUP_RESPONSE_HELP); + + if (num_but == 2 && iupAttribGetInt(ih, "BUTTONDEFAULT") == 2) + gtk_dialog_set_default_response(GTK_DIALOG(dialog), IUP_RESPONSE_2); + else + gtk_dialog_set_default_response(GTK_DIALOG(dialog), IUP_RESPONSE_1); + + /* initialize the widget */ + gtk_widget_realize(dialog); + + ih->handle = dialog; + iupDialogUpdatePosition(ih); + ih->handle = NULL; + + do + { + response = gtk_dialog_run(GTK_DIALOG(dialog)); + + if (response == IUP_RESPONSE_HELP) + { + Icallback cb = IupGetCallback(ih, "HELP_CB"); + if (cb && cb(ih) == IUP_CLOSE) + response = (num_but == 2)? IUP_RESPONSE_2: IUP_RESPONSE_1; + } + } while (response == IUP_RESPONSE_HELP); + + if (response == IUP_RESPONSE_1) + IupSetAttribute(ih, "BUTTONRESPONSE", "1"); + else + IupSetAttribute(ih, "BUTTONRESPONSE", "2"); + + gtk_widget_destroy(dialog); + + return IUP_NOERROR; +} + +void iupdrvMessageDlgInitClass(Iclass* ic) +{ + ic->DlgPopup = gtkMessageDlgPopup; +} diff --git a/iup/src/gtk/iupgtk_open.c b/iup/src/gtk/iupgtk_open.c new file mode 100755 index 0000000..66e46e8 --- /dev/null +++ b/iup/src/gtk/iupgtk_open.c @@ -0,0 +1,172 @@ +/** \file + * \brief GTK Driver Core + * + * See Copyright Notice in "iup.h" + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <gtk/gtk.h> + +#include "iup.h" + +#include "iup_str.h" +#include "iup_drv.h" +#include "iup_drvinfo.h" +#include "iup_object.h" +#include "iup_globalattrib.h" + +#include "iupgtk_drv.h" + + +#ifdef WIN32 /******************************** WIN32 ************************************/ +#include <gdk/gdkwin32.h> + +char* iupgtkGetNativeWindowHandle(Ihandle* ih) +{ + GdkWindow* window = ih->handle->window; + if (window) + return (char*)GDK_WINDOW_HWND(window); + else + return NULL; +} + +void* iupgtkGetNativeGraphicsContext(GtkWidget* widget) +{ + return GetDC(GDK_WINDOW_HWND(widget->window)); +} + +void iupgtkReleaseNativeGraphicsContext(GtkWidget* widget, void* gc) +{ + ReleaseDC(GDK_WINDOW_HWND(widget->window), (HDC)gc); +} + +void* iupdrvGetDisplay(void) +{ + return NULL; +} + +void iupgtkPushVisualAndColormap(void* visual, void* colormap) +{ + (void)visual; + (void)colormap; +} + +static void gtkSetDrvGlobalAttrib(void) +{ +} + +#else /******************************** X11 ************************************/ +#include <gdk/gdkx.h> + +char* iupgtkGetNativeWindowHandle(Ihandle* ih) +{ + GdkWindow* window = ih->handle->window; + if (window) + return (char*)GDK_WINDOW_XID(window); + else + return NULL; +} + +void* iupgtkGetNativeGraphicsContext(GtkWidget* widget) +{ + GdkDisplay* display = gdk_display_get_default(); + return (void*)XCreateGC(GDK_DISPLAY_XDISPLAY(display), GDK_WINDOW_XID(widget->window), 0, NULL); +} + +void iupgtkReleaseNativeGraphicsContext(GtkWidget* widget, void* gc) +{ + GdkDisplay* display = gdk_display_get_default(); + XFreeGC(GDK_DISPLAY_XDISPLAY(display), (GC)gc); + (void)widget; +} + +void* iupdrvGetDisplay(void) +{ + GdkDisplay* display = gdk_display_get_default(); + return GDK_DISPLAY_XDISPLAY(display); +} + +void iupgtkPushVisualAndColormap(void* visual, void* colormap) +{ + GdkColormap* gdk_colormap; + GdkVisual *gdk_visual = gdkx_visual_get(XVisualIDFromVisual((Visual*)visual)); + if (colormap) + gdk_colormap = gdk_x11_colormap_foreign_new(gdk_visual, (Colormap)colormap); + else + gdk_colormap = gdk_colormap_new(gdk_visual, FALSE); + + gtk_widget_push_colormap(gdk_colormap); + + /* gtk_widget_push_visual is now deprecated */ +} + +static void gtkSetDrvGlobalAttrib(void) +{ + GdkDisplay* display = gdk_display_get_default(); + Display* xdisplay = GDK_DISPLAY_XDISPLAY(display); + IupSetGlobal("XDISPLAY", (char*)xdisplay); + IupSetGlobal("XSCREEN", (char*)XDefaultScreen(xdisplay)); + IupSetGlobal("XSERVERVENDOR", ServerVendor(xdisplay)); + IupSetfAttribute(NULL, "XVENDORRELEASE", "%d", VendorRelease(xdisplay)); +} + +#endif + +static void gtkSetGlobalColorAttrib(const char* name, GdkColor *color) +{ + iupGlobalSetDefaultColorAttrib(name, (int)iupCOLOR16TO8(color->red), + (int)iupCOLOR16TO8(color->green), + (int)iupCOLOR16TO8(color->blue)); +} + +void iupgtkUpdateGlobalColors(GtkStyle* style) +{ + GdkColor color = style->bg[GTK_STATE_NORMAL]; + gtkSetGlobalColorAttrib("DLGBGCOLOR", &color); + + color = style->fg[GTK_STATE_NORMAL]; + gtkSetGlobalColorAttrib("DLGFGCOLOR", &color); + + color = style->base[GTK_STATE_NORMAL]; + gtkSetGlobalColorAttrib("TXTBGCOLOR", &color); + + color = style->text[GTK_STATE_NORMAL]; + gtkSetGlobalColorAttrib("TXTFGCOLOR", &color); +} + +int iupdrvOpen(int *argc, char ***argv) +{ + GtkStyle* style; + + if (!gtk_init_check(argc, argv)) + return IUP_ERROR; + + IupSetGlobal("DRIVER", "GTK"); + + IupStoreGlobal("SYSTEMLANGUAGE", pango_language_to_string(gtk_get_default_language())); + + /* driver system version */ + IupSetfAttribute(NULL, "GTKVERSION", "%d.%d.%d", gtk_major_version, + gtk_minor_version, + gtk_micro_version); + IupSetfAttribute(NULL, "GTKDEVVERSION", "%d.%d.%d", GTK_MAJOR_VERSION, + GTK_MINOR_VERSION, + GTK_MICRO_VERSION); + + gtkSetDrvGlobalAttrib(); + + style = gtk_style_new(); + iupgtkUpdateGlobalColors(style); + IupSetGlobal("_IUP_RESET_GLOBALCOLORS", "YES"); /* will update the global colors when the first dialog is mapped */ + g_object_unref(style); + + return IUP_NOERROR; +} + +void iupdrvClose(void) +{ + iupgtkReleaseConvertUTF8(); +} diff --git a/iup/src/gtk/iupgtk_progressbar.c b/iup/src/gtk/iupgtk_progressbar.c new file mode 100755 index 0000000..7bc6cbb --- /dev/null +++ b/iup/src/gtk/iupgtk_progressbar.c @@ -0,0 +1,131 @@ +/** \file +* \brief Progress bar Control +* +* See Copyright Notice in "iup.h" +*/ + +#undef GTK_DISABLE_DEPRECATED +#include <gtk/gtk.h> + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <memory.h> +#include <stdarg.h> + +#include "iup.h" +#include "iupcbs.h" + +#include "iup_object.h" +#include "iup_layout.h" +#include "iup_attrib.h" +#include "iup_str.h" +#include "iup_progressbar.h" +#include "iup_drv.h" + +#include "iupgtk_drv.h" + + +static int gtkProgressBarSetMarqueeAttrib(Ihandle* ih, const char* value) +{ + GtkProgress* progress = (GtkProgress*)ih->handle; + + if (iupStrBoolean(value)) + { + ih->data->marquee = 1; + gtk_progress_set_activity_mode(progress, TRUE); + } + else + { + gtk_progress_set_activity_mode(progress, FALSE); + ih->data->marquee = 0; + } + + return 1; +} + +static int gtkProgressBarSetValueAttrib(Ihandle* ih, const char* value) +{ + GtkProgressBar* pbar = (GtkProgressBar*)ih->handle; + + if (!value) + ih->data->value = 0; + else + ih->data->value = atof(value); + iProgressBarCropValue(ih); + + if (ih->data->marquee) + gtk_progress_bar_pulse(pbar); + else + gtk_progress_bar_set_fraction(pbar, (ih->data->value - ih->data->vmin) / (ih->data->vmax - ih->data->vmin)); + + return 0; +} + +static int gtkProgressBarSetDashedAttrib(Ihandle* ih, const char* value) +{ + GtkProgressBar* pbar = (GtkProgressBar*)ih->handle; + + /* gtk_progress_bar_set_bar_style is deprecated */ + if (iupStrBoolean(value)) + { + ih->data->dashed = 1; + gtk_progress_bar_set_bar_style(pbar, GTK_PROGRESS_DISCRETE); + } + else /* Default */ + { + ih->data->dashed = 0; + gtk_progress_bar_set_bar_style(pbar, GTK_PROGRESS_CONTINUOUS); + } + + return 0; +} + +static int gtkProgressBarMapMethod(Ihandle* ih) +{ + ih->handle = gtk_progress_bar_new(); + if (!ih->handle) + return IUP_ERROR; + + /* add to the parent, all GTK controls must call this. */ + iupgtkBaseAddToParent(ih); + + gtk_widget_realize(ih->handle); + + if (iupStrEqualNoCase(iupAttribGetStr(ih, "ORIENTATION"), "VERTICAL")) + { + gtk_progress_bar_set_orientation((GtkProgressBar*)ih->handle, GTK_PROGRESS_BOTTOM_TO_TOP); + + if (ih->currentheight < ih->currentwidth) + { + int tmp = ih->currentheight; + ih->currentheight = ih->currentwidth; + ih->currentwidth = tmp; + } + } + else + gtk_progress_bar_set_orientation((GtkProgressBar*)ih->handle, GTK_PROGRESS_LEFT_TO_RIGHT); + + return IUP_NOERROR; +} + +void iupdrvProgressBarInitClass(Iclass* ic) +{ + /* Driver Dependent Class functions */ + ic->Map = gtkProgressBarMapMethod; + + /* Driver Dependent Attribute functions */ + + /* Visual */ + iupClassRegisterAttribute(ic, "BGCOLOR", NULL, iupdrvBaseSetBgColorAttrib, IUPAF_SAMEASSYSTEM, "DLGBGCOLOR", IUPAF_DEFAULT); + + /* Special */ + iupClassRegisterAttribute(ic, "FGCOLOR", NULL, NULL, NULL, NULL, IUPAF_DEFAULT); + + /* IupProgressBar only */ + iupClassRegisterAttribute(ic, "VALUE", iProgressBarGetValueAttrib, gtkProgressBarSetValueAttrib, NULL, NULL, IUPAF_NO_DEFAULTVALUE|IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "DASHED", iProgressBarGetDashedAttrib, gtkProgressBarSetDashedAttrib, NULL, NULL, IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "ORIENTATION", NULL, NULL, IUPAF_SAMEASSYSTEM, "HORIZONTAL", IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "MARQUEE", NULL, gtkProgressBarSetMarqueeAttrib, NULL, NULL, IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "DASHED", NULL, NULL, NULL, NULL, IUPAF_NO_INHERIT); +} diff --git a/iup/src/gtk/iupgtk_tabs.c b/iup/src/gtk/iupgtk_tabs.c new file mode 100755 index 0000000..8029826 --- /dev/null +++ b/iup/src/gtk/iupgtk_tabs.c @@ -0,0 +1,444 @@ +/** \file +* \brief Tabs Control +* +* See Copyright Notice in "iup.h" +*/ + +#include <gtk/gtk.h> + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <math.h> +#include <memory.h> +#include <stdarg.h> + +#include "iup.h" +#include "iupcbs.h" + +#include "iup_object.h" +#include "iup_layout.h" +#include "iup_attrib.h" +#include "iup_str.h" +#include "iup_dialog.h" +#include "iup_drv.h" +#include "iup_drvfont.h" +#include "iup_stdcontrols.h" +#include "iup_image.h" +#include "iup_tabs.h" + +#include "iupgtk_drv.h" + + +int iupdrvTabsExtraDecor(Ihandle* ih) +{ + (void)ih; + return 0; +} + +int iupdrvTabsGetLineCountAttrib(Ihandle* ih) +{ + (void)ih; + return 1; +} + +void iupdrvTabsSetCurrentTab(Ihandle* ih, int pos) +{ + iupAttribSetStr(ih, "_IUPGTK_IGNORE_CHANGE", "1"); + gtk_notebook_set_current_page((GtkNotebook*)ih->handle, pos); + iupAttribSetStr(ih, "_IUPGTK_IGNORE_CHANGE", NULL); +} + +int iupdrvTabsGetCurrentTab(Ihandle* ih) +{ + return gtk_notebook_get_current_page((GtkNotebook*)ih->handle); +} + +static void gtkTabsUpdatePageFont(Ihandle* ih) +{ + Ihandle* child; + PangoFontDescription* fontdesc = (PangoFontDescription*)iupgtkGetPangoFontDescAttrib(ih); + + for (child = ih->firstchild; child; child = child->brother) + { + GtkWidget* tab_label = (GtkWidget*)iupAttribGet(child, "_IUPGTK_TABLABEL"); + if (tab_label) + { + gtk_widget_modify_font(tab_label, fontdesc); + iupgtkFontUpdatePangoLayout(ih, gtk_label_get_layout((GtkLabel*)tab_label)); + } + } +} + +static void gtkTabsUpdatePageBgColor(Ihandle* ih, unsigned char r, unsigned char g, unsigned char b) +{ + Ihandle* child; + + for (child = ih->firstchild; child; child = child->brother) + { + GtkWidget* tab_page = (GtkWidget*)iupAttribGet(child, "_IUPTAB_CONTAINER"); + if (tab_page) + { + GtkWidget* tab_label = (GtkWidget*)iupAttribGet(child, "_IUPGTK_TABLABEL"); + if (tab_label) + iupgtkBaseSetBgColor(tab_label, r, g, b); + iupgtkBaseSetBgColor(tab_page, r, g, b); + } + } +} + +static void gtkTabsUpdatePageFgColor(Ihandle* ih, unsigned char r, unsigned char g, unsigned char b) +{ + Ihandle* child; + + for (child = ih->firstchild; child; child = child->brother) + { + GtkWidget* tab_label = (GtkWidget*)iupAttribGet(child, "_IUPGTK_TABLABEL"); + if (tab_label) + iupgtkBaseSetFgColor(tab_label, r, g, b); + } +} + +static void gtkTabsUpdatePagePadding(Ihandle* ih) +{ + Ihandle* child; + + for (child = ih->firstchild; child; child = child->brother) + { + GtkWidget* tab_label = (GtkWidget*)iupAttribGet(child, "_IUPGTK_TABLABEL"); + if (tab_label) + gtk_misc_set_padding((GtkMisc*)tab_label, ih->data->horiz_padding, ih->data->vert_padding); + } +} + +/* ------------------------------------------------------------------------- */ +/* gtkTabs - Sets and Gets accessors */ +/* ------------------------------------------------------------------------- */ + + +static int gtkTabsSetPaddingAttrib(Ihandle* ih, const char* value) +{ + iupStrToIntInt(value, &ih->data->horiz_padding, &ih->data->vert_padding, 'x'); + + if (ih->handle) + gtkTabsUpdatePagePadding(ih); + return 0; +} + +static void gtkTabsUpdateTabType(Ihandle* ih) +{ + GtkNotebook* tab_page = (GtkNotebook*)ih->handle; + int iup2gtk[4] = {GTK_POS_TOP, GTK_POS_BOTTOM, GTK_POS_LEFT, GTK_POS_RIGHT}; + gtk_notebook_set_tab_pos(tab_page, iup2gtk[ih->data->type]); +} + +static int gtkTabsSetTabTypeAttrib(Ihandle* ih, const char* value) +{ + if(iupStrEqualNoCase(value, "BOTTOM")) + ih->data->type = ITABS_BOTTOM; + else if(iupStrEqualNoCase(value, "LEFT")) + ih->data->type = ITABS_LEFT; + else if(iupStrEqualNoCase(value, "RIGHT")) + ih->data->type = ITABS_RIGHT; + else /* "TOP" */ + ih->data->type = ITABS_TOP; + + if (ih->handle) + gtkTabsUpdateTabType(ih); + + return 0; +} + +static int gtkTabsSetTabOrientationAttrib(Ihandle* ih, const char* value) +{ + if (ih->handle) /* allow to set only before mapping */ + return 0; + + if(iupStrEqualNoCase(value, "VERTICAL")) + ih->data->orientation = ITABS_VERTICAL; + else /* HORIZONTAL */ + ih->data->orientation = ITABS_HORIZONTAL; + + return 0; +} + +static int gtkTabsSetTabTitleAttrib(Ihandle* ih, const char* name_id, const char* value) +{ + int pos; + if (value && iupStrToInt(name_id, &pos)==1) + { + Ihandle* child = IupGetChild(ih, pos); + GtkWidget* tab_label = (GtkWidget*)iupAttribGet(child, "_IUPGTK_TABLABEL"); + if (tab_label) + { + GtkWidget* tab_page = (GtkWidget*)iupAttribGet(child, "_IUPTAB_CONTAINER"); + gtk_label_set_text((GtkLabel*)tab_label, iupgtkStrConvertToUTF8(value)); + gtk_notebook_set_menu_label_text((GtkNotebook*)ih->handle, tab_page, gtk_label_get_text((GtkLabel*)tab_label)); + } + } + return 1; +} + +static int gtkTabsSetTabImageAttrib(Ihandle* ih, const char* name_id, const char* value) +{ + int pos; + if (value && iupStrToInt(name_id, &pos)==1) + { + Ihandle* child = IupGetChild(ih, pos); + GtkWidget* tab_image = (GtkWidget*)iupAttribGet(child, "_IUPGTK_TABIMAGE"); + if (tab_image) + { + GdkPixbuf* pixbuf = iupImageGetImage(value, ih, 0); + if (pixbuf) + gtk_image_set_from_pixbuf((GtkImage*)tab_image, pixbuf); + } + } + return 1; +} + +static int gtkTabsSetStandardFontAttrib(Ihandle* ih, const char* value) +{ + iupdrvSetStandardFontAttrib(ih, value); + if (ih->handle) + gtkTabsUpdatePageFont(ih); + return 1; +} + +static int gtkTabsSetFgColorAttrib(Ihandle* ih, const char* value) +{ + unsigned char r, g, b; + if (!iupStrToRGB(value, &r, &g, &b)) + return 0; + + iupgtkBaseSetFgColor(ih->handle, r, g, b); + gtkTabsUpdatePageFgColor(ih, r, g, b); + + return 1; +} + +static int gtkTabsSetBgColorAttrib(Ihandle* ih, const char* value) +{ + unsigned char r, g, b; + if (!iupStrToRGB(value, &r, &g, &b)) + return 0; + + iupgtkBaseSetBgColor(ih->handle, r, g, b); + gtkTabsUpdatePageBgColor(ih, r, g, b); + + return 1; +} + + +/* ------------------------------------------------------------------------- */ +/* gtkTabs - Callbacks */ +/* ------------------------------------------------------------------------- */ + +void gtkTabSwitchPage(GtkNotebook* notebook, GtkNotebookPage *page, int pos, Ihandle* ih) +{ + IFnnn cb; + Ihandle* child = IupGetChild(ih, pos); + Ihandle* prev_child = IupGetChild(ih, iupdrvTabsGetCurrentTab(ih)); + IupSetAttribute(child, "VISIBLE", "YES"); + IupSetAttribute(prev_child, "VISIBLE", "NO"); + + if (iupAttribGet(ih, "_IUPGTK_IGNORE_CHANGE")) + return; + + cb = (IFnnn)IupGetCallback(ih, "TABCHANGE_CB"); + if (cb) + cb(ih, child, prev_child); + + (void)notebook; + (void)page; +} + +/* ------------------------------------------------------------------------- */ +/* gtkTabs - Methods and Init Class */ +/* ------------------------------------------------------------------------- */ + +static void gtkTabsChildAddedMethod(Ihandle* ih, Ihandle* child) +{ + if (IupGetName(child) == NULL) + iupAttribSetHandleName(child); + + if (ih->handle) + { + GtkWidget* tab_page; + GtkWidget *tab_label = NULL, *tab_image = NULL; + char *tabtitle, *tabimage; + int pos; + unsigned char r, g, b; + + pos = IupGetChildPos(ih, child); + + tab_page = gtk_fixed_new(); + gtk_widget_show(tab_page); + + tabtitle = iupAttribGet(child, "TABTITLE"); + if (!tabtitle) tabtitle = iupTabsAttribGetStrId(ih, "TABTITLE", pos); + tabimage = iupAttribGet(child, "TABIMAGE"); + if (!tabimage) tabimage = iupTabsAttribGetStrId(ih, "TABIMAGE", pos); + if (!tabtitle && !tabimage) + tabtitle = " "; + + if (tabtitle) + { + tab_label = gtk_label_new(iupgtkStrConvertToUTF8(tabtitle)); + +#if GTK_CHECK_VERSION(2, 6, 0) + if (ih->data->orientation == ITABS_VERTICAL) + gtk_label_set_angle((GtkLabel*)tab_label, 90); +#endif + } + + if (tabimage) + { + GdkPixbuf* pixbuf = iupImageGetImage(tabimage, ih, 0); + + tab_image = gtk_image_new(); + + if (pixbuf) + gtk_image_set_from_pixbuf((GtkImage*)tab_image, pixbuf); + } + + iupAttribSetStr(ih, "_IUPGTK_IGNORE_CHANGE", "1"); + + if (tabimage && tabtitle) + { + GtkWidget* box; + if (ih->data->orientation == ITABS_VERTICAL) + box = gtk_vbox_new(FALSE, 2); + else + box = gtk_hbox_new(FALSE, 2); + gtk_widget_show(box); + + gtk_container_add((GtkContainer*)box, tab_image); + gtk_container_add((GtkContainer*)box, tab_label); + + gtk_notebook_insert_page((GtkNotebook*)ih->handle, tab_page, box, pos); + gtk_notebook_set_menu_label_text((GtkNotebook*)ih->handle, tab_page, gtk_label_get_text((GtkLabel*)tab_label)); + } + else if (tabimage) + gtk_notebook_insert_page((GtkNotebook*)ih->handle, tab_page, tab_image, pos); + else + gtk_notebook_insert_page((GtkNotebook*)ih->handle, tab_page, tab_label, pos); + + gtk_widget_realize(tab_page); + + iupAttribSetStr(child, "_IUPGTK_TABIMAGE", (char*)tab_image); /* store it even if its NULL */ + iupAttribSetStr(child, "_IUPGTK_TABLABEL", (char*)tab_label); + iupAttribSetStr(child, "_IUPTAB_CONTAINER", (char*)tab_page); + iupStrToRGB(IupGetAttribute(ih, "BGCOLOR"), &r, &g, &b); + iupgtkBaseSetBgColor(tab_page, r, g, b); + + if (tabtitle) + { + PangoFontDescription* fontdesc = (PangoFontDescription*)iupgtkGetPangoFontDescAttrib(ih); + gtk_widget_modify_font(tab_label, fontdesc); + iupgtkFontUpdatePangoLayout(ih, gtk_label_get_layout((GtkLabel*)tab_label)); + + iupgtkBaseSetBgColor(tab_label, r, g, b); + + iupStrToRGB(IupGetAttribute(ih, "FGCOLOR"), &r, &g, &b); + iupgtkBaseSetFgColor(tab_label, r, g, b); + + gtk_widget_show(tab_label); + gtk_widget_realize(tab_label); + } + + if (tabimage) + { + gtk_widget_show(tab_image); + gtk_widget_realize(tab_image); + } + + iupAttribSetStr(ih, "_IUPGTK_IGNORE_CHANGE", NULL); + + if (pos == iupdrvTabsGetCurrentTab(ih)) + IupSetAttribute(child, "VISIBLE", "YES"); + else + IupSetAttribute(child, "VISIBLE", "NO"); + } +} + +static void gtkTabsChildRemovedMethod(Ihandle* ih, Ihandle* child) +{ + if (ih->handle) + { + GtkWidget* tab_page = (GtkWidget*)iupAttribGet(child, "_IUPTAB_CONTAINER"); + if (tab_page) + { + int pos = gtk_notebook_page_num((GtkNotebook*)ih->handle, tab_page); + + iupAttribSetStr(ih, "_IUPGTK_IGNORE_CHANGE", "1"); + gtk_notebook_remove_page((GtkNotebook*)ih->handle, pos); + iupAttribSetStr(ih, "_IUPGTK_IGNORE_CHANGE", NULL); + + iupAttribSetStr(child, "_IUPGTK_TABIMAGE", NULL); + iupAttribSetStr(child, "_IUPGTK_TABLABEL", NULL); + iupAttribSetStr(child, "_IUPTAB_CONTAINER", NULL); + } + } +} + +static int gtkTabsMapMethod(Ihandle* ih) +{ + ih->handle = gtk_notebook_new(); + if (!ih->handle) + return IUP_ERROR; + + gtk_notebook_set_scrollable((GtkNotebook*)ih->handle, TRUE); + gtk_notebook_popup_enable((GtkNotebook*)ih->handle); + + gtkTabsUpdateTabType(ih); + + /* add to the parent, all GTK controls must call this. */ + iupgtkBaseAddToParent(ih); + + gtk_widget_add_events(ih->handle, GDK_ENTER_NOTIFY_MASK|GDK_LEAVE_NOTIFY_MASK); + + g_signal_connect(G_OBJECT(ih->handle), "enter-notify-event", G_CALLBACK(iupgtkEnterLeaveEvent), ih); + g_signal_connect(G_OBJECT(ih->handle), "leave-notify-event", G_CALLBACK(iupgtkEnterLeaveEvent), ih); + g_signal_connect(G_OBJECT(ih->handle), "focus-in-event", G_CALLBACK(iupgtkFocusInOutEvent), ih); + g_signal_connect(G_OBJECT(ih->handle), "focus-out-event", G_CALLBACK(iupgtkFocusInOutEvent), ih); + g_signal_connect(G_OBJECT(ih->handle), "key-press-event", G_CALLBACK(iupgtkKeyPressEvent), ih); + g_signal_connect(G_OBJECT(ih->handle), "show-help", G_CALLBACK(iupgtkShowHelp), ih); + + g_signal_connect(G_OBJECT(ih->handle), "switch-page", G_CALLBACK(gtkTabSwitchPage), ih); + + gtk_widget_realize(ih->handle); + + /* Create pages and tabs */ + if (ih->firstchild) + { + Ihandle* child; + for (child = ih->firstchild; child; child = child->brother) + gtkTabsChildAddedMethod(ih, child); + } + + return IUP_NOERROR; +} + +void iupdrvTabsInitClass(Iclass* ic) +{ + /* Driver Dependent Class functions */ + ic->Map = gtkTabsMapMethod; + ic->ChildAdded = gtkTabsChildAddedMethod; + ic->ChildRemoved = gtkTabsChildRemovedMethod; + + /* Driver Dependent Attribute functions */ + + /* Common */ + iupClassRegisterAttribute(ic, "STANDARDFONT", NULL, gtkTabsSetStandardFontAttrib, IUPAF_SAMEASSYSTEM, "DEFAULTFONT", IUPAF_NOT_MAPPED); + + /* Visual */ + iupClassRegisterAttribute(ic, "BGCOLOR", NULL, gtkTabsSetBgColorAttrib, IUPAF_SAMEASSYSTEM, "DLGBGCOLOR", IUPAF_DEFAULT); + iupClassRegisterAttribute(ic, "FGCOLOR", NULL, gtkTabsSetFgColorAttrib, IUPAF_SAMEASSYSTEM, "DLGFGCOLOR", IUPAF_DEFAULT); + + /* IupTabs only */ + iupClassRegisterAttribute(ic, "TABTYPE", iupTabsGetTabTypeAttrib, gtkTabsSetTabTypeAttrib, IUPAF_SAMEASSYSTEM, "TOP", IUPAF_NOT_MAPPED|IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "TABORIENTATION", iupTabsGetTabOrientationAttrib, gtkTabsSetTabOrientationAttrib, IUPAF_SAMEASSYSTEM, "HORIZONTAL", IUPAF_NOT_MAPPED|IUPAF_NO_INHERIT); + iupClassRegisterAttributeId(ic, "TABTITLE", NULL, gtkTabsSetTabTitleAttrib, IUPAF_NO_INHERIT); + iupClassRegisterAttributeId(ic, "TABIMAGE", NULL, gtkTabsSetTabImageAttrib, IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "PADDING", iupTabsGetPaddingAttrib, gtkTabsSetPaddingAttrib, IUPAF_SAMEASSYSTEM, "0x0", IUPAF_NOT_MAPPED); +} diff --git a/iup/src/gtk/iupgtk_text.c b/iup/src/gtk/iupgtk_text.c new file mode 100755 index 0000000..4c2906a --- /dev/null +++ b/iup/src/gtk/iupgtk_text.c @@ -0,0 +1,1716 @@ +/** \file + * \brief Text Control + * + * See Copyright Notice in "iup.h" + */ + +#include <gtk/gtk.h> + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <memory.h> +#include <stdarg.h> + +#include "iup.h" +#include "iupcbs.h" + +#include "iup_object.h" +#include "iup_layout.h" +#include "iup_attrib.h" +#include "iup_str.h" +#include "iup_image.h" +#include "iup_mask.h" +#include "iup_drv.h" +#include "iup_drvfont.h" +#include "iup_image.h" +#include "iup_key.h" +#include "iup_array.h" +#include "iup_text.h" + +#include "iupgtk_drv.h" + +#ifndef PANGO_WEIGHT_SEMIBOLD +#define PANGO_WEIGHT_SEMIBOLD 600 +#endif + +void iupdrvTextAddSpin(int *w, int h) +{ + int spin_size = 16; + *w += spin_size; + (void)h; +} + +void iupdrvTextAddBorders(int *x, int *y) +{ + int border_size = 2*5; + (*x) += border_size; + (*y) += border_size; +} + +static void gtkTextParseParagraphFormat(Ihandle* formattag, GtkTextTag* tag) +{ + int val; + char* format; + + format = iupAttribGet(formattag, "INDENT"); + if (format && iupStrToInt(format, &val)) + g_object_set(G_OBJECT(tag), "indent", val, NULL); + + format = iupAttribGet(formattag, "ALIGNMENT"); + if (format) + { + if (iupStrEqualNoCase(format, "JUSTIFY")) + val = GTK_JUSTIFY_FILL; + else if (iupStrEqualNoCase(format, "RIGHT")) + val = GTK_JUSTIFY_RIGHT; + else if (iupStrEqualNoCase(format, "CENTER")) + val = GTK_JUSTIFY_CENTER; + else /* "LEFT" */ + val = GTK_JUSTIFY_LEFT; + + g_object_set(G_OBJECT(tag), "justification", val, NULL); + } + + format = iupAttribGet(formattag, "TABSARRAY"); + { + PangoTabArray *tabs; + int pos, i = 0; + PangoTabAlign align; + char* str; + + tabs = pango_tab_array_new(32, FALSE); + + while (format) + { + str = iupStrCopyUntil((char**)&format, ' '); + if (!str) break; + pos = atoi(str); + free(str); + + str = iupStrCopyUntil((char**)&format, ' '); + if (!str) break; + +/* if (iupStrEqualNoCase(str, "DECIMAL")) unsupported for now + align = PANGO_TAB_NUMERIC; + else if (iupStrEqualNoCase(str, "RIGHT")) + align = PANGO_TAB_RIGHT; + else if (iupStrEqualNoCase(str, "CENTER")) + align = PANGO_TAB_CENTER; + else */ /* "LEFT" */ + align = PANGO_TAB_LEFT; + free(str); + + pango_tab_array_set_tab(tabs, i, align, IUPGTK_PIXELS2PANGOUNITS(pos)); + i++; + if (i == 32) break; + } + + g_object_set(G_OBJECT(tag), "tabs", tabs, NULL); + pango_tab_array_free(tabs); + } + + format = iupAttribGet(formattag, "SPACEBEFORE"); + if (format && iupStrToInt(format, &val)) + g_object_set(G_OBJECT(tag), "pixels-above-lines", val, NULL); + + format = iupAttribGet(formattag, "SPACEAFTER"); + if (format && iupStrToInt(format, &val)) + g_object_set(G_OBJECT(tag), "pixels-below-lines", val, NULL); + + format = iupAttribGet(formattag, "LINESPACING"); + if (format && iupStrToInt(format, &val)) + g_object_set(G_OBJECT(tag), "pixels-inside-wrap", val, NULL); +} + +static void gtkTextParseCharacterFormat(Ihandle* formattag, GtkTextTag* tag) +{ + int val; + char* format; + + format = iupAttribGet(formattag, "LANGUAGE"); + if (format) + g_object_set(G_OBJECT(tag), "language", format, NULL); + + format = iupAttribGet(formattag, "STRETCH"); + if (format) + { + if (iupStrEqualNoCase(format, "EXTRA_CONDENSED")) + val = PANGO_STRETCH_EXTRA_CONDENSED; + else if (iupStrEqualNoCase(format, "CONDENSED")) + val = PANGO_STRETCH_CONDENSED; + else if (iupStrEqualNoCase(format, "SEMI_CONDENSED")) + val = PANGO_STRETCH_SEMI_CONDENSED; + else if (iupStrEqualNoCase(format, "SEMI_EXPANDED")) + val = PANGO_STRETCH_SEMI_EXPANDED; + else if (iupStrEqualNoCase(format, "EXPANDED")) + val = PANGO_STRETCH_EXPANDED; + else if (iupStrEqualNoCase(format, "EXTRA_EXPANDED")) + val = PANGO_STRETCH_EXTRA_EXPANDED; + else /* "NORMAL" */ + val = PANGO_STRETCH_NORMAL; + + g_object_set(G_OBJECT(tag), "stretch", val, NULL); + } + + format = iupAttribGet(formattag, "RISE"); + if (format) + { + val = 0; + + if (iupStrEqualNoCase(format, "SUPERSCRIPT")) + { + g_object_set(G_OBJECT(tag), "scale", PANGO_SCALE_X_SMALL, NULL); + val = 10; /* 10 pixels up */ + } + else if (iupStrEqualNoCase(format, "SUBSCRIPT")) + { + g_object_set(G_OBJECT(tag), "scale", PANGO_SCALE_X_SMALL, NULL); + val = -10; /* 10 pixels down */ + } + else + iupStrToInt(format, &val); + + val = IUPGTK_PIXELS2PANGOUNITS(val); + g_object_set(G_OBJECT(tag), "rise", val, NULL); + } + + format = iupAttribGet(formattag, "SMALLCAPS"); + if (format) + { + if (iupStrBoolean(format)) + val = PANGO_VARIANT_SMALL_CAPS; + else + val = PANGO_VARIANT_NORMAL; + g_object_set(G_OBJECT(tag), "variant", val, NULL); + } + + format = iupAttribGet(formattag, "ITALIC"); + if (format) + { + if (iupStrBoolean(format)) + val = PANGO_STYLE_ITALIC; + else + val = PANGO_STYLE_NORMAL; + g_object_set(G_OBJECT(tag), "style", val, NULL); + } + + format = iupAttribGet(formattag, "STRIKEOUT"); + if (format) + { + val = iupStrBoolean(format); + g_object_set(G_OBJECT(tag), "strikethrough", val, NULL); + } + + format = iupAttribGet(formattag, "PROTECTED"); + if (format) + { + val = iupStrBoolean(format); + g_object_set(G_OBJECT(tag), "editable", val, NULL); + } + + format = iupAttribGet(formattag, "FONTSIZE"); + if (format && iupStrToInt(format, &val)) + { + if (val < 0) /* in pixels */ + { + val = IUPGTK_PIXELS2PANGOUNITS(-val); + g_object_set(G_OBJECT(tag), "size", val, NULL); + } + else /* in points */ + g_object_set(G_OBJECT(tag), "size-points", (double)val, NULL); + } + + format = iupAttribGet(formattag, "FONTSCALE"); + if (format) + { + float fval = 0; + if (iupStrEqualNoCase(format, "XX-SMALL")) + fval = (float)PANGO_SCALE_XX_SMALL; + else if (iupStrEqualNoCase(format, "X-SMALL")) + fval = (float)PANGO_SCALE_X_SMALL; + else if (iupStrEqualNoCase(format, "SMALL")) + fval = (float)PANGO_SCALE_SMALL; + else if (iupStrEqualNoCase(format, "MEDIUM")) + fval = (float)PANGO_SCALE_MEDIUM; + else if (iupStrEqualNoCase(format, "LARGE")) + fval = (float)PANGO_SCALE_LARGE; + else if (iupStrEqualNoCase(format, "X-LARGE")) + fval = (float)PANGO_SCALE_X_LARGE; + else if (iupStrEqualNoCase(format, "XX-LARGE")) + fval = (float)PANGO_SCALE_XX_LARGE; + else + iupStrToFloat(format, &fval); + + if (fval > 0) + g_object_set(G_OBJECT(tag), "scale", (double)fval, NULL); + } + + format = iupAttribGet(formattag, "FONTFACE"); + if (format) + g_object_set(G_OBJECT(tag), "family", format, NULL); + + format = iupAttribGet(formattag, "FGCOLOR"); + if (format) + { + unsigned char r, g, b; + if (iupStrToRGB(format, &r, &g, &b)) + { + GdkColor color; + iupgdkColorSet(&color, r, g, b); + g_object_set(G_OBJECT(tag), "foreground-gdk", &color, NULL); + } + } + + format = iupAttribGet(formattag, "BGCOLOR"); + if (format) + { + unsigned char r, g, b; + if (iupStrToRGB(format, &r, &g, &b)) + { + GdkColor color; + iupgdkColorSet(&color, r, g, b); + g_object_set(G_OBJECT(tag), "background-gdk", &color, NULL); + } + } + + format = iupAttribGet(formattag, "UNDERLINE"); + if (format) + { + if (iupStrEqualNoCase(format, "SINGLE")) + val = PANGO_UNDERLINE_SINGLE; + else if (iupStrEqualNoCase(format, "DOUBLE")) + val = PANGO_UNDERLINE_DOUBLE; + else /* "NONE" */ + val = PANGO_UNDERLINE_NONE; + + g_object_set(G_OBJECT(tag), "underline", val, NULL); + } + + format = iupAttribGet(formattag, "WEIGHT"); + if (format) + { + if (iupStrEqualNoCase(format, "EXTRALIGHT")) + val = PANGO_WEIGHT_ULTRALIGHT; + else if (iupStrEqualNoCase(format, "LIGHT")) + val = PANGO_WEIGHT_LIGHT; + else if (iupStrEqualNoCase(format, "SEMIBOLD")) + val = PANGO_WEIGHT_SEMIBOLD; + else if (iupStrEqualNoCase(format, "BOLD")) + val = PANGO_WEIGHT_BOLD; + else if (iupStrEqualNoCase(format, "EXTRABOLD")) + val = PANGO_WEIGHT_ULTRABOLD; + else if (iupStrEqualNoCase(format, "HEAVY")) + val = PANGO_WEIGHT_HEAVY; + else /* "NORMAL" */ + val = PANGO_WEIGHT_NORMAL; + + g_object_set(G_OBJECT(tag), "weight", val, NULL); + } +} + +static void gtkTextMoveIterToLinCol(GtkTextBuffer *buffer, GtkTextIter *iter, int lin, int col) +{ + int line_count, line_length; + + lin--; /* IUP starts at 1 */ + col--; + + line_count = gtk_text_buffer_get_line_count(buffer); + if (lin < 0) lin = 0; + if (lin >= line_count) + lin = line_count-1; + + gtk_text_buffer_get_iter_at_line(buffer, iter, lin); + line_length = gtk_text_iter_get_chars_in_line(iter); + + if (col < 0) col = 0; + if (col > line_length) + col = line_length; /* after the last character */ + + gtk_text_iter_set_line_offset(iter, col); +} + +static void gtkTextGetLinColFromPosition(const GtkTextIter *iter, int *lin, int *col) +{ + *lin = gtk_text_iter_get_line(iter); + *col = gtk_text_iter_get_line_offset(iter); + + (*lin)++; /* IUP starts at 1 */ + (*col)++; +} + +static int gtkTextGetCharSize(Ihandle* ih) +{ + int charwidth; + PangoFontMetrics* metrics; + PangoContext* context; + PangoFontDescription* fontdesc = (PangoFontDescription*)iupgtkGetPangoFontDescAttrib(ih); + if (!fontdesc) + return 0; + + context = gdk_pango_context_get(); + metrics = pango_context_get_metrics(context, fontdesc, pango_context_get_language(context)); + charwidth = pango_font_metrics_get_approximate_char_width(metrics); + pango_font_metrics_unref(metrics); + return charwidth; +} + +void iupdrvTextConvertLinColToPos(Ihandle* ih, int lin, int col, int *pos) +{ + GtkTextIter iter; + GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ih->handle)); + gtkTextMoveIterToLinCol(buffer, &iter, lin, col); + *pos = gtk_text_iter_get_offset(&iter); +} + +void iupdrvTextConvertPosToLinCol(Ihandle* ih, int pos, int *lin, int *col) +{ + GtkTextIter iter; + GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ih->handle)); + gtk_text_buffer_get_iter_at_offset(buffer, &iter, pos); + gtkTextGetLinColFromPosition(&iter, lin, col); +} + +static int gtkTextConvertXYToPos(Ihandle* ih, int x, int y) +{ + if (ih->data->is_multiline) + { + GtkTextIter iter; + gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(ih->handle), GTK_TEXT_WINDOW_WIDGET, x, y, &x, &y); + gtk_text_view_get_iter_at_location(GTK_TEXT_VIEW(ih->handle), &iter, x, y); + return gtk_text_iter_get_offset(&iter); + } + else + { + int trailing, off_x, off_y, pos; + + /* transform to Layout coordinates */ + gtk_entry_get_layout_offsets(GTK_ENTRY(ih->handle), &off_x, &off_y); + x = IUPGTK_PIXELS2PANGOUNITS(x - off_x); + y = IUPGTK_PIXELS2PANGOUNITS(y - off_y); + + pango_layout_xy_to_index(gtk_entry_get_layout(GTK_ENTRY(ih->handle)), x, y, &pos, &trailing); + return pos; + } +} + +static void gtkTextScrollToVisible(Ihandle* ih) +{ + GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ih->handle)); + GtkTextMark* mark = gtk_text_buffer_get_insert(buffer); + gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(ih->handle), mark); +} + + +/*******************************************************************************************/ + + +static int gtkTextSetSelectionAttrib(Ihandle* ih, const char* value) +{ + if (!value) + return 0; + + if (!value || iupStrEqualNoCase(value, "NONE")) + { + if (ih->data->is_multiline) + { + GtkTextIter start_iter; + GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ih->handle)); + gtk_text_buffer_get_start_iter(buffer, &start_iter); + gtk_text_buffer_select_range(buffer, &start_iter, &start_iter); + } + else + gtk_editable_select_region(GTK_EDITABLE(ih->handle), 0, 0); + return 0; + } + + if (iupStrEqualNoCase(value, "ALL")) + { + if (ih->data->is_multiline) + { + GtkTextIter start_iter; + GtkTextIter end_iter; + GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ih->handle)); + gtk_text_buffer_get_start_iter(buffer, &start_iter); + gtk_text_buffer_get_end_iter(buffer, &end_iter); + gtk_text_buffer_select_range(buffer, &start_iter, &end_iter); + } + else + gtk_editable_select_region(GTK_EDITABLE(ih->handle), 0, -1); + return 0; + } + + if (ih->data->is_multiline) + { + int lin_start=1, col_start=1, lin_end=1, col_end=1; + GtkTextIter start_iter, end_iter; + GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ih->handle)); + + if (sscanf(value, "%d,%d:%d,%d", &lin_start, &col_start, &lin_end, &col_end)!=4) return 0; + if (lin_start<1 || col_start<1 || lin_end<1 || col_end<1) return 0; + + gtkTextMoveIterToLinCol(buffer, &start_iter, lin_start, col_start); + gtkTextMoveIterToLinCol(buffer, &end_iter, lin_end, col_end); + + gtk_text_buffer_select_range(buffer, &start_iter, &end_iter); + } + else + { + int start=1, end=1; + if (iupStrToIntInt(value, &start, &end, ':')!=2) + return 0; + + if(start<1 || end<1) + return 0; + + start--; /* IUP starts at 1 */ + end--; + + gtk_editable_select_region(GTK_EDITABLE(ih->handle), start, end); + } + + return 0; +} + +static char* gtkTextGetSelectionAttrib(Ihandle* ih) +{ + char *str; + + if (ih->data->is_multiline) + { + int start_col, start_lin, end_col, end_lin; + + GtkTextIter start_iter, end_iter; + GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ih->handle)); + if (gtk_text_buffer_get_selection_bounds(buffer, &start_iter, &end_iter)) + { + gtkTextGetLinColFromPosition(&start_iter, &start_lin, &start_col); + gtkTextGetLinColFromPosition(&end_iter, &end_lin, &end_col); + + str = iupStrGetMemory(100); + sprintf(str,"%d,%d:%d,%d", start_lin, start_col, end_lin, end_col); + return str; + } + } + else + { + int start, end; + if (gtk_editable_get_selection_bounds(GTK_EDITABLE(ih->handle), &start, &end)) + { + start++; /* IUP starts at 1 */ + end++; + str = iupStrGetMemory(100); + sprintf(str, "%d:%d", (int)start, (int)end); + return str; + } + } + + return NULL; +} + +static int gtkTextSetSelectionPosAttrib(Ihandle* ih, const char* value) +{ + int start=0, end=0; + + if (!value) + return 0; + + if (!value || iupStrEqualNoCase(value, "NONE")) + { + if (ih->data->is_multiline) + { + GtkTextIter start_iter; + GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ih->handle)); + gtk_text_buffer_get_start_iter(buffer, &start_iter); + gtk_text_buffer_select_range(buffer, &start_iter, &start_iter); + } + else + gtk_editable_select_region(GTK_EDITABLE(ih->handle), 0, 0); + return 0; + } + + if (iupStrEqualNoCase(value, "ALL")) + { + if (ih->data->is_multiline) + { + GtkTextIter start_iter; + GtkTextIter end_iter; + GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ih->handle)); + gtk_text_buffer_get_start_iter(buffer, &start_iter); + gtk_text_buffer_get_end_iter(buffer, &end_iter); + gtk_text_buffer_select_range(buffer, &start_iter, &end_iter); + } + else + gtk_editable_select_region(GTK_EDITABLE(ih->handle), 0, -1); + return 0; + } + + if (iupStrToIntInt(value, &start, &end, ':')!=2) + return 0; + + if(start<0 || end<0) + return 0; + + if (ih->data->is_multiline) + { + GtkTextIter start_iter, end_iter; + GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ih->handle)); + + gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start); + gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end); + + gtk_text_buffer_select_range(buffer, &start_iter, &end_iter); + } + else + gtk_editable_select_region(GTK_EDITABLE(ih->handle), start, end); + + return 0; +} + +static char* gtkTextGetSelectionPosAttrib(Ihandle* ih) +{ + int start, end; + char *str; + + if (ih->data->is_multiline) + { + GtkTextIter start_iter, end_iter; + GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ih->handle)); + if (gtk_text_buffer_get_selection_bounds(buffer, &start_iter, &end_iter)) + { + start = gtk_text_iter_get_offset(&start_iter); + end = gtk_text_iter_get_offset(&end_iter); + + str = iupStrGetMemory(100); + sprintf(str, "%d:%d", (int)start, (int)end); + return str; + } + } + else + { + if (gtk_editable_get_selection_bounds(GTK_EDITABLE(ih->handle), &start, &end)) + { + str = iupStrGetMemory(100); + sprintf(str, "%d:%d", (int)start, (int)end); + return str; + } + } + + return NULL; +} + +static int gtkTextSetSelectedTextAttrib(Ihandle* ih, const char* value) +{ + if (!value) + return 0; + + if (ih->data->is_multiline) + { + GtkTextIter start_iter, end_iter; + GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ih->handle)); + if (gtk_text_buffer_get_selection_bounds(buffer, &start_iter, &end_iter)) + { + /* disable callbacks */ + iupAttribSetStr(ih, "_IUPGTK_DISABLE_TEXT_CB", "1"); + gtk_text_buffer_delete(buffer, &start_iter, &end_iter); + gtk_text_buffer_insert(buffer, &start_iter, iupgtkStrConvertToUTF8(value), -1); + iupAttribSetStr(ih, "_IUPGTK_DISABLE_TEXT_CB", NULL); + } + } + else + { + int start, end; + if (gtk_editable_get_selection_bounds(GTK_EDITABLE(ih->handle), &start, &end)) + { + /* disable callbacks */ + iupAttribSetStr(ih, "_IUPGTK_DISABLE_TEXT_CB", "1"); + gtk_editable_delete_selection(GTK_EDITABLE(ih->handle)); + gtk_editable_insert_text(GTK_EDITABLE(ih->handle), iupgtkStrConvertToUTF8(value), -1, &start); + iupAttribSetStr(ih, "_IUPGTK_DISABLE_TEXT_CB", NULL); + } + } + + return 0; +} + +static char* gtkTextGetSelectedTextAttrib(Ihandle* ih) +{ + if (ih->data->is_multiline) + { + GtkTextIter start_iter, end_iter; + GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ih->handle)); + if (gtk_text_buffer_get_selection_bounds(buffer, &start_iter, &end_iter)) + return iupStrGetMemoryCopy(iupgtkStrConvertFromUTF8(gtk_text_buffer_get_text(buffer, &start_iter, &end_iter, TRUE))); + } + else + { + int start, end; + if (gtk_editable_get_selection_bounds(GTK_EDITABLE(ih->handle), &start, &end)) + { + char* selectedtext = gtk_editable_get_chars(GTK_EDITABLE(ih->handle), start, end); + char* str = iupStrGetMemoryCopy(iupgtkStrConvertFromUTF8(selectedtext)); + g_free(selectedtext); + return str; + } + } + + return NULL; +} + +static int gtkTextSetCaretAttrib(Ihandle* ih, const char* value) +{ + int pos = 1; + + if (!value) + return 0; + + if (ih->data->is_multiline) + { + int lin = 1, col = 1; + GtkTextIter iter; + + GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ih->handle)); + + iupStrToIntInt(value, &lin, &col, ','); + + gtkTextMoveIterToLinCol(buffer, &iter, lin, col); + + gtk_text_buffer_place_cursor(buffer, &iter); + gtkTextScrollToVisible(ih); + } + else + { + sscanf(value,"%i",&pos); + pos--; /* IUP starts at 1 */ + if (pos < 0) pos = 0; + + gtk_editable_set_position(GTK_EDITABLE(ih->handle), pos); + } + + return 0; +} + +static char* gtkTextGetCaretAttrib(Ihandle* ih) +{ + char* str = iupStrGetMemory(50); + + if (ih->data->is_multiline) + { + int col, lin; + GtkTextIter iter; + + GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ih->handle)); + gtk_text_buffer_get_iter_at_mark(buffer, &iter, gtk_text_buffer_get_insert(buffer)); + gtkTextGetLinColFromPosition(&iter, &lin, &col); + + sprintf(str, "%d,%d", lin, col); + } + else + { + int pos = gtk_editable_get_position(GTK_EDITABLE(ih->handle)); + pos++; /* IUP starts at 1 */ + sprintf(str, "%d", (int)pos); + } + + return str; +} + +static int gtkTextSetCaretPosAttrib(Ihandle* ih, const char* value) +{ + int pos = 0; + + if (!value) + return 0; + + sscanf(value,"%i",&pos); + if (pos < 0) pos = 0; + + if (ih->data->is_multiline) + { + GtkTextIter iter; + GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ih->handle)); + gtk_text_buffer_get_iter_at_offset(buffer, &iter, pos); + gtk_text_buffer_place_cursor(buffer, &iter); + gtkTextScrollToVisible(ih); + } + else + gtk_editable_set_position(GTK_EDITABLE(ih->handle), pos); + + return 0; +} + +static char* gtkTextGetCaretPosAttrib(Ihandle* ih) +{ + int pos; + char* str = iupStrGetMemory(50); + + if (ih->data->is_multiline) + { + GtkTextIter iter; + GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ih->handle)); + gtk_text_buffer_get_iter_at_mark(buffer, &iter, gtk_text_buffer_get_insert(buffer)); + pos = gtk_text_iter_get_offset(&iter); + } + else + pos = gtk_editable_get_position(GTK_EDITABLE(ih->handle)); + + sprintf(str, "%d", (int)pos); + return str; +} + +static int gtkTextSetScrollToAttrib(Ihandle* ih, const char* value) +{ + if (!value) + return 0; + + if (ih->data->is_multiline) + { + int lin = 1, col = 1; + GtkTextIter iter; + GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ih->handle)); + + iupStrToIntInt(value, &lin, &col, ','); + if (lin < 1) lin = 1; + if (col < 1) col = 1; + + gtkTextMoveIterToLinCol(buffer, &iter, lin, col); + gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(ih->handle), &iter, 0, FALSE, 0, 0); + } + else + { + int pos = 1; + sscanf(value,"%i",&pos); + if (pos < 1) pos = 1; + pos--; /* return to GTK referece */ + gtk_editable_set_position(GTK_EDITABLE(ih->handle), pos); + } + + return 0; +} + +static int gtkTextSetScrollToPosAttrib(Ihandle* ih, const char* value) +{ + int pos = 0; + + if (!value) + return 0; + + sscanf(value,"%i",&pos); + if (pos < 0) pos = 0; + + if (ih->data->is_multiline) + { + GtkTextIter iter; + GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ih->handle)); + gtk_text_buffer_get_iter_at_offset(buffer, &iter, pos); + gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(ih->handle), &iter, 0, FALSE, 0, 0); + } + else + gtk_editable_set_position(GTK_EDITABLE(ih->handle), pos); + + return 0; +} + +static int gtkTextSetValueAttrib(Ihandle* ih, const char* value) +{ + if (!value) value = ""; + /* disable callbacks */ + iupAttribSetStr(ih, "_IUPGTK_DISABLE_TEXT_CB", "1"); + if (ih->data->is_multiline) + { + GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ih->handle)); + gtk_text_buffer_set_text(buffer, iupgtkStrConvertToUTF8(value), -1); + } + else + gtk_entry_set_text(GTK_ENTRY(ih->handle), iupgtkStrConvertToUTF8(value)); + iupAttribSetStr(ih, "_IUPGTK_DISABLE_TEXT_CB", NULL); + return 0; +} + +static char* gtkTextGetValueAttrib(Ihandle* ih) +{ + char* value; + + if (ih->data->is_multiline) + { + GtkTextIter start_iter; + GtkTextIter end_iter; + GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ih->handle)); + gtk_text_buffer_get_start_iter(buffer, &start_iter); + gtk_text_buffer_get_end_iter(buffer, &end_iter); + value = iupStrGetMemoryCopy(iupgtkStrConvertFromUTF8(gtk_text_buffer_get_text(buffer, &start_iter, &end_iter, TRUE))); + } + else + value = iupStrGetMemoryCopy(iupgtkStrConvertFromUTF8(gtk_entry_get_text(GTK_ENTRY(ih->handle)))); + + if (!value) value = ""; + + return value; +} + +static int gtkTextSetInsertAttrib(Ihandle* ih, const char* value) +{ + if (!ih->handle) /* do not store the action before map */ + return 0; + if (!value) + return 0; + + /* disable callbacks */ + iupAttribSetStr(ih, "_IUPGTK_DISABLE_TEXT_CB", "1"); + if (ih->data->is_multiline) + { + GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ih->handle)); + gtk_text_buffer_insert_at_cursor(buffer, iupgtkStrConvertToUTF8(value), -1); + } + else + { + gint pos = gtk_editable_get_position(GTK_EDITABLE(ih->handle)); + gtk_editable_insert_text(GTK_EDITABLE(ih->handle), iupgtkStrConvertToUTF8(value), -1, &pos); + } + iupAttribSetStr(ih, "_IUPGTK_DISABLE_TEXT_CB", NULL); + + return 0; +} + +static int gtkTextSetAppendAttrib(Ihandle* ih, const char* value) +{ + if (!ih->handle) /* do not store the action before map */ + return 0; + /* disable callbacks */ + iupAttribSetStr(ih, "_IUPGTK_DISABLE_TEXT_CB", "1"); + if (ih->data->is_multiline) + { + GtkTextIter iter; + GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ih->handle)); + gtk_text_buffer_get_end_iter(buffer, &iter); + if (ih->data->append_newline) + gtk_text_buffer_insert(buffer, &iter, "\n", 1); + gtk_text_buffer_insert(buffer, &iter, iupgtkStrConvertToUTF8(value), -1); + } + else + { + gint pos = strlen(gtk_entry_get_text(GTK_ENTRY(ih->handle)))+1; + gtk_editable_insert_text(GTK_EDITABLE(ih->handle), iupgtkStrConvertToUTF8(value), -1, &pos); + } + iupAttribSetStr(ih, "_IUPGTK_DISABLE_TEXT_CB", NULL); + return 0; +} + +static int gtkTextSetAlignmentAttrib(Ihandle* ih, const char* value) +{ + float xalign; + GtkJustification justification; + + if (iupStrEqualNoCase(value, "ARIGHT")) + { + xalign = 1.0f; + justification = GTK_JUSTIFY_RIGHT; + } + else if (iupStrEqualNoCase(value, "ACENTER")) + { + xalign = 0.5f; + justification = GTK_JUSTIFY_CENTER; + } + else /* "ALEFT" */ + { + xalign = 0; + justification = GTK_JUSTIFY_LEFT; + } + + if (ih->data->is_multiline) + gtk_text_view_set_justification(GTK_TEXT_VIEW(ih->handle), justification); + else + gtk_entry_set_alignment(GTK_ENTRY(ih->handle), xalign); + + return 1; +} + +static int gtkTextSetPaddingAttrib(Ihandle* ih, const char* value) +{ + iupStrToIntInt(value, &ih->data->horiz_padding, &ih->data->vert_padding, 'x'); + if (ih->handle) + { + if (ih->data->is_multiline) + { + gtk_text_view_set_left_margin(GTK_TEXT_VIEW(ih->handle), ih->data->horiz_padding); + gtk_text_view_set_right_margin(GTK_TEXT_VIEW(ih->handle), ih->data->horiz_padding); + ih->data->vert_padding = 0; + } + else + { +#if GTK_CHECK_VERSION(2, 10, 0) + GtkBorder border; + border.bottom = border.top = ih->data->vert_padding; + border.left = border.right = ih->data->horiz_padding; + gtk_entry_set_inner_border(GTK_ENTRY(ih->handle), &border); +#endif + } + } + return 0; +} + +static int gtkTextSetNCAttrib(Ihandle* ih, const char* value) +{ + if (!iupStrToInt(value, &ih->data->nc)) + ih->data->nc = INT_MAX; + + if (!ih->data->is_multiline && ih->handle) + gtk_entry_set_max_length(GTK_ENTRY(ih->handle), ih->data->nc); + + return 0; +} + +static int gtkTextSetClipboardAttrib(Ihandle *ih, const char *value) +{ + /* disable callbacks */ + iupAttribSetStr(ih, "_IUPGTK_DISABLE_TEXT_CB", "1"); + if (iupStrEqualNoCase(value, "COPY")) + { + if (ih->data->is_multiline) + { + GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ih->handle)); + GtkClipboard *clipboard = gtk_clipboard_get(gdk_atom_intern("CLIPBOARD", FALSE)); + gtk_text_buffer_copy_clipboard(buffer, clipboard); + } + else + gtk_editable_copy_clipboard(GTK_EDITABLE(ih->handle)); + } + else if (iupStrEqualNoCase(value, "CUT")) + { + if (ih->data->is_multiline) + { + GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ih->handle)); + GtkClipboard *clipboard = gtk_clipboard_get(gdk_atom_intern("CLIPBOARD", FALSE)); + gtk_text_buffer_cut_clipboard(buffer, clipboard, TRUE); + } + else + gtk_editable_cut_clipboard(GTK_EDITABLE(ih->handle)); + } + else if (iupStrEqualNoCase(value, "PASTE")) + { + if (ih->data->is_multiline) + { + GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ih->handle)); + GtkClipboard *clipboard = gtk_clipboard_get(gdk_atom_intern("CLIPBOARD", FALSE)); + gtk_text_buffer_paste_clipboard(buffer, clipboard, NULL, TRUE); + } + else + gtk_editable_paste_clipboard(GTK_EDITABLE(ih->handle)); + } + else if (iupStrEqualNoCase(value, "CLEAR")) + { + if (ih->data->is_multiline) + { + GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ih->handle)); + gtk_text_buffer_delete_selection(buffer, FALSE, TRUE); + } + else + gtk_editable_delete_selection(GTK_EDITABLE(ih->handle)); + } + iupAttribSetStr(ih, "_IUPGTK_DISABLE_TEXT_CB", NULL); + return 0; +} + +static int gtkTextSetReadOnlyAttrib(Ihandle* ih, const char* value) +{ + if (ih->data->is_multiline) + gtk_text_view_set_editable(GTK_TEXT_VIEW(ih->handle), !iupStrBoolean(value)); + else + gtk_editable_set_editable(GTK_EDITABLE(ih->handle), !iupStrBoolean(value)); + return 0; +} + +static char* gtkTextGetReadOnlyAttrib(Ihandle* ih) +{ + int editable; + if (ih->data->is_multiline) + editable = gtk_text_view_get_editable(GTK_TEXT_VIEW(ih->handle)); + else + editable = gtk_editable_get_editable(GTK_EDITABLE(ih->handle)); + if (!editable) + return "YES"; + else + return "NO"; +} + +static char* gtkTextGetPangoLayoutAttrib(Ihandle* ih) +{ + if (ih->data->is_multiline) + return NULL; + else + return (char*)gtk_entry_get_layout(GTK_ENTRY(ih->handle)); +} + +static int gtkTextSetBgColorAttrib(Ihandle* ih, const char* value) +{ + if (ih->data->is_multiline) + { + GtkScrolledWindow* scrolled_window = (GtkScrolledWindow*)iupAttribGet(ih, "_IUP_EXTRAPARENT"); + unsigned char r, g, b; + + /* ignore given value, must use only from parent for the scrollbars */ + char* parent_value = iupBaseNativeParentGetBgColor(ih); + + if (iupStrToRGB(parent_value, &r, &g, &b)) + { + GtkWidget* sb; + + iupgtkBaseSetBgColor((GtkWidget*)scrolled_window, r, g, b); + +#if GTK_CHECK_VERSION(2, 8, 0) + sb = gtk_scrolled_window_get_hscrollbar(scrolled_window); + if (sb) iupgtkBaseSetBgColor(sb, r, g, b); + + sb = gtk_scrolled_window_get_vscrollbar(scrolled_window); + if (sb) iupgtkBaseSetBgColor(sb, r, g, b); +#endif + } + } + + return iupdrvBaseSetBgColorAttrib(ih, value); +} + +static int gtkTextSetTabSizeAttrib(Ihandle* ih, const char* value) +{ + PangoTabArray *tabs; + int tabsize, charwidth; + if (!ih->data->is_multiline) + return 0; + + iupStrToInt(value, &tabsize); + charwidth = gtkTextGetCharSize(ih); + tabsize *= charwidth; + tabs = pango_tab_array_new_with_positions(1, FALSE, PANGO_TAB_LEFT, tabsize); + gtk_text_view_set_tabs(GTK_TEXT_VIEW(ih->handle), tabs); + pango_tab_array_free(tabs); + return 1; +} + +static int gtkTextSetOverwriteAttrib(Ihandle* ih, const char* value) +{ + if (!ih->data->is_multiline) + return 0; + gtk_text_view_set_overwrite(GTK_TEXT_VIEW(ih->handle), iupStrBoolean(value)); + return 0; +} + +static char* gtkTextGetOverwriteAttrib(Ihandle* ih) +{ + if (!ih->data->is_multiline) + return "NO"; + if (gtk_text_view_get_overwrite(GTK_TEXT_VIEW(ih->handle))) + return "YES"; + else + return "NO"; +} + +void iupdrvTextAddFormatTag(Ihandle* ih, Ihandle* formattag) +{ + GtkTextBuffer *buffer; + GtkTextIter start_iter, end_iter; + GtkTextTag* tag; + char *selection; + + if (!ih->data->is_multiline) + return; + + selection = iupAttribGet(formattag, "SELECTION"); + if (selection) + { + /* simulate Windows behavior and change the current selection */ + gtkTextSetSelectionAttrib(ih, selection); + iupAttribSetStr(ih, "SELECTION", NULL); + } + else + { + char* selectionpos = iupAttribGet(formattag, "SELECTIONPOS"); + if (selectionpos) + { + /* simulate Windows behavior and change the current selection */ + gtkTextSetSelectionPosAttrib(ih, selectionpos); + iupAttribSetStr(ih, "SELECTIONPOS", NULL); + } + } + + buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ih->handle)); + if (!gtk_text_buffer_get_selection_bounds(buffer, &start_iter, &end_iter)) + { + GtkTextMark* mark = gtk_text_buffer_get_insert(buffer); + gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark); + gtk_text_buffer_get_iter_at_mark(buffer, &end_iter, mark); + } + + tag = gtk_text_buffer_create_tag(buffer, NULL, NULL); + gtkTextParseParagraphFormat(formattag, tag); + gtkTextParseCharacterFormat(formattag, tag); + gtk_text_buffer_apply_tag(buffer, tag, &start_iter, &end_iter); + + /* reset the selection */ + gtkTextSetSelectionAttrib(ih, NULL); +} + +static int gtkTextSetRemoveFormattingAttrib(Ihandle* ih, const char* value) +{ + GtkTextBuffer *buffer; + GtkTextIter start_iter, end_iter; + + if (!ih->data->is_multiline) + return 0; + + buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ih->handle)); + if (gtk_text_buffer_get_selection_bounds(buffer, &start_iter, &end_iter)) + gtk_text_buffer_remove_all_tags(buffer, &start_iter, &end_iter); + + (void)value; + return 0; +} + + +/************************************************************************************************/ + +static gboolean gtkTextSpinOutput(GtkSpinButton *spin, Ihandle* ih) +{ + if (iupAttribGet(ih, "_IUPGTK_SPIN_NOAUTO")) + { + iupAttribSetInt(ih, "_IUPGTK_SPIN_VALUE", (int)spin->adjustment->value); + return TRUE; /* disable output update */ + } + else + { + iupAttribSetStr(ih, "_IUPGTK_SPIN_OLDVALUE", gtk_entry_get_text(GTK_ENTRY(ih->handle))); + iupAttribSetStr(ih, "_IUPGTK_DISABLE_TEXT_CB", "1"); + return FALSE; + } +} + +static gint gtkTextSpinInput(GtkSpinButton *spin, gdouble *val, Ihandle* ih) +{ + (void)spin; + *val = (double)iupAttribGetInt(ih, "_IUPGTK_SPIN_VALUE"); + /* called only when SPINAUTO=NO */ + return TRUE; /* disable input update */ +} + +static void gtkTextSpinValueChanged(GtkSpinButton* spin, Ihandle* ih) +{ + IFni cb; + + iupAttribSetStr(ih, "_IUPGTK_DISABLE_TEXT_CB", NULL); + + cb = (IFni)IupGetCallback(ih, "SPIN_CB"); + if (cb) + { + int pos, ret; + if (iupAttribGet(ih, "_IUPGTK_SPIN_NOAUTO")) + pos = iupAttribGetInt(ih, "_IUPGTK_SPIN_VALUE"); + else + pos = gtk_spin_button_get_value_as_int((GtkSpinButton*)ih->handle); + + ret = cb(ih, pos); + if (ret == IUP_IGNORE) + { + /* this is not working: g_signal_stop_emission_by_name(spin, "value_changed"); */ + } + } + + (void)spin; +} + +static int gtkTextSetSpinMinAttrib(Ihandle* ih, const char* value) +{ + if (GTK_IS_SPIN_BUTTON(ih->handle)) + { + int min; + if (iupStrToInt(value, &min)) + { + int max = iupAttribGetInt(ih, "SPINMAX"); + + iupAttribSetStr(ih, "_IUPGTK_DISABLE_TEXT_CB", "1"); + g_signal_handlers_block_by_func(G_OBJECT(ih->handle), G_CALLBACK(gtkTextSpinValueChanged), ih); + + gtk_spin_button_set_range((GtkSpinButton*)ih->handle, (double)min, (double)max); + + g_signal_handlers_unblock_by_func(G_OBJECT(ih->handle), G_CALLBACK(gtkTextSpinValueChanged), ih); + iupAttribSetStr(ih, "_IUPGTK_DISABLE_TEXT_CB", NULL); + } + } + return 1; +} + +static int gtkTextSetSpinMaxAttrib(Ihandle* ih, const char* value) +{ + if (GTK_IS_SPIN_BUTTON(ih->handle)) + { + int max; + if (iupStrToInt(value, &max)) + { + int min = iupAttribGetInt(ih, "SPINMIN"); + iupAttribSetStr(ih, "_IUPGTK_DISABLE_TEXT_CB", "1"); + g_signal_handlers_block_by_func(G_OBJECT(ih->handle), G_CALLBACK(gtkTextSpinValueChanged), ih); + + gtk_spin_button_set_range((GtkSpinButton*)ih->handle, (double)min, (double)max); + + g_signal_handlers_unblock_by_func(G_OBJECT(ih->handle), G_CALLBACK(gtkTextSpinValueChanged), ih); + iupAttribSetStr(ih, "_IUPGTK_DISABLE_TEXT_CB", NULL); + } + } + return 1; +} + +static int gtkTextSetSpinIncAttrib(Ihandle* ih, const char* value) +{ + if (GTK_IS_SPIN_BUTTON(ih->handle)) + { + int inc; + if (iupStrToInt(value, &inc)) + gtk_spin_button_set_increments((GtkSpinButton*)ih->handle, (double)inc, (double)(inc*10)); + } + return 1; +} + +static int gtkTextSetSpinValueAttrib(Ihandle* ih, const char* value) +{ + if (GTK_IS_SPIN_BUTTON(ih->handle)) + { + int pos; + if (iupStrToInt(value, &pos)) + { + iupAttribSetStr(ih, "_IUPGTK_DISABLE_TEXT_CB", "1"); + g_signal_handlers_block_by_func(G_OBJECT(ih->handle), G_CALLBACK(gtkTextSpinValueChanged), ih); + + gtk_spin_button_set_value((GtkSpinButton*)ih->handle, (double)pos); + + g_signal_handlers_unblock_by_func(G_OBJECT(ih->handle), G_CALLBACK(gtkTextSpinValueChanged), ih); + iupAttribSetStr(ih, "_IUPGTK_DISABLE_TEXT_CB", NULL); + + if (iupAttribGet(ih, "_IUPGTK_SPIN_NOAUTO")) + iupAttribSetInt(ih, "_IUPGTK_SPIN_VALUE", pos); + } + } + return 1; +} + +static char* gtkTextGetSpinValueAttrib(Ihandle* ih) +{ + if (GTK_IS_SPIN_BUTTON(ih->handle)) + { + int pos; + char *str = iupStrGetMemory(50); + + if (iupAttribGet(ih, "_IUPGTK_SPIN_NOAUTO")) + pos = iupAttribGetInt(ih, "_IUPGTK_SPIN_VALUE"); + else + pos = gtk_spin_button_get_value_as_int((GtkSpinButton*)ih->handle); + + sprintf(str, "%d", pos); + return str; + } + return NULL; +} + + +/**********************************************************************************************************/ + + +static void gtkTextMoveCursor(GtkWidget *w, GtkMovementStep step, gint count, gboolean extend_selection, Ihandle* ih) +{ + int col, lin, pos; + + IFniii cb = (IFniii)IupGetCallback(ih, "CARET_CB"); + if (!cb) return; + + if (ih->data->is_multiline) + { + GtkTextIter iter; + + GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ih->handle)); + gtk_text_buffer_get_iter_at_mark(buffer, &iter, gtk_text_buffer_get_insert(buffer)); + gtkTextGetLinColFromPosition(&iter, &lin, &col); + pos = gtk_text_iter_get_offset(&iter); + } + else + { + pos = gtk_editable_get_position(GTK_EDITABLE(ih->handle)); + col = pos; + col++; /* IUP starts at 1 */ + lin = 1; + } + + if (pos != ih->data->last_caret_pos) + { + ih->data->last_caret_pos = pos; + + cb(ih, lin, col, pos); + } + + (void)w; + (void)step; + (void)count; + (void)extend_selection; +} + +static gboolean gtkTextKeyReleaseEvent(GtkWidget *widget, GdkEventKey *evt, Ihandle *ih) +{ + gtkTextMoveCursor(NULL, 0, 0, 0, ih); + (void)widget; + (void)evt; + return FALSE; +} + +static gboolean gtkTextButtonEvent(GtkWidget *widget, GdkEventButton *evt, Ihandle *ih) +{ + gtkTextMoveCursor(NULL, 0, 0, 0, ih); + return iupgtkButtonEvent(widget, evt, ih); +} + +static int gtkTextCallActionCb(Ihandle* ih, const char* insert_value, int len, int start, int end) +{ + char *new_value, *value; + int ret = -1, key = 0; + + IFnis cb = (IFnis)IupGetCallback(ih, "ACTION"); + if (!cb && !ih->data->mask) + return -1; /* continue */ + + value = gtkTextGetValueAttrib(ih); /* new_value is the internal buffer */ + + if (!insert_value) + { + new_value = iupStrDup(value); + if (end<0) end = strlen(value)+1; + iupStrRemove(new_value, start, end, 1); + } + else + { + if (value[0]==0) + new_value = iupStrDup(insert_value); + else + { + if (len < end-start) + { + new_value = iupStrDup(value); + new_value = iupStrInsert(new_value, insert_value, start, end); + } + else + new_value = iupStrInsert(value, insert_value, start, end); + } + } + + if (insert_value && insert_value[0]!=0 && insert_value[1]==0) + key = insert_value[0]; + + if (!new_value) + return -1; /* continue */ + + if (ih->data->nc && (int)strlen(new_value) > ih->data->nc) + { + if (new_value != value) free(new_value); + return 0; /* abort */ + } + + if (ih->data->mask && iupMaskCheck(ih->data->mask, new_value)==0) + { + if (new_value != value) free(new_value); + return 0; /* abort */ + } + + if (cb) + { + int cb_ret = cb(ih, key, (char*)new_value); + if (cb_ret==IUP_IGNORE) + ret = 0; /* abort */ + else if (cb_ret==IUP_CLOSE) + { + IupExitLoop(); + ret = 0; /* abort */ + } + else if (cb_ret!=0 && key!=0 && + cb_ret != IUP_DEFAULT && cb_ret != IUP_CONTINUE) + ret = cb_ret; /* abort and replace */ + } + + if (new_value != value) free(new_value); + return ret; /* continue */ +} + +static void gtkTextEntryDeleteText(GtkEditable *editable, int start, int end, Ihandle* ih) +{ + if (iupAttribGet(ih, "_IUPGTK_DISABLE_TEXT_CB")) + return; + + if (gtkTextCallActionCb(ih, NULL, 0, start, end)==0) + g_signal_stop_emission_by_name (editable, "delete_text"); +} + +static void gtkTextEntryInsertText(GtkEditable *editable, char *insert_value, int len, int *pos, Ihandle* ih) +{ + int ret; + + if (iupAttribGet(ih, "_IUPGTK_DISABLE_TEXT_CB")) + return; + + ret = gtkTextCallActionCb(ih, iupStrGetMemoryCopy(iupgtkStrConvertFromUTF8(insert_value)), len, *pos, *pos); + if (ret == 0) + g_signal_stop_emission_by_name(editable, "insert_text"); + else if (ret != -1) + { + insert_value[0] = (char)ret; /* replace key */ + + /* disable callbacks */ + iupAttribSetStr(ih, "_IUPGTK_DISABLE_TEXT_CB", "1"); + gtk_editable_insert_text(editable, insert_value, 1, pos); + iupAttribSetStr(ih, "_IUPGTK_DISABLE_TEXT_CB", NULL); + + g_signal_stop_emission_by_name(editable, "insert_text"); + } +} + +static void gtkTextBufferDeleteRange(GtkTextBuffer *textbuffer, GtkTextIter *start_iter, GtkTextIter *end_iter, Ihandle* ih) +{ + int start, end; + if (iupAttribGet(ih, "_IUPGTK_DISABLE_TEXT_CB")) + return; + + start = gtk_text_iter_get_offset(start_iter); + end = gtk_text_iter_get_offset(end_iter); + + if (gtkTextCallActionCb(ih, NULL, 0, start, end)==0) + g_signal_stop_emission_by_name (textbuffer, "delete_range"); +} + +static void gtkTextBufferInsertText(GtkTextBuffer *textbuffer, GtkTextIter *pos_iter, gchar *insert_value, gint len, Ihandle* ih) +{ + int ret, pos; + + if (iupAttribGet(ih, "_IUPGTK_DISABLE_TEXT_CB")) + return; + + pos = gtk_text_iter_get_offset(pos_iter); + + ret = gtkTextCallActionCb(ih, iupStrGetMemoryCopy(iupgtkStrConvertFromUTF8(insert_value)), len, pos, pos); + if (ret == 0) + g_signal_stop_emission_by_name(textbuffer, "insert_text"); + else if (ret != -1) + { + insert_value[0] = (char)ret; /* replace key */ + + /* disable callbacks */ + iupAttribSetStr(ih, "_IUPGTK_DISABLE_TEXT_CB", "1"); + gtk_text_buffer_insert(textbuffer, pos_iter, insert_value, 1); + iupAttribSetStr(ih, "_IUPGTK_DISABLE_TEXT_CB", NULL); + + g_signal_stop_emission_by_name(textbuffer, "insert_text"); + } +} + +static void gtkTextChanged(void* dummy, Ihandle* ih) +{ + if (iupAttribGet(ih, "_IUPGTK_DISABLE_TEXT_CB")) + return; + + iupBaseCallValueChangedCb(ih); + (void)dummy; +} + +/**********************************************************************************************************/ + + +static int gtkTextMapMethod(Ihandle* ih) +{ + GtkScrolledWindow* scrolled_window = NULL; + + if (ih->data->is_multiline) + { + GtkPolicyType hscrollbar_policy, vscrollbar_policy; + int wordwrap = 0; + + ih->handle = gtk_text_view_new(); + if (!ih->handle) + return IUP_ERROR; + + scrolled_window = (GtkScrolledWindow*)gtk_scrolled_window_new(NULL, NULL); + if (!scrolled_window) + return IUP_ERROR; + + gtk_container_add((GtkContainer*)scrolled_window, ih->handle); + gtk_widget_show((GtkWidget*)scrolled_window); + + iupAttribSetStr(ih, "_IUP_EXTRAPARENT", (char*)scrolled_window); + + /* formatting is always supported when MULTILINE=YES */ + ih->data->has_formatting = 1; + + if (iupAttribGetBoolean(ih, "WORDWRAP")) + { + wordwrap = 1; + ih->data->sb &= ~IUP_SB_HORIZ; /* must remove the horizontal scroolbar */ + } + + if (iupAttribGetBoolean(ih, "BORDER")) + gtk_scrolled_window_set_shadow_type(scrolled_window, GTK_SHADOW_IN); + else + gtk_scrolled_window_set_shadow_type(scrolled_window, GTK_SHADOW_NONE); + + if (ih->data->sb & IUP_SB_HORIZ) + { + if (iupAttribGetBoolean(ih, "AUTOHIDE")) + hscrollbar_policy = GTK_POLICY_AUTOMATIC; + else + hscrollbar_policy = GTK_POLICY_ALWAYS; + } + else + hscrollbar_policy = GTK_POLICY_NEVER; + + if (ih->data->sb & IUP_SB_VERT) + { + if (iupAttribGetBoolean(ih, "AUTOHIDE")) + vscrollbar_policy = GTK_POLICY_AUTOMATIC; + else + vscrollbar_policy = GTK_POLICY_ALWAYS; + } + else + vscrollbar_policy = GTK_POLICY_NEVER; + + gtk_scrolled_window_set_policy(scrolled_window, hscrollbar_policy, vscrollbar_policy); + + if (wordwrap) + gtk_text_view_set_wrap_mode((GtkTextView*)ih->handle, GTK_WRAP_WORD); + + gtk_widget_add_events(ih->handle, GDK_ENTER_NOTIFY_MASK|GDK_LEAVE_NOTIFY_MASK); + } + else + { + if (iupAttribGetBoolean(ih, "SPIN")) + ih->handle = gtk_spin_button_new_with_range(0, 100, 1); + else + ih->handle = gtk_entry_new(); + + if (!ih->handle) + return IUP_ERROR; + + /* formatting is never supported when MULTILINE=NO */ + ih->data->has_formatting = 0; + + gtk_entry_set_has_frame((GtkEntry*)ih->handle, IupGetInt(ih, "BORDER")); + + if (iupAttribGetBoolean(ih, "PASSWORD")) + gtk_entry_set_visibility((GtkEntry*)ih->handle, FALSE); + + if (GTK_IS_SPIN_BUTTON(ih->handle)) + { + gtk_spin_button_set_numeric((GtkSpinButton*)ih->handle, FALSE); + gtk_spin_button_set_digits((GtkSpinButton*)ih->handle, 0); + + gtk_spin_button_set_wrap((GtkSpinButton*)ih->handle, iupAttribGetBoolean(ih, "SPINWRAP")); + + g_signal_connect(G_OBJECT(ih->handle), "value-changed", G_CALLBACK(gtkTextSpinValueChanged), ih); + g_signal_connect(G_OBJECT(ih->handle), "output", G_CALLBACK(gtkTextSpinOutput), ih); + + if (!iupAttribGetBoolean(ih, "SPINAUTO")) + { + g_signal_connect(G_OBJECT(ih->handle), "input", G_CALLBACK(gtkTextSpinInput), ih); + iupAttribSetStr(ih, "_IUPGTK_SPIN_NOAUTO", "1"); + } + } + } + + /* add to the parent, all GTK controls must call this. */ + iupgtkBaseAddToParent(ih); + + if (!iupAttribGetBoolean(ih, "CANFOCUS")) + GTK_WIDGET_FLAGS(ih->handle) &= ~GTK_CAN_FOCUS; + + g_signal_connect(G_OBJECT(ih->handle), "enter-notify-event", G_CALLBACK(iupgtkEnterLeaveEvent), ih); + g_signal_connect(G_OBJECT(ih->handle), "leave-notify-event", G_CALLBACK(iupgtkEnterLeaveEvent), ih); + g_signal_connect(G_OBJECT(ih->handle), "focus-in-event", G_CALLBACK(iupgtkFocusInOutEvent), ih); + g_signal_connect(G_OBJECT(ih->handle), "focus-out-event", G_CALLBACK(iupgtkFocusInOutEvent), ih); + g_signal_connect(G_OBJECT(ih->handle), "key-press-event", G_CALLBACK(iupgtkKeyPressEvent), ih); + g_signal_connect(G_OBJECT(ih->handle), "show-help", G_CALLBACK(iupgtkShowHelp), ih); + + g_signal_connect_after(G_OBJECT(ih->handle), "move-cursor", G_CALLBACK(gtkTextMoveCursor), ih); /* only report some caret movements */ + g_signal_connect_after(G_OBJECT(ih->handle), "key-release-event", G_CALLBACK(gtkTextKeyReleaseEvent), ih); + g_signal_connect(G_OBJECT(ih->handle), "button-press-event", G_CALLBACK(gtkTextButtonEvent), ih); /* if connected "after" then it is ignored */ + g_signal_connect(G_OBJECT(ih->handle), "button-release-event",G_CALLBACK(gtkTextButtonEvent), ih); + g_signal_connect(G_OBJECT(ih->handle), "motion-notify-event",G_CALLBACK(iupgtkMotionNotifyEvent), ih); + + if (ih->data->is_multiline) + { + GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ih->handle)); + g_signal_connect(G_OBJECT(buffer), "delete-range", G_CALLBACK(gtkTextBufferDeleteRange), ih); + g_signal_connect(G_OBJECT(buffer), "insert-text", G_CALLBACK(gtkTextBufferInsertText), ih); + g_signal_connect(G_OBJECT(buffer), "changed", G_CALLBACK(gtkTextChanged), ih); + } + else + { + g_signal_connect(G_OBJECT(ih->handle), "delete-text", G_CALLBACK(gtkTextEntryDeleteText), ih); + g_signal_connect(G_OBJECT(ih->handle), "insert-text", G_CALLBACK(gtkTextEntryInsertText), ih); + g_signal_connect(G_OBJECT(ih->handle), "changed", G_CALLBACK(gtkTextChanged), ih); + } + + if (scrolled_window) + gtk_widget_realize((GtkWidget*)scrolled_window); + gtk_widget_realize(ih->handle); + + /* configure for DRAG&DROP */ + if (IupGetCallback(ih, "DROPFILES_CB")) + iupAttribSetStr(ih, "DRAGDROP", "YES"); + + /* update a mnemonic in a label if necessary */ + iupgtkUpdateMnemonic(ih); + + if (ih->data->formattags) + iupTextUpdateFormatTags(ih); + + IupSetCallback(ih, "_IUP_XY2POS_CB", (Icallback)gtkTextConvertXYToPos); + + return IUP_NOERROR; +} + +void iupdrvTextInitClass(Iclass* ic) +{ + /* Driver Dependent Class functions */ + ic->Map = gtkTextMapMethod; + + /* Driver Dependent Attribute functions */ + + /* Common GTK only (when text is in a secondary element) */ + iupClassRegisterAttribute(ic, "PANGOLAYOUT", gtkTextGetPangoLayoutAttrib, NULL, NULL, NULL, IUPAF_NO_INHERIT); + + /* Visual */ + iupClassRegisterAttribute(ic, "BGCOLOR", NULL, gtkTextSetBgColorAttrib, IUPAF_SAMEASSYSTEM, "TXTBGCOLOR", IUPAF_DEFAULT); + + /* Special */ + iupClassRegisterAttribute(ic, "FGCOLOR", NULL, iupdrvBaseSetFgColorAttrib, IUPAF_SAMEASSYSTEM, "TXTFGCOLOR", IUPAF_DEFAULT); + + /* IupText only */ + iupClassRegisterAttribute(ic, "PADDING", iupTextGetPaddingAttrib, gtkTextSetPaddingAttrib, IUPAF_SAMEASSYSTEM, "0x0", IUPAF_NOT_MAPPED); + iupClassRegisterAttribute(ic, "VALUE", gtkTextGetValueAttrib, gtkTextSetValueAttrib, NULL, NULL, IUPAF_NO_DEFAULTVALUE|IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "SELECTEDTEXT", gtkTextGetSelectedTextAttrib, gtkTextSetSelectedTextAttrib, NULL, NULL, IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "SELECTION", gtkTextGetSelectionAttrib, gtkTextSetSelectionAttrib, NULL, NULL, IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "SELECTIONPOS", gtkTextGetSelectionPosAttrib, gtkTextSetSelectionPosAttrib, NULL, NULL, IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "CARET", gtkTextGetCaretAttrib, gtkTextSetCaretAttrib, NULL, NULL, IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "CARETPOS", gtkTextGetCaretPosAttrib, gtkTextSetCaretPosAttrib, NULL, NULL, IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "INSERT", NULL, gtkTextSetInsertAttrib, NULL, NULL, IUPAF_NOT_MAPPED|IUPAF_WRITEONLY|IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "APPEND", NULL, gtkTextSetAppendAttrib, NULL, NULL, IUPAF_NOT_MAPPED|IUPAF_WRITEONLY|IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "READONLY", gtkTextGetReadOnlyAttrib, gtkTextSetReadOnlyAttrib, NULL, NULL, IUPAF_DEFAULT); + iupClassRegisterAttribute(ic, "NC", iupTextGetNCAttrib, gtkTextSetNCAttrib, NULL, NULL, IUPAF_NOT_MAPPED); + iupClassRegisterAttribute(ic, "CLIPBOARD", NULL, gtkTextSetClipboardAttrib, NULL, NULL, IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "SCROLLTO", NULL, gtkTextSetScrollToAttrib, NULL, NULL, IUPAF_WRITEONLY|IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "SCROLLTOPOS", NULL, gtkTextSetScrollToPosAttrib, NULL, NULL, IUPAF_WRITEONLY|IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "SPINMIN", NULL, gtkTextSetSpinMinAttrib, IUPAF_SAMEASSYSTEM, "0", IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "SPINMAX", NULL, gtkTextSetSpinMaxAttrib, IUPAF_SAMEASSYSTEM, "100", IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "SPININC", NULL, gtkTextSetSpinIncAttrib, IUPAF_SAMEASSYSTEM, "1", IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "SPINVALUE", gtkTextGetSpinValueAttrib, gtkTextSetSpinValueAttrib, IUPAF_SAMEASSYSTEM, "0", IUPAF_NO_INHERIT); + + /* IupText Windows and GTK only */ + iupClassRegisterAttribute(ic, "ADDFORMATTAG", NULL, iupTextSetAddFormatTagAttrib, NULL, NULL, IUPAF_NOT_MAPPED|IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "ADDFORMATTAG_HANDLE", NULL, iupTextSetAddFormatTagHandleAttrib, NULL, NULL, IUPAF_NOT_MAPPED|IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "ALIGNMENT", NULL, gtkTextSetAlignmentAttrib, IUPAF_SAMEASSYSTEM, "ALEFT", IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "DRAGDROP", NULL, iupgtkSetDragDropAttrib, NULL, NULL, IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "FORMATTING", iupTextGetFormattingAttrib, iupTextSetFormattingAttrib, NULL, NULL, IUPAF_NOT_MAPPED|IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "OVERWRITE", gtkTextGetOverwriteAttrib, gtkTextSetOverwriteAttrib, NULL, NULL, IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "REMOVEFORMATTING", NULL, gtkTextSetRemoveFormattingAttrib, NULL, NULL, IUPAF_WRITEONLY|IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "TABSIZE", NULL, gtkTextSetTabSizeAttrib, "8", NULL, IUPAF_DEFAULT); /* force new default value */ + iupClassRegisterAttribute(ic, "PASSWORD", NULL, NULL, NULL, NULL, IUPAF_NO_INHERIT); +} diff --git a/iup/src/gtk/iupgtk_timer.c b/iup/src/gtk/iupgtk_timer.c new file mode 100755 index 0000000..ecbefa3 --- /dev/null +++ b/iup/src/gtk/iupgtk_timer.c @@ -0,0 +1,61 @@ +/** \file + * \brief Timer for the GTK Driver. + * + * See Copyright Notice in "iup.h" + */ + + +#include <stdio.h> +#include <stdlib.h> + +#include <gtk/gtk.h> + +#include "iup.h" + +#include "iup_object.h" +#include "iup_attrib.h" +#include "iup_str.h" +#include "iup_assert.h" +#include "iup_timer.h" + + +static gboolean gtkTimerProc(gpointer data) +{ + Ihandle *ih = (Ihandle*)data; + Icallback cb; + + if (!iupObjectCheck(ih)) /* control could be destroyed before timer callback */ + return FALSE; + + cb = IupGetCallback(ih, "ACTION_CB"); + if (cb && cb(ih)==IUP_CLOSE) + IupExitLoop(); + + return TRUE; +} + +void iupdrvTimerRun(Ihandle *ih) +{ + unsigned int time_ms; + + if (ih->serial > 0) /* timer already started */ + return; + + time_ms = iupAttribGetInt(ih, "TIME"); + if (time_ms > 0) + ih->serial = g_timeout_add(time_ms, gtkTimerProc, (gpointer)ih); +} + +void iupdrvTimerStop(Ihandle* ih) +{ + if (ih->serial > 0) + { + g_source_remove(ih->serial); + ih->serial = -1; + } +} + +void iupdrvTimerInitClass(Iclass* ic) +{ + (void)ic; +} diff --git a/iup/src/gtk/iupgtk_tips.c b/iup/src/gtk/iupgtk_tips.c new file mode 100755 index 0000000..6289d70 --- /dev/null +++ b/iup/src/gtk/iupgtk_tips.c @@ -0,0 +1,100 @@ +/** \file + * \brief Windows Driver TIPS management + * + * See Copyright Notice in "iup.h" + */ + +#include <stdio.h> + +#include <gtk/gtk.h> + +#include "iup.h" + +#include "iup_object.h" +#include "iup_str.h" +#include "iup_attrib.h" +#include "iup_image.h" + +#include "iupgtk_drv.h" + +#if GTK_CHECK_VERSION(2, 12, 0) +#else +static GtkTooltips* gtk_tips = NULL; /* old TIPS */ +#endif + +#if GTK_CHECK_VERSION(2, 12, 0) +static gboolean gtkQueryTooltip(GtkWidget *widget, gint x, gint y, gboolean keyboard_mode, GtkTooltip *tooltip, Ihandle* ih) +{ + char* value = iupAttribGet(ih, "TIPRECT"); + if (value && !keyboard_mode) + { + GdkRectangle rect; + int x1, x2, y1, y2; + sscanf(value, "%d %d %d %d", &x1, &y1, &x2, &y2); + rect.x = x1; + rect.y = y1; + rect.width = x2-x1+1; + rect.height = y2-y1+1; + gtk_tooltip_set_tip_area(tooltip, &rect); + } + else + gtk_tooltip_set_tip_area(tooltip, NULL); + + value = iupAttribGet(ih, "TIPICON"); + if (!value) + gtk_tooltip_set_icon(tooltip, NULL); + else + { + GdkPixbuf* icon = (GdkPixbuf*)iupImageGetIcon(value); + if (icon) + gtk_tooltip_set_icon(tooltip, icon); + } + + (void)y; + (void)x; + (void)widget; + return FALSE; +} +#endif + +int iupdrvBaseSetTipAttrib(Ihandle* ih, const char* value) +{ + GtkWidget* widget = (GtkWidget*)iupAttribGet(ih, "_IUP_EXTRAPARENT"); + if (!widget) + widget = ih->handle; + +#if GTK_CHECK_VERSION(2, 12, 0) + if (iupAttribGetBoolean(ih, "TIPMARKUP")) + gtk_widget_set_tooltip_markup(widget, iupgtkStrConvertToUTF8(value)); + else + gtk_widget_set_tooltip_text(widget, iupgtkStrConvertToUTF8(value)); + + g_signal_connect(widget, "query-tooltip", G_CALLBACK(gtkQueryTooltip), ih); +#else + if (gtk_tips == NULL) + gtk_tips = gtk_tooltips_new(); + + gtk_tooltips_set_tip(gtk_tips, widget, iupgtkStrConvertToUTF8(value), NULL); +#endif + + return 1; +} + +int iupdrvBaseSetTipVisibleAttrib(Ihandle* ih, const char* value) +{ + GtkWidget* widget = (GtkWidget*)iupAttribGet(ih, "_IUP_EXTRAPARENT"); + if (!widget) + widget = ih->handle; + (void)value; + + /* must use IupGetAttribute to use inheritance */ + if (!IupGetAttribute(ih, "TIP")) + return 0; + +#if GTK_CHECK_VERSION(2, 12, 0) + gtk_widget_trigger_tooltip_query(widget); +#endif + + return 0; +} + diff --git a/iup/src/gtk/iupgtk_toggle.c b/iup/src/gtk/iupgtk_toggle.c new file mode 100755 index 0000000..8ff7df5 --- /dev/null +++ b/iup/src/gtk/iupgtk_toggle.c @@ -0,0 +1,519 @@ +/** \file + * \brief Toggle Control + * + * See Copyright Notice in "iup.h" + */ + +#include <gtk/gtk.h> +#include <gdk/gdkkeysyms.h> + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <memory.h> +#include <stdarg.h> + +#include "iup.h" +#include "iupcbs.h" + +#include "iup_object.h" +#include "iup_layout.h" +#include "iup_attrib.h" +#include "iup_str.h" +#include "iup_image.h" +#include "iup_drv.h" +#include "iup_drvfont.h" +#include "iup_image.h" +#include "iup_key.h" +#include "iup_toggle.h" + +#include "iupgtk_drv.h" + + +#if !GTK_CHECK_VERSION(2, 6, 0) +static void gtk_button_set_image(GtkButton *button, GtkWidget *image) +{ +} +static GtkWidget* gtk_button_get_image(GtkButton *button) +{ + return NULL; +} +#endif + +void iupdrvToggleAddCheckBox(int *x, int *y) +{ +#ifdef HILDON + (*x) += 30+4; + if ((*y) < 30) (*y) = 30; /* minimum height */ +#else + (*x) += 16+4; + if ((*y) < 16) (*y) = 16; /* minimum height */ +#endif + (*y) += 4; +} + +static int gtkToggleGetCheck(Ihandle* ih) +{ + if (gtk_toggle_button_get_inconsistent((GtkToggleButton*)ih->handle)) + return -1; + if (gtk_toggle_button_get_active((GtkToggleButton*)ih->handle)) + return 1; + else + return 0; +} + +static void gtkToggleSetPixbuf(Ihandle* ih, const char* name, int make_inactive) +{ + GtkButton* button = (GtkButton*)ih->handle; + GtkImage* image = (GtkImage*)gtk_button_get_image(button); + + if (name) + { + GdkPixbuf* pixbuf = iupImageGetImage(name, ih, make_inactive); + GdkPixbuf* old_pixbuf = gtk_image_get_pixbuf(image); + if (pixbuf != old_pixbuf) + gtk_image_set_from_pixbuf(image, pixbuf); + return; + } + + /* if not defined */ +#if GTK_CHECK_VERSION(2, 8, 0) + gtk_image_clear(image); +#endif +} + +static void gtkToggleUpdateImage(Ihandle* ih, int active, int check) +{ + char* name; + + if (!active) + { + name = iupAttribGet(ih, "IMINACTIVE"); + if (name) + gtkToggleSetPixbuf(ih, name, 0); + else + { + /* if not defined then automaticaly create one based on IMAGE */ + name = iupAttribGet(ih, "IMAGE"); + gtkToggleSetPixbuf(ih, name, 1); /* make_inactive */ + } + } + else + { + /* must restore the normal image */ + if (check) + { + name = iupAttribGet(ih, "IMPRESS"); + if (name) + gtkToggleSetPixbuf(ih, name, 0); + else + { + /* if not defined then automaticaly create one based on IMAGE */ + name = iupAttribGet(ih, "IMAGE"); + gtkToggleSetPixbuf(ih, name, 0); + } + } + else + { + name = iupAttribGet(ih, "IMAGE"); + if (name) + gtkToggleSetPixbuf(ih, name, 0); + } + } +} + + +/*************************************************************************/ + + +static int gtkToggleSetValueAttrib(Ihandle* ih, const char* value) +{ + if (iupStrEqualNoCase(value,"NOTDEF")) + gtk_toggle_button_set_inconsistent((GtkToggleButton*)ih->handle, TRUE); + else + { + gtk_toggle_button_set_inconsistent((GtkToggleButton*)ih->handle, FALSE); + + /* This action causes the toggled signal to be emitted. */ + iupAttribSetStr(ih, "_IUPGTK_IGNORE_TOGGLE", "1"); + + if (iupStrBoolean(value)) + gtk_toggle_button_set_active((GtkToggleButton*)ih->handle, TRUE); + else + gtk_toggle_button_set_active((GtkToggleButton*)ih->handle, FALSE); + + if (ih->data->type == IUP_TOGGLE_IMAGE) + gtkToggleUpdateImage(ih, iupdrvIsActive(ih), gtkToggleGetCheck(ih)); + + iupAttribSetStr(ih, "_IUPGTK_IGNORE_TOGGLE", NULL); + } + + return 0; +} + +static char* gtkToggleGetValueAttrib(Ihandle* ih) +{ + int check = gtkToggleGetCheck(ih); + if (check == -1) + return "NOTDEF"; + else if (check == 1) + return "ON"; + else + return "OFF"; +} + +static int gtkToggleSetTitleAttrib(Ihandle* ih, const char* value) +{ + if (ih->data->type == IUP_TOGGLE_TEXT) + { + GtkButton* button = (GtkButton*)ih->handle; + GtkLabel* label = (GtkLabel*)gtk_button_get_image(button); + iupgtkSetMnemonicTitle(ih, label, value); + return 1; + } + + return 0; +} + +static int gtkToggleSetAlignmentAttrib(Ihandle* ih, const char* value) +{ + GtkButton* button = (GtkButton*)ih->handle; + float xalign, yalign; + char value1[30]="", value2[30]=""; + + if (ih->data->type == IUP_TOGGLE_TEXT) + return 0; + + iupStrToStrStr(value, value1, value2, ':'); + + if (iupStrEqualNoCase(value1, "ARIGHT")) + xalign = 1.0f; + else if (iupStrEqualNoCase(value1, "ACENTER")) + xalign = 0.5f; + else /* "ALEFT" */ + xalign = 0; + + if (iupStrEqualNoCase(value2, "ABOTTOM")) + yalign = 1.0f; + else if (iupStrEqualNoCase(value2, "ATOP")) + yalign = 0; + else /* ACENTER (default) */ + yalign = 0.5f; + + gtk_button_set_alignment(button, xalign, yalign); + + return 1; +} + +static int gtkToggleSetPaddingAttrib(Ihandle* ih, const char* value) +{ + iupStrToIntInt(value, &ih->data->horiz_padding, &ih->data->vert_padding, 'x'); + if (ih->handle && ih->data->type == IUP_TOGGLE_IMAGE) + { + GtkButton* button = (GtkButton*)ih->handle; + GtkMisc* misc = (GtkMisc*)gtk_button_get_image(button); + gtk_misc_set_padding(misc, ih->data->horiz_padding, ih->data->vert_padding); + } + return 0; +} + +static int gtkToggleSetFgColorAttrib(Ihandle* ih, const char* value) +{ + unsigned char r, g, b; + GtkWidget* label = (GtkWidget*)gtk_button_get_image((GtkButton*)ih->handle); + if (!label) return 0; + + if (!iupStrToRGB(value, &r, &g, &b)) + return 0; + + iupgtkBaseSetFgColor(label, r, g, b); + + return 1; +} + +static int gtkToggleSetStandardFontAttrib(Ihandle* ih, const char* value) +{ + iupdrvSetStandardFontAttrib(ih, value); + + if (ih->handle) + { + GtkWidget* label = gtk_button_get_image((GtkButton*)ih->handle); + if (!label) return 1; + + gtk_widget_modify_font(label, (PangoFontDescription*)iupgtkGetPangoFontDescAttrib(ih)); + + if (ih->data->type == IUP_TOGGLE_TEXT) + iupgtkFontUpdatePangoLayout(ih, gtk_label_get_layout((GtkLabel*)label)); + } + return 1; +} + +static int gtkToggleSetImageAttrib(Ihandle* ih, const char* value) +{ + if (ih->data->type == IUP_TOGGLE_IMAGE) + { + if (value != iupAttribGet(ih, "IMAGE")) + iupAttribSetStr(ih, "IMAGE", (char*)value); + gtkToggleUpdateImage(ih, iupdrvIsActive(ih), gtkToggleGetCheck(ih)); + return 1; + } + else + return 0; +} + +static int gtkToggleSetImInactiveAttrib(Ihandle* ih, const char* value) +{ + if (ih->data->type == IUP_TOGGLE_IMAGE) + { + if (value != iupAttribGet(ih, "IMINACTIVE")) + iupAttribSetStr(ih, "IMINACTIVE", (char*)value); + gtkToggleUpdateImage(ih, iupdrvIsActive(ih), gtkToggleGetCheck(ih)); + return 1; + } + else + return 0; +} + +static int gtkToggleSetImPressAttrib(Ihandle* ih, const char* value) +{ + if (ih->data->type == IUP_TOGGLE_IMAGE) + { + if (value != iupAttribGet(ih, "IMPRESS")) + iupAttribSetStr(ih, "IMPRESS", (char*)value); + gtkToggleUpdateImage(ih, iupdrvIsActive(ih), gtkToggleGetCheck(ih)); + return 1; + } + else + return 0; +} + +static int gtkToggleSetActiveAttrib(Ihandle* ih, const char* value) +{ + /* update the inactive image if necessary */ + if (ih->data->type == IUP_TOGGLE_IMAGE) + gtkToggleUpdateImage(ih, iupStrBoolean(value), gtkToggleGetCheck(ih)); + + return iupBaseSetActiveAttrib(ih, value); +} + +/****************************************************************************************************/ + +static void gtkToggleToggled(GtkToggleButton *widget, Ihandle* ih) +{ + IFni cb; + int check; + + if (iupAttribGet(ih, "_IUPGTK_IGNORE_TOGGLE")) + return; + + check = gtkToggleGetCheck(ih); + + if (ih->data->type == IUP_TOGGLE_IMAGE) + gtkToggleUpdateImage(ih, iupdrvIsActive(ih), check); + + cb = (IFni)IupGetCallback(ih, "ACTION"); + if (cb && cb(ih, check) == IUP_CLOSE) + IupExitLoop(); + + iupBaseCallValueChangedCb(ih); + + (void)widget; +} + +static int gtkToggleUpdate3StateCheck(Ihandle *ih, int keyb) +{ + int check = gtkToggleGetCheck(ih); + if (check == 1) /* GOTO check == -1 */ + { + gtk_toggle_button_set_inconsistent((GtkToggleButton*)ih->handle, TRUE); + gtkToggleToggled((GtkToggleButton*)ih->handle, ih); + return TRUE; /* ignore message to avoid change toggle state */ + } + else if (check == -1) /* GOTO check == 0 */ + { + gtk_toggle_button_set_inconsistent((GtkToggleButton*)ih->handle, FALSE); + if (keyb) + { + gtk_toggle_button_set_active((GtkToggleButton*)ih->handle, FALSE); + return TRUE; /* ignore message to avoid change toggle state */ + } + } + else /* (check == 0) GOTO check == 1 */ + { + gtk_toggle_button_set_inconsistent((GtkToggleButton*)ih->handle, FALSE); + if (keyb) + { + gtk_toggle_button_set_active((GtkToggleButton*)ih->handle, TRUE); + return TRUE; /* ignore message to avoid change toggle state */ + } + } + + return FALSE; +} + +static gboolean gtkToggleButtonEvent(GtkWidget *widget, GdkEventButton *evt, Ihandle *ih) +{ + if (iupAttribGet(ih, "_IUPGTK_IGNORE_TOGGLE")) + return FALSE; + + if (ih->data->type == IUP_TOGGLE_IMAGE) + { + char* name = iupAttribGet(ih, "IMPRESS"); + if (name) + { + if (evt->type == GDK_BUTTON_PRESS) + gtkToggleUpdateImage(ih, iupdrvIsActive(ih), 1); + else + gtkToggleUpdateImage(ih, iupdrvIsActive(ih), 0); + } + } + else + { + if (evt->type == GDK_BUTTON_RELEASE) + { + if (gtkToggleUpdate3StateCheck(ih, 0)) + return TRUE; /* ignore message to avoid change toggle state */ + } + } + + (void)widget; + return FALSE; +} + +static gboolean gtkToggleKeyEvent(GtkWidget *widget, GdkEventKey *evt, Ihandle *ih) +{ + if (evt->type == GDK_KEY_PRESS) + { + if (evt->keyval == GDK_space || evt->keyval == GDK_Return) + return TRUE; /* ignore message to avoid change toggle state */ + } + else + { + if (evt->keyval == GDK_space || evt->keyval == GDK_Return) + { + if (gtkToggleUpdate3StateCheck(ih, 1)) + return TRUE; /* ignore message to avoid change toggle state */ + } + } + + (void)widget; + return FALSE; +} + +static int gtkToggleMapMethod(Ihandle* ih) +{ + Ihandle* radio = iupRadioFindToggleParent(ih); + char *value; + int is3state = 0; + + if (!ih->parent) + return IUP_ERROR; + + if (radio) + ih->data->radio = 1; + + value = iupAttribGet(ih, "IMAGE"); + if (value) + ih->data->type = IUP_TOGGLE_IMAGE; + else + ih->data->type = IUP_TOGGLE_TEXT; + + if (radio) + { + GtkRadioButton* last_tg = (GtkRadioButton*)iupAttribGet(radio, "_IUPGTK_LASTRADIOBUTTON"); + if (last_tg) + ih->handle = gtk_radio_button_new_from_widget(last_tg); + else + ih->handle = gtk_radio_button_new(NULL); + iupAttribSetStr(radio, "_IUPGTK_LASTRADIOBUTTON", (char*)ih->handle); + } + else + { + if (ih->data->type == IUP_TOGGLE_TEXT) + { + ih->handle = gtk_check_button_new(); + + if (iupAttribGetBoolean(ih, "3STATE")) + is3state = 1; + } + else + ih->handle = gtk_toggle_button_new(); + } + + if (!ih->handle) + return IUP_ERROR; + + if (ih->data->type == IUP_TOGGLE_TEXT) + { + gtk_button_set_image((GtkButton*)ih->handle, gtk_label_new(NULL)); + gtk_toggle_button_set_mode((GtkToggleButton*)ih->handle, TRUE); + } + else + { + gtk_button_set_image((GtkButton*)ih->handle, gtk_image_new()); + gtk_toggle_button_set_mode((GtkToggleButton*)ih->handle, FALSE); + } + + /* add to the parent, all GTK controls must call this. */ + iupgtkBaseAddToParent(ih); + + if (!iupAttribGetBoolean(ih, "CANFOCUS")) + GTK_WIDGET_FLAGS(ih->handle) &= ~GTK_CAN_FOCUS; + + g_signal_connect(G_OBJECT(ih->handle), "enter-notify-event", G_CALLBACK(iupgtkEnterLeaveEvent), ih); + g_signal_connect(G_OBJECT(ih->handle), "leave-notify-event", G_CALLBACK(iupgtkEnterLeaveEvent), ih); + + g_signal_connect(G_OBJECT(ih->handle), "focus-in-event", G_CALLBACK(iupgtkFocusInOutEvent), ih); + g_signal_connect(G_OBJECT(ih->handle), "focus-out-event", G_CALLBACK(iupgtkFocusInOutEvent), ih); + g_signal_connect(G_OBJECT(ih->handle), "key-press-event", G_CALLBACK(iupgtkKeyPressEvent), ih); + g_signal_connect(G_OBJECT(ih->handle), "show-help", G_CALLBACK(iupgtkShowHelp), ih); + + g_signal_connect(G_OBJECT(ih->handle), "toggled", G_CALLBACK(gtkToggleToggled), ih); + + if (ih->data->type == IUP_TOGGLE_IMAGE || is3state) + { + g_signal_connect(G_OBJECT(ih->handle), "button-press-event", G_CALLBACK(gtkToggleButtonEvent), ih); + g_signal_connect(G_OBJECT(ih->handle), "button-release-event",G_CALLBACK(gtkToggleButtonEvent), ih); + } + + if (is3state) + { + g_signal_connect(G_OBJECT(ih->handle), "key-press-event", G_CALLBACK(gtkToggleKeyEvent), ih); + g_signal_connect(G_OBJECT(ih->handle), "key-release-event", G_CALLBACK(gtkToggleKeyEvent), ih); + } + + gtk_widget_realize(ih->handle); + + return IUP_NOERROR; +} + +void iupdrvToggleInitClass(Iclass* ic) +{ + /* Driver Dependent Class functions */ + ic->Map = gtkToggleMapMethod; + + /* Driver Dependent Attribute functions */ + + /* Overwrite Common */ + iupClassRegisterAttribute(ic, "STANDARDFONT", NULL, gtkToggleSetStandardFontAttrib, IUPAF_SAMEASSYSTEM, "DEFAULTFONT", IUPAF_NOT_MAPPED); + + /* Overwrite Visual */ + iupClassRegisterAttribute(ic, "ACTIVE", iupBaseGetActiveAttrib, gtkToggleSetActiveAttrib, IUPAF_SAMEASSYSTEM, "YES", IUPAF_DEFAULT); + + /* Visual */ + iupClassRegisterAttribute(ic, "BGCOLOR", NULL, iupdrvBaseSetBgColorAttrib, IUPAF_SAMEASSYSTEM, "DLGBGCOLOR", IUPAF_DEFAULT); + + /* Special */ + iupClassRegisterAttribute(ic, "FGCOLOR", NULL, gtkToggleSetFgColorAttrib, IUPAF_SAMEASSYSTEM, "DLGFGCOLOR", IUPAF_DEFAULT); /* black */ + iupClassRegisterAttribute(ic, "TITLE", NULL, gtkToggleSetTitleAttrib, NULL, NULL, IUPAF_NO_DEFAULTVALUE|IUPAF_NO_INHERIT); + + /* IupToggle only */ + iupClassRegisterAttribute(ic, "ALIGNMENT", NULL, gtkToggleSetAlignmentAttrib, "ACENTER:ACENTER", NULL, IUPAF_NO_INHERIT); /* force new default value */ + iupClassRegisterAttribute(ic, "IMAGE", NULL, gtkToggleSetImageAttrib, NULL, NULL, IUPAF_NO_DEFAULTVALUE|IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "IMINACTIVE", NULL, gtkToggleSetImInactiveAttrib, NULL, NULL, IUPAF_NO_DEFAULTVALUE|IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "IMPRESS", NULL, gtkToggleSetImPressAttrib, NULL, NULL, IUPAF_NO_DEFAULTVALUE|IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "VALUE", gtkToggleGetValueAttrib, gtkToggleSetValueAttrib, NULL, NULL, IUPAF_NO_DEFAULTVALUE|IUPAF_NO_INHERIT); + + iupClassRegisterAttribute(ic, "PADDING", iupToggleGetPaddingAttrib, gtkToggleSetPaddingAttrib, IUPAF_SAMEASSYSTEM, "0x0", IUPAF_NOT_MAPPED); + iupClassRegisterAttribute(ic, "MARKUP", NULL, NULL, NULL, NULL, IUPAF_DEFAULT); +} diff --git a/iup/src/gtk/iupgtk_tree.c b/iup/src/gtk/iupgtk_tree.c new file mode 100755 index 0000000..d408f27 --- /dev/null +++ b/iup/src/gtk/iupgtk_tree.c @@ -0,0 +1,2369 @@ +/** \file + * \brief Tree Control + * + * See Copyright Notice in iup.h + */ + +#include <gtk/gtk.h> +#include <gdk/gdkkeysyms.h> + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <memory.h> +#include <stdarg.h> + +#include "iup.h" +#include "iupcbs.h" + +#include "iup_object.h" +#include "iup_layout.h" +#include "iup_attrib.h" +#include "iup_str.h" +#include "iup_drv.h" +#include "iup_drvfont.h" +#include "iup_stdcontrols.h" +#include "iup_key.h" +#include "iup_image.h" +#include "iup_array.h" +#include "iup_tree.h" + +#include "iup_drvinfo.h" +#include "iupgtk_drv.h" + +enum +{ + IUPGTK_TREE_IMAGE, + IUPGTK_TREE_HAS_IMAGE, + IUPGTK_TREE_IMAGE_EXPANDED, + IUPGTK_TREE_HAS_IMAGE_EXPANDED, + IUPGTK_TREE_TITLE, + IUPGTK_TREE_KIND, + IUPGTK_TREE_COLOR, + IUPGTK_TREE_FONT, + IUPGTK_TREE_USERDATA +}; + +static GtkTreeIter gtkTreeInvalidIter = {0,0,0,0}; + +/*****************************************************************************/ +/* COPYING ITEMS (Branches and its children) */ +/*****************************************************************************/ +/* Insert the copied item in a new location. Returns the new item. */ +static void gtkTreeCopyItem(GtkTreeModel* model, GtkTreeIter* iterItem, GtkTreeIter* iterParent, int position, GtkTreeIter *iterNewItem, int full_copy) +{ + GtkTreeStore* store = GTK_TREE_STORE(model); + int kind; + char* title; + gboolean has_image, has_image_expanded; + PangoFontDescription* font; + void* userdata; + GdkColor *color; + GdkPixbuf* image, *image_expanded; + + gtk_tree_model_get(GTK_TREE_MODEL(store), iterItem, IUPGTK_TREE_IMAGE, &image, + IUPGTK_TREE_HAS_IMAGE, &has_image, + IUPGTK_TREE_IMAGE_EXPANDED, &image_expanded, + IUPGTK_TREE_HAS_IMAGE_EXPANDED, &has_image_expanded, + IUPGTK_TREE_TITLE, &title, + IUPGTK_TREE_KIND, &kind, + IUPGTK_TREE_COLOR, &color, + IUPGTK_TREE_FONT, &font, + IUPGTK_TREE_USERDATA, &userdata, + -1); + + if (position == 2) + gtk_tree_store_append(store, iterNewItem, iterParent); + else if (position == 1) /* copy as first child of expanded branch */ + gtk_tree_store_insert(store, iterNewItem, iterParent, 0); /* iterParent is parent of the new item (firstchild of it) */ + else /* copy as next brother of item or collapsed branch */ + gtk_tree_store_insert_after(store, iterNewItem, NULL, iterParent); /* iterParent is sibling of the new item */ + + if (full_copy) /* during a full copy the userdata reference is not copied */ + userdata = NULL; + + gtk_tree_store_set(store, iterNewItem, IUPGTK_TREE_IMAGE, image, + IUPGTK_TREE_HAS_IMAGE, has_image, + IUPGTK_TREE_IMAGE_EXPANDED, image_expanded, + IUPGTK_TREE_HAS_IMAGE_EXPANDED, has_image_expanded, + IUPGTK_TREE_TITLE, title, + IUPGTK_TREE_KIND, kind, + IUPGTK_TREE_COLOR, color, + IUPGTK_TREE_FONT, font, + IUPGTK_TREE_USERDATA, userdata, + -1); +} + +static void gtkTreeCopyChildren(Ihandle* ih, GtkTreeModel* model, GtkTreeIter *iterItemSrc, GtkTreeIter *iterItemDst, int full_copy) +{ + GtkTreeIter iterChildSrc; + int hasItem = gtk_tree_model_iter_children(model, &iterChildSrc, iterItemSrc); /* get the firstchild */ + while(hasItem) + { + GtkTreeIter iterNewItem; + gtkTreeCopyItem(model, &iterChildSrc, iterItemDst, 2, &iterNewItem, full_copy); /* append always */ + + /* Recursively transfer all the items */ + gtkTreeCopyChildren(ih, model, &iterChildSrc, &iterNewItem, full_copy); + + /* Go to next sibling item */ + hasItem = gtk_tree_model_iter_next(model, &iterChildSrc); + } +} + +/* Copies all items in a branch to a new location. Returns the new branch node. */ +static void gtkTreeCopyNode(Ihandle* ih, GtkTreeModel* model, GtkTreeIter *iterItemSrc, GtkTreeIter *iterItemDst, GtkTreeIter* iterNewItem, int full_copy) +{ + int kind, position = 0; /* insert after iterItemDst */ + gtk_tree_model_get(model, iterItemDst, IUPGTK_TREE_KIND, &kind, -1); + + if (kind == ITREE_BRANCH) + { + GtkTreePath* path = gtk_tree_model_get_path(model, iterItemDst); + if (gtk_tree_view_row_expanded(GTK_TREE_VIEW(ih->handle), path)) + position = 1; /* insert as first child of iterItemDst */ + gtk_tree_path_free(path); + } + + gtkTreeCopyItem(model, iterItemSrc, iterItemDst, position, iterNewItem, full_copy); + + gtkTreeCopyChildren(ih, model, iterItemSrc, iterNewItem, full_copy); +} + +/*****************************************************************************/ +/* FINDING ITEMS */ +/*****************************************************************************/ + +static void gtkTreeInvertAllNodeMarking(Ihandle* ih, GtkTreeModel* model, GtkTreeSelection* selection, GtkTreeIter* iterItem) +{ + GtkTreeIter iterChild; + int hasItem = TRUE; + + while(hasItem) + { + if(gtk_tree_selection_iter_is_selected(selection, iterItem)) + gtk_tree_selection_unselect_iter(selection, iterItem); + else + gtk_tree_selection_select_iter(selection, iterItem); + + /* Check whether we have child items */ + if(gtk_tree_model_iter_has_child(model, iterItem)) + { + gtk_tree_model_iter_children(model, &iterChild, iterItem); /* get the firstchild */ + gtkTreeInvertAllNodeMarking(ih, model, selection, &iterChild); + } + + /* Go to next sibling item */ + hasItem = gtk_tree_model_iter_next(model, iterItem); + } +} + +static GtkTreeIter gtkTreeFindVisibleNodeId(Ihandle* ih, GtkTreeModel* model, GtkTreeIter iterItem, GtkTreeIter iterNode) +{ + GtkTreeIter iterChild; + GtkTreePath* path; + int hasItem = TRUE; + + while(hasItem) + { + /* ID control to traverse items */ + ih->data->id_control++; /* not the real id since it counts only the visible ones */ + + /* StateID founded! */ + if(iterItem.user_data == iterNode.user_data) + return iterItem; + + path = gtk_tree_model_get_path(model, &iterItem); + + /* Check whether we have child items and it is expanded (visible) */ + if (gtk_tree_model_iter_has_child(model, &iterItem) && gtk_tree_view_row_expanded(GTK_TREE_VIEW(ih->handle), path)) + { + gtk_tree_model_iter_children(model, &iterChild, &iterItem); /* get the firstchild */ + iterChild = gtkTreeFindVisibleNodeId(ih, model, iterChild, iterNode); + + /* StateID founded! */ + if(iterChild.user_data) + { + gtk_tree_path_free(path); + return iterChild; + } + } + + gtk_tree_path_free(path); + /* Go to next sibling item */ + hasItem = gtk_tree_model_iter_next(model, &iterItem); + } + + return gtkTreeInvalidIter; /* invalid since gtk_tree_model_iter_next returned false */ +} + +static GtkTreeIter gtkTreeFindVisibleNodeFromId(Ihandle* ih, GtkTreeModel* model, GtkTreeIter iterItem) +{ + GtkTreeIter iterChild; + GtkTreePath* path; + int hasItem = TRUE; + + while(hasItem) + { + /* ID control to traverse items */ + ih->data->id_control--; /* not the real id since it counts only the visible ones */ + + /* StateID founded! */ + if(ih->data->id_control < 0) + return iterItem; + + path = gtk_tree_model_get_path(model, &iterItem); + + /* Check whether we have child items and it is expanded (visible) */ + if(gtk_tree_model_iter_has_child(model, &iterItem) && gtk_tree_view_row_expanded(GTK_TREE_VIEW(ih->handle), path)) + { + gtk_tree_model_iter_children(model, &iterChild, &iterItem); /* get the firstchild */ + iterChild = gtkTreeFindVisibleNodeFromId(ih, model, iterChild); + + /* StateID founded! */ + if(ih->data->id_control < 0) + { + gtk_tree_path_free(path); + return iterChild; + } + } + + gtk_tree_path_free(path); + /* Go to next sibling item */ + hasItem = gtk_tree_model_iter_next(model, &iterItem); + } + + return gtkTreeInvalidIter; /* invalid since gtk_tree_model_iter_next returned false */ +} + +static GtkTreeIter gtkTreeGetLastVisibleNode(Ihandle* ih, GtkTreeModel* model, GtkTreeIter iterItem) +{ + GtkTreeIter iterChild, iterPrev = gtkTreeInvalidIter; + GtkTreePath* path = gtk_tree_model_get_path(model, &iterItem); + + /* Check whether we have child items and it is expanded (visible) */ + if(gtk_tree_model_iter_has_child(model, &iterItem) && gtk_tree_view_row_expanded(GTK_TREE_VIEW(ih->handle), path)) + { + int hasItem = TRUE; + gtk_tree_model_iter_children(model, &iterChild, &iterItem); /* get the firstchild */ + + while(hasItem) + { + iterPrev = iterChild; + + /* Go to next sibling item */ + hasItem = gtk_tree_model_iter_next(model, &iterChild); + } + + iterItem = gtkTreeGetLastVisibleNode(ih, model, iterPrev); + } + gtk_tree_path_free(path); + + return iterItem; +} + +static GtkTreeIter gtkTreeFindNodeID(Ihandle* ih, GtkTreeModel* model, GtkTreeIter iterItem, GtkTreeIter iterNode) +{ + GtkTreeIter iterChild; + int hasItem = TRUE; + + while(hasItem) + { + /* ID control to traverse items */ + ih->data->id_control++; + + /* StateID founded! */ + if (iterItem.user_data == iterNode.user_data) + return iterItem; + + /* Check whether we have child items */ + if (gtk_tree_model_iter_has_child(model, &iterItem)) + { + gtk_tree_model_iter_children(model, &iterChild, &iterItem); /* get the firstchild */ + iterChild = gtkTreeFindNodeID(ih, model, iterChild, iterNode); + + /* StateID founded! */ + if(iterChild.user_data) + return iterChild; + } + + /* Go to next sibling item */ + hasItem = gtk_tree_model_iter_next(model, &iterItem); + } + + return gtkTreeInvalidIter; /* invalid since gtk_tree_model_iter_next returned false */ +} + +static int gtkTreeGetNodeId(Ihandle* ih, GtkTreeIter iterItem) +{ + GtkTreeModel* model = gtk_tree_view_get_model(GTK_TREE_VIEW(ih->handle)); + GtkTreeIter iterRoot; + gtk_tree_model_get_iter_first(model, &iterRoot); + + ih->data->id_control = -1; + iterItem = gtkTreeFindNodeID(ih, model, iterRoot, iterItem); + if (iterItem.user_data) + return ih->data->id_control; + else + return -1; +} + +static GtkTreeIter gtkTreeFindUserData(Ihandle* ih, GtkTreeModel* model, GtkTreeIter iterItem, void* userdata) +{ + GtkTreeIter iterChild; + int hasItem = TRUE; + void* node_userdata; + + while(hasItem) + { + /* ID control to traverse items */ + ih->data->id_control++; + + gtk_tree_model_get(model, &iterItem, IUPGTK_TREE_USERDATA, &node_userdata, -1); + + /* userdata founded! */ + if (node_userdata == userdata) + return iterItem; + + /* Check whether we have child items */ + if (gtk_tree_model_iter_has_child(model, &iterItem)) + { + gtk_tree_model_iter_children(model, &iterChild, &iterItem); /* get the firstchild */ + iterChild = gtkTreeFindUserData(ih, model, iterChild, userdata); + + /* userdata founded! */ + if (iterChild.user_data) + return iterChild; + } + + /* Go to next sibling item */ + hasItem = gtk_tree_model_iter_next(model, &iterItem); + } + + return gtkTreeInvalidIter; /* invalid since gtk_tree_model_iter_next returned false */ +} + +static int gtkTreeGetUserDataId(Ihandle* ih, GtkTreeModel* model, void* userdata) +{ + GtkTreeIter iterRoot, iterItem; + gtk_tree_model_get_iter_first(model, &iterRoot); + + ih->data->id_control = -1; + iterItem = gtkTreeFindUserData(ih, model, iterRoot, userdata); + if (iterItem.user_data) + return ih->data->id_control; + else + return -1; +} + +static void gtkTreeCallNodeRemovedRec(Ihandle* ih, GtkTreeModel* model, GtkTreeIter iterItem, IFnis cb) +{ + GtkTreeIter iterChild; + int hasItem = TRUE; + void* node_userdata; + + while(hasItem) + { + /* ID control to traverse items */ + ih->data->id_control++; + + gtk_tree_model_get(model, &iterItem, IUPGTK_TREE_USERDATA, &node_userdata, -1); + + cb(ih, ih->data->id_control, (char*)node_userdata); + + /* Check whether we have child items */ + if (gtk_tree_model_iter_has_child(model, &iterItem)) + { + gtk_tree_model_iter_children(model, &iterChild, &iterItem); /* get the firstchild */ + gtkTreeCallNodeRemovedRec(ih, model, iterChild, cb); + } + + /* Go to next sibling item */ + hasItem = gtk_tree_model_iter_next(model, &iterItem); + } +} + +static void gtkTreeCallNodeRemoved(Ihandle* ih, GtkTreeModel* model, GtkTreeIter *iterItem) +{ + IFnis cb = (IFnis)IupGetCallback(ih, "NODEREMOVED_CB"); + if (cb) + { + ih->data->id_control = gtkTreeGetNodeId(ih, *iterItem)-1; + gtkTreeCallNodeRemovedRec(ih, model, *iterItem, cb); + } +} + +static gboolean gtkTreeFindNodeFromID(Ihandle* ih, GtkTreeModel* model, GtkTreeIter *iterItem, int *id) +{ + GtkTreeIter iterChild; + int hasItem = TRUE; + + while(hasItem) + { + /* ID control to traverse items */ + (*id)--; + + /* StateID founded! */ + if (*id < 0) + return TRUE; + + /* Check whether we have child items */ + if (gtk_tree_model_iter_has_child(model, iterItem)) + { + gtk_tree_model_iter_children(model, &iterChild, iterItem); /* get the firstchild */ + + if (gtkTreeFindNodeFromID(ih, model, &iterChild, id)) + { + *iterItem = iterChild; + return TRUE; + } + } + + /* Go to next sibling item */ + hasItem = gtk_tree_model_iter_next(model, iterItem); + } + + return FALSE; +} + +static gboolean gtkTreeFindNodeFromString(Ihandle* ih, GtkTreeModel* model, const char* name_id, GtkTreeIter *iterItem) +{ + if (name_id[0]) + { + int id; + if (iupStrToInt(name_id, &id)) + { + gtk_tree_model_get_iter_first(model, iterItem); + return gtkTreeFindNodeFromID(ih, model, iterItem, &id); + } + } + else + { + GtkTreePath* path = NULL; + gtk_tree_view_get_cursor(GTK_TREE_VIEW(ih->handle), &path, NULL); + if (path) + { + gtk_tree_model_get_iter(model, iterItem, path); + gtk_tree_path_free(path); + return TRUE; + } + } + return FALSE; +} + +/*****************************************************************************/ +/* MANIPULATING IMAGES */ +/*****************************************************************************/ +static void gtkTreeUpdateImages(Ihandle* ih, GtkTreeModel* model, GtkTreeIter iterItem, int mode) +{ + GtkTreeIter iterChild; + int hasItem = TRUE; + int kind; + + while(hasItem) + { + gtk_tree_model_get(model, &iterItem, IUPGTK_TREE_KIND, &kind, -1); + + if (kind == ITREE_BRANCH) + { + if (mode == ITREE_UPDATEIMAGE_EXPANDED) + { + gboolean has_image_expanded = FALSE; + gtk_tree_model_get(model, &iterItem, IUPGTK_TREE_HAS_IMAGE_EXPANDED, &has_image_expanded, -1); + if (!has_image_expanded) + gtk_tree_store_set(GTK_TREE_STORE(model), &iterItem, IUPGTK_TREE_IMAGE_EXPANDED, ih->data->def_image_expanded, -1); + } + else if(mode == ITREE_UPDATEIMAGE_COLLAPSED) + { + gboolean has_image = FALSE; + gtk_tree_model_get(model, &iterItem, IUPGTK_TREE_HAS_IMAGE, &has_image, -1); + if (!has_image) + gtk_tree_store_set(GTK_TREE_STORE(model), &iterItem, IUPGTK_TREE_IMAGE, ih->data->def_image_collapsed, -1); + } + + if (gtk_tree_model_iter_has_child(model, &iterItem)) + { + + /* Recursively traverse child items */ + gtk_tree_model_iter_children(model, &iterChild, &iterItem); + gtkTreeUpdateImages(ih, model, iterChild, mode); + } + } + else + { + if (mode == ITREE_UPDATEIMAGE_LEAF) + { + gboolean has_image = FALSE; + gtk_tree_model_get(model, &iterItem, IUPGTK_TREE_HAS_IMAGE, &has_image, -1); + if (!has_image) + gtk_tree_store_set(GTK_TREE_STORE(model), &iterItem, IUPGTK_TREE_IMAGE, ih->data->def_image_leaf, -1); + } + } + + /* Go to next sibling item */ + hasItem = gtk_tree_model_iter_next(model, &iterItem); + } +} + +static void gtkTreeExpandItem(Ihandle* ih, GtkTreePath* path, int expand) +{ + if (expand == -1) + expand = !gtk_tree_view_row_expanded(GTK_TREE_VIEW(ih->handle), path); /* toggle */ + + if (expand) + gtk_tree_view_expand_row(GTK_TREE_VIEW(ih->handle), path, FALSE); + else + gtk_tree_view_collapse_row(GTK_TREE_VIEW(ih->handle), path); +} + +int iupgtkGetColor(const char* value, GdkColor *color) +{ + unsigned char r, g, b; + if (iupStrToRGB(value, &r, &g, &b)) + { + iupgdkColorSet(color, r, g, b); + return 1; + } + return 0; +} + +/*****************************************************************************/ +/* ADDING ITEMS */ +/*****************************************************************************/ +void iupdrvTreeAddNode(Ihandle* ih, const char* name_id, int kind, const char* title, int add) +{ + GtkTreeStore* store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(ih->handle))); + GtkTreeIter iterPrev, iterNewItem, iterParent; + GtkTreePath* path; + GdkColor color = {0L,0,0,0}; + int kindPrev; + + if (!gtkTreeFindNodeFromString(ih, GTK_TREE_MODEL(store), name_id, &iterPrev)) + return; + + gtk_tree_model_get(GTK_TREE_MODEL(store), &iterPrev, IUPGTK_TREE_KIND, &kindPrev, -1); + + if (kindPrev == ITREE_BRANCH && add) + gtk_tree_store_insert(store, &iterNewItem, &iterPrev, 0); /* iterPrev is parent of the new item (firstchild of it) */ + else + gtk_tree_store_insert_after(store, &iterNewItem, NULL, &iterPrev); /* iterPrev is sibling of the new item */ + + iupgtkGetColor(iupAttribGetStr(ih, "FGCOLOR"), &color); + + /* set the attributes of the new node */ + gtk_tree_store_set(store, &iterNewItem, IUPGTK_TREE_HAS_IMAGE, FALSE, + IUPGTK_TREE_HAS_IMAGE_EXPANDED, FALSE, + IUPGTK_TREE_TITLE, iupgtkStrConvertToUTF8(title), + IUPGTK_TREE_KIND, kind, + IUPGTK_TREE_COLOR, &color, -1); + + if (kind == ITREE_LEAF) + gtk_tree_store_set(store, &iterNewItem, IUPGTK_TREE_IMAGE, ih->data->def_image_leaf, -1); + else + gtk_tree_store_set(store, &iterNewItem, IUPGTK_TREE_IMAGE, ih->data->def_image_collapsed, + IUPGTK_TREE_IMAGE_EXPANDED, ih->data->def_image_expanded, -1); + + if (kindPrev == ITREE_BRANCH && add) + iterParent = iterPrev; + else + gtk_tree_model_iter_parent(GTK_TREE_MODEL(store), &iterParent, &iterNewItem); + + /* If this is the first child of the parent, then handle the ADDEXPANDED attribute */ + if (gtk_tree_model_iter_n_children(GTK_TREE_MODEL(store), &iterParent) == 1) + { + int depth; + path = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &iterParent); + depth = gtk_tree_path_get_depth(path)-1; + if (ih->data->add_expanded || depth==0) /* if this is the first child of the root, expand always */ + { + iupAttribSetStr(ih, "_IUPTREE_IGNORE_BRANCHOPEN_CB", "1"); + gtk_tree_view_expand_row(GTK_TREE_VIEW(ih->handle), path, FALSE); + } + else + gtk_tree_view_collapse_row(GTK_TREE_VIEW(ih->handle), path); + gtk_tree_path_free(path); + } +} + +static void gtkTreeAddRootNode(Ihandle* ih) +{ + GtkTreeStore* store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(ih->handle))); + GtkTreePath* path; + GtkTreeIter iterRoot; + GdkColor color = {0L,0,0,0}; + + iupgtkGetColor(iupAttribGetStr(ih, "FGCOLOR"), &color); + + gtk_tree_store_append(store, &iterRoot, NULL); /* root node */ + gtk_tree_store_set(store, &iterRoot, IUPGTK_TREE_IMAGE, ih->data->def_image_collapsed, + IUPGTK_TREE_HAS_IMAGE, FALSE, + IUPGTK_TREE_IMAGE_EXPANDED, ih->data->def_image_expanded, + IUPGTK_TREE_HAS_IMAGE_EXPANDED, FALSE, + IUPGTK_TREE_KIND, ITREE_BRANCH, + IUPGTK_TREE_COLOR, &color, -1); + + path = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &iterRoot); + /* MarkStart node */ + iupAttribSetStr(ih, "_IUPTREE_MARKSTART_NODE", (char*)path); + + /* Set the default VALUE */ + gtk_tree_view_set_cursor(GTK_TREE_VIEW(ih->handle), path, NULL, FALSE); +} + +/*****************************************************************************/ +/* AUXILIAR FUNCTIONS */ +/*****************************************************************************/ +static void gtkTreeOpenCloseEvent(Ihandle* ih) +{ + GtkTreeModel* model = gtk_tree_view_get_model(GTK_TREE_VIEW(ih->handle)); + GtkTreeIter iterItem; + GtkTreePath* path; + int kind; + + if (!gtkTreeFindNodeFromString(ih, model, "", &iterItem)) + return; + + path = gtk_tree_model_get_path(model, &iterItem); + if (path) + { + gtk_tree_model_get(model, &iterItem, IUPGTK_TREE_KIND, &kind, -1); + + if (kind == ITREE_LEAF) /* leafs */ + gtk_tree_view_row_activated(GTK_TREE_VIEW(ih->handle), path, (GtkTreeViewColumn*)iupAttribGet(ih, "_IUPGTK_COLUMN")); + else /* branches */ + gtkTreeExpandItem(ih, path, -1); /* toggle */ + + gtk_tree_path_free(path); + } +} + +static gboolean gtkTreeSelected_Foreach_Func(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, GList **rowref_list) +{ + GtkTreeRowReference *rowref; + + rowref = gtk_tree_row_reference_new(model, path); + *rowref_list = g_list_append(*rowref_list, rowref); + + (void)iter; + return FALSE; /* do not stop walking the store, call us with next row */ +} + +static gboolean gtkTreeSelected_Iter_Func(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, GList **rowref_list) +{ + GtkTreeRowReference *rowref; + GtkTreeIter iterParent; + if (!gtk_tree_model_iter_parent(model, &iterParent, iter)) /* the root node can't be deleted */ + return FALSE; /* do not stop walking the store, call us with next row */ + + rowref = gtk_tree_row_reference_new(model, path); + *rowref_list = g_list_append(*rowref_list, rowref); + + return FALSE; /* do not stop walking the store, call us with next row */ +} + +/*****************************************************************************/ +/* CALLBACKS */ +/*****************************************************************************/ +static void gtkTreeCallMultiSelectionCb(Ihandle* ih) +{ + IFnIi cbMulti = (IFnIi)IupGetCallback(ih, "MULTISELECTION_CB"); + IFnii cbSelec = (IFnii)IupGetCallback(ih, "SELECTION_CB"); + if (cbMulti || cbSelec) + { + GtkTreeModel* model = gtk_tree_view_get_model(GTK_TREE_VIEW(ih->handle)); + GtkTreeSelection* selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(ih->handle)); + GtkTreeIter iterRoot; + GList *rr_list = NULL; + GList *node; + int* id_rowItem; + int count_selected_rows, i = 0; + + gtk_tree_model_get_iter_first(model, &iterRoot); + + gtk_tree_selection_selected_foreach(selection, (GtkTreeSelectionForeachFunc)gtkTreeSelected_Foreach_Func, &rr_list); + count_selected_rows = g_list_length(rr_list); + id_rowItem = malloc(sizeof(int) * count_selected_rows); + + for(node = rr_list; node != NULL; node = node->next) + { + GtkTreePath* path = gtk_tree_row_reference_get_path(node->data); + if (path) + { + GtkTreeIter iterItem; + gtk_tree_model_get_iter(model, &iterItem, path); + + id_rowItem[i] = gtkTreeGetNodeId(ih, iterItem); + i++; + + gtk_tree_path_free(path); + } + } + + g_list_foreach(rr_list, (GFunc) gtk_tree_row_reference_free, NULL); + g_list_free(rr_list); + + if (cbMulti) + cbMulti(ih, id_rowItem, count_selected_rows); + else + { + for (i=0; i<count_selected_rows; i++) + cbSelec(ih, id_rowItem[i], 1); + } + + free(id_rowItem); + } +} + + +/*****************************************************************************/ +/* GET AND SET ATTRIBUTES */ +/*****************************************************************************/ + + +static char* gtkTreeGetIndentationAttrib(Ihandle* ih) +{ + char* str = iupStrGetMemory(255); +#if GTK_CHECK_VERSION(2, 12, 0) + int indent = gtk_tree_view_get_level_indentation(GTK_TREE_VIEW(ih->handle)); +#else + int indent = 0; +#endif + sprintf(str, "%d", indent); + return str; +} + +static int gtkTreeSetIndentationAttrib(Ihandle* ih, const char* value) +{ +#if GTK_CHECK_VERSION(2, 12, 0) + int indent; + if (iupStrToInt(value, &indent)) + gtk_tree_view_set_level_indentation(GTK_TREE_VIEW(ih->handle), indent); +#endif + return 0; +} + +static int gtkTreeSetTopItemAttrib(Ihandle* ih, const char* value) +{ + GtkTreeStore* store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(ih->handle))); + GtkTreeIter iterItem; + GtkTreePath* path; + + if (!gtkTreeFindNodeFromString(ih, GTK_TREE_MODEL(store), value, &iterItem)) + return 0; + + path = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &iterItem); + + if (!gtk_tree_view_row_expanded(GTK_TREE_VIEW(ih->handle), path)) + gtk_tree_view_expand_to_path(GTK_TREE_VIEW(ih->handle), path); + + gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(ih->handle), path, NULL, FALSE, 0, 0); /* scroll to visible */ + + gtk_tree_path_free(path); + + return 0; +} + +static int gtkTreeSetSpacingAttrib(Ihandle* ih, const char* value) +{ + if(!iupStrToInt(value, &ih->data->spacing)) + ih->data->spacing = 1; + + if(ih->data->spacing < 1) + ih->data->spacing = 1; + + if (ih->handle) + { + GtkCellRenderer *renderer_img = (GtkCellRenderer*)iupAttribGet(ih, "_IUPGTK_RENDERER_IMG"); + GtkCellRenderer *renderer_txt = (GtkCellRenderer*)iupAttribGet(ih, "_IUPGTK_RENDERER_TEXT"); + g_object_set(G_OBJECT(renderer_img), "ypad", ih->data->spacing, NULL); + g_object_set(G_OBJECT(renderer_txt), "ypad", ih->data->spacing, NULL); + return 0; + } + else + return 1; /* store until not mapped, when mapped will be set again */ +} + +static int gtkTreeSetExpandAllAttrib(Ihandle* ih, const char* value) +{ + if (iupStrBoolean(value)) + gtk_tree_view_expand_all(GTK_TREE_VIEW(ih->handle)); + else + { + GtkTreeModel* model = gtk_tree_view_get_model(GTK_TREE_VIEW(ih->handle)); + GtkTreeIter iterRoot; + GtkTreePath* pathRoot; + + gtk_tree_view_collapse_all(GTK_TREE_VIEW(ih->handle)); + + /* The root node is always expanded */ + gtk_tree_model_get_iter_first(model, &iterRoot); + pathRoot = gtk_tree_model_get_path(model, &iterRoot); + gtk_tree_view_expand_row(GTK_TREE_VIEW(ih->handle), pathRoot, FALSE); + gtk_tree_path_free(pathRoot); + } + + return 0; +} + +static char* gtkTreeGetDepthAttrib(Ihandle* ih, const char* name_id) +{ + char* depth; + GtkTreeStore* store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(ih->handle))); + GtkTreeIter iterItem; + if (!gtkTreeFindNodeFromString(ih, GTK_TREE_MODEL(store), name_id, &iterItem)) + return NULL; + + depth = iupStrGetMemory(10); + sprintf(depth, "%d", gtk_tree_store_iter_depth(store, &iterItem)); + return depth; +} + +static int gtkTreeSetMoveNodeAttrib(Ihandle* ih, const char* name_id, const char* value) +{ + GtkTreeModel* model; + GtkTreeIter iterItemSrc, iterItemDst, iterNewItem; + GtkTreeIter iterParent, iterNextParent; + + if (!ih->handle) /* do not store the action before map */ + return 0; + + model = gtk_tree_view_get_model(GTK_TREE_VIEW(ih->handle)); + if (!gtkTreeFindNodeFromString(ih, model, name_id, &iterItemSrc)) + return 0; + + if (!gtkTreeFindNodeFromString(ih, model, value, &iterItemDst)) + return 0; + + /* If Drag item is an ancestor of Drop item then return */ + iterParent = iterItemDst; + while(gtk_tree_model_iter_parent(model, &iterNextParent, &iterParent)) + { + if (iterNextParent.user_data == iterItemSrc.user_data) + return 0; + iterParent = iterNextParent; + } + + /* Copying the node and its children to the new position */ + gtkTreeCopyNode(ih, model, &iterItemSrc, &iterItemDst, &iterNewItem, 0); /* not a full copy, preserve user data */ + + /* Deleting the node of its old position */ + /* do not delete the user data, we copy the references in CopyNode */ + gtk_tree_store_remove(GTK_TREE_STORE(model), &iterItemSrc); + + return 0; +} + +static int gtkTreeSetCopyNodeAttrib(Ihandle* ih, const char* name_id, const char* value) +{ + GtkTreeModel* model; + GtkTreeIter iterItemSrc, iterItemDst, iterNewItem; + GtkTreeIter iterParent, iterNextParent; + + if (!ih->handle) /* do not store the action before map */ + return 0; + + model = gtk_tree_view_get_model(GTK_TREE_VIEW(ih->handle)); + if (!gtkTreeFindNodeFromString(ih, model, name_id, &iterItemSrc)) + return 0; + + if (!gtkTreeFindNodeFromString(ih, model, value, &iterItemDst)) + return 0; + + /* If Drag item is an ancestor of Drop item then return */ + iterParent = iterItemDst; + while(gtk_tree_model_iter_parent(model, &iterNextParent, &iterParent)) + { + if (iterNextParent.user_data == iterItemSrc.user_data) + return 0; + iterParent = iterNextParent; + } + + /* Copying the node and its children to the new position */ + gtkTreeCopyNode(ih, model, &iterItemSrc, &iterItemDst, &iterNewItem, 1); + + return 0; +} + +static char* gtkTreeGetColorAttrib(Ihandle* ih, const char* name_id) +{ + char* str; + GtkTreeModel* model = gtk_tree_view_get_model(GTK_TREE_VIEW(ih->handle)); + GtkTreeIter iterItem; + GdkColor *color; + + if (!gtkTreeFindNodeFromString(ih, model, name_id, &iterItem)) + return NULL; + + gtk_tree_model_get(model, &iterItem, IUPGTK_TREE_COLOR, &color, -1); + if (!color) + return NULL; + + str = iupStrGetMemory(20); + sprintf(str, "%d %d %d", iupCOLOR16TO8(color->red), + iupCOLOR16TO8(color->green), + iupCOLOR16TO8(color->blue)); + return str; +} + +static int gtkTreeSetColorAttrib(Ihandle* ih, const char* name_id, const char* value) +{ + GtkTreeStore* store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(ih->handle))); + GtkTreeIter iterItem; + GdkColor color; + unsigned char r, g, b; + + if (!gtkTreeFindNodeFromString(ih, GTK_TREE_MODEL(store), name_id, &iterItem)) + return 0; + + if (!iupStrToRGB(value, &r, &g, &b)) + return 0; + + iupgdkColorSet(&color, r, g, b); + gtk_tree_store_set(store, &iterItem, IUPGTK_TREE_COLOR, &color, -1); + + return 0; +} + +static char* gtkTreeGetParentAttrib(Ihandle* ih, const char* name_id) +{ + GtkTreeModel* model = gtk_tree_view_get_model(GTK_TREE_VIEW(ih->handle)); + GtkTreeIter iterItem; + GtkTreeIter iterParent; + char* str; + + if (!gtkTreeFindNodeFromString(ih, model, name_id, &iterItem)) + return NULL; + + if (!gtk_tree_model_iter_parent(model, &iterParent, &iterItem)) + return NULL; + + str = iupStrGetMemory(10); + sprintf(str, "%d", gtkTreeGetNodeId(ih, iterParent)); + return str; +} + +static char* gtkTreeGetChildCountAttrib(Ihandle* ih, const char* name_id) +{ + GtkTreeModel* model = gtk_tree_view_get_model(GTK_TREE_VIEW(ih->handle)); + GtkTreeIter iterItem; + char* str; + + if (!gtkTreeFindNodeFromString(ih, model, name_id, &iterItem)) + return NULL; + + str = iupStrGetMemory(10); + sprintf(str, "%d", gtk_tree_model_iter_n_children(model, &iterItem)); + return str; +} + +static int gtkTreeCount(GtkTreeModel* model, GtkTreeIter iterBranch) +{ + GtkTreeIter iterChild; + int count = 0; + int hasItem = gtk_tree_model_iter_children(model, &iterChild, &iterBranch); /* get the firstchild */ + count++; + while(hasItem) + { + count += gtkTreeCount(model, iterChild); + + /* Go to next sibling item */ + hasItem = gtk_tree_model_iter_next(model, &iterChild); + } + + return count; +} + +static char* gtkTreeGetCountAttrib(Ihandle* ih) +{ + GtkTreeIter iterRoot; + GtkTreeModel* model = gtk_tree_view_get_model(GTK_TREE_VIEW(ih->handle)); + char* str = iupStrGetMemory(10); + gtk_tree_model_get_iter_first(model, &iterRoot); + sprintf(str, "%d", gtkTreeCount(model, iterRoot)); + return str; +} + +static char* gtkTreeGetKindAttrib(Ihandle* ih, const char* name_id) +{ + GtkTreeModel* model = gtk_tree_view_get_model(GTK_TREE_VIEW(ih->handle)); + GtkTreeIter iterItem; + int kind; + + if (!gtkTreeFindNodeFromString(ih, model, name_id, &iterItem)) + return NULL; + + gtk_tree_model_get(model, &iterItem, IUPGTK_TREE_KIND, &kind, -1); + + if(!kind) + return "BRANCH"; + else + return "LEAF"; +} + +static char* gtkTreeGetStateAttrib(Ihandle* ih, const char* name_id) +{ + GtkTreeModel* model = gtk_tree_view_get_model(GTK_TREE_VIEW(ih->handle)); + GtkTreeIter iterItem; + + if (!gtkTreeFindNodeFromString(ih, model, name_id, &iterItem)) + return NULL; + + if (gtk_tree_model_iter_has_child(model, &iterItem)) + { + GtkTreePath* path = gtk_tree_model_get_path(model, &iterItem); + int expanded = gtk_tree_view_row_expanded(GTK_TREE_VIEW(ih->handle), path); + gtk_tree_path_free(path); + + if (expanded) + return "EXPANDED"; + else + return "COLLAPSED"; + } + + return NULL; +} + +static int gtkTreeSetStateAttrib(Ihandle* ih, const char* name_id, const char* value) +{ + GtkTreeModel* model = gtk_tree_view_get_model(GTK_TREE_VIEW(ih->handle)); + GtkTreeIter iterItem; + GtkTreePath* path; + + if (!gtkTreeFindNodeFromString(ih, model, name_id, &iterItem)) + return 0; + + path = gtk_tree_model_get_path(model, &iterItem); + gtkTreeExpandItem(ih, path, iupStrEqualNoCase(value, "EXPANDED")); + gtk_tree_path_free(path); + + return 0; +} + +static char* gtkTreeGetTitle(GtkTreeModel* model, GtkTreeIter iterItem) +{ + char* title; + gtk_tree_model_get(model, &iterItem, IUPGTK_TREE_TITLE, &title, -1); + return iupgtkStrConvertFromUTF8(title); +} + +static char* gtkTreeGetTitleAttrib(Ihandle* ih, const char* name_id) +{ + GtkTreeModel* model = gtk_tree_view_get_model(GTK_TREE_VIEW(ih->handle)); + GtkTreeIter iterItem; + if (!gtkTreeFindNodeFromString(ih, model, name_id, &iterItem)) + return NULL; + return gtkTreeGetTitle(model, iterItem); +} + +static int gtkTreeSetTitleAttrib(Ihandle* ih, const char* name_id, const char* value) +{ + GtkTreeStore* store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(ih->handle))); + GtkTreeIter iterItem; + if (!gtkTreeFindNodeFromString(ih, GTK_TREE_MODEL(store), name_id, &iterItem)) + return 0; + gtk_tree_store_set(store, &iterItem, IUPGTK_TREE_TITLE, iupgtkStrConvertToUTF8(value), -1); + return 0; +} + +static int gtkTreeSetTitleFontAttrib(Ihandle* ih, const char* name_id, const char* value) +{ + PangoFontDescription* fontdesc = NULL; + GtkTreeStore* store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(ih->handle))); + GtkTreeIter iterItem; + if (!gtkTreeFindNodeFromString(ih, GTK_TREE_MODEL(store), name_id, &iterItem)) + return 0; + if (value) + fontdesc = iupgtkGetPangoFontDesc(value); + gtk_tree_store_set(store, &iterItem, IUPGTK_TREE_FONT, fontdesc, -1); + return 0; +} + +static char* gtkTreeGetTitleFontAttrib(Ihandle* ih, const char* name_id) +{ + PangoFontDescription* fontdesc; + GtkTreeModel* model = gtk_tree_view_get_model(GTK_TREE_VIEW(ih->handle)); + GtkTreeIter iterItem; + if (!gtkTreeFindNodeFromString(ih, model, name_id, &iterItem)) + return NULL; + gtk_tree_model_get(model, &iterItem, IUPGTK_TREE_FONT, &fontdesc, -1); + return pango_font_description_to_string(fontdesc); +} + +static char* gtkTreeGetFindUserDataAttrib(Ihandle* ih, const char* name_id) +{ + GtkTreeModel* model = gtk_tree_view_get_model(GTK_TREE_VIEW(ih->handle)); + int id; + char* str = (char*)(name_id+1); /* skip ':' */ + void* userdata = NULL; + if (sscanf(str, "%p", &userdata)!=1) + return NULL; + id = gtkTreeGetUserDataId(ih, model, userdata); + if (id == -1) + return NULL; + str = iupStrGetMemory(16); + sprintf(str, "%d", id); + return str; +} + +static char* gtkTreeGetUserDataAttrib(Ihandle* ih, const char* name_id) +{ + GtkTreeModel* model = gtk_tree_view_get_model(GTK_TREE_VIEW(ih->handle)); + GtkTreeIter iterItem; + char* userdata; + if (!gtkTreeFindNodeFromString(ih, model, name_id, &iterItem)) + return NULL; + gtk_tree_model_get(model, &iterItem, IUPGTK_TREE_USERDATA, &userdata, -1); + return userdata; +} + +static int gtkTreeSetUserDataAttrib(Ihandle* ih, const char* name_id, const char* value) +{ + GtkTreeStore* store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(ih->handle))); + GtkTreeIter iterItem; + if (!gtkTreeFindNodeFromString(ih, GTK_TREE_MODEL(store), name_id, &iterItem)) + return 0; + gtk_tree_store_set(store, &iterItem, IUPGTK_TREE_USERDATA, value, -1); + return 0; +} + +static char* gtkTreeGetValueAttrib(Ihandle* ih) +{ + GtkTreeModel* model = gtk_tree_view_get_model(GTK_TREE_VIEW(ih->handle)); + GtkTreePath* path = NULL; + char* str; + + gtk_tree_view_get_cursor(GTK_TREE_VIEW(ih->handle), &path, NULL); + if (path) + { + GtkTreeIter iterItem; + gtk_tree_model_get_iter(model, &iterItem, path); + gtk_tree_path_free(path); + + str = iupStrGetMemory(16); + sprintf(str, "%d", gtkTreeGetNodeId(ih, iterItem)); + return str; + } + + return "0"; /* default VALUE is root */ +} + +static int gtkTreeSetMarkAttrib(Ihandle* ih, const char* value) +{ + GtkTreeModel* model = gtk_tree_view_get_model(GTK_TREE_VIEW(ih->handle)); + GtkTreeSelection* selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(ih->handle)); + GtkTreeIter iterRoot; + + if (ih->data->mark_mode==ITREE_MARK_SINGLE) + return 0; + + gtk_tree_model_get_iter_first(model, &iterRoot); + + if(iupStrEqualNoCase(value, "BLOCK")) + { + GtkTreePath* pathFocus; + gtk_tree_view_get_cursor(GTK_TREE_VIEW(ih->handle), &pathFocus, NULL); + gtk_tree_selection_select_range(selection, (GtkTreePath*)iupAttribGet(ih, "_IUPTREE_MARKSTART_NODE"), pathFocus); + gtk_tree_path_free(pathFocus); + } + else if(iupStrEqualNoCase(value, "CLEARALL")) + gtk_tree_selection_unselect_all(selection); + else if(iupStrEqualNoCase(value, "MARKALL")) + gtk_tree_selection_select_all(selection); + else if(iupStrEqualNoCase(value, "INVERTALL")) /* INVERTALL *MUST* appear before INVERT, or else INVERTALL will never be called. */ + gtkTreeInvertAllNodeMarking(ih, model, selection, &iterRoot); + else if(iupStrEqualPartial(value, "INVERT")) + { + /* iupStrEqualPartial allows the use of "INVERTid" form */ + GtkTreeIter iterItem; + if (!gtkTreeFindNodeFromString(ih, model, &value[strlen("INVERT")], &iterItem)) + return 0; + + if(gtk_tree_selection_iter_is_selected(selection, &iterItem)) + gtk_tree_selection_unselect_iter(selection, &iterItem); + else + gtk_tree_selection_select_iter(selection, &iterItem); + } + else + { + GtkTreePath *path1, *path2; + GtkTreeIter iterItem1, iterItem2; + char str1[50], str2[50]; + if (iupStrToStrStr(value, str1, str2, '-')!=2) + return 0; + + if (!gtkTreeFindNodeFromString(ih, model, str1, &iterItem1)) + return 0; + if (!gtkTreeFindNodeFromString(ih, model, str2, &iterItem2)) + return 0; + + path1 = gtk_tree_model_get_path(model, &iterItem1); + path2 = gtk_tree_model_get_path(model, &iterItem2); + gtk_tree_selection_select_range(selection, path1, path2); + gtk_tree_path_free(path1); + gtk_tree_path_free(path2); + } + + return 1; +} + +static int gtkTreeSetValueAttrib(Ihandle* ih, const char* value) +{ + GtkTreeModel* model = gtk_tree_view_get_model(GTK_TREE_VIEW(ih->handle)); + GtkTreeSelection* selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(ih->handle)); + GtkTreeIter iterRoot, iterItem; + GtkTreePath* path; + + if (gtkTreeSetMarkAttrib(ih, value)) + return 0; + + gtk_tree_model_get_iter_first(model, &iterRoot); + + if (iupStrEqualNoCase(value, "ROOT")) + iterItem = iterRoot; + else if(iupStrEqualNoCase(value, "LAST")) + iterItem = gtkTreeGetLastVisibleNode(ih, model, iterRoot); + else if(iupStrEqualNoCase(value, "PGUP")) + { + GtkTreeIter iterPrev; + + GtkTreePath* pathFocus; + gtk_tree_view_get_cursor(GTK_TREE_VIEW(ih->handle), &pathFocus, NULL); + gtk_tree_model_get_iter(model, &iterPrev, pathFocus); + gtk_tree_path_free(pathFocus); + + ih->data->id_control = -1; + gtkTreeFindVisibleNodeId(ih, model, iterRoot, iterPrev); + ih->data->id_control -= 10; /* less 10 visible nodes */ + + if(ih->data->id_control < 0) + ih->data->id_control = 0; /* Begin of tree = Root id */ + + iterItem = gtkTreeFindVisibleNodeFromId(ih, model, iterRoot); + } + else if(iupStrEqualNoCase(value, "PGDN")) + { + GtkTreeIter iterNext; + + GtkTreePath* pathFocus; + gtk_tree_view_get_cursor(GTK_TREE_VIEW(ih->handle), &pathFocus, NULL); + gtk_tree_model_get_iter(model, &iterNext, pathFocus); + gtk_tree_path_free(pathFocus); + + ih->data->id_control = -1; + gtkTreeFindVisibleNodeId(ih, model, iterRoot, iterNext); + ih->data->id_control += 10; /* more 10 visible nodes */ + + iterNext = gtkTreeFindVisibleNodeFromId(ih, model, iterRoot); + + if (ih->data->id_control >= 0) + iterNext = gtkTreeGetLastVisibleNode(ih, model, iterRoot); + + iterItem = iterNext; + } + else if(iupStrEqualNoCase(value, "NEXT")) + { + GtkTreeIter iterNext; + + GtkTreePath* pathFocus; + gtk_tree_view_get_cursor(GTK_TREE_VIEW(ih->handle), &pathFocus, NULL); + gtk_tree_model_get_iter(model, &iterNext, pathFocus); + gtk_tree_path_free(pathFocus); + + ih->data->id_control = -1; + gtkTreeFindVisibleNodeId(ih, model, iterRoot, iterNext); + ih->data->id_control++; /* more 1 visible node */ + + iterNext = gtkTreeFindVisibleNodeFromId(ih, model, iterRoot); + + if (ih->data->id_control >= 0) + iterNext = gtkTreeGetLastVisibleNode(ih, model, iterRoot); + + iterItem = iterNext; + } + else if(iupStrEqualNoCase(value, "PREVIOUS")) + { + GtkTreeIter iterPrev; + + GtkTreePath* pathFocus; + gtk_tree_view_get_cursor(GTK_TREE_VIEW(ih->handle), &pathFocus, NULL); + gtk_tree_model_get_iter(model, &iterPrev, pathFocus); + gtk_tree_path_free(pathFocus); + + ih->data->id_control = -1; + gtkTreeFindVisibleNodeId(ih, model, iterRoot, iterPrev); + ih->data->id_control--; /* less 1 visible node */ + + if (ih->data->id_control < 0) + ih->data->id_control = 0; + + iterItem = gtkTreeFindVisibleNodeFromId(ih, model, iterRoot); + if (!iterItem.user_data) + return 0; + } + else + { + if (!gtkTreeFindNodeFromString(ih, model, value, &iterItem)) + return 0; + } + + /* select */ + if (ih->data->mark_mode==ITREE_MARK_SINGLE) + { + iupAttribSetStr(ih, "_IUP_IGNORE_SELECTION", "1"); + gtk_tree_selection_select_iter(selection, &iterItem); + } + + path = gtk_tree_model_get_path(model, &iterItem); + gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(ih->handle), path, NULL, FALSE, 0, 0); /* scroll to visible */ + gtk_tree_view_set_cursor(GTK_TREE_VIEW(ih->handle), path, NULL, FALSE); /* set focus */ + gtk_tree_path_free(path); + + iupAttribSetInt(ih, "_IUPTREE_OLDVALUE", gtkTreeGetNodeId(ih, iterItem)); + + return 0; +} + +static int gtkTreeSetMarkStartAttrib(Ihandle* ih, const char* name_id) +{ + GtkTreePath *pathMarkStart, *pathMarkStartPrev; + GtkTreeModel* model = gtk_tree_view_get_model(GTK_TREE_VIEW(ih->handle)); + GtkTreeIter iterItem; + if (!gtkTreeFindNodeFromString(ih, model, name_id, &iterItem)) + return 0; + + pathMarkStart = gtk_tree_model_get_path(model, &iterItem); + + pathMarkStartPrev = (GtkTreePath*)iupAttribGet(ih, "_IUPTREE_MARKSTART_NODE"); + if (pathMarkStartPrev) + gtk_tree_path_free(pathMarkStartPrev); + + iupAttribSetStr(ih, "_IUPTREE_MARKSTART_NODE", (char*)pathMarkStart); + + return 1; +} + +static char* gtkTreeGetMarkedAttrib(Ihandle* ih, const char* name_id) +{ + GtkTreeSelection* selection; + GtkTreeModel* model = gtk_tree_view_get_model(GTK_TREE_VIEW(ih->handle)); + GtkTreeIter iterItem; + if (!gtkTreeFindNodeFromString(ih, model, name_id, &iterItem)) + return 0; + + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(ih->handle)); + if (gtk_tree_selection_iter_is_selected(selection, &iterItem)) + return "YES"; + else + return "NO"; +} + +static int gtkTreeSetMarkedAttrib(Ihandle* ih, const char* name_id, const char* value) +{ + GtkTreeSelection* selection; + GtkTreeModel* model = gtk_tree_view_get_model(GTK_TREE_VIEW(ih->handle)); + GtkTreeIter iterItem; + if (!gtkTreeFindNodeFromString(ih, model, name_id, &iterItem)) + return 0; + + iupAttribSetStr(ih, "_IUP_IGNORE_SELECTION", "1"); + + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(ih->handle)); + if (iupStrBoolean(value)) + gtk_tree_selection_select_iter(selection, &iterItem); + else + gtk_tree_selection_unselect_iter(selection, &iterItem); + + return 0; +} + +static int gtkTreeSetDelNodeAttrib(Ihandle* ih, const char* name_id, const char* value) +{ + if (!ih->handle) /* do not store the action before map */ + return 0; + if (iupStrEqualNoCase(value, "SELECTED")) /* selected here means the specified one */ + { + GtkTreeModel* model = gtk_tree_view_get_model(GTK_TREE_VIEW(ih->handle)); + GtkTreeIter iterItem; + GtkTreeIter iterParent; + + if (!gtkTreeFindNodeFromString(ih, model, name_id, &iterItem)) + return 0; + + if (!gtk_tree_model_iter_parent(model, &iterParent, &iterItem)) /* the root node can't be deleted */ + return 0; + + gtkTreeCallNodeRemoved(ih, model, &iterItem); + + /* deleting the specified node (and it's children) */ + gtk_tree_store_remove(GTK_TREE_STORE(model), &iterItem); + } + else if(iupStrEqualNoCase(value, "CHILDREN")) /* children of the specified one */ + { + GtkTreeModel* model = gtk_tree_view_get_model(GTK_TREE_VIEW(ih->handle)); + GtkTreeIter iterItem, iterChild; + int hasChildren; + + if (!gtkTreeFindNodeFromString(ih, model, name_id, &iterItem)) + return 0; + + hasChildren = gtk_tree_model_iter_children(model, &iterChild, &iterItem); + + /* deleting the selected node's children */ + while(hasChildren) + { + gtkTreeCallNodeRemoved(ih, model, &iterChild); + hasChildren = gtk_tree_store_remove(GTK_TREE_STORE(model), &iterChild); + } + } + else if(iupStrEqualNoCase(value, "MARKED")) /* Delete the array of marked nodes */ + { + GtkTreeModel* model = gtk_tree_view_get_model(GTK_TREE_VIEW(ih->handle)); + GtkTreeSelection* selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(ih->handle)); + GList *rr_list = NULL; + GList *node; + + gtk_tree_selection_selected_foreach(selection, (GtkTreeSelectionForeachFunc)gtkTreeSelected_Iter_Func, &rr_list); + + for(node = rr_list; node != NULL; node = node->next) + { + GtkTreePath* path = gtk_tree_row_reference_get_path(node->data); + if (path) + { + GtkTreeIter iterItem; + if (gtk_tree_model_get_iter(model, &iterItem, path)) + { + gtkTreeCallNodeRemoved(ih, model, &iterItem); + gtk_tree_store_remove(GTK_TREE_STORE(model), &iterItem); + } + gtk_tree_path_free(path); + } + gtk_tree_row_reference_free(node->data); + } + g_list_free(rr_list); + } + + return 0; +} + +static int gtkTreeSetRenameAttrib(Ihandle* ih, const char* value) +{ + if (ih->data->show_rename) + { + GtkTreePath* path; + IFni cbShowRename = (IFni)IupGetCallback(ih, "SHOWRENAME_CB"); + GtkTreeViewColumn *focus_column; + + gtk_tree_view_get_cursor(GTK_TREE_VIEW(ih->handle), &path, &focus_column); + + if (cbShowRename) + { + GtkTreeIter iterItem; + GtkTreeModel* model = gtk_tree_view_get_model(GTK_TREE_VIEW(ih->handle)); + gtk_tree_model_get_iter(model, &iterItem, path); + cbShowRename(ih, gtkTreeGetNodeId(ih, iterItem)); + } + + gtk_tree_view_set_cursor(GTK_TREE_VIEW(ih->handle), path, focus_column, TRUE); + gtk_tree_path_free(path); + } + else + { + IFnis cbRenameNode = (IFnis)IupGetCallback(ih, "RENAMENODE_CB"); + if (cbRenameNode) + { + GtkTreePath* path; + GtkTreeIter iterItem; + GtkTreeModel* model = gtk_tree_view_get_model(GTK_TREE_VIEW(ih->handle)); + gtk_tree_view_get_cursor(GTK_TREE_VIEW(ih->handle), &path, NULL); + gtk_tree_model_get_iter(model, &iterItem, path); + gtk_tree_path_free(path); + cbRenameNode(ih, gtkTreeGetNodeId(ih, iterItem), gtkTreeGetTitle(model, iterItem)); + } + } + + (void)value; + return 0; +} + +static int gtkTreeSetImageExpandedAttrib(Ihandle* ih, const char* name_id, const char* value) +{ + int kind; + GtkTreeStore* store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(ih->handle))); + GdkPixbuf* pixExpand = iupImageGetImage(value, ih, 0); + GtkTreeIter iterItem; + if (!gtkTreeFindNodeFromString(ih, GTK_TREE_MODEL(store), name_id, &iterItem)) + return 0; + + gtk_tree_model_get(GTK_TREE_MODEL(store), &iterItem, IUPGTK_TREE_KIND, &kind, -1); + + if (kind == ITREE_BRANCH) + { + if (pixExpand) + gtk_tree_store_set(store, &iterItem, IUPGTK_TREE_IMAGE_EXPANDED, pixExpand, + IUPGTK_TREE_HAS_IMAGE_EXPANDED, TRUE, -1); + else + gtk_tree_store_set(store, &iterItem, IUPGTK_TREE_IMAGE_EXPANDED, ih->data->def_image_expanded, + IUPGTK_TREE_HAS_IMAGE_EXPANDED, FALSE, -1); + } + + return 1; +} + +static int gtkTreeSetImageAttrib(Ihandle* ih, const char* name_id, const char* value) +{ + GtkTreeStore* store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(ih->handle))); + GdkPixbuf* pixImage = iupImageGetImage(value, ih, 0); + GtkTreeIter iterItem; + if (!gtkTreeFindNodeFromString(ih, GTK_TREE_MODEL(store), name_id, &iterItem)) + return 0; + + if (pixImage) + { + gtk_tree_store_set(store, &iterItem, IUPGTK_TREE_IMAGE, pixImage, + IUPGTK_TREE_HAS_IMAGE, TRUE, -1); + } + else + { + int kind; + gtk_tree_model_get(GTK_TREE_MODEL(store), &iterItem, IUPGTK_TREE_KIND, &kind, -1); + if (kind == ITREE_BRANCH) + gtk_tree_store_set(store, &iterItem, IUPGTK_TREE_IMAGE, ih->data->def_image_collapsed, + IUPGTK_TREE_HAS_IMAGE, FALSE, -1); + else + gtk_tree_store_set(store, &iterItem, IUPGTK_TREE_IMAGE, ih->data->def_image_leaf, + IUPGTK_TREE_HAS_IMAGE, FALSE, -1); + } + + return 1; +} + +static int gtkTreeSetImageBranchExpandedAttrib(Ihandle* ih, const char* value) +{ + GtkTreeIter iterRoot; + GtkTreeModel* model = gtk_tree_view_get_model(GTK_TREE_VIEW(ih->handle)); + ih->data->def_image_expanded = iupImageGetImage(value, ih, 0); + + gtk_tree_model_get_iter_first(model, &iterRoot); + + /* Update all images, starting at root node */ + gtkTreeUpdateImages(ih, model, iterRoot, ITREE_UPDATEIMAGE_EXPANDED); + + return 1; +} + +static int gtkTreeSetImageBranchCollapsedAttrib(Ihandle* ih, const char* value) +{ + GtkTreeIter iterRoot; + GtkTreeModel* model = gtk_tree_view_get_model(GTK_TREE_VIEW(ih->handle)); + ih->data->def_image_collapsed = iupImageGetImage(value, ih, 0); + + gtk_tree_model_get_iter_first(model, &iterRoot); + + /* Update all images, starting at root node */ + gtkTreeUpdateImages(ih, model, iterRoot, ITREE_UPDATEIMAGE_COLLAPSED); + + return 1; +} + +static int gtkTreeSetImageLeafAttrib(Ihandle* ih, const char* value) +{ + GtkTreeIter iterRoot; + GtkTreeModel* model = gtk_tree_view_get_model(GTK_TREE_VIEW(ih->handle)); + ih->data->def_image_leaf = iupImageGetImage(value, ih, 0); + + gtk_tree_model_get_iter_first(model, &iterRoot); + + /* Update all images, starting at root node */ + gtkTreeUpdateImages(ih, model, iterRoot, ITREE_UPDATEIMAGE_LEAF); + + return 1; +} + +static int gtkTreeSetBgColorAttrib(Ihandle* ih, const char* value) +{ + unsigned char r, g, b; + + GtkScrolledWindow* scrolled_window = (GtkScrolledWindow*)iupAttribGet(ih, "_IUP_EXTRAPARENT"); + if (scrolled_window) + { + /* ignore given value, must use only from parent for the scrollbars */ + char* parent_value = iupBaseNativeParentGetBgColor(ih); + + if (iupStrToRGB(parent_value, &r, &g, &b)) + { + GtkWidget* sb; + + if (!GTK_IS_SCROLLED_WINDOW(scrolled_window)) + scrolled_window = (GtkScrolledWindow*)iupAttribGet(ih, "_IUPGTK_SCROLLED_WINDOW"); + + iupgtkBaseSetBgColor((GtkWidget*)scrolled_window, r, g, b); + +#if GTK_CHECK_VERSION(2, 8, 0) + sb = gtk_scrolled_window_get_hscrollbar(scrolled_window); + if (sb) iupgtkBaseSetBgColor(sb, r, g, b); + + sb = gtk_scrolled_window_get_vscrollbar(scrolled_window); + if (sb) iupgtkBaseSetBgColor(sb, r, g, b); +#endif + } + } + + if (!iupStrToRGB(value, &r, &g, &b)) + return 0; + + { + GtkCellRenderer* renderer_txt = (GtkCellRenderer*)iupAttribGet(ih, "_IUPGTK_RENDERER_TEXT"); + GtkCellRenderer* renderer_img = (GtkCellRenderer*)iupAttribGet(ih, "_IUPGTK_RENDERER_IMG"); + GdkColor color; + iupgdkColorSet(&color, r, g, b); + g_object_set(G_OBJECT(renderer_txt), "cell-background-gdk", &color, NULL); + g_object_set(G_OBJECT(renderer_img), "cell-background-gdk", &color, NULL); + } + + iupdrvBaseSetBgColorAttrib(ih, value); /* use given value for contents */ + + /* no need to update internal image cache in GTK */ + + return 1; +} + +static int gtkTreeSetFgColorAttrib(Ihandle* ih, const char* value) +{ + unsigned char r, g, b; + if (!iupStrToRGB(value, &r, &g, &b)) + return 0; + + iupgtkBaseSetFgColor(ih->handle, r, g, b); + + { + GtkCellRenderer* renderer_txt = (GtkCellRenderer*)iupAttribGet(ih, "_IUPGTK_RENDERER_TEXT"); + GdkColor color; + iupgdkColorSet(&color, r, g, b); + g_object_set(G_OBJECT(renderer_txt), "foreground-gdk", &color, NULL); + g_object_get(G_OBJECT(renderer_txt), "foreground-gdk", &color, NULL); + color.blue = 0; + } + + return 1; +} + +void iupdrvTreeUpdateMarkMode(Ihandle *ih) +{ + GtkTreeSelection* selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(ih->handle)); + gtk_tree_selection_set_mode(selection, (ih->data->mark_mode==ITREE_MARK_SINGLE)? GTK_SELECTION_SINGLE: GTK_SELECTION_MULTIPLE); + + if (ih->data->mark_mode==ITREE_MARK_MULTIPLE && !ih->data->show_dragdrop) + { +#if GTK_CHECK_VERSION(2, 10, 0) + gtk_tree_view_set_rubber_banding(GTK_TREE_VIEW(ih->handle), TRUE); +#endif + } +} + + +/***********************************************************************************************/ + + +static void gtkTreeSetRenameCaretPos(GtkCellEditable *editable, const char* value) +{ + int pos = 1; + + if (iupStrToInt(value, &pos)) + { + if (pos < 1) pos = 1; + pos--; /* IUP starts at 1 */ + + gtk_editable_set_position(GTK_EDITABLE(editable), pos); + } +} + +static void gtkTreeSetRenameSelectionPos(GtkCellEditable *editable, const char* value) +{ + int start = 1, end = 1; + + if (iupStrToIntInt(value, &start, &end, ':') != 2) + return; + + if(start < 1 || end < 1) + return; + + start--; /* IUP starts at 1 */ + end--; + + gtk_editable_select_region(GTK_EDITABLE(editable), start, end); +} + +/*****************************************************************************/ +/* SIGNALS */ +/*****************************************************************************/ + +static void gtkTreeCellTextEditingStarted(GtkCellRenderer *cell, GtkCellEditable *editable, const gchar *path_string, Ihandle *ih) +{ + char* value; + GtkTreeIter iterItem; + PangoFontDescription* fontdesc = NULL; + GdkColor *color = NULL; + GtkTreeModel* model = gtk_tree_view_get_model(GTK_TREE_VIEW(ih->handle)); + + value = iupAttribGetStr(ih, "RENAMECARET"); + if (value) + gtkTreeSetRenameCaretPos(editable, value); + + value = iupAttribGetStr(ih, "RENAMESELECTION"); + if (value) + gtkTreeSetRenameSelectionPos(editable, value); + + gtk_tree_model_get_iter_from_string(model, &iterItem, path_string); + gtk_tree_model_get(model, &iterItem, IUPGTK_TREE_FONT, &fontdesc, -1); + if (fontdesc) + gtk_widget_modify_font(GTK_WIDGET(editable), fontdesc); + + gtk_tree_model_get(model, &iterItem, IUPGTK_TREE_COLOR, &color, -1); + if (color) + iupgtkBaseSetFgGdkColor(GTK_WIDGET(editable), color); + + (void)cell; +} + +static void gtkTreeCellTextEdited(GtkCellRendererText *cell, gchar *path_string, gchar *new_text, Ihandle* ih) +{ + GtkTreeModel* model; + GtkTreeIter iterItem; + IFnis cbRename; + + if (!new_text) + return; + + model = gtk_tree_view_get_model(GTK_TREE_VIEW(ih->handle)); + if (!gtk_tree_model_get_iter_from_string(model, &iterItem, path_string)) + return; + + cbRename = (IFnis)IupGetCallback(ih, "RENAME_CB"); + if (cbRename) + { + if (cbRename(ih, gtkTreeGetNodeId(ih, iterItem), iupgtkStrConvertFromUTF8(new_text)) == IUP_IGNORE) + return; + } + + /* It is the responsibility of the application to update the model and store new_text at the position indicated by path. */ + gtk_tree_store_set(GTK_TREE_STORE(model), &iterItem, IUPGTK_TREE_TITLE, new_text, -1); + + (void)cell; +} + +static int gtkTreeCallDragDropCb(Ihandle* ih, GtkTreeIter *iterDrag, GtkTreeIter *iterDrop, int *is_ctrl) +{ + IFniiii cbDragDrop = (IFniiii)IupGetCallback(ih, "DRAGDROP_CB"); + int is_shift = 0; + char key[5]; + iupdrvGetKeyState(key); + if (key[0] == 'S') + is_shift = 1; + if (key[1] == 'C') + *is_ctrl = 1; + else + *is_ctrl = 0; + + if (cbDragDrop) + { + int drag_id = gtkTreeGetNodeId(ih, *iterDrag); + int drop_id = gtkTreeGetNodeId(ih, *iterDrop); + return cbDragDrop(ih, drag_id, drop_id, is_shift, *is_ctrl); + } + + return IUP_CONTINUE; /* allow to move by default if callback not defined */ +} + +static void gtkTreeDragDataReceived(GtkWidget *widget, GdkDragContext *context, gint x, gint y, + GtkSelectionData *selection_data, guint info, guint time, Ihandle* ih) +{ + GtkTreePath* pathDrag = (GtkTreePath*)iupAttribGet(ih, "_IUPTREE_DRAGITEM"); + GtkTreePath* pathDrop = (GtkTreePath*)iupAttribGet(ih, "_IUPTREE_DROPITEM"); + int accepted = FALSE; + int is_ctrl; + + if (pathDrag && pathDrop) + { + GtkTreeIter iterDrag, iterDrop, iterParent, iterNextParent; + GtkTreeModel* model = gtk_tree_view_get_model(GTK_TREE_VIEW(ih->handle)); + + gtk_tree_model_get_iter(model, &iterDrag, pathDrag); + gtk_tree_model_get_iter(model, &iterDrop, pathDrop); + + iterParent = iterDrop; + while(gtk_tree_model_iter_parent(model, &iterNextParent, &iterParent)) + { + if (iterNextParent.user_data == iterDrag.user_data) + goto gtkTreeDragDataReceived_FINISH; + iterParent = iterNextParent; + } + + accepted = TRUE; + + if (gtkTreeCallDragDropCb(ih, &iterDrag, &iterDrop, &is_ctrl) == IUP_CONTINUE) + { + GtkTreeIter iterNewItem; + + /* Copy the dragged item to the new position. */ + gtkTreeCopyNode(ih, model, &iterDrag, &iterDrop, &iterNewItem, is_ctrl); + + if (!is_ctrl) + { + /* do not delete the user data, we copy the references in CopyNode */ + gtk_tree_store_remove(GTK_TREE_STORE(model), &iterDrag); + } + + /* set focus and selection */ + { + GtkTreePath *pathNew; + GtkTreeSelection* selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(ih->handle)); + + pathNew = gtk_tree_model_get_path(model, &iterNewItem); + gtk_tree_selection_select_path(selection, pathNew); + + gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(ih->handle), pathNew, NULL, FALSE, 0, 0); + gtk_tree_view_set_cursor(GTK_TREE_VIEW(ih->handle), pathNew, NULL, FALSE); + + gtk_tree_path_free(pathNew); + } + } + } + +gtkTreeDragDataReceived_FINISH: + if (pathDrag) gtk_tree_path_free(pathDrag); + if (pathDrop) gtk_tree_path_free(pathDrop); + + iupAttribSetStr(ih, "_IUPTREE_DRAGITEM", NULL); + iupAttribSetStr(ih, "_IUPTREE_DROPITEM", NULL); + + gtk_drag_finish(context, accepted, (context->action == GDK_ACTION_MOVE), time); + + (void)widget; + (void)info; + (void)x; + (void)y; + (void)selection_data; +} + +static gboolean gtkTreeDragDrop(GtkWidget *widget, GdkDragContext *context, gint x, gint y, guint time, Ihandle* ih) +{ + GtkTreePath* path; + GtkTreeViewDropPosition pos; + GdkAtom target = GDK_NONE; + + /* unset any highlight row */ + gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW(widget), NULL, GTK_TREE_VIEW_DROP_BEFORE); + + if (gtk_tree_view_get_dest_row_at_pos(GTK_TREE_VIEW(ih->handle), x, y, &path, &pos)) + { + if ((pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE || pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER) && + (gtk_tree_path_compare(path, (GtkTreePath*)iupAttribGet(ih, "_IUPTREE_DRAGITEM")) != 0)) + { + target = gtk_drag_dest_find_target(widget, context, gtk_drag_dest_get_target_list(widget)); + if (target != GDK_NONE) + { + iupAttribSetStr(ih, "_IUPTREE_DROPITEM", (char*)path); + gtk_drag_get_data(widget, context, target, time); + return TRUE; + } + } + } + + (void)widget; + return FALSE; +} + +static void gtkTreeDragLeave(GtkWidget *widget, GdkDragContext *context, guint time) +{ + /* unset any highlight row */ + gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW(widget), NULL, GTK_TREE_VIEW_DROP_BEFORE); + (void)context; + (void)time; +} + +static gboolean gtkTreeDragMotion(GtkWidget *widget, GdkDragContext *context, gint x, gint y, guint time, Ihandle* ih) +{ + GtkTreePath* path; + GtkTreeViewDropPosition pos; + GtkTreePath* pathDrag = (GtkTreePath*)iupAttribGet(ih, "_IUPTREE_DRAGITEM"); + if (pathDrag && gtk_tree_view_get_dest_row_at_pos(GTK_TREE_VIEW(ih->handle), x, y, &path, &pos)) + { + if ((pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE || pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER) && + (gtk_tree_path_compare(path, pathDrag) != 0)) + { + gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW(widget), path, pos); + gdk_drag_status(context, context->actions, time); + return TRUE; + } + + gtk_tree_path_free(path); + } + + (void)widget; + return FALSE; +} + +static void gtkTreeDragBegin(GtkWidget *widget, GdkDragContext *context, Ihandle* ih) +{ + int x = iupAttribGetInt(ih, "_IUPTREE_DRAG_X"); + int y = iupAttribGetInt(ih, "_IUPTREE_DRAG_Y"); + GtkTreePath* path; + GtkTreeViewDropPosition pos; + if (gtk_tree_view_get_dest_row_at_pos(GTK_TREE_VIEW(ih->handle), x, y, &path, &pos)) + { + if ((pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE || pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER)) + { + GdkPixmap* pixmap; + iupAttribSetStr(ih, "_IUPTREE_DRAGITEM", (char*)path); + + pixmap = gtk_tree_view_create_row_drag_icon(GTK_TREE_VIEW(ih->handle), path); + gtk_drag_source_set_icon(widget, gtk_widget_get_colormap(widget), pixmap, NULL); + g_object_unref(pixmap); + return; + } + } + + (void)context; + (void)widget; +} + +static void gtkTreeSelectionChanged(GtkTreeSelection* selection, Ihandle* ih) +{ + IFnii cbSelec; + int is_ctrl = 0; + + if (ih->data->mark_mode == ITREE_MARK_MULTIPLE) + { + char key[5]; + iupdrvGetKeyState(key); + if (key[0] == 'S') + return; + else if (key[1] == 'C') + is_ctrl = 1; + + if (iupAttribGetInt(ih, "_IUPTREE_EXTENDSELECT")==2 && !is_ctrl) + { + iupAttribSetStr(ih, "_IUPTREE_EXTENDSELECT", NULL); + gtkTreeCallMultiSelectionCb(ih); + return; + } + } + + cbSelec = (IFnii)IupGetCallback(ih, "SELECTION_CB"); + if (cbSelec) + { + int curpos = -1, is_selected = 0; + + if (iupAttribGet(ih, "_IUP_IGNORE_SELECTION")) + { + iupAttribSetStr(ih, "_IUP_IGNORE_SELECTION", NULL); + return; + } + + { + GtkTreeIter iterFocus; + GtkTreePath* pathFocus; + GtkTreeModel* model = gtk_tree_view_get_model(GTK_TREE_VIEW(ih->handle)); + gtk_tree_view_get_cursor(GTK_TREE_VIEW(ih->handle), &pathFocus, NULL); + if (pathFocus) + { + gtk_tree_model_get_iter(model, &iterFocus, pathFocus); + gtk_tree_path_free(pathFocus); + curpos = gtkTreeGetNodeId(ih, iterFocus); + is_selected = gtk_tree_selection_iter_is_selected(selection, &iterFocus); + } + } + + if (curpos == -1) + return; + + if (is_ctrl) + cbSelec(ih, curpos, is_selected); + else + { + int oldpos = iupAttribGetInt(ih, "_IUPTREE_OLDVALUE"); + if(oldpos != curpos) + { + cbSelec(ih, oldpos, 0); /* unselected */ + cbSelec(ih, curpos, 1); /* selected */ + + iupAttribSetInt(ih, "_IUPTREE_OLDVALUE", curpos); + } + } + } +} + +static gboolean gtkTreeTestExpandRow(GtkTreeView* tree_view, GtkTreeIter *iterItem, GtkTreePath *path, Ihandle* ih) +{ + IFni cbBranchOpen = (IFni)IupGetCallback(ih, "BRANCHOPEN_CB"); + if (cbBranchOpen) + { + if (iupAttribGet(ih, "_IUPTREE_IGNORE_BRANCHOPEN_CB")) + { + iupAttribSetStr(ih, "_IUPTREE_IGNORE_BRANCHOPEN_CB", NULL); + return FALSE; + } + + if (cbBranchOpen(ih, gtkTreeGetNodeId(ih, *iterItem)) == IUP_IGNORE) + return TRUE; /* prevent the change */ + } + + (void)path; + (void)tree_view; + return FALSE; +} + +static gboolean gtkTreeTestCollapseRow(GtkTreeView* tree_view, GtkTreeIter *iterItem, GtkTreePath *path, Ihandle* ih) +{ + IFni cbBranchClose = (IFni)IupGetCallback(ih, "BRANCHCLOSE_CB"); + if (cbBranchClose) + { + if (cbBranchClose(ih, gtkTreeGetNodeId(ih, *iterItem)) == IUP_IGNORE) + return TRUE; + } + + (void)path; + (void)tree_view; + return FALSE; +} + +static void gtkTreeRowActived(GtkTreeView* tree_view, GtkTreePath *path, GtkTreeViewColumn *column, Ihandle* ih) +{ + GtkTreeIter iterItem; + GtkTreeModel* model; + int kind; /* used for nodes defined as branches, but do not have children */ + IFni cbExecuteLeaf = (IFni)IupGetCallback(ih, "EXECUTELEAF_CB"); + if (!cbExecuteLeaf) + return; + + model = gtk_tree_view_get_model(GTK_TREE_VIEW(ih->handle)); + gtk_tree_model_get_iter(model, &iterItem, path); + gtk_tree_model_get(model, &iterItem, IUPGTK_TREE_KIND, &kind, -1); + + /* just to leaf nodes */ + if(gtk_tree_model_iter_has_child(model, &iterItem) == 0 && kind == ITREE_LEAF) + cbExecuteLeaf(ih, gtkTreeGetNodeId(ih, iterItem)); + + (void)column; + (void)tree_view; +} + +static int gtkTreeConvertXYToPos(Ihandle* ih, int x, int y) +{ + GtkTreePath* path; + if (gtk_tree_view_get_dest_row_at_pos(GTK_TREE_VIEW(ih->handle), x, y, &path, NULL)) + { + GtkTreeIter iterItem; + GtkTreeModel* model = gtk_tree_view_get_model(GTK_TREE_VIEW(ih->handle)); + gtk_tree_model_get_iter(model, &iterItem, path); + gtk_tree_path_free (path); + return gtkTreeGetNodeId(ih, iterItem); + } + return -1; +} + +static gboolean gtkTreeButtonEvent(GtkWidget *treeview, GdkEventButton *evt, Ihandle* ih) +{ + if (iupgtkButtonEvent(treeview, evt, ih) == TRUE) + return TRUE; + + if (evt->type == GDK_BUTTON_PRESS && evt->button == 3) /* right single click */ + { + IFni cbRightClick = (IFni)IupGetCallback(ih, "RIGHTCLICK_CB"); + if (cbRightClick) + { + int id = gtkTreeConvertXYToPos(ih, (int)evt->x, (int)evt->y); + if (id != -1) + cbRightClick(ih, id); + return TRUE; + } + } + else if (evt->type == GDK_2BUTTON_PRESS && evt->button == 1) /* left double click */ + { + GtkTreePath *path; + + if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(treeview), (gint) evt->x, (gint) evt->y, &path, NULL, NULL, NULL)) + { + GtkTreeIter iter; + GtkTreeModel* model = gtk_tree_view_get_model(GTK_TREE_VIEW(ih->handle)); + int kind; /* used for nodes defined as branches, but do not have children */ + + gtk_tree_model_get_iter(model, &iter, path); + gtk_tree_model_get(model, &iter, IUPGTK_TREE_KIND, &kind, -1); + + if (kind == ITREE_BRANCH) + gtkTreeExpandItem(ih, path, -1); /* toggle */ + + gtk_tree_path_free(path); + } + } + else if (evt->type == GDK_BUTTON_RELEASE && evt->button == 1) /* left single release */ + { + if (ih->data->mark_mode==ITREE_MARK_MULTIPLE && (evt->state & GDK_SHIFT_MASK)) + gtkTreeCallMultiSelectionCb(ih); /* Multi Selection Callback */ + + if (ih->data->mark_mode==ITREE_MARK_MULTIPLE && + !(evt->state & GDK_SHIFT_MASK) && !(evt->state & GDK_CONTROL_MASK)) + { + if (iupAttribGet(ih, "_IUPTREE_EXTENDSELECT")) + iupAttribSetStr(ih, "_IUPTREE_EXTENDSELECT", "2"); + } + } + else if (evt->type == GDK_BUTTON_PRESS && evt->button == 1) /* left single press */ + { + iupAttribSetInt(ih, "_IUPTREE_DRAG_X", (int)evt->x); + iupAttribSetInt(ih, "_IUPTREE_DRAG_Y", (int)evt->y); + + if (ih->data->mark_mode==ITREE_MARK_MULTIPLE && + !(evt->state & GDK_SHIFT_MASK) && !(evt->state & GDK_CONTROL_MASK)) + iupAttribSetStr(ih, "_IUPTREE_EXTENDSELECT", "1"); + } + + return FALSE; +} + +static gboolean gtkTreeKeyReleaseEvent(GtkWidget *widget, GdkEventKey *evt, Ihandle *ih) +{ + if (ih->data->mark_mode==ITREE_MARK_MULTIPLE && (evt->state & GDK_SHIFT_MASK)) + { + if (evt->keyval == GDK_Up || evt->keyval == GDK_Down || evt->keyval == GDK_Home || evt->keyval == GDK_End) + gtkTreeCallMultiSelectionCb(ih); /* Multi Selection Callback */ + } + + (void)widget; + return TRUE; +} + +static gboolean gtkTreeKeyPressEvent(GtkWidget *widget, GdkEventKey *evt, Ihandle *ih) +{ + if (iupgtkKeyPressEvent(widget, evt, ih) == TRUE) + return TRUE; + + if (evt->keyval == GDK_F2) + { + gtkTreeSetRenameAttrib(ih, NULL); + return TRUE; + } + else if (evt->keyval == GDK_Return || evt->keyval == GDK_KP_Enter) + { + gtkTreeOpenCloseEvent(ih); + return TRUE; + } + + return FALSE; +} + +static void gtkTreeEnableDragDrop(Ihandle* ih) +{ + const GtkTargetEntry row_targets[] = { + { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, 0 } + }; + + if (iupAttribGetBoolean(ih, "AUTODRAGDROP")) + { + gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW(ih->handle), + GDK_BUTTON1_MASK, + row_targets, + G_N_ELEMENTS(row_targets), + GDK_ACTION_MOVE|GDK_ACTION_COPY); + gtk_tree_view_enable_model_drag_dest (GTK_TREE_VIEW(ih->handle), + row_targets, + G_N_ELEMENTS(row_targets), + GDK_ACTION_MOVE|GDK_ACTION_COPY); + } + else + { + gtk_drag_source_set(ih->handle, GDK_BUTTON1_MASK, row_targets, G_N_ELEMENTS(row_targets), GDK_ACTION_MOVE|GDK_ACTION_COPY); + gtk_drag_dest_set(ih->handle, GDK_BUTTON1_MASK, row_targets, G_N_ELEMENTS(row_targets), GDK_ACTION_MOVE|GDK_ACTION_COPY); + + g_signal_connect(G_OBJECT(ih->handle), "drag-begin", G_CALLBACK(gtkTreeDragBegin), ih); + g_signal_connect(G_OBJECT(ih->handle), "drag-motion", G_CALLBACK(gtkTreeDragMotion), ih); + g_signal_connect(G_OBJECT(ih->handle), "drag-leave", G_CALLBACK(gtkTreeDragLeave), NULL); + g_signal_connect(G_OBJECT(ih->handle), "drag-drop", G_CALLBACK(gtkTreeDragDrop), ih); + g_signal_connect(G_OBJECT(ih->handle), "drag-data-received", G_CALLBACK(gtkTreeDragDataReceived), ih); + } +} + +/*****************************************************************************/ + +static int gtkTreeMapMethod(Ihandle* ih) +{ + GtkScrolledWindow* scrolled_window = NULL; + GtkTreeStore *store; + GtkCellRenderer *renderer_img, *renderer_txt; + GtkTreeSelection* selection; + GtkTreeViewColumn *column; + + store = gtk_tree_store_new(9, GDK_TYPE_PIXBUF, G_TYPE_BOOLEAN, GDK_TYPE_PIXBUF, G_TYPE_BOOLEAN, + G_TYPE_STRING, G_TYPE_INT, GDK_TYPE_COLOR, PANGO_TYPE_FONT_DESCRIPTION, G_TYPE_POINTER); + + ih->handle = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store)); + + g_object_unref(store); + + if (!ih->handle) + return IUP_ERROR; + + scrolled_window = (GtkScrolledWindow*)gtk_scrolled_window_new(NULL, NULL); + iupAttribSetStr(ih, "_IUP_EXTRAPARENT", (char*)scrolled_window); + + /* Column and renderers */ + column = gtk_tree_view_column_new(); + iupAttribSetStr(ih, "_IUPGTK_COLUMN", (char*)column); + + renderer_img = gtk_cell_renderer_pixbuf_new(); + gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(column), renderer_img, FALSE); + gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(column), renderer_img, "pixbuf", IUPGTK_TREE_IMAGE, + "pixbuf-expander-open", IUPGTK_TREE_IMAGE_EXPANDED, + "pixbuf-expander-closed", IUPGTK_TREE_IMAGE, + NULL); + iupAttribSetStr(ih, "_IUPGTK_RENDERER_IMG", (char*)renderer_img); + + renderer_txt = gtk_cell_renderer_text_new(); + gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(column), renderer_txt, TRUE); + gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(column), renderer_txt, "text", IUPGTK_TREE_TITLE, + "is-expander", IUPGTK_TREE_KIND, + "font-desc", IUPGTK_TREE_FONT, + "foreground-gdk", IUPGTK_TREE_COLOR, + NULL); + iupAttribSetStr(ih, "_IUPGTK_RENDERER_TEXT", (char*)renderer_txt); + + if (ih->data->show_rename) + g_object_set(G_OBJECT(renderer_txt), "editable", TRUE, NULL); + + g_object_set(G_OBJECT(renderer_txt), "xpad", 0, NULL); + g_object_set(G_OBJECT(renderer_txt), "ypad", 0, NULL); + gtk_tree_view_append_column(GTK_TREE_VIEW(ih->handle), column); + + gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(ih->handle), FALSE); + gtk_tree_view_set_enable_search(GTK_TREE_VIEW(ih->handle), FALSE); + +#if GTK_CHECK_VERSION(2, 10, 0) + if (iupAttribGetBoolean(ih, "HIDELINES")) + gtk_tree_view_set_enable_tree_lines(GTK_TREE_VIEW(ih->handle), FALSE); + else + gtk_tree_view_set_enable_tree_lines(GTK_TREE_VIEW(ih->handle), TRUE); +#endif + +#if GTK_CHECK_VERSION(2, 12, 0) + if (iupAttribGetBoolean(ih, "HIDEBUTTONS")) + gtk_tree_view_set_show_expanders(GTK_TREE_VIEW(ih->handle), FALSE); + else + gtk_tree_view_set_show_expanders(GTK_TREE_VIEW(ih->handle), TRUE); +#endif + + if (ih->data->show_dragdrop) + gtkTreeEnableDragDrop(ih); + + gtk_container_add((GtkContainer*)scrolled_window, ih->handle); + gtk_widget_show((GtkWidget*)scrolled_window); + gtk_scrolled_window_set_shadow_type(scrolled_window, GTK_SHADOW_IN); + + gtk_scrolled_window_set_policy(scrolled_window, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(ih->handle)); + + gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE); + gtk_tree_view_set_reorderable(GTK_TREE_VIEW(ih->handle), FALSE); + + /* callbacks */ + g_signal_connect(selection, "changed", G_CALLBACK(gtkTreeSelectionChanged), ih); + + g_signal_connect(renderer_txt, "editing-started", G_CALLBACK(gtkTreeCellTextEditingStarted), ih); + g_signal_connect(renderer_txt, "edited", G_CALLBACK(gtkTreeCellTextEdited), ih); + + g_signal_connect(G_OBJECT(ih->handle), "enter-notify-event", G_CALLBACK(iupgtkEnterLeaveEvent), ih); + g_signal_connect(G_OBJECT(ih->handle), "leave-notify-event", G_CALLBACK(iupgtkEnterLeaveEvent), ih); + g_signal_connect(G_OBJECT(ih->handle), "focus-in-event", G_CALLBACK(iupgtkFocusInOutEvent), ih); + g_signal_connect(G_OBJECT(ih->handle), "focus-out-event", G_CALLBACK(iupgtkFocusInOutEvent), ih); + g_signal_connect(G_OBJECT(ih->handle), "show-help", G_CALLBACK(iupgtkShowHelp), ih); + g_signal_connect(G_OBJECT(ih->handle), "motion-notify-event",G_CALLBACK(iupgtkMotionNotifyEvent), ih); + + g_signal_connect(G_OBJECT(ih->handle), "test-expand-row", G_CALLBACK(gtkTreeTestExpandRow), ih); + g_signal_connect(G_OBJECT(ih->handle), "test-collapse-row", G_CALLBACK(gtkTreeTestCollapseRow), ih); + g_signal_connect(G_OBJECT(ih->handle), "row-activated", G_CALLBACK(gtkTreeRowActived), ih); + g_signal_connect(G_OBJECT(ih->handle), "key-press-event", G_CALLBACK(gtkTreeKeyPressEvent), ih); + g_signal_connect(G_OBJECT(ih->handle), "key-release-event", G_CALLBACK(gtkTreeKeyReleaseEvent), ih); + g_signal_connect(G_OBJECT(ih->handle), "button-press-event", G_CALLBACK(gtkTreeButtonEvent), ih); + g_signal_connect(G_OBJECT(ih->handle), "button-release-event",G_CALLBACK(gtkTreeButtonEvent), ih); + + /* add to the parent, all GTK controls must call this. */ + iupgtkBaseAddToParent(ih); + + if (!iupAttribGetBoolean(ih, "CANFOCUS")) + GTK_WIDGET_FLAGS(ih->handle) &= ~GTK_CAN_FOCUS; + + gtk_widget_realize((GtkWidget*)scrolled_window); + gtk_widget_realize(ih->handle); + + /* Initialize the default images */ + ih->data->def_image_leaf = iupImageGetImage("IMGLEAF", ih, 0); + ih->data->def_image_collapsed = iupImageGetImage("IMGCOLLAPSED", ih, 0); + ih->data->def_image_expanded = iupImageGetImage("IMGEXPANDED", ih, 0); + + gtkTreeAddRootNode(ih); + + /* configure for DRAG&DROP of files */ + if (IupGetCallback(ih, "DROPFILES_CB")) + iupAttribSetStr(ih, "DRAGDROP", "YES"); + + IupSetCallback(ih, "_IUP_XY2POS_CB", (Icallback)gtkTreeConvertXYToPos); + + return IUP_NOERROR; +} + +void iupdrvTreeInitClass(Iclass* ic) +{ + /* Driver Dependent Class functions */ + ic->Map = gtkTreeMapMethod; + + /* Visual */ + iupClassRegisterAttribute(ic, "BGCOLOR", NULL, gtkTreeSetBgColorAttrib, IUPAF_SAMEASSYSTEM, "TXTBGCOLOR", IUPAF_DEFAULT); + iupClassRegisterAttribute(ic, "FGCOLOR", NULL, gtkTreeSetFgColorAttrib, IUPAF_SAMEASSYSTEM, "TXTFGCOLOR", IUPAF_DEFAULT); + + /* IupTree Attributes - GENERAL */ + iupClassRegisterAttribute(ic, "EXPANDALL", NULL, gtkTreeSetExpandAllAttrib, NULL, NULL, IUPAF_WRITEONLY|IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "INDENTATION", gtkTreeGetIndentationAttrib, gtkTreeSetIndentationAttrib, NULL, NULL, IUPAF_DEFAULT); + iupClassRegisterAttribute(ic, "COUNT", gtkTreeGetCountAttrib, NULL, NULL, NULL, IUPAF_NO_DEFAULTVALUE|IUPAF_READONLY|IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "DRAGDROP", NULL, iupgtkSetDragDropAttrib, NULL, NULL, IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "SPACING", iupTreeGetSpacingAttrib, gtkTreeSetSpacingAttrib, NULL, NULL, IUPAF_NOT_MAPPED); + iupClassRegisterAttribute(ic, "TOPITEM", NULL, gtkTreeSetTopItemAttrib, NULL, NULL, IUPAF_WRITEONLY|IUPAF_NO_INHERIT); + + /* IupTree Attributes - IMAGES */ + iupClassRegisterAttributeId(ic, "IMAGE", NULL, gtkTreeSetImageAttrib, IUPAF_WRITEONLY|IUPAF_NO_INHERIT); + iupClassRegisterAttributeId(ic, "IMAGEEXPANDED", NULL, gtkTreeSetImageExpandedAttrib, IUPAF_WRITEONLY|IUPAF_NO_INHERIT); + + iupClassRegisterAttribute(ic, "IMAGELEAF", NULL, gtkTreeSetImageLeafAttrib, IUPAF_SAMEASSYSTEM, "IMGLEAF", IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "IMAGEBRANCHCOLLAPSED", NULL, gtkTreeSetImageBranchCollapsedAttrib, IUPAF_SAMEASSYSTEM, "IMGCOLLAPSED", IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "IMAGEBRANCHEXPANDED", NULL, gtkTreeSetImageBranchExpandedAttrib, IUPAF_SAMEASSYSTEM, "IMGEXPANDED", IUPAF_NO_INHERIT); + + /* IupTree Attributes - NODES */ + iupClassRegisterAttributeId(ic, "STATE", gtkTreeGetStateAttrib, gtkTreeSetStateAttrib, IUPAF_NO_INHERIT); + iupClassRegisterAttributeId(ic, "DEPTH", gtkTreeGetDepthAttrib, NULL, IUPAF_READONLY|IUPAF_NO_INHERIT); + iupClassRegisterAttributeId(ic, "KIND", gtkTreeGetKindAttrib, NULL, IUPAF_READONLY|IUPAF_NO_INHERIT); + iupClassRegisterAttributeId(ic, "PARENT", gtkTreeGetParentAttrib, NULL, IUPAF_READONLY|IUPAF_NO_INHERIT); + iupClassRegisterAttributeId(ic, "COLOR", gtkTreeGetColorAttrib, gtkTreeSetColorAttrib, IUPAF_NO_INHERIT); + iupClassRegisterAttributeId(ic, "NAME", gtkTreeGetTitleAttrib, gtkTreeSetTitleAttrib, IUPAF_NO_INHERIT); + iupClassRegisterAttributeId(ic, "TITLE", gtkTreeGetTitleAttrib, gtkTreeSetTitleAttrib, IUPAF_NO_INHERIT); + iupClassRegisterAttributeId(ic, "USERDATA", gtkTreeGetUserDataAttrib, gtkTreeSetUserDataAttrib, IUPAF_NO_STRING|IUPAF_NO_INHERIT); + + iupClassRegisterAttributeId(ic, "CHILDCOUNT", gtkTreeGetChildCountAttrib, NULL, IUPAF_READONLY|IUPAF_NO_INHERIT); + iupClassRegisterAttributeId(ic, "TITLEFONT", gtkTreeGetTitleFontAttrib, gtkTreeSetTitleFontAttrib, IUPAF_NO_INHERIT); + + /* IupTree Attributes - MARKS */ + iupClassRegisterAttributeId(ic, "MARKED", gtkTreeGetMarkedAttrib, gtkTreeSetMarkedAttrib, IUPAF_NO_DEFAULTVALUE|IUPAF_NO_INHERIT); + iupClassRegisterAttribute (ic, "MARK", NULL, gtkTreeSetMarkAttrib, NULL, NULL, IUPAF_WRITEONLY|IUPAF_NO_INHERIT); + iupClassRegisterAttribute (ic, "STARTING", NULL, gtkTreeSetMarkStartAttrib, NULL, NULL, IUPAF_NO_DEFAULTVALUE|IUPAF_NO_INHERIT); + iupClassRegisterAttribute (ic, "MARKSTART", NULL, gtkTreeSetMarkStartAttrib, NULL, NULL, IUPAF_NO_DEFAULTVALUE|IUPAF_NO_INHERIT); + + iupClassRegisterAttribute (ic, "VALUE", gtkTreeGetValueAttrib, gtkTreeSetValueAttrib, NULL, NULL, IUPAF_NO_DEFAULTVALUE|IUPAF_NO_INHERIT); + + /* IupTree Attributes - ACTION */ + iupClassRegisterAttributeId(ic, "DELNODE", NULL, gtkTreeSetDelNodeAttrib, IUPAF_NOT_MAPPED|IUPAF_WRITEONLY|IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "RENAME", NULL, gtkTreeSetRenameAttrib, NULL, NULL, IUPAF_WRITEONLY|IUPAF_NO_INHERIT); + iupClassRegisterAttributeId(ic, "MOVENODE", NULL, gtkTreeSetMoveNodeAttrib, IUPAF_NOT_MAPPED|IUPAF_WRITEONLY|IUPAF_NO_INHERIT); + iupClassRegisterAttributeId(ic, "COPYNODE", NULL, gtkTreeSetCopyNodeAttrib, IUPAF_NOT_MAPPED|IUPAF_WRITEONLY|IUPAF_NO_INHERIT); + iupClassRegisterAttributeId(ic, "FINDUSERDATA", gtkTreeGetFindUserDataAttrib, NULL, IUPAF_READONLY|IUPAF_NO_INHERIT); + + iupClassRegisterAttribute (ic, "AUTODRAGDROP", NULL, NULL, NULL, NULL, IUPAF_DEFAULT); +} diff --git a/iup/src/gtk/iupgtk_val.c b/iup/src/gtk/iupgtk_val.c new file mode 100755 index 0000000..018af83 --- /dev/null +++ b/iup/src/gtk/iupgtk_val.c @@ -0,0 +1,208 @@ +/** \file + * \brief Valuator Control + * + * See Copyright Notice in "iup.h" + */ + +#include <gtk/gtk.h> +#include <gdk/gdkkeysyms.h> + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <memory.h> +#include <stdarg.h> + +#include "iup.h" +#include "iupcbs.h" + +#include "iup_object.h" +#include "iup_layout.h" +#include "iup_attrib.h" +#include "iup_str.h" +#include "iup_val.h" +#include "iup_drv.h" +#include "iup_drvfont.h" +#include "iup_key.h" + +#include "iupgtk_drv.h" + + +void iupdrvValGetMinSize(Ihandle* ih, int *w, int *h) +{ + if (ih->data->type == IVAL_HORIZONTAL) + { + *w = 20; + *h = 35; + } + else + { + *w = 35; + *h = 20; + } +} + +static int gtkValSetStepAttrib(Ihandle* ih, const char* value) +{ + ih->data->step = atof(value); + gtk_range_set_increments(GTK_RANGE(ih->handle), ih->data->step, ih->data->pagestep); + return 0; /* do not store value in hash table */ +} + +static int gtkValSetPageStepAttrib(Ihandle* ih, const char* value) +{ + ih->data->pagestep = atof(value); + gtk_range_set_increments(GTK_RANGE(ih->handle), ih->data->step, ih->data->pagestep); + return 0; /* do not store value in hash table */ +} + +static int gtkValSetValueAttrib(Ihandle* ih, const char* value) +{ + double fval; + ih->data->val = atof(value); + fval = (ih->data->val-ih->data->vmin)/(ih->data->vmax - ih->data->vmin); + gtk_range_set_value(GTK_RANGE(ih->handle), fval); + return 0; /* do not store value in hash table */ +} + + +/*********************************************************************************************/ + + +static gboolean gtkValChangeValue(GtkRange *range, GtkScrollType scroll, double fval, Ihandle *ih) +{ + double old_val = ih->data->val; + IFn cb; + + if (fval < 0.0) + gtk_range_set_value(GTK_RANGE(ih->handle), 0.0); + if (fval > 1.0) + gtk_range_set_value(GTK_RANGE(ih->handle), 1.0); + + ih->data->val = fval*(ih->data->vmax - ih->data->vmin) + ih->data->vmin; + iupValCropValue(ih); + + cb = (IFn)IupGetCallback(ih, "VALUECHANGED_CB"); + if (cb) + { + if (ih->data->val == old_val) + return FALSE; + + cb(ih); + } + else + { + IFnd cb_old; + if (scroll == GTK_SCROLL_JUMP) /* scroll == 1 */ + cb_old = (IFnd)IupGetCallback(ih, "MOUSEMOVE_CB"); + else if((scroll >= GTK_SCROLL_STEP_BACKWARD) && (scroll <= GTK_SCROLL_END)) + cb_old = (IFnd)IupGetCallback(ih, "BUTTON_PRESS_CB"); + else + cb_old = (IFnd)IupGetCallback(ih, "BUTTON_RELEASE_CB"); + if (cb_old) + cb_old(ih, ih->data->val); + } + + if (fval < 0.0 || fval > 1.0) + return TRUE; + + (void)range; + return FALSE; +} + +static gboolean gtkValKeyPressEvent(GtkWidget *widget, GdkEventKey *evt, Ihandle *ih) +{ + if (iupgtkKeyPressEvent(widget, evt, ih) == TRUE) + return TRUE; + + /* change Home and End default behaviour */ + if (ih->data->inverted) + { + if (evt->keyval==GDK_Home || evt->keyval==GDK_KP_Home) + { + /* set to maximum */ + gtk_range_set_value(GTK_RANGE(ih->handle), 1.0); + gtkValChangeValue(GTK_RANGE(ih->handle), GTK_SCROLL_START, 1.0, ih); + return TRUE; + } + if (evt->keyval==GDK_End || evt->keyval==GDK_KP_End) + { + /* set to minimum */ + gtk_range_set_value(GTK_RANGE(ih->handle), 0.0); + gtkValChangeValue(GTK_RANGE(ih->handle), GTK_SCROLL_END, 0.0, ih); + return TRUE; + } + } + + return FALSE; +} + +/*********************************************************************************************/ + + +static int gtkValMapMethod(Ihandle* ih) +{ + GtkObject *adjustment; + + /* value, lower, upper, step_increment, page_increment, page_size */ + /* page_size value only makes a difference for scrollbar widgets */ + adjustment = gtk_adjustment_new (0, 0, 1.0, 0.01, 0.1, 0); + if (!adjustment) + return IUP_ERROR; + + if (ih->data->type == IVAL_HORIZONTAL) + ih->handle = gtk_hscale_new(GTK_ADJUSTMENT(adjustment)); + else + ih->handle = gtk_vscale_new(GTK_ADJUSTMENT(adjustment)); + + if (!ih->handle) + return IUP_ERROR; + + /* add to the parent, all GTK controls must call this. */ + iupgtkBaseAddToParent(ih); + + if (!iupAttribGetBoolean(ih, "CANFOCUS")) + GTK_WIDGET_FLAGS(ih->handle) &= ~GTK_CAN_FOCUS; + + g_signal_connect(G_OBJECT(ih->handle), "enter-notify-event", G_CALLBACK(iupgtkEnterLeaveEvent), ih); + g_signal_connect(G_OBJECT(ih->handle), "leave-notify-event", G_CALLBACK(iupgtkEnterLeaveEvent), ih); + g_signal_connect(G_OBJECT(ih->handle), "focus-in-event", G_CALLBACK(iupgtkFocusInOutEvent), ih); + g_signal_connect(G_OBJECT(ih->handle), "focus-out-event", G_CALLBACK(iupgtkFocusInOutEvent), ih); + g_signal_connect(G_OBJECT(ih->handle), "show-help", G_CALLBACK(iupgtkShowHelp), ih); + + g_signal_connect(G_OBJECT(ih->handle), "key-press-event", G_CALLBACK(gtkValKeyPressEvent), ih); + g_signal_connect(G_OBJECT(ih->handle), "change-value", G_CALLBACK(gtkValChangeValue), ih); + + /* configure the scale */ + gtk_scale_set_draw_value(GTK_SCALE(ih->handle), FALSE); + gtk_range_set_range(GTK_RANGE(ih->handle), 0.0, 1.0); + + if (ih->data->inverted) + gtk_range_set_inverted(GTK_RANGE(ih->handle), TRUE); + + gtk_widget_realize(ih->handle); + + /* update a mnemonic in a label if necessary */ + iupgtkUpdateMnemonic(ih); + + return IUP_NOERROR; +} + +void iupdrvValInitClass(Iclass* ic) +{ + /* Driver Dependent Class functions */ + ic->Map = gtkValMapMethod; + + /* Driver Dependent Attribute functions */ + + /* Visual */ + iupClassRegisterAttribute(ic, "BGCOLOR", NULL, iupdrvBaseSetBgColorAttrib, IUPAF_SAMEASSYSTEM, "DLGBGCOLOR", IUPAF_DEFAULT); + + /* Special */ + + /* IupVal only */ + iupClassRegisterAttribute(ic, "VALUE", iupValGetValueAttrib, gtkValSetValueAttrib, NULL, NULL, IUPAF_NO_DEFAULTVALUE|IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "PAGESTEP", iupValGetPageStepAttrib, gtkValSetPageStepAttrib, IUPAF_SAMEASSYSTEM, "0.1", IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "STEP", iupValGetStepAttrib, gtkValSetStepAttrib, IUPAF_SAMEASSYSTEM, "0.01", IUPAF_NO_INHERIT); + iupClassRegisterAttribute(ic, "SHOWTICKS", NULL, NULL, NULL, NULL, IUPAF_WRITEONLY|IUPAF_READONLY); /* showticks is not supported in GTK */ +} |