/*
* xoptions.c -- Move list window, part of X front end for XBoard
*
- * Copyright 2000, 2009, 2010, 2011, 2012, 2013 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
#include <cairo/cairo-xlib.h>
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>
+#ifdef __APPLE__
+# include <gtkmacintegration/gtkosxapplication.h>
+#endif
#include "common.h"
#include "backend.h"
ScrollToCursor (Option *opt, int caretPos)
{
static GtkTextIter iter;
+ static GtkTextMark *mark;
+ if(!mark) mark = gtk_text_mark_new(NULL, 0);
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_add_mark((GtkTextBuffer *) opt->handle, mark, &iter);
+ gtk_text_view_scroll_to_mark((GtkTextView *) opt->textValue, mark, 0.0, 0, 0.5, 0.5);
+// gtk_text_view_scroll_to_iter((GtkTextView *) opt->textValue, &iter, 0.0, 0, 0.5, 0.5);
+ gtk_text_buffer_delete_mark((GtkTextBuffer *) opt->handle, mark);
}
int
{
char *msg = mb[i].string;
if(!msg) break;
+#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, "----")) { //
if(!(opt->min & NO_GETTEXT)) msg = _(msg);
if(mb[i].handle) {
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
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;
GtkTextIter start, end;
String val = NULL;
gboolean res;
- gint index, x, y;
+ gint index = 0, x, y;
switch(event->type) { // figure out what's up
case GDK_MOTION_NOTIFY:
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;
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, NULL, NULL, NULL, &wpDualBoard, &wpMoveHistory, &wpGameList, &wpEngineOutput, &wpEvalGraph,
NULL, NULL, NULL, NULL, &wpMain
};
// 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
/* 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++;
// 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;
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)
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) )
+ if (currentOption[opt_i].textValue != NULL)
{
- strcat(fileext, currentOption[opt_i].textValue);
- gtk_file_filter_add_pattern(gtkfilter, fileext);
- gtk_file_filter_set_name (gtkfilter, currentOption[opt_i].textValue);
+ 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);
GtkWidget *box;
GtkWidget *checkbutton;
GtkWidget *entry;
- GtkWidget *oldHbox, *hbox = NULL;
+ GtkWidget *oldHbox = NULL, *hbox = NULL;
GtkWidget *pane = NULL;
GtkWidget *button;
GtkWidget *table;
GtkWidget *list;
GtkWidget *graph;
GtkWidget *menuButton;
- GtkWidget *menuBar;
+ GtkWidget *menuBar = NULL;
GtkWidget *menu;
- int i, j, arraysize, left, top, height=999, width=1, boxStart, breakType = 0, r;
+ 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);
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
// 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
}
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);
arraysize = 0;
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);
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);
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);
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 );
/* hide OK/cancel buttons */
- 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",
+ 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)
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