X-Git-Url: http://winboard.nl/cgi-bin?a=blobdiff_plain;f=xoptions.c;h=583ab9dcdd5e9c523813f48eede4319638251594;hb=6004960398b36a83d05c068ee633591b855b2c0b;hp=d33dd7fcccd409df17e940983f5b5a8a8cb8d818;hpb=e7de571f4c62c122a3cbf9299722c69930aa6967;p=xboard.git diff --git a/xoptions.c b/xoptions.c index d33dd7f..583ab9d 100644 --- a/xoptions.c +++ b/xoptions.c @@ -50,6 +50,7 @@ extern char *getenv(); #include #include #include +#include #include "common.h" #include "backend.h" @@ -139,13 +140,9 @@ MarkMenuItem (char *menuRef, int state) { MenuItem *item = MenuNameToItem(menuRef); -#ifdef TODO_GTK if(item) { - Arg args[2]; - XtSetArg(args[0], XtNleftBitmap, state ? xMarkPixmap : None); - XtSetValues(item->handle, args, 1); + ((GtkCheckMenuItem *) (item->handle))->active = state; } -#endif } void GetWidgetTextGTK(GtkWidget *w, char **buf) @@ -153,6 +150,9 @@ void GetWidgetTextGTK(GtkWidget *w, char **buf) GtkTextIter start; GtkTextIter end; + if (GTK_IS_ENTRY(w)) { + *buf = gtk_entry_get_text(GTK_ENTRY (w)); + } else if (GTK_IS_TEXT_BUFFER(w)) { gtk_text_buffer_get_start_iter(GTK_TEXT_BUFFER(w), &start); gtk_text_buffer_get_end_iter(GTK_TEXT_BUFFER(w), &end); @@ -184,16 +184,16 @@ GetWidgetText (Option *opt, char **buf) } } -void SetSpinValue(Option *opt, int val, int n) +void SetSpinValue(Option *opt, char *val, int n) { if (opt->type == Spin) { - if (val == -1) + if (!strcmp(val, _("Unused"))) gtk_widget_set_sensitive(opt->handle, FALSE); else { gtk_widget_set_sensitive(opt->handle, TRUE); - gtk_spin_button_set_value(opt->handle, val); + gtk_spin_button_set_value(opt->handle, atoi(val)); } } else @@ -202,11 +202,13 @@ void SetSpinValue(Option *opt, int val, int n) void SetWidgetTextGTK(GtkWidget *w, char *text) { - if (!GTK_IS_TEXT_BUFFER(w)) { - printf("error: SetWidgetTextGTK arg is not a GtkTextBuffer\n"); - return; - } - gtk_text_buffer_set_text(GTK_TEXT_BUFFER(w), text, -1); + if (GTK_IS_ENTRY(w)) { + gtk_entry_set_text (GTK_ENTRY (w), text); + } else + if (GTK_IS_TEXT_BUFFER(w)) { + gtk_text_buffer_set_text(GTK_TEXT_BUFFER(w), text, -1); + } else + printf("error: SetWidgetTextGTK arg is neitherGtkEntry nor GtkTextBuffer\n"); } void @@ -217,7 +219,7 @@ SetWidgetText (Option *opt, char *buf, int n) case FileName: case PathName: case TextBox: SetWidgetTextGTK((GtkWidget *) opt->handle, buf); break; - case Spin: SetSpinValue(opt, atoi(buf), n); break; + case Spin: SetSpinValue(opt, buf, n); break; default: printf("unexpected case (%d) in GetWidgetText\n", opt->type); } @@ -230,123 +232,103 @@ SetWidgetText (Option *opt, char *buf, int n) void GetWidgetState (Option *opt, int *state) { -#ifdef TODO_GTK - Arg arg; - XtSetArg(arg, XtNstate, state); - XtGetValues(opt->handle, &arg, 1); -#endif + *state = gtk_toggle_button_get_active(opt->handle); } void SetWidgetState (Option *opt, int state) { -#ifdef TODO_GTK - Arg arg; - XtSetArg(arg, XtNstate, state); - XtSetValues(opt->handle, &arg, 1); -#endif + gtk_toggle_button_set_active(opt->handle, state); } void SetWidgetLabel (Option *opt, char *buf) { -#ifdef TODO_GTK - Arg arg; - XtSetArg(arg, XtNlabel, (XtArgVal) buf); - XtSetValues(opt->handle, &arg, 1); -#endif + gtk_label_set_text(opt->handle, buf); } void SetDialogTitle (DialogClass dlg, char *title) { -#ifdef TODO_GTK - Arg args[16]; - XtSetArg(args[0], XtNtitle, title); - XtSetValues(shells[dlg], args, 1); -#endif + gtk_window_set_title(GTK_WINDOW(shells[dlg]), title); } void -LoadListBox (Option *opt, char *emptyText, int n1, int n2) +SetListBoxItem (GtkListStore *store, int n, char *msg) { -#ifdef TODO_GTK - static char *dummyList[2]; - dummyList[0] = emptyText; // empty listboxes tend to crash X, so display user-supplied warning string instead - XawListChange(opt->handle, *(char*)opt->target ? opt->target : dummyList, 0, 0, True); -#endif + GtkTreeIter iter; + GtkTreePath *path = gtk_tree_path_new_from_indices(n, -1); + gtk_tree_model_get_iter(GTK_TREE_MODEL (store), &iter, path); + gtk_tree_path_free(path); + gtk_list_store_set(store, &iter, 0, msg, -1); } -int -ReadScroll (Option *opt, float *top, float *bottom) -{ // retreives fractions of top and bottom of thumb -#ifdef TODO_GTK - Arg args[16]; - Widget w = XtParent(opt->handle); // viewport - Widget v = XtNameToWidget(w, "vertical"); - int j=0; - float h; - if(!v) return FALSE; // no scroll bar - XtSetArg(args[j], XtNshown, &h); j++; - XtSetArg(args[j], XtNtopOfThumb, top); j++; - XtGetValues(v, args, j); - *bottom = *top + h; -#endif - return TRUE; +void +LoadListBox (Option *opt, char *emptyText, int n1, int n2) +{ + char **data = (char **) (opt->target); + GtkWidget *list = (GtkWidget *) (opt->handle); + GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(list)); + GtkListStore *store = GTK_LIST_STORE(model); + GtkTreeIter iter; + + if(n1 >= 0 && n2 >= 0) { + SetListBoxItem(store, n1, data[n1]); + SetListBoxItem(store, n2, data[n2]); + return; + } + + if (gtk_tree_model_get_iter_first(model, &iter)) + gtk_list_store_clear(store); + + while(*data) { // add elements to listbox one by one + gtk_list_store_append(store, &iter); + gtk_list_store_set(store, &iter, 0, *data++, -1); // 0 = first column + } } void -SetScroll (Option *opt, float f) -{ // sets top of thumb to given fraction -#ifdef TODO_GTK - static char *params[3] = { "", "Continuous", "Proportional" }; - static XEvent event; - Widget w = XtParent(opt->handle); // viewport - Widget v = XtNameToWidget(w, "vertical"); - if(!v) return; // no scroll bar - XtCallActionProc(v, "StartScroll", &event, params+1, 1); - XawScrollbarSetThumb(v, f, -1.0); - XtCallActionProc(v, "NotifyThumb", &event, params, 0); -// XtCallActionProc(v, "NotifyScroll", &event, params+2, 1); - XtCallActionProc(v, "EndScroll", &event, params, 0); -#endif +HighlightItem (Option *opt, int index, int scroll) +{ + char *value, **data = (char **) (opt->target); + GtkWidget *list = (GtkWidget *) (opt->handle); + GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(list)); + GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(list)); + GtkListStore *store = GTK_LIST_STORE(model); + GtkTreePath *path = gtk_tree_path_new_from_indices(index, -1); + GtkTreeIter iter; + gtk_tree_selection_select_path(selection, path); + if(scroll) gtk_tree_view_scroll_to_cell(list, path, NULL, 0, 0, 0); + gtk_tree_path_free(path); } void -HighlightListBoxItem (Option *opt, int nr) +HighlightListBoxItem (Option *opt, int index) { -#ifdef TODO_GTK - XawListHighlight(opt->handle, nr); -#endif + HighlightItem (opt, index, FALSE); } void -HighlightWithScroll (Option *opt, int sel, int max) +HighlightWithScroll (Option *opt, int index, int max) { -#ifdef TODO_GTK - float top, bottom, f, g; - HighlightListBoxItem(opt, sel); - if(!ReadScroll(opt, &top, &bottom)) return; // no scroll bar - bottom = bottom*max - 1.f; - f = g = top; - top *= max; - if(sel > (top + 3*bottom)/4) f = (sel - 0.75f*(bottom-top))/max; else - if(sel < (3*top + bottom)/4) f = (sel - 0.25f*(bottom-top))/max; - if(f < 0.f) f = 0.; if(f + 1.f/max > 1.f) f = 1. - 1./max; - if(f != g) SetScroll(opt, f); -#endif + HighlightItem (opt, index, TRUE); // ignore max } int SelectedListBoxItem (Option *opt) { -#ifdef TODO_GTK - XawListReturnStruct *rs; - rs = XawListShowCurrent(opt->handle); - return rs->list_index; -#else - return 0; -#endif + int i; + char *value, **data = (char **) (opt->target); + GtkWidget *list = (GtkWidget *) (opt->handle); + GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(list)); + + GtkTreeModel *model; + GtkTreeIter iter; + if (!gtk_tree_selection_get_selected(GTK_TREE_SELECTION(selection), &model, &iter)) return -1; + gtk_tree_model_get(model, &iter, 0, &value, -1); + for(i=0; data[i]; i++) if(!strcmp(data[i], value)) return i; + g_free(value); + return -1; } void @@ -356,6 +338,7 @@ FocusOnWidget (Option *opt, DialogClass dlg) #ifdef TODO_GTK XtSetKeyboardFocus(shells[dlg], opt->handle); #endif + gtk_widget_grab_focus(opt->handle); } void @@ -370,55 +353,6 @@ SetIconName (DialogClass dlg, char *name) #endif } -#ifdef TODO_GTK -static void -CheckCallback (Widget ww, XtPointer client_data, XEvent *event, Boolean *b) -{ - int s, data = (intptr_t) client_data; - Option *opt = dialogOptions[data >> 8] + (data & 255); - - if(opt->type == Label) { ((ButtonCallback*) opt->target)(data&255); return; } - - GetWidgetState(opt, &s); - SetWidgetState(opt, !s); -} -#endif - -#ifdef TODO_GTK -static void -SpinCallback (Widget w, XtPointer client_data, XtPointer call_data) -{ - String name, val; - Arg args[16]; - char buf[MSG_SIZ], *p; - int j = 0; // Initialisation is necessary because the text value may be non-numeric causing the scanf conversion to fail - int data = (intptr_t) client_data; - Option *opt = dialogOptions[data >> 8] + (data & 255); - - XtSetArg(args[0], XtNlabel, &name); - XtGetValues(w, args, 1); - - GetWidgetText(opt, &val); - sscanf(val, "%d", &j); - if (strcmp(name, _("browse")) == 0) { - char *q=val, *r; - for(r = ""; *q; q++) if(*q == '.') r = q; else if(*q == '/') r = ""; // last dot after last slash - if(!strcmp(r, "") && !currentCps && opt->type == FileName && opt->textValue) - r = opt->textValue; - Browse(data>>8, opt->name, NULL, r, opt->type == PathName, "", &p, (FILE**) opt); - return; - } else - if (strcmp(name, "+") == 0) { - if(++j > opt->max) return; - } else - if (strcmp(name, "-") == 0) { - if(--j < opt->min) return; - } else return; - snprintf(buf, MSG_SIZ, "%d", j); - SetWidgetText(opt, buf, TransientDlg); -} -#endif - void ComboSelect(GtkWidget *widget, gpointer addr) { Option *opt = dialogOptions[((intptr_t)addr)>>8]; // applicable option list @@ -455,33 +389,6 @@ CreateMenuItem (Widget menu, char *msg, XtCallbackProc CB, int n) } #endif -#ifdef TODO_GTK -static Widget -CreateComboPopup (Widget parent, Option *opt, int n, int fromList, int def) -{ // fromList determines if the item texts are taken from a list of strings, or from a menu table - int i; - Widget menu, entry; - Arg arg; - MenuItem *mb = (MenuItem *) opt->choice; - char **list = (char **) opt->choice; - - if(list[0] == NULL) return NULL; // avoid empty menus, as they cause crash - menu = XtCreatePopupShell(opt->name, simpleMenuWidgetClass, parent, NULL, 0); - - for (i=0; 1; i++) - { - char *msg = fromList ? list[i] : mb[i].string; - if(!msg) break; - entry = CreateMenuItem(menu, opt->min & NO_GETTEXT ? msg : _(msg), (XtCallbackProc) ComboSelect, (n<<16)+i); - if(!fromList) mb[i].handle = (void*) entry; // save item ID, for enabling / checkmarking - if(i==def) { - XtSetArg(arg, XtNpopupOnEntry, entry); - XtSetValues(menu, &arg, 1); - } - } - return menu; -} -#else static void MenuSelect (gpointer addr) // callback for all combo items { @@ -526,7 +433,6 @@ CreateMenuPopup (Option *opt, int n, int def) } return menu; } -#endif char moveTypeInTranslations[] = "Return: TypeInProc(1) \n" @@ -541,15 +447,192 @@ char *translationTable[] = { // beware: order is essential! filterTranslations, gameListTranslations, memoTranslations }; +Option *typeIn; // kludge to distinguish type-in callback from input-box callback + void -AddHandler (Option *opt, int nr) +CursorAtEnd (Option *opt) +{ + gtk_editable_set_position(opt->handle, -1); +} + +static gboolean +ICSKeyEvent(GtkWidget *widget, GdkEventKey *event, gpointer g) +{ + Option *opt = (Option *) g; + if(opt == typeIn) { + if(event->keyval == GDK_Return) { + char *val; + GetWidgetText(opt, &val); + TypeInDoneEvent(val); + PopDown(TransientDlg); + return TRUE; + } + return FALSE; + } + + switch(event->keyval) { + case GDK_Return: IcsKey(0); return TRUE; + case GDK_Up: IcsKey(1); return TRUE; + case GDK_Down: IcsKey(-1); return TRUE; + default: return FALSE; + } +} + +int shiftState, controlState; + +static gboolean +TypeInProc (GtkWidget *widget, GdkEventKey *event, gpointer gdata) +{ // This callback catches key presses on text-entries, and uses and as synonyms for dialog OK or Cancel + // *** kludge alert *** If a dialog does want some other action, like sending the line typed in the text-entry to an ICS, + // it should define an OK handler that does so, and returns FALSE to suppress the popdown. + int n = (intptr_t) gdata; + int dlg = n >> 16; + Option *opt; + n &= 0xFFFF; + opt = &dialogOptions[dlg][n]; + + if(opt == icsBox) return ICSKeyEvent(event->keyval); // Intercept ICS Input Box, which needs special treatment + + shiftState = event->state & GDK_SHIFT_MASK; + controlState = event->state & GDK_CONTROL_MASK; + switch(event->keyval) { + case GDK_Return: + if(GenericReadout(dialogOptions[dlg], -1)) PopDown(dlg); + break; + case GDK_Escape: + PopDown(dlg); + break; + default: + return FALSE; + } + return TRUE; +} + +void +HighlightText (Option *opt, int from, int to, Boolean highlight) +{ +# define INIT 0x8000 + static GtkTextIter start, end; + + if(!(opt->min & INIT)) { + opt->min |= INIT; // each memo its own init flag! + gtk_text_buffer_create_tag(opt->handle, "highlight", "background", "yellow", NULL); + gtk_text_buffer_create_tag(opt->handle, "normal", "background", "white", NULL); + } + gtk_text_buffer_get_iter_at_offset(opt->handle, &start, from); + gtk_text_buffer_get_iter_at_offset(opt->handle, &end, to); + gtk_text_buffer_apply_tag_by_name(opt->handle, highlight ? "highlight" : "normal", &start, &end); +} + +int +ShiftKeys () +{ // bassic primitive for determining if modifier keys are pressed + return 3*(shiftState != 0) + 0xC*(controlState != 0); // rely on what last mouse button press left us +} + +static gboolean +GameListEvent(GtkWidget *widget, GdkEvent *event, gpointer gdata) +{ + int n = (int) gdata; + + if(n == 4) { + if(((GdkEventKey *) event)->keyval != GDK_Return) return FALSE; + SetFilter(); + return TRUE; + } + + if(event->type == GDK_KEY_PRESS) { + int ctrl = (((GdkEventKey *) event)->state & GDK_CONTROL_MASK) != 0; + switch(((GdkEventKey *) event)->keyval) { + case GDK_Up: GameListClicks(-1 - 2*ctrl); return TRUE; + case GDK_Left: GameListClicks(-1); return TRUE; + case GDK_Down: GameListClicks(1 + 2*ctrl); return TRUE; + case GDK_Right: GameListClicks(1); return TRUE; + case GDK_Prior: GameListClicks(-4); return TRUE; + case GDK_Next: GameListClicks(4); return TRUE; + case GDK_Home: GameListClicks(-2); return TRUE; + case GDK_End: GameListClicks(2); return TRUE; + case GDK_Return: GameListClicks(0); return TRUE; + default: return FALSE; + } + } + if(event->type != GDK_2BUTTON_PRESS || ((GdkEventButton *) event)->button != 1) return FALSE; + GameListClicks(0); + return TRUE; +} + +static gboolean +MemoEvent(GtkWidget *widget, GdkEvent *event, gpointer gdata) +{ // handle mouse clicks on text widgets that need it + int w, h; + int button=10, f=1; + Option *opt, *memo = (Option *) gdata; + MemoCallback *userHandler = (MemoCallback *) memo->choice; + GdkEventButton *bevent = (GdkEventButton *) event; + GdkEventMotion *mevent = (GdkEventMotion *) event; + GtkTextIter start, end; + String val = NULL; + gboolean res; + gint index, x, y; + + if(memo->type == Label) { ((ButtonCallback*) memo->target)(memo->value); return TRUE; } // only clock widgets use this + + switch(event->type) { // figure out what's up + case GDK_MOTION_NOTIFY: + f = 0; + w = mevent->x; h = mevent->y; + break; + case GDK_BUTTON_RELEASE: + f = -1; // release indicated by negative button numbers + w = bevent->x; h = bevent->y; + button = bevent->button; + break; + case GDK_BUTTON_PRESS: + w = bevent->x; h = bevent->y; + button = bevent->button; + shiftState = bevent->state & GDK_SHIFT_MASK; + controlState = bevent->state & GDK_CONTROL_MASK; +// GTK_TODO: is this really the most efficient way to get the character at the mouse cursor??? + gtk_text_view_window_to_buffer_coords(widget, GTK_TEXT_WINDOW_WIDGET, w, h, &x, &y); + gtk_text_view_get_iter_at_location(widget, &start, x, y); + gtk_text_buffer_place_cursor(memo->handle, &start); + /* get cursor position into index */ + g_object_get(memo->handle, "cursor-position", &index, NULL); + /* get text from textbuffer */ + gtk_text_buffer_get_start_iter (memo->handle, &start); + gtk_text_buffer_get_end_iter (memo->handle, &end); + val = gtk_text_buffer_get_text (memo->handle, &start, &end, FALSE); + break; + default: + return FALSE; // should not happen + } + button *= f; + // hand click parameters as well as text & location to user + res = (userHandler) (memo, button, w, h, val, index); + if(val) g_free(val); + return res; +} + +void +AddHandler (Option *opt, DialogClass dlg, int nr) { -#ifdef TODO_GTK switch(nr) { - case + case 0: // history (now uses generic textview callback) + case 1: // comment (likewise) + break; + case 2: // move type-in + typeIn = opt; + case 3: // input box + g_signal_connect(opt->handle, "key-press-event", G_CALLBACK (ICSKeyEvent), (gpointer) opt); + break; + case 5: // game list + g_signal_connect(opt->handle, "button-press-event", G_CALLBACK (GameListEvent), (gpointer) 0 ); + case 4: // game-list filter + g_signal_connect(opt->handle, "key-press-event", G_CALLBACK (GameListEvent), (gpointer) nr ); + break; + case 6: // engine output (uses generic textview callback) + break; } - XtOverrideTranslations(opt->handle, XtParseTranslationTable(translationTable[nr])); -#endif } //----------------------------Generic dialog -------------------------------------------- @@ -588,7 +671,9 @@ RaiseWindow (DialogClass dlg) xev.xclient.data.l[1] = CurrentTime; XSendEvent (xDisplay, - root, False, + root, False,static gboolean +MemoEvent(GtkWidget *widget, GdkEvent *event, gpointer gdata) + SubstructureRedirectMask | SubstructureNotifyMask, &xev); @@ -597,30 +682,6 @@ RaiseWindow (DialogClass dlg) #endif } -/* Sets a check box on (True) or off (False) in the menu */ -void SetCheckMenuItemActive(gchar *name, int menuDlgNr, gboolean active) -{ -#ifdef TODO_GTK -// This doesn't work anymore, as we don't use builder - gchar menuItemName[50]; - - if (name != NULL) - strcpy(menuItemName, name); - else - GetMenuItemName(menuDlgNr, menuItemName); - - if (strcmp(menuItemName, "") == 0) return; - - GtkCheckMenuItem *chk; - chk = GTK_CHECK_MENU_ITEM(gtk_builder_get_object(GTK_BUILDER(builder), menuItemName)); - if (chk == NULL) { - printf("Error: failed to get check menu item %s from builder\n", menuItemName); - return; - } - chk->active = active; // set the check box without emitting any signals -#endif -} - int PopDown (DialogClass n) { @@ -645,19 +706,17 @@ PopDown (DialogClass n) gtk_widget_hide(shells[n]); shellUp[n]--; // count rather than clear + if(n == 0 || n >= PromoDlg) { gtk_widget_destroy(shells[n]); shells[n] = NULL; - } -#ifdef TODO_GTK + } + if(marked[n]) { MarkMenuItem(marked[n], False); marked[n] = NULL; } -#else - // when popping down uncheck the check box of the menu item - SetCheckMenuItemActive(NULL, n, False); -#endif + if(!n) currentCps = NULL; // if an Engine Settings dialog was up, we must be popping it down now currentOption = dialogOptions[TransientDlg]; // just in case a transient dialog was up (to allow its check and combo callbacks to work) #ifdef TODO_GTK @@ -667,26 +726,37 @@ PopDown (DialogClass n) return 1; } -gboolean GenericPopDown(w, event, gdata) +/* GTK callback used when OK/cancel clicked in genericpopup for non-modal dialog */ +gboolean GenericPopDown(w, resptype, gdata) GtkWidget *w; - GdkEvent *event; - gpointer gdata; + GtkResponseType resptype; + gpointer gdata; { - int dlg = (intptr_t) gdata; /* dialog number dlgnr */ - + DialogClass dlg = (intptr_t) gdata; /* dialog number dlgnr */ + GtkWidget *sh = shells[dlg]; + + currentOption = dialogOptions[dlg]; + #ifdef TODO_GTK // I guess BrowserDlg will be abandoned, as GTK has a better browser of its own - if(shellUp[BrowserDlg] && dlg != BrowserDlg || dialogError) return; // prevent closing dialog when it has an open file-browse daughter + if(shellUp[BrowserDlg] && dlg != BrowserDlg || dialogError) return True; // prevent closing dialog when it has an open file-browse daughter #else - if(browserUp || dialogError) return True; // prevent closing dialog when it has an open file-browse daughter + if(browserUp || dialogError && dlg != FatalDlg) return True; // prevent closing dialog when it has an open file-browse or error-popup daughter #endif - GtkWidget *sh = shells[dlg]; -printf("popdown %d\n", dlg); shells[dlg] = w; // make sure we pop down the right one in case of multiple instances - PopDown(dlg); + + /* OK pressed */ + if (resptype == GTK_RESPONSE_ACCEPT) { + if (GenericReadout(currentOption, -1)) PopDown(dlg); + return TRUE; + } else + /* cancel pressed */ + { + if(dlg == BoardWindow) ExitEvent(0); + PopDown(dlg); + } shells[dlg] = sh; // restore - if(dlg == BoardWindow) ExitEvent(0); - return True; /* don't propagate to default handler */ + return TRUE; } int AppendText(Option *opt, char *s) @@ -707,23 +777,11 @@ int AppendText(Option *opt, char *s) void SetColor (char *colorName, Option *box) { // sets the color of a widget -#ifdef TODO_GTK - Arg args[5]; - Pixel buttonColor; - XrmValue vFrom, vTo; - if (!appData.monoMode) { - vFrom.addr = (caddr_t) colorName; - vFrom.size = strlen(colorName); - XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo); - if (vTo.addr == NULL) { - buttonColor = (Pixel) -1; - } else { - buttonColor = *(Pixel *) vTo.addr; - } - } else buttonColor = timerBackgroundPixel; - XtSetArg(args[0], XtNbackground, buttonColor);; - XtSetValues(box->handle, args, 1); -#endif + GdkColor color; + + /* set the colour of the colour button to the colour that will be used */ + gdk_color_parse( colorName, &color ); + gtk_widget_modify_bg ( GTK_WIDGET(box->handle), GTK_STATE_NORMAL, &color ); } #ifdef TODO_GTK @@ -737,7 +795,7 @@ ColorChanged (Widget w, XtPointer data, XEvent *event, Boolean *b) #endif static void -GraphEventProc(GtkWidget *widget, GdkEventExpose *event, gpointer gdata) +GraphEventProc(GtkWidget *widget, GdkEvent *event, gpointer gdata) { // handle expose and mouse events on Graph widget int w, h; int j, button=10, f=1, sizing=0; @@ -745,7 +803,7 @@ GraphEventProc(GtkWidget *widget, GdkEventExpose *event, gpointer gdata) PointerCallback *userHandler = graph->target; GdkEventExpose *eevent = (GdkEventExpose *) event; GdkEventButton *bevent = (GdkEventButton *) event; - GdkEventMotion *mevent = (GdkEventButton *) event; + GdkEventMotion *mevent = (GdkEventMotion *) event; cairo_t *cr; // if (!XtIsRealized(widget)) return; @@ -799,6 +857,8 @@ GraphEventProc(GtkWidget *widget, GdkEventExpose *event, gpointer gdata) case GDK_BUTTON_PRESS: w = bevent->x; h = bevent->y; button = bevent->button; + shiftState = bevent->state & GDK_SHIFT_MASK; + controlState = bevent->state & GDK_CONTROL_MASK; } button *= f; @@ -816,33 +876,17 @@ GraphEventProc(GtkWidget *widget, GdkEventExpose *event, gpointer gdata) void GraphExpose (Option *opt, int x, int y, int w, int h) { +#if 0 + GdkRectangle r; + r.x = x; r.y = y; r.width = w; r.height = h; + gdk_window_invalidate_rect(((GtkWidget *)(opt->handle))->window, &r, FALSE); +#endif GdkEventExpose e; if(!opt->handle) return; e.area.x = x; e.area.y = y; e.area.width = w; e.area.height = h; e.count = -1; e.type = GDK_EXPOSE; // count = -1: kludge to suppress sizing GraphEventProc(opt->handle, (GdkEvent *) &e, (gpointer) opt); // fake expose event } -/* GTK callback used when OK/cancel clicked in genericpopup for non-modal dialog */ -void GenericPopUpCallback(w, resptype, gdata) - GtkWidget *w; - GtkResponseType resptype; - gpointer gdata; -{ - int data = (intptr_t) gdata; /* dialog number dlgnr */ - DialogClass dlg; - - currentOption = dialogOptions[dlg=data>>16]; data &= 0xFFFF; - - /* OK pressed */ - if (resptype == GTK_RESPONSE_ACCEPT) { - if (GenericReadout(currentOption, -1)) PopDown(data); - return; - } - - /* cancel pressed */ - PopDown(dlg); -} - void GenericCallback(GtkWidget *widget, gpointer gdata) { const gchar *name; @@ -860,15 +904,13 @@ void GenericCallback(GtkWidget *widget, gpointer gdata) sh = shells[dlg]; // make following line a no-op, as we haven't found out what the real shell is yet (breaks multiple popups of same type!) #endif oldSh = shells[dlg]; shells[dlg] = sh; // bow to reality - -#ifdef TODO_GTK + if (data == 30000) { // cancel PopDown(dlg); } else if (data == 30001) { // save buttons imply OK if(GenericReadout(currentOption, -1)) PopDown(dlg); // calls OK-proc after full readout, but no popdown if it returns false } else -#endif if(currentCps) { name = gtk_button_get_label (GTK_BUTTON(widget)); @@ -880,54 +922,63 @@ void GenericCallback(GtkWidget *widget, gpointer gdata) shells[dlg] = oldSh; // in case of multiple instances, restore previous (as this one could be popped down now) } -#ifdef TODO_GTK -void -TabProc (Widget w, XEvent *event, String *prms, Cardinal *nprms) -{ // for transfering focus to the next text-edit - Option *opt; - for(opt = currentOption; opt->type != EndMark; opt++) { - if(opt->handle == w) { - while(++opt) { - if(opt->type == EndMark) opt = currentOption; // wrap - if(opt->handle == w) return; // full circle - if(opt->type == TextBox || opt->type == Spin || opt->type == Fractional || opt->type == FileName || opt->type == PathName) { - SetFocus(opt->handle, XtParent(XtParent(XtParent(w))), NULL, 0); - return; - } - } - } - } -} -#endif +void BrowseGTK(GtkWidget *widget, gpointer gdata) +{ + GtkWidget *entry; + GtkWidget *dialog; + GtkFileFilter *gtkfilter; + GtkFileFilter *gtkfilter_all; + int opt_i = (intptr_t) gdata; + GtkFileChooserAction fc_action; + + gtkfilter = gtk_file_filter_new(); + gtkfilter_all = gtk_file_filter_new(); -#ifdef TODO_GTK -void -WheelProc (Widget w, XEvent *event, String *prms, Cardinal *nprms) -{ // for scrolling a widget seen through a viewport with the mouse wheel (ListBox!) - int j=0, n = atoi(prms[0]); - static char *params[3] = { "", "Continuous", "Proportional" }; - Arg args[16]; - float h, top; - Widget v; - if(!n) { // transient dialogs also use this for list-selection callback - n = prms[1][0]-'0'; - Option *opt=dialogOptions[prms[2][0]-'A'] + n; - if(opt->textValue) ((ListBoxCallback*) opt->textValue)(n, SelectedListBoxItem(opt)); - return; - } - v = XtNameToWidget(XtParent(w), "vertical"); - if(!v) return; - XtSetArg(args[j], XtNshown, &h); j++; - XtSetArg(args[j], XtNtopOfThumb, &top); j++; - XtGetValues(v, args, j); - top += 0.1f*h*n; if(top < 0.f) top = 0.; - XtCallActionProc(v, "StartScroll", event, params+1, 1); - XawScrollbarSetThumb(v, top, -1.0); - XtCallActionProc(v, "NotifyThumb", event, params, 0); -// XtCallActionProc(w, "NotifyScroll", event, params+2, 1); - XtCallActionProc(v, "EndScroll", event, params, 0); + char fileext[10] = "*"; + + /* select file or folder depending on option_type */ + if (currentOption[opt_i].type == PathName) + fc_action = GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER; + else + fc_action = GTK_FILE_CHOOSER_ACTION_OPEN; + + dialog = gtk_file_chooser_dialog_new ("Open File", + NULL, + fc_action, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, + NULL); + + /* one filter to show everything */ + gtk_file_filter_add_pattern(gtkfilter_all, "*"); + gtk_file_filter_set_name (gtkfilter_all, "All Files"); + gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog),gtkfilter_all); + + /* filter for specific filetypes e.g. pgn or fen */ + if (currentOption[opt_i].textValue != NULL && (strcmp(currentOption[opt_i].textValue, "") != 0) ) + { + strcat(fileext, currentOption[opt_i].textValue); + gtk_file_filter_add_pattern(gtkfilter, fileext); + gtk_file_filter_set_name (gtkfilter, currentOption[opt_i].textValue); + gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter); + /* activate filter */ + gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(dialog),gtkfilter); + } + else + gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(dialog),gtkfilter_all); + + if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) + { + char *filename; + filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog)); + entry = currentOption[opt_i].handle; + gtk_entry_set_text (GTK_ENTRY (entry), filename); + g_free (filename); + + } + gtk_widget_destroy (dialog); + dialog = NULL; } -#endif static char *oneLiner = "Return: redraw-display() \n \ @@ -1000,6 +1051,19 @@ SetPositionAndSize (Arg *args, Widget leftNeigbor, Widget topNeigbor, int b, int } #endif +static int +SameRow (Option *opt) +{ + return (opt->min & SAME_ROW && (opt->type == Button || opt->type == SaveButton || opt->type == Label || opt->type == ListBox)); +} + +static void +Pack (GtkWidget *hbox, GtkWidget *table, GtkWidget *entry, int left, int right, int top) +{ + if(hbox) gtk_box_pack_start(GTK_BOX (hbox), entry, TRUE, TRUE, 0); + else gtk_table_attach_defaults(GTK_TABLE(table), entry, left, right, top, top+1); +} + int GenericPopUp (Option *option, char *title, DialogClass dlgNr, DialogClass parent, int modal, int topLevel) { @@ -1009,7 +1073,7 @@ GenericPopUp (Option *option, char *title, DialogClass dlgNr, DialogClass parent GtkWidget *box; GtkWidget *checkbutton; GtkWidget *entry; - GtkWidget *hbox; + GtkWidget *hbox = NULL; GtkWidget *button; GtkWidget *table; GtkWidget *spinner; @@ -1028,7 +1092,7 @@ GenericPopUp (Option *option, char *title, DialogClass dlgNr, DialogClass parent int i, j, arraysize, left, top, height=999, width=1, boxStart; char def[MSG_SIZ], *msg, engineDlg = (currentCps != NULL && dlgNr != BrowserDlg); - + if(dlgNr < PromoDlg && shellUp[dlgNr]) return 0; // already up if(dlgNr && dlgNr < PromoDlg && shells[dlgNr]) { // reusable, and used before (but popped down) @@ -1050,9 +1114,8 @@ GenericPopUp (Option *option, char *title, DialogClass dlgNr, DialogClass parent currentOption[n].type = EndMark; currentOption[n].target = NULL; // delimit list by callback-less end mark } + parents[dlgNr] = parent; #ifdef TODO_GTK - i = 0; - XtSetArg(args[i], XtNresizable, True); i++; shells[BoardWindow] = shellWidget; parents[dlgNr] = parent; if(dlgNr == BoardWindow) dialog = shellWidget; else @@ -1061,7 +1124,7 @@ GenericPopUp (Option *option, char *title, DialogClass dlgNr, DialogClass parent shells[parent], args, i); #endif dialog = gtk_dialog_new_with_buttons( title, - NULL, + GTK_WINDOW(shells[parent]), GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR | (modal ? GTK_DIALOG_MODAL : 0), GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, @@ -1090,6 +1153,17 @@ GenericPopUp (Option *option, char *title, DialogClass dlgNr, DialogClass parent left = left + 3; gtk_table_resize(GTK_TABLE(table), height, left + 3); } + if(!SameRow(&option[i])) { + if(SameRow(&option[i+1])) { + // make sure hbox is always available when we have more options on same row + hbox = gtk_hbox_new (option[i].type == Button && option[i].textValue, 0); + if (strcmp(option[i].name, "") == 0 || option[i].type == Label || option[i].type == Button) + // for Label and Button name is contained inside option + gtk_table_attach_defaults(GTK_TABLE(table), hbox, left, left+3, top, top+1); + else + gtk_table_attach_defaults(GTK_TABLE(table), hbox, left+1, left+3, top, top+1); + } else hbox = NULL; //and also make sure no hbox exists if only singl option on row + } switch(option[i].type) { case Fractional: snprintf(def, MSG_SIZ, "%.2f", *(float*)option[i].target); @@ -1110,12 +1184,18 @@ GenericPopUp (Option *option, char *title, DialogClass dlgNr, DialogClass parent w = option[i].type == Spin || option[i].type == Fractional ? 70 : option[i].max ? option[i].max : 205; if(option[i].type == FileName || option[i].type == PathName) w -= 55; - if (option[i].type==TextBox && option[i].min > 80){ + if (option[i].type==TextBox && option[i].value > 80){ textview = gtk_text_view_new(); - gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(textview), GTK_WRAP_WORD); + gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(textview), option[i].min & T_WRAP ? GTK_WRAP_WORD : GTK_WRAP_NONE); +#ifdef TODO_GTK + if(option[i].min & T_FILL) { XtSetArg(args[j], XtNautoFill, True); j++; } + if(option[i].min & T_TOP) { XtSetArg(args[j], XtNtop, XtChainTop); j++; +#endif /* add textview to scrolled window so we have vertical scroll bar */ sw = gtk_scrolled_window_new(NULL, NULL); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), + option[i].min & T_HSCRL ? GTK_POLICY_ALWAYS : GTK_POLICY_AUTOMATIC, + option[i].min & T_VSCRL ? GTK_POLICY_ALWAYS : GTK_POLICY_NEVER); gtk_container_add(GTK_CONTAINER(sw), textview); gtk_widget_set_size_request(GTK_WIDGET(sw), w, -1); @@ -1124,17 +1204,23 @@ GenericPopUp (Option *option, char *title, DialogClass dlgNr, DialogClass parent /* check if label is empty */ if (strcmp(option[i].name,"") != 0) { gtk_table_attach_defaults(GTK_TABLE(table), label, left, left+1, top, top+1); - gtk_table_attach_defaults(GTK_TABLE(table), sw, left+1, left+3, top, top+1); + Pack(hbox, table, sw, left+1, left+3, top); } else { /* no label so let textview occupy all columns */ - gtk_table_attach_defaults(GTK_TABLE(table), sw, left, left+3, top, top+1); + Pack(hbox, table, sw, left, left+3, top); } if ( *(char**)option[i].target != NULL ) gtk_text_buffer_set_text (textbuffer, *(char**)option[i].target, -1); else gtk_text_buffer_set_text (textbuffer, "", -1); option[i].handle = (void*)textbuffer; + option[i].textValue = (char*)textview; + if(option[i].choice) { // textviews can request a handler for mouse events in the choice field + g_signal_connect(textview, "button-press-event", G_CALLBACK (MemoEvent), (gpointer) &option[i] ); + g_signal_connect(textview, "button-release-event", G_CALLBACK (MemoEvent), (gpointer) &option[i] ); + g_signal_connect(textview, "motion-notify-event", G_CALLBACK (MemoEvent), (gpointer) &option[i] ); + } break; } @@ -1164,17 +1250,11 @@ GenericPopUp (Option *option, char *title, DialogClass dlgNr, DialogClass parent gtk_table_attach_defaults(GTK_TABLE(table), entry, left+1, left+2, top, top+1); button = gtk_button_new_with_label ("Browse"); gtk_table_attach_defaults(GTK_TABLE(table), button, left+2, left+3, top, top+1); - g_signal_connect (button, "clicked", G_CALLBACK (Browse), (gpointer)(intptr_t) i); + g_signal_connect (button, "clicked", G_CALLBACK (BrowseGTK), (gpointer)(intptr_t) i); option[i].handle = (void*)entry; } else { - hbox = gtk_hbox_new (FALSE, 0); - if (strcmp(option[i].name, "") == 0) - gtk_table_attach_defaults(GTK_TABLE(table), hbox, left, left+3, top, top+1); - else - gtk_table_attach_defaults(GTK_TABLE(table), hbox, left+1, left+3, top, top+1); - gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0); - //gtk_table_attach_defaults(GTK_TABLE(table), entry, left+1, left+3, top, top+1); + Pack(hbox, table, entry, left + (strcmp(option[i].name, "") != 0), left+3, top); option[i].handle = (void*)entry; } break; @@ -1186,10 +1266,19 @@ GenericPopUp (Option *option, char *title, DialogClass dlgNr, DialogClass parent option[i].handle = (void *)checkbutton; break; case Label: - label = gtk_label_new(option[i].name); + option[i].handle = (void *) (label = gtk_label_new(option[i].name)); /* Left Justify */ gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); - gtk_table_attach_defaults(GTK_TABLE(table), label, left, left+3, top, top+1); + if(option[i].min & BORDER) { + GtkWidget *frame = gtk_frame_new(NULL); + gtk_container_add(GTK_CONTAINER(frame), label); + label = frame; + } + Pack(hbox, table, label, left, left+3, top); + if(option[i].target) { // allow user to specify event handler for button presses + gtk_widget_add_events(GTK_WIDGET(label), GDK_BUTTON_PRESS_MASK); + g_signal_connect(label, "button-press-event", G_CALLBACK(MemoEvent), (gpointer) &option[i]); + } break; case SaveButton: case Button: @@ -1209,18 +1298,7 @@ GenericPopUp (Option *option, char *title, DialogClass dlgNr, DialogClass parent || strstr(first.variants, VariantName(option[i].value))); } - if (!(option[i].min & 1)) { - if(option[i].textValue) // for new variant dialog give buttons equal space so they line up nicely - hbox = gtk_hbox_new (TRUE, 0); - else - hbox = gtk_hbox_new (FALSE, 0); - // if only 1 button then put it in 1st column of table only - if ( (arraysize >= (i+1)) && option[i+1].type != Button ) - gtk_table_attach_defaults(GTK_TABLE(table), hbox, left, left+1, top, top+1); - else - gtk_table_attach_defaults(GTK_TABLE(table), hbox, left, left+3, top, top+1); - } - gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0); + Pack(hbox, table, button, left, left+1, top); g_signal_connect (button, "clicked", G_CALLBACK (GenericCallback), (gpointer)(intptr_t) i + (dlgNr<<16)); option[i].handle = (void*)button; break; @@ -1234,14 +1312,14 @@ GenericPopUp (Option *option, char *title, DialogClass dlgNr, DialogClass parent for(j=0;;j++) { if ( ((char **) option[i].textValue)[j] == NULL) break; - gtk_combo_box_append_text(GTK_COMBO_BOX(combobox), ((char **) option[i].textValue)[j]); + gtk_combo_box_append_text(GTK_COMBO_BOX(combobox), ((char **) option[i].choice)[j]); } if(currentCps) option[i].choice = (char**) option[i].textValue; - else { + else { for(j=0; option[i].choice[j]; j++) { - if(*(char**)option[i].target && !strcmp(*(char**)option[i].target, option[i].choice[j])) break; + if(*(char**)option[i].target && !strcmp(*(char**)option[i].target, ((char**)(option[i].textValue))[j])) break; } /* If choice is NULL set to first */ if (option[i].choice[j] == NULL) @@ -1253,12 +1331,7 @@ GenericPopUp (Option *option, char *title, DialogClass dlgNr, DialogClass parent //option[i].value = j + (option[i].choice[j] == NULL); gtk_combo_box_set_active(GTK_COMBO_BOX(combobox), option[i].value); - - hbox = gtk_hbox_new (FALSE, 0); - gtk_table_attach_defaults(GTK_TABLE(table), hbox, left+1, left+3, top, top+1); - gtk_box_pack_start (GTK_BOX (hbox), combobox, TRUE, TRUE, 0); - //gtk_table_attach_defaults(GTK_TABLE(table), combobox, 1, 2, i, i+1); - + Pack(hbox, table, combobox, left+1, left+3, top); g_signal_connect(G_OBJECT(combobox), "changed", G_CALLBACK(ComboSelect), (gpointer) (intptr_t) (i + 256*dlgNr)); option[i].handle = (void*)combobox; @@ -1285,10 +1358,10 @@ GenericPopUp (Option *option, char *title, DialogClass dlgNr, DialogClass parent sw = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); gtk_container_add(GTK_CONTAINER(sw), list); - gtk_widget_set_size_request(GTK_WIDGET(sw), w, 300); + gtk_widget_set_size_request(GTK_WIDGET(sw), option[i].max ? option[i].max : -1, option[i].value ? option[i].value : -1); /* never has label, so let listbox occupy all columns */ - gtk_table_attach_defaults(GTK_TABLE(table), sw, left, left+3, top, top+1); + Pack(hbox, table, sw, left, left+3, top); } break; case Graph: @@ -1301,7 +1374,6 @@ GenericPopUp (Option *option, char *title, DialogClass dlgNr, DialogClass parent g_signal_connect (graph, "button-press-event", G_CALLBACK (GraphEventProc), (gpointer) &option[i]); g_signal_connect (graph, "button-release-event", G_CALLBACK (GraphEventProc), (gpointer) &option[i]); g_signal_connect (graph, "motion-notify-event", G_CALLBACK (GraphEventProc), (gpointer) &option[i]); -// g_signal_connect (graph, "motion-event", G_CALLBACK (GraphEventProc), (gpointer) &option[i]); #ifdef TODO_GTK XtAddEventHandler(last, ExposureMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask, False, @@ -1362,7 +1434,7 @@ GenericPopUp (Option *option, char *title, DialogClass dlgNr, DialogClass parent if(option[i].target) ((ButtonCallback*)option[i].target)(boxStart); // callback that can make sizing decisions break; case Break: - top = height; // force next option to start in a new column + if(option[i].min & SAME_ROW) top = height; // force next option to start in a new column break; default: printf("GenericPopUp: unexpected case in switch. i=%d type=%d name=%s.\n", i, option[i].type, option[i].name); @@ -1383,8 +1455,8 @@ GenericPopUp (Option *option, char *title, DialogClass dlgNr, DialogClass parent } g_signal_connect (dialog, "response", - G_CALLBACK (GenericPopUpCallback), - (gpointer)(intptr_t) (dlgNr<<16 | i)); + G_CALLBACK (GenericPopDown), + (gpointer)(intptr_t) dlgNr); g_signal_connect (dialog, "delete-event", G_CALLBACK (GenericPopDown), (gpointer)(intptr_t) dlgNr);