X-Git-Url: http://winboard.nl/cgi-bin?a=blobdiff_plain;f=gtk%2Fxoptions.c;h=04d566a4e3906ccf48e8933f2d95eed9138cbbac;hb=51e9503489f2cee28a076bf52c56185a5283b069;hp=01775e893317e9ef317881911bbaecead7f8079c;hpb=c5488ffd0c0e61617f06cc09444e7b7d642d00a7;p=xboard.git diff --git a/gtk/xoptions.c b/gtk/xoptions.c index 01775e8..04d566a 100644 --- a/gtk/xoptions.c +++ b/gtk/xoptions.c @@ -1,7 +1,7 @@ /* * xoptions.c -- Move list window, part of X front end for XBoard * - * Copyright 2000, 2009, 2010, 2011, 2012 Free Software Foundation, Inc. + * Copyright 2000, 2009, 2010, 2011, 2012, 2013, 2014 Free Software Foundation, Inc. * ------------------------------------------------------------------------ * * GNU XBoard is free software: you can redistribute it and/or modify @@ -50,7 +50,10 @@ extern char *getenv(); #include #include #include -#include +#include +#ifdef __APPLE__ +# include +#endif #include "common.h" #include "backend.h" @@ -140,15 +143,15 @@ MarkMenuItem (char *menuRef, int state) { MenuItem *item = MenuNameToItem(menuRef); - if(item) { + if(item && item->handle) { ((GtkCheckMenuItem *) (item->handle))->active = state; } } void GetWidgetTextGTK(GtkWidget *w, char **buf) -{ +{ GtkTextIter start; - GtkTextIter end; + GtkTextIter end; if (GTK_IS_ENTRY(w)) { *buf = (char *) gtk_entry_get_text(GTK_ENTRY (w)); @@ -160,7 +163,7 @@ void GetWidgetTextGTK(GtkWidget *w, char **buf) } else { printf("error in GetWidgetText, invalid widget\n"); - *buf = NULL; + *buf = NULL; } } @@ -175,7 +178,7 @@ GetWidgetText (Option *opt, char **buf) case PathName: case TextBox: GetWidgetTextGTK((GtkWidget *) opt->handle, buf); break; case Spin: - x = gtk_spin_button_get_value (GTK_SPIN_BUTTON(opt->handle)); + x = gtk_spin_button_get_value (GTK_SPIN_BUTTON(opt->handle)); snprintf(val, 12, "%d", x); *buf = val; break; default: @@ -185,19 +188,19 @@ GetWidgetText (Option *opt, char **buf) } void SetSpinValue(Option *opt, char *val, int n) -{ +{ if (opt->type == Spin) { if (!strcmp(val, _("Unused"))) gtk_widget_set_sensitive(opt->handle, FALSE); else { - gtk_widget_set_sensitive(opt->handle, TRUE); + gtk_widget_set_sensitive(opt->handle, TRUE); gtk_spin_button_set_value(opt->handle, atoi(val)); } } else - printf("error in SetSpinValue, unknown type %d\n", opt->type); + printf("error in SetSpinValue, unknown type %d\n", opt->type); } void SetWidgetTextGTK(GtkWidget *w, char *text) @@ -244,7 +247,10 @@ SetWidgetState (Option *opt, int state) void SetWidgetLabel (Option *opt, char *buf) { - gtk_label_set_text(opt->handle, buf); + if(opt->type == Button) // Chat window uses this routine for changing button labels + gtk_button_set_label(opt->handle, buf); + else + gtk_label_set_text(opt->handle, buf); } void @@ -271,14 +277,14 @@ LoadListBox (Option *opt, char *emptyText, int n1, int n2) 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)) + if (gtk_tree_model_get_iter_first(model, &iter)) gtk_list_store_clear(store); while(*data) { // add elements to listbox one by one @@ -314,8 +320,10 @@ void ScrollToCursor (Option *opt, int caretPos) { static GtkTextIter iter; + GtkTextMark *mark = gtk_text_buffer_get_mark((GtkTextBuffer *) opt->handle, "scrollmark"); gtk_text_buffer_get_iter_at_offset((GtkTextBuffer *) opt->handle, &iter, caretPos); - gtk_text_view_scroll_to_iter((GtkTextView *) opt->textValue, &iter, 0.0, 0, 0.5, 0.5); + gtk_text_buffer_move_mark((GtkTextBuffer *) opt->handle, mark, &iter); + gtk_text_view_scroll_to_mark((GtkTextView *) opt->textValue, mark, 0.0, 0, 0.5, 0.5); } int @@ -363,7 +371,7 @@ void ComboSelect(GtkWidget *widget, gpointer addr) gint i = ((intptr_t)addr) & 255; // option number gint g; - g = gtk_combo_box_get_active(GTK_COMBO_BOX(widget)); + g = gtk_combo_box_get_active(GTK_COMBO_BOX(widget)); values[i] = g; // store in temporary, for transfer at OK #if TODO_GTK @@ -413,20 +421,39 @@ CreateMenuPopup (Option *opt, int n, int def) menu = gtk_menu_new(); // menu = XtCreatePopupShell(opt->name, simpleMenuWidgetClass, parent, NULL, 0); - for (i=0; 1; i++) + for (i=0; 1; i++) { char *msg = mb[i].string; if(!msg) break; - if(strcmp(msg, "----")) { // +#ifdef __APPLE__ + if(!strcmp(msg, "Quit ")) continue; // Quit item will appear automatically in App menu + if(!strcmp(msg, "About XBoard")) msg = "About"; // 'XBoard' will be appended automatically when moved to App menu 1st item +#endif + if(!strcmp(msg, "ICS Input Box")) { mb[i].handle = NULL; continue; } // suppress ICS Input Box in GTK + if(strcmp(msg, "----")) { // if(!(opt->min & NO_GETTEXT)) msg = _(msg); if(mb[i].handle) { entry = gtk_check_menu_item_new_with_label(msg); // should be used for items that can be checkmarked if(mb[i].handle == RADIO) gtk_check_menu_item_set_draw_as_radio(GTK_CHECK_MENU_ITEM(entry), True); } else entry = gtk_menu_item_new_with_label(msg); - gtk_signal_connect_object (GTK_OBJECT (entry), "activate", GTK_SIGNAL_FUNC(MenuSelect), (gpointer) (n<<16)+i); - gtk_widget_show(entry); + gtk_signal_connect_object (GTK_OBJECT (entry), "activate", GTK_SIGNAL_FUNC(MenuSelect), (gpointer) (intptr_t) ((n<<16)+i)); + if(mb[i].accel) { + guint accelerator_key; + GdkModifierType accelerator_mods; + + gtk_accelerator_parse(mb[i].accel, &accelerator_key, &accelerator_mods); +#ifdef __APPLE__ + if(accelerator_mods & GDK_CONTROL_MASK) { // in OSX use Meta where Linux uses Ctrl + accelerator_mods &= ~GDK_CONTROL_MASK; // clear Ctrl flag + accelerator_mods |= GDK_META_MASK; // set Meta flag + } +#endif + gtk_widget_add_accelerator (GTK_WIDGET(entry), "activate",GtkAccelerators, + accelerator_key, accelerator_mods, GTK_ACCEL_VISIBLE); + } } else entry = gtk_separator_menu_item_new(); + gtk_widget_show(entry); gtk_menu_append(GTK_MENU (menu), entry); //CreateMenuItem(menu, opt->min & NO_GETTEXT ? msg : _(msg), (XtCallbackProc) ComboSelect, (n<<16)+i); mb[i].handle = (void*) entry; // save item ID, for enabling / checkmarking @@ -438,7 +465,7 @@ CreateMenuPopup (Option *opt, int n, int def) return menu; } -Option *typeIn; // kludge to distinguish type-in callback from input-box callback +Option *icsBox; // kludge to distinguish type-in callback from input-box callback void CursorAtEnd (Option *opt) @@ -447,21 +474,9 @@ CursorAtEnd (Option *opt) } 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) { +ICSKeyEvent (int keyval) +{ // TODO_GTK: arrow-handling should really be integrated in type-in proc, and this should be a backe-end OK handler + switch(keyval) { case GDK_Return: IcsKey(0); return TRUE; case GDK_Up: IcsKey(1); return TRUE; case GDK_Down: IcsKey(-1); return TRUE; @@ -487,6 +502,8 @@ TypeInProc (GtkWidget *widget, GdkEventKey *event, gpointer gdata) shiftState = event->state & GDK_SHIFT_MASK; controlState = event->state & GDK_CONTROL_MASK; switch(event->keyval) { + case GDK_Up: IcsHist(1, opt, dlg); break; + case GDK_Down: IcsHist(-1, opt, dlg); break; case GDK_Return: if(GenericReadout(dialogOptions[dlg], -1)) PopDown(dlg); break; @@ -515,6 +532,61 @@ HighlightText (Option *opt, int from, int to, Boolean highlight) gtk_text_buffer_apply_tag_by_name(opt->handle, highlight ? "highlight" : "normal", &start, &end); } +static char **names; +static int curFG, curBG, curAttr; +static GdkColor backgroundColor; + +void +SetTextColor(char **cnames, int fg, int bg, int attr) +{ + if(fg < 0) fg = 0; if(bg < 0) bg = 7; + names = cnames; curFG = fg; curBG = bg, curAttr = attr; + if(attr == -2) { // background color of ICS console. + gdk_color_parse(cnames[bg&7], &backgroundColor); + curAttr = 0; + } +} + +void +AppendColorized (Option *opt, char *s, int count) +{ + static GtkTextIter end; + static GtkTextTag *fgTags[8], *bgTags[8], *font, *bold, *normal, *attr = NULL; + + if(!font) { + font = gtk_text_buffer_create_tag(opt->handle, NULL, "font", "Monospace normal", NULL); + gtk_widget_modify_base(GTK_WIDGET(opt->textValue), GTK_STATE_NORMAL, &backgroundColor); + } + + gtk_text_buffer_get_end_iter(GTK_TEXT_BUFFER(opt->handle), &end); + + if(names) { + if(curAttr == 1) { + if(!bold) bold = gtk_text_buffer_create_tag(opt->handle, NULL, "weight", PANGO_WEIGHT_BOLD, NULL); + attr = bold; + } else { + if(!normal) normal = gtk_text_buffer_create_tag(opt->handle, NULL, "weight", PANGO_WEIGHT_NORMAL, NULL); + attr = normal; + } + if(!fgTags[curFG]) { + fgTags[curFG] = gtk_text_buffer_create_tag(opt->handle, NULL, "foreground", names[curFG], NULL); + } + if(!bgTags[curBG]) { + bgTags[curBG] = gtk_text_buffer_create_tag(opt->handle, NULL, "background", names[curBG], NULL); + } + gtk_text_buffer_insert_with_tags(opt->handle, &end, s, count, fgTags[curFG], bgTags[curBG], font, attr, NULL); + } else + gtk_text_buffer_insert_with_tags(opt->handle, &end, s, count, font, NULL); + +} + +void +Show (Option *opt, int hide) +{ + if(hide) gtk_widget_hide(opt->handle); + else gtk_widget_show(opt->handle); +} + int ShiftKeys () { // bassic primitive for determining if modifier keys are pressed @@ -524,7 +596,7 @@ ShiftKeys () static gboolean GameListEvent(GtkWidget *widget, GdkEvent *event, gpointer gdata) { - int n = (int) gdata; + int n = (intptr_t) gdata; if(n == 4) { if(((GdkEventKey *) event)->keyval != GDK_Return) return FALSE; @@ -564,9 +636,7 @@ MemoEvent(GtkWidget *widget, GdkEvent *event, gpointer gdata) 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 + gint index = 0, x, y; switch(event->type) { // figure out what's up case GDK_MOTION_NOTIFY: @@ -583,6 +653,10 @@ MemoEvent(GtkWidget *widget, GdkEvent *event, gpointer gdata) button = bevent->button; shiftState = bevent->state & GDK_SHIFT_MASK; controlState = bevent->state & GDK_CONTROL_MASK; + if(memo->type == Label) { // only clock widgets use this + ((ButtonCallback*) memo->target)(button == 1 ? memo->value : -memo->value); + return TRUE; + } // GTK_TODO: is this really the most efficient way to get the character at the mouse cursor??? gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(widget), GTK_TEXT_WINDOW_WIDGET, w, h, &x, &y); gtk_text_view_get_iter_at_location(GTK_TEXT_VIEW(widget), &start, x, y); @@ -592,7 +666,7 @@ MemoEvent(GtkWidget *widget, GdkEvent *event, gpointer gdata) /* 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); + val = gtk_text_buffer_get_text (memo->handle, &start, &end, FALSE); break; default: return FALSE; // should not happen @@ -611,15 +685,15 @@ AddHandler (Option *opt, DialogClass dlg, int nr) 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); + icsBox = opt; + case 2: // move type-in + g_signal_connect(opt->handle, "key-press-event", G_CALLBACK (TypeInProc), (gpointer) (dlg<<16 | (opt - dialogOptions[dlg]))); 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 ); + g_signal_connect(opt->handle, "key-press-event", G_CALLBACK (GameListEvent), (gpointer) (intptr_t) nr ); break; case 6: // engine output (uses generic textview callback) break; @@ -633,7 +707,7 @@ AddHandler (Option *opt, DialogClass dlg, int nr) GtkWidget *shells[NrOfDialogs]; DialogClass parents[NrOfDialogs]; WindowPlacement *wp[NrOfDialogs] = { // Beware! Order must correspond to DialogClass enum - NULL, &wpComment, &wpTags, NULL, NULL, NULL, NULL, &wpMoveHistory, &wpGameList, &wpEngineOutput, &wpEvalGraph, + NULL, &wpComment, &wpTags, &wpTextMenu, NULL, &wpConsole, &wpDualBoard, &wpMoveHistory, &wpGameList, &wpEngineOutput, &wpEvalGraph, NULL, NULL, NULL, NULL, &wpMain }; @@ -668,7 +742,7 @@ MemoEvent(GtkWidget *widget, GdkEvent *event, gpointer gdata) SubstructureRedirectMask | SubstructureNotifyMask, &xev); - XFlush(xDisplay); + XFlush(xDisplay); XSync(xDisplay, False); #endif } @@ -676,13 +750,13 @@ MemoEvent(GtkWidget *widget, GdkEvent *event, gpointer gdata) int PopDown (DialogClass n) { - //Arg args[10]; - - if (!shellUp[n] || !shells[n]) return 0; + //Arg args[10]; + + if (!shellUp[n] || !shells[n]) return 0; if(n && wp[n]) { // remember position GetActualPlacement(shells[n], wp[n]); } - + gtk_widget_hide(shells[n]); shellUp[n]--; // count rather than clear @@ -720,11 +794,12 @@ gboolean GenericPopDown(w, resptype, gdata) // I guess BrowserDlg will be abandoned, as GTK has a better browser of its own if(shellUp[BrowserDlg] && dlg != BrowserDlg || dialogError) return True; // prevent closing dialog when it has an open file-browse daughter #else - if(browserUp || dialogError && dlg != FatalDlg) return True; // prevent closing dialog when it has an open file-browse or error-popup daughter + if(browserUp || dialogError && dlg != FatalDlg || dlg == MasterDlg && shellUp[TransientDlg]) + return True; // prevent closing dialog when it has an open file-browse, transient or error-popup daughter #endif shells[dlg] = w; // make sure we pop down the right one in case of multiple instances - /* OK pressed */ + /* OK pressed */ if (resptype == GTK_RESPONSE_ACCEPT) { if (GenericReadout(currentOption, -1)) PopDown(dlg); return TRUE; @@ -739,11 +814,11 @@ gboolean GenericPopDown(w, resptype, gdata) } int AppendText(Option *opt, char *s) -{ +{ char *v; int len; - GtkTextIter end; - + GtkTextIter end; + GetWidgetTextGTK(opt->handle, &v); len = strlen(v); g_free(v); @@ -793,7 +868,7 @@ GraphEventProc(GtkWidget *widget, GdkEvent *event, gpointer gdata) /* Get window size */ gtk_widget_get_allocation(widget, &a); w = a.width; h = a.height; -//printf("expose %dx%d @ (%d,%d)\n", w, h, a.x, a.y); +//printf("expose %dx%d @ (%d,%d): %dx%d @(%d,%d)\n", w, h, a.x, a.y, eevent->area.width, eevent->area.height, eevent->area.x, eevent->area.y); #ifdef TODO_GTK j = 0; XtSetArg(args[j], XtNwidth, &w); j++; @@ -817,8 +892,15 @@ GraphEventProc(GtkWidget *widget, GdkEvent *event, gpointer gdata) // to give drawing routines opportunity to use it before first expose event // (which are only processed when main gets to the event loop, so after all init!) // so only change when size is no longer good + cairo_t *cr; if(graph->choice) cairo_surface_destroy((cairo_surface_t *) graph->choice); graph->choice = (char**) cairo_image_surface_create (CAIRO_FORMAT_ARGB32, w, h); + // paint white, to prevent weirdness when people maximize window and drag pieces over space next to board + cr = cairo_create ((cairo_surface_t *) graph->choice); + cairo_rectangle (cr, 0, 0, w, h); + cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 1.0); + cairo_fill(cr); + cairo_destroy (cr); break; } w = eevent->area.width; @@ -874,8 +956,8 @@ GraphExpose (Option *opt, int x, int y, int w, int h) void GenericCallback(GtkWidget *widget, gpointer gdata) { const gchar *name; - char buf[MSG_SIZ]; - int data = (intptr_t) gdata; + char buf[MSG_SIZ]; + int data = (intptr_t) gdata; DialogClass dlg; #ifdef TODO_GTK GtkWidget *sh = XtParent(XtParent(XtParent(w))), *oldSh; @@ -890,18 +972,18 @@ void GenericCallback(GtkWidget *widget, gpointer gdata) oldSh = shells[dlg]; shells[dlg] = sh; // bow to reality if (data == 30000) { // cancel - PopDown(dlg); + 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 if(currentCps) { - name = gtk_button_get_label (GTK_BUTTON(widget)); + name = gtk_button_get_label (GTK_BUTTON(widget)); if(currentOption[data].type == SaveButton) GenericReadout(currentOption, -1); snprintf(buf, MSG_SIZ, "option %s\n", name); SendToProgram(buf, currentCps); - } else ((ButtonCallback*) currentOption[data].target)(data); + } else ((ButtonCallback*) currentOption[data].target)(data); shells[dlg] = oldSh; // in case of multiple instances, restore previous (as this one could be popped down now) } @@ -914,11 +996,11 @@ void BrowseGTK(GtkWidget *widget, gpointer gdata) GtkFileFilter *gtkfilter_all; int opt_i = (intptr_t) gdata; GtkFileChooserAction fc_action; - + gtkfilter = gtk_file_filter_new(); gtkfilter_all = gtk_file_filter_new(); - char fileext[10] = "*"; + char fileext[MSG_SIZ]; /* select file or folder depending on option_type */ if (currentOption[opt_i].type == PathName) @@ -937,26 +1019,31 @@ void BrowseGTK(GtkWidget *widget, gpointer gdata) 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); + if (currentOption[opt_i].textValue != NULL) + { + char *q, *p = currentOption[opt_i].textValue; + gtk_file_filter_set_name (gtkfilter, p); + while(*p) { + snprintf(fileext, MSG_SIZ, "*%s", p); + while(*p) if(*p++ == ' ') break; + for(q=fileext; *q; q++) if(*q == ' ') { *q = NULLCHAR; break; } + gtk_file_filter_add_pattern(gtkfilter, fileext); + } 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); + 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)); + filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog)); entry = currentOption[opt_i].handle; - gtk_entry_set_text (GTK_ENTRY (entry), filename); + gtk_entry_set_text (GTK_ENTRY (entry), filename); g_free (filename); } @@ -1071,41 +1158,44 @@ Pack (GtkWidget *hbox, GtkWidget *table, GtkWidget *entry, int left, int right, int GenericPopUp (Option *option, char *title, DialogClass dlgNr, DialogClass parent, int modal, int topLevel) -{ +{ GtkWidget *dialog = NULL; gint w; GtkWidget *label; GtkWidget *box; GtkWidget *checkbutton; GtkWidget *entry; - GtkWidget *oldHbox, *hbox = NULL; + GtkWidget *oldHbox = NULL, *hbox = NULL; GtkWidget *pane = NULL; GtkWidget *button; GtkWidget *table; - GtkWidget *spinner; + GtkWidget *spinner; GtkAdjustment *spinner_adj; GtkWidget *combobox; GtkWidget *textview; - GtkTextBuffer *textbuffer; - GdkColor color; + GtkTextBuffer *textbuffer; + GdkColor color; GtkWidget *actionarea; - GtkWidget *sw; - GtkWidget *list; - GtkWidget *graph; - GtkWidget *menuButton; - GtkWidget *menuBar; - GtkWidget *menu; - - int i, j, arraysize, left, top, height=999, width=1, boxStart, breakType = 0, r; + GtkWidget *sw; + GtkWidget *list; + GtkWidget *graph; + GtkWidget *menuButton; + GtkWidget *menuBar = NULL; + GtkWidget *menu; + + int i, j, arraysize, left, top, height=999, width=1, boxStart=0, breakType = 0, r; char def[MSG_SIZ], *msg, engineDlg = (currentCps != NULL && dlgNr != BrowserDlg); + gboolean expandable = FALSE; if(dlgNr < PromoDlg && shellUp[dlgNr]) return 0; // already up if(dlgNr && dlgNr < PromoDlg && shells[dlgNr]) { // reusable, and used before (but popped down) gtk_widget_show(shells[dlgNr]); shellUp[dlgNr] = True; + if(wp[dlgNr]) gtk_window_move(GTK_WINDOW(shells[dlgNr]), wp[dlgNr]->x, wp[dlgNr]->y); return 0; } + if(dlgNr == TransientDlg && parent == BoardWindow && shellUp[MasterDlg]) parent = MasterDlg; // MasterDlg can always take role of main window dialogOptions[dlgNr] = option; // make available to callback // post currentOption globally, so Spin and Combo callbacks can already use it @@ -1117,10 +1207,10 @@ GenericPopUp (Option *option, char *title, DialogClass dlgNr, DialogClass parent // if(n > 50) width = 4; else if(n>24) width = 2; else width = 1; width = n / 20 + 1; height = n / width + 1; -printf("n=%d, h=%d, w=%d\n",n,height,width); +if(appData.debugMode) printf("n=%d, h=%d, w=%d\n",n,height,width); // if(n && (currentOption[n-1].type == Button || currentOption[n-1].type == SaveButton)) currentOption[n].min = SAME_ROW; // OK on same line currentOption[n].type = EndMark; currentOption[n].target = NULL; // delimit list by callback-less end mark - } + } parents[dlgNr] = parent; #ifdef TODO_GTK @@ -1131,45 +1221,56 @@ printf("n=%d, h=%d, w=%d\n",n,height,width); XtCreatePopupShell(title, !top || !appData.topLevel ? transientShellWidgetClass : topLevelShellWidgetClass, shells[parent], args, i); #endif - dialog = gtk_dialog_new_with_buttons( title, - GTK_WINDOW(shells[parent]), - GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR | - (modal ? GTK_DIALOG_MODAL : 0), - GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, - GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, - NULL ); + + if(topLevel) + { + dialog = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_title(GTK_WINDOW(dialog), title); + box = gtk_vbox_new(FALSE,0); + gtk_container_add (GTK_CONTAINER (dialog), box); + } + else + { + dialog = gtk_dialog_new_with_buttons( title, + GTK_WINDOW(shells[parent]), + GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR | + (modal ? GTK_DIALOG_MODAL : 0), + GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, + GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, + NULL ); + box = gtk_dialog_get_content_area( GTK_DIALOG( dialog ) ); + } shells[dlgNr] = dialog; - box = gtk_dialog_get_content_area( GTK_DIALOG( dialog ) ); -// gtk_box_set_spacing(GTK_BOX(box), 5); +// gtk_box_set_spacing(GTK_BOX(box), 5); arraysize = 0; for (i=0;option[i].type != EndMark;i++) { - arraysize++; + arraysize++; } table = gtk_table_new(arraysize, r=TableWidth(option), FALSE); left = 0; - top = -1; + top = -1; for (i=0;option[i].type != EndMark;i++) { - if(option[i].type == -1) continue; + if(option[i].type == Skip) continue; top++; //printf("option =%2d, top =%2d\n", i, top); - if (top >= height) { - gtk_table_resize(GTK_TABLE(table), height, r); + if (top >= height || breakType) { + gtk_table_resize(GTK_TABLE(table), top - (breakType != 0), r); if(!pane) { // multi-column: put tables in intermediate hbox - if(breakType || engineDlg) + if(breakType & SAME_ROW || engineDlg) pane = gtk_hbox_new (FALSE, 0); else pane = gtk_vbox_new (FALSE, 0); gtk_box_set_spacing(GTK_BOX(pane), 5 + 5*breakType); gtk_box_pack_start (GTK_BOX (/*GTK_DIALOG (dialog)->vbox*/box), pane, TRUE, TRUE, 0); } - gtk_box_pack_start (GTK_BOX (pane), table, TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (pane), table, expandable, TRUE, 0); table = gtk_table_new(arraysize - i, r=TableWidth(option + i), FALSE); - top = 0; - } + top = breakType = 0; expandable = FALSE; + } if(!SameRow(&option[i])) { if(SameRow(&option[i+1])) { GtkAttachOptions x = GTK_FILL; @@ -1184,7 +1285,7 @@ printf("n=%d, h=%d, w=%d\n",n,height,width); } else hbox = NULL; //and also make sure no hbox exists if only singl option on row } else top--; switch(option[i].type) { - case Fractional: + case Fractional: snprintf(def, MSG_SIZ, "%.2f", *(float*)option[i].target); option[i].value = *(float*)option[i].target; goto tBox; @@ -1192,7 +1293,7 @@ printf("n=%d, h=%d, w=%d\n",n,height,width); if(!currentCps) option[i].value = *(int*)option[i].target; snprintf(def, MSG_SIZ, "%d", option[i].value); case TextBox: - case FileName: + case FileName: case PathName: tBox: label = gtk_label_new(option[i].name); @@ -1203,8 +1304,10 @@ printf("n=%d, h=%d, w=%d\n",n,height,width); 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].value > 80){ - textview = gtk_text_view_new(); + if (option[i].type==TextBox && option[i].value > 80){ + GtkTextIter iter; + expandable = TRUE; + textview = gtk_text_view_new(); 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++; } @@ -1217,9 +1320,10 @@ printf("n=%d, h=%d, w=%d\n",n,height,width); 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); - - textbuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview)); - /* check if label is empty */ + gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_OUT); + + textbuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview)); + /* check if label is empty */ if (strcmp(option[i].name,"") != 0) { gtk_table_attach(GTK_TABLE(table), label, left, left+1, top, top+1, GTK_FILL, GTK_FILL, 2, 1); Pack(hbox, table, sw, left+1, left+r, top, 0); @@ -1227,19 +1331,21 @@ printf("n=%d, h=%d, w=%d\n",n,height,width); else { /* no label so let textview occupy all columns */ Pack(hbox, table, sw, left, left+r, top, GTK_EXPAND); - } + } 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); + gtk_text_buffer_set_text (textbuffer, "", -1); option[i].handle = (void*)textbuffer; option[i].textValue = (char*)textview; + gtk_text_buffer_get_iter_at_offset(textbuffer, &iter, -1); + gtk_text_buffer_create_mark(textbuffer, "scrollmark", &iter, FALSE); // permanent mark 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; + break; } entry = gtk_entry_new(); @@ -1249,7 +1355,7 @@ printf("n=%d, h=%d, w=%d\n",n,height,width); else if (currentCps) gtk_entry_set_text (GTK_ENTRY (entry), option[i].textValue); else if ( *(char**)option[i].target != NULL ) - gtk_entry_set_text (GTK_ENTRY (entry), *(char**)option[i].target); + gtk_entry_set_text (GTK_ENTRY (entry), *(char**)option[i].target); //gtk_entry_set_width_chars (GTK_ENTRY (entry), 18); gtk_entry_set_max_length (GTK_ENTRY (entry), w); @@ -1258,7 +1364,7 @@ printf("n=%d, h=%d, w=%d\n",n,height,width); if (strcmp(option[i].name, "") != 0) gtk_table_attach(GTK_TABLE(table), label, left, left+1, top, top+1, GTK_FILL, GTK_FILL, 2, 1); // leading names do not expand - if (option[i].type == Spin) { + if (option[i].type == Spin) { spinner_adj = (GtkAdjustment *) gtk_adjustment_new (option[i].value, option[i].min, option[i].max, 1.0, 0.0, 0.0); spinner = gtk_spin_button_new (spinner_adj, 1.0, 0); gtk_table_attach(GTK_TABLE(table), spinner, left+1, left+r, top, top+1, GTK_FILL | GTK_EXPAND, GTK_FILL, 2, 1); @@ -1269,26 +1375,26 @@ printf("n=%d, h=%d, w=%d\n",n,height,width); button = gtk_button_new_with_label ("Browse"); gtk_table_attach(GTK_TABLE(table), button, left+2, left+r, top, top+1, GTK_FILL, GTK_FILL, 2, 1); // Browse button does not expand g_signal_connect (button, "clicked", G_CALLBACK (BrowseGTK), (gpointer)(intptr_t) i); - option[i].handle = (void*)entry; + option[i].handle = (void*)entry; } else { Pack(hbox, table, entry, left + (strcmp(option[i].name, "") != 0), left+r, top, 0); option[i].handle = (void*)entry; - } + } break; case CheckBox: - checkbutton = gtk_check_button_new_with_label(option[i].name); + checkbutton = gtk_check_button_new_with_label(option[i].name); if(!currentCps) option[i].value = *(Boolean*)option[i].target; gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbutton), option[i].value); gtk_table_attach(GTK_TABLE(table), checkbutton, left, left+r, top, top+1, GTK_FILL | GTK_EXPAND, GTK_FILL, 2, 0); - option[i].handle = (void *)checkbutton; - break; - case Icon: + option[i].handle = (void *)checkbutton; + break; + case Icon: option[i].handle = (void *) (label = gtk_image_new_from_pixbuf(NULL)); gtk_widget_set_size_request(label, option[i].max ? option[i].max : -1, -1); Pack(hbox, table, label, left, left+2, top, 0); - break; - case Label: + break; + case Label: option[i].handle = (void *) (label = gtk_label_new(option[i].name)); /* Left Justify */ gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); @@ -1298,11 +1404,15 @@ printf("n=%d, h=%d, w=%d\n",n,height,width); label = frame; } gtk_widget_set_size_request(label, option[i].max ? option[i].max : -1, -1); - Pack(hbox, table, label, left, left+2, top, 0); if(option[i].target) { // allow user to specify event handler for button presses + button = gtk_event_box_new(); + gtk_container_add(GTK_CONTAINER(button), label); + label = button; gtk_widget_add_events(GTK_WIDGET(label), GDK_BUTTON_PRESS_MASK); g_signal_connect(label, "button-press-event", G_CALLBACK(MemoEvent), (gpointer) &option[i]); + gtk_widget_set_sensitive(label, TRUE); } + Pack(hbox, table, label, left, left+2, top, 0); break; case SaveButton: case Button: @@ -1318,48 +1428,48 @@ printf("n=%d, h=%d, w=%d\n",n,height,width); if(option[i].textValue) { gdk_color_parse( option[i].textValue, &color ); gtk_widget_modify_bg ( GTK_WIDGET(button), GTK_STATE_NORMAL, &color ); - gtk_widget_set_sensitive(button, appData.noChessProgram || option[i].value < 0 - || strstr(first.variants, VariantName(option[i].value))); + gtk_widget_set_sensitive(button, option[i].value >= 0 && (appData.noChessProgram + || strstr(first.variants, VariantName(option[i].value)))); } - + Pack(hbox, table, button, left, left+1, top, 0); - g_signal_connect (button, "clicked", G_CALLBACK (GenericCallback), (gpointer)(intptr_t) i + (dlgNr<<16)); - option[i].handle = (void*)button; - break; + g_signal_connect (button, "clicked", G_CALLBACK (GenericCallback), (gpointer)(intptr_t) i + (dlgNr<<16)); + option[i].handle = (void*)button; + break; case ComboBox: label = gtk_label_new(option[i].name); /* Left Justify */ gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); gtk_table_attach(GTK_TABLE(table), label, left, left+1, top, top+1, GTK_FILL, GTK_FILL, 2, 1); - combobox = gtk_combo_box_new_text(); + combobox = gtk_combo_box_new_text(); for(j=0;;j++) { if ( ((char **) option[i].textValue)[j] == NULL) break; - gtk_combo_box_append_text(GTK_COMBO_BOX(combobox), ((char **) option[i].choice)[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 { - for(j=0; option[i].choice[j]; j++) { + for(j=0; option[i].choice[j]; j++) { 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) option[i].value = 0; - else + else option[i].value = j; } - //option[i].value = j + (option[i].choice[j] == NULL); - gtk_combo_box_set_active(GTK_COMBO_BOX(combobox), option[i].value); - + //option[i].value = j + (option[i].choice[j] == NULL); + gtk_combo_box_set_active(GTK_COMBO_BOX(combobox), option[i].value); + Pack(hbox, table, combobox, left+1, left+r, top, 0); g_signal_connect(G_OBJECT(combobox), "changed", G_CALLBACK(ComboSelect), (gpointer) (intptr_t) (i + 256*dlgNr)); option[i].handle = (void*)combobox; - values[i] = option[i].value; + values[i] = option[i].value; break; case ListBox: { @@ -1383,12 +1493,14 @@ printf("n=%d, h=%d, w=%d\n",n,height,width); 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), option[i].max ? option[i].max : -1, option[i].value ? option[i].value : -1); - + gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_OUT); + if(option[i].textValue) // generic callback for double-clicking listbox item - g_signal_connect(list, "button-press-event", G_CALLBACK(ListCallback), (gpointer) (dlgNr<<16 | i) ); + g_signal_connect(list, "button-press-event", G_CALLBACK(ListCallback), (gpointer) (intptr_t) (dlgNr<<16 | i) ); /* never has label, so let listbox occupy all columns */ Pack(hbox, table, sw, left, left+r, top, GTK_EXPAND); + expandable = TRUE; } break; case Graph: @@ -1410,6 +1522,7 @@ printf("n=%d, h=%d, w=%d\n",n,height,width); graph = frame; } Pack(hbox, table, graph, left, left+r, top, GTK_EXPAND); + expandable = TRUE; #ifdef TODO_GTK if(option[i].min & SAME_ROW) last = forelast, forelast = lastrow; @@ -1449,9 +1562,21 @@ printf("n=%d, h=%d, w=%d\n",n,height,width); break; case BarEnd: top--; +#ifndef __APPLE__ gtk_table_attach(GTK_TABLE(table), menuBar, left, left+r, top, top+1, GTK_FILL | GTK_EXPAND, GTK_FILL, 2, 1); - + if(option[i].target) ((ButtonCallback*)option[i].target)(boxStart); // callback that can make sizing decisions +#else + top--; // in OSX menu bar is not put in window, so also don't count it + { // in stead, offer it to OSX, and move About item to top of App menu + GtkosxApplication *theApp = g_object_new(GTKOSX_TYPE_APPLICATION, NULL); + extern MenuItem helpMenu[]; // oh, well... Adding items in help menu breaks this anyway + gtk_widget_hide (menuBar); + gtkosx_application_set_menu_bar(theApp, GTK_MENU_SHELL(menuBar)); + gtkosx_application_insert_app_menu_item(theApp, GTK_MENU_ITEM(helpMenu[8].handle), 0); // hack + gtkosx_application_sync_menubar(theApp); + } +#endif break; case BoxEnd: // XtManageChildren(&form, 1); @@ -1460,50 +1585,57 @@ printf("n=%d, h=%d, w=%d\n",n,height,width); if(option[i].target) ((ButtonCallback*)option[i].target)(boxStart); // callback that can make sizing decisions break; case Break: - breakType = option[i].min & SAME_ROW; - top = height; // force next option to start in a new table - break; + breakType = option[i].min & SAME_ROW | BORDER; // kludge to flag we must break + option[i].handle = table; + break; case PopUp: top--; - break; + break; default: printf("GenericPopUp: unexpected case in switch. i=%d type=%d name=%s.\n", i, option[i].type, option[i].name); break; - } + } } + gtk_table_resize(GTK_TABLE(table), top+1, r); if(pane) - gtk_box_pack_start (GTK_BOX (pane), table, TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (pane), table, expandable, TRUE, 0); else - gtk_table_resize(GTK_TABLE(table), top+1, r), gtk_box_pack_start (GTK_BOX (/*GTK_DIALOG (dialog)->vbox*/box), table, TRUE, TRUE, 0); option[i].handle = (void *) table; // remember last table in EndMark handle (for hiding Engine-Output pane). gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_NONE); + /* Show dialog */ - gtk_widget_show_all( dialog ); + gtk_widget_show_all( dialog ); /* hide OK/cancel buttons */ - if((option[i].min & 2)) { - actionarea = gtk_dialog_get_action_area(GTK_DIALOG(dialog)); - gtk_widget_hide(actionarea); - } - - g_signal_connect (dialog, "response", + if(!topLevel) + { + if((option[i].min & NO_OK)) { + actionarea = gtk_dialog_get_action_area(GTK_DIALOG(dialog)); + gtk_widget_hide(actionarea); + } else if((option[i].min & NO_CANCEL)) { + button = gtk_dialog_get_widget_for_response(GTK_DIALOG(dialog), GTK_RESPONSE_REJECT); + gtk_widget_hide(button); + } + g_signal_connect (dialog, "response", G_CALLBACK (GenericPopDown), (gpointer)(intptr_t) dlgNr); + } + g_signal_connect (dialog, "delete-event", G_CALLBACK (GenericPopDown), (gpointer)(intptr_t) dlgNr); shellUp[dlgNr]++; - if(dlgNr && wp[dlgNr] && wp[dlgNr]->width > 0) { // if persistent window-info available, reposition + if(dlgNr && wp[dlgNr]) { // if persistent window-info available, reposition + if(wp[dlgNr]->x > 0 && wp[dlgNr]->y > 0) gtk_window_move(GTK_WINDOW(dialog), wp[dlgNr]->x, wp[dlgNr]->y); -//printf("moved %d to (%d,%d)\n", dlgNr, wp[dlgNr]->x, wp[dlgNr]->y); + if(wp[dlgNr]->width > 0 && wp[dlgNr]->height > 0) gtk_window_resize(GTK_WINDOW(dialog), wp[dlgNr]->width, wp[dlgNr]->height); -//printf("resized %d to %dx%d\n", dlgNr, wp[dlgNr]->width, wp[dlgNr]->height); } return 1; // tells caller he must do initialization (e.g. add specific event handlers) @@ -1529,8 +1661,8 @@ SendTextCB (Widget w, XtPointer client_data, Atom *selection, void SendText (int n) { -#ifdef TODO_GTK char *p = (char*) textOptions[n].choice; +#ifdef TODO_GTK if(strstr(p, "$name")) { XtGetSelectionValue(menuBarWidget, XA_PRIMARY, XA_STRING, @@ -1538,28 +1670,20 @@ SendText (int n) (XtPointer) (intptr_t) n, /* client_data passed to PastePositionCB */ CurrentTime ); - } else SendString(p); + } else #endif + SendString(p); } void SetInsertPos (Option *opt, int pos) { -#ifdef TODO_GTK - Arg args[16]; - XtSetArg(args[0], XtNinsertPosition, pos); - XtSetValues(opt->handle, args, 1); -// SetFocus(opt->handle, shells[InputBoxDlg], NULL, False); // No idea why this does not work, and the following is needed: -// XSetInputFocus(xDisplay, XtWindow(opt->handle), RevertToPointerRoot, CurrentTime); -#endif + if(opt->value > 80) ScrollToCursor(opt, pos); + else gtk_editable_set_position(GTK_EDITABLE(opt->handle), pos); } void HardSetFocus (Option *opt) { -#ifdef TODO_GTK - XSetInputFocus(xDisplay, XtWindow(opt->handle), RevertToPointerRoot, CurrentTime); -#endif + FocusOnWidget(opt, 0); // second arg not used in GDK } - -