Improve resize/co-dragging GTK
[xboard.git] / gtk / xboard.c
index dbb2670..5ad75b1 100644 (file)
@@ -175,15 +175,12 @@ extern char *getenv();
 #  define IMG ".png"
    // redefine some defaults
 #  undef ICS_LOGON
-#  undef DATADIR
 #  undef LOCALEDIR
 #  undef SETTINGS_FILE
 #  define ICS_LOGON "Library/Preferences/XboardICS.conf"
-#  define DATADIR dataDir
 #  define LOCALEDIR localeDir
 #  define SETTINGS_FILE masterSettings
 #  define SYNC_MENUBAR gtkosx_application_sync_menubar(theApp)
-   char dataDir[MSG_SIZ]; // for expanding ~~
    char localeDir[MSG_SIZ];
    char masterSettings[MSG_SIZ];
 #else
@@ -259,6 +256,7 @@ GtkAccelGroup *GtkAccelerators;
 typedef unsigned int BoardSize;
 BoardSize boardSize;
 Boolean chessProgram;
+int initialSquareSize;
 
 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner
 int smallLayout = 0, tinyLayout = 0,
@@ -383,7 +381,6 @@ colorVariable[] = {
 
 // [HGM] font: keep a font for each square size, even non-stndard ones
 #define NUM_SIZES 18
-#define MAX_SIZE 130
 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
 char *fontTable[NUM_FONTS][MAX_SIZE];
 
@@ -445,6 +442,17 @@ SetFontDefaults ()
   appData.gameListFont = strdup(GAMELIST_FONT_NAME);
 }
 
+void
+ChangeFont (int force, char **font, int fnr, int size, char *def, int pix)
+{
+    if(!fontValid[fnr][size]) {
+       if(fontIsSet[fnr] && !force) return; // unless forced we do not replace an explicitly specified font by a default
+       ASSIGN(fontTable[fnr][size], def);   // use default
+       fontIsSet[fnr] = False;
+    } else fontIsSet[fnr] = True;
+    FREE(*font); *font = InsertPxlSize(fontTable[fnr][size], pix);
+}
+
 void
 CreateFonts ()
 { // no-op, until we identify the code for this already in XBoard and move it here
@@ -518,10 +526,11 @@ SaveFontArg (FILE *f, ArgDescriptor *ad)
     default:
       return;
   }
+  if(fontIsSet[n])           // only save fonts that were not defaults
   for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
-    if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
-       fontTable[n][squareSize] = strdup(name);
-       fontValid[n][squareSize] = True;
+    if(sizeDefaults[i].squareSize == initialSquareSize) { // only for standard sizes!
+       ASSIGN(fontTable[n][initialSquareSize], name);
+       fontValid[n][initialSquareSize] = True;
        break;
   }
   for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
@@ -655,9 +664,9 @@ ResizeBoardWindow (int w, int h, int inhibit)
     gtk_widget_set_size_request(optList[W_BOARD].handle, w, h);   // protect board widget
 //    w += marginW + 1; // [HGM] not sure why the +1 is (sometimes) needed...
 //    h += marginH + a.height + 1;
-    gtk_window_resize(GTK_WINDOW(shellWidget), w, h);
+    gtk_window_resize(GTK_WINDOW(shellWidget), w, 10);
     DoEvents();
-    gtk_widget_set_size_request(optList[W_BOARD].handle, -1, -1); // liberate board again
+    gtk_widget_set_size_request(optList[W_BOARD].handle, 100, 100); // liberate board again
 }
 
 int
@@ -671,21 +680,21 @@ InitializeFonts (int clockFontPxlSize, int coordFontPxlSize, int fontPxlSize)
 {   // determine what fonts to use, and create them
 
     if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
-       appData.clockFont = fontTable[CLOCK_FONT][squareSize];
+       appData.clockFont = fontTable[CLOCK_FONT][squareSize], fontIsSet[CLOCK_FONT] = True;
     if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
-       appData.font = fontTable[MESSAGE_FONT][squareSize];
+       appData.font = fontTable[MESSAGE_FONT][squareSize], fontIsSet[MESSAGE_FONT] = True;
     if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
-       appData.coordFont = fontTable[COORD_FONT][squareSize];
+       appData.coordFont = fontTable[COORD_FONT][squareSize], fontIsSet[COORD_FONT] = True;
     if(!fontIsSet[CONSOLE_FONT] && fontValid[CONSOLE_FONT][squareSize])
-       appData.icsFont = fontTable[CONSOLE_FONT][squareSize];
+       appData.icsFont = fontTable[CONSOLE_FONT][squareSize], fontIsSet[CONSOLE_FONT] = True;
     if(!fontIsSet[EDITTAGS_FONT] && fontValid[EDITTAGS_FONT][squareSize])
-       appData.tagsFont = fontTable[EDITTAGS_FONT][squareSize];
+       appData.tagsFont = fontTable[EDITTAGS_FONT][squareSize], fontIsSet[EDITTAGS_FONT] = True;
     if(!fontIsSet[COMMENT_FONT] && fontValid[COMMENT_FONT][squareSize])
-       appData.commentFont = fontTable[COMMENT_FONT][squareSize];
+       appData.commentFont = fontTable[COMMENT_FONT][squareSize], fontIsSet[COMMENT_FONT] = True;
     if(!fontIsSet[MOVEHISTORY_FONT] && fontValid[MOVEHISTORY_FONT][squareSize])
-       appData.historyFont = fontTable[MOVEHISTORY_FONT][squareSize];
+       appData.historyFont = fontTable[MOVEHISTORY_FONT][squareSize], fontIsSet[MOVEHISTORY_FONT] = True;
     if(!fontIsSet[GAMELIST_FONT] && fontValid[GAMELIST_FONT][squareSize])
-       appData.gameListFont = fontTable[GAMELIST_FONT][squareSize];
+       appData.gameListFont = fontTable[GAMELIST_FONT][squareSize], fontIsSet[GAMELIST_FONT] = True;
 
     appData.font = InsertPxlSize(appData.font, coordFontPxlSize);
     appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
@@ -892,6 +901,7 @@ main (int argc, char **argv)
         theApp = g_object_new(GTKOSX_TYPE_APPLICATION, NULL);
         snprintf(masterSettings, MSG_SIZ, "%s/Contents/Resources/etc/xboard.conf", path);
         snprintf(dataDir, MSG_SIZ, "%s/Contents/Resources/share/xboard", path);
+        snprintf(manDir, MSG_SIZ, "%s/Contents/Resources/share/man", path);
         snprintf(svgDir, MSG_SIZ, "%s/themes/default", dataDir);
         g_signal_connect(theApp, "NSApplicationOpenFile", G_CALLBACK(StartNewXBoard), NULL);
         g_signal_connect(theApp, "NSApplicationWillTerminate", G_CALLBACK(ExitEvent), NULL);
@@ -915,7 +925,8 @@ main (int argc, char **argv)
     if(argc > 1 && !strcmp(argv[1], "--show-config")) { // [HGM] install: called to print config info
        typedef struct {char *name, *value; } Config;
        static Config configList[] = {
-         { "Datadir", DATADIR },
+         { "Datadir", dataDir },
+         { "Mandir", manDir },
          { "Sysconfdir", SYSCONFDIR },
          { NULL }
        };
@@ -960,7 +971,7 @@ main (int argc, char **argv)
 
     { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
        static char buf[MSG_SIZ];
-       snprintf(buf, MSG_SIZ, appData.sysOpen, DATADIR);
+       snprintf(buf, MSG_SIZ, appData.sysOpen, dataDir);
        ASSIGN(appData.sysOpen, buf); // expand %s in -openCommand to DATADIR (usefull for OS X configuring)
        EscapeExpand(buf, appData.firstInitString);
        appData.firstInitString = strdup(buf);
@@ -1074,6 +1085,7 @@ main (int argc, char **argv)
        tinyLayout = szd->tinyLayout;
        // [HGM] font: use defaults from settings file if available and not overruled
     }
+    initialSquareSize = squareSize; // [HGM] remember for saving font info
     if(BOARD_WIDTH != 8) {
        squareSize = (squareSize*8 + BOARD_WIDTH/2)/BOARD_WIDTH; // keep width the same
        lineGap = (squareSize < 37 ? 1 : squareSize < 59 ? 2 : squareSize < 116 ? 3 : 4);
@@ -1139,7 +1151,7 @@ main (int argc, char **argv)
     boardWidget      = optList[W_BOARD].handle;
     menuBarWidget    = optList[W_MENU].handle;
     dropMenu         = optList[W_DROP].handle;
-    titleWidget = optList[optList[W_TITLE].type != -1 ? W_TITLE : W_SMALL].handle;
+    titleWidget = optList[optList[W_TITLE].type != Skip ? W_TITLE : W_SMALL].handle;
 #ifdef TODO_GTK
     formWidget  = XtParent(boardWidget);
     XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
@@ -1661,8 +1673,9 @@ void
 ReSize (WindowPlacement *wp)
 {
        GtkAllocation a;
-       int sqx, sqy, w, h, lg = lineGap;
+       int sqx, sqy, i, w, h, lg = lineGap;
        static int first = 1;
+//     DisplayBothClocks();
        if(wp->width == wpMain.width && wp->height == wpMain.height && !first) return; // not sized
        gtk_widget_get_allocation(optList[W_DROP+1].handle, &a); // table that should contain everything
        w = a.width; h = a.height;
@@ -1688,6 +1701,41 @@ ReSize (WindowPlacement *wp)
            lg = sqx < 37 ? 1 : sqx < 59 ? 2 : sqx < 116 ? 3 : 4;
            if(sqx == oldSqx + 1 && lg == lineGap + 1) sqx = oldSqx, squareSize = 0; // prevent oscillations, force resize by kludge
        }
+       for(h=0; sizeDefaults[h+1].name && sizeDefaults[h].squareSize*8 > sqx*BOARD_WIDTH; h++) {}
+       if(initialSquareSize != sizeDefaults[h].squareSize) { // boardSize changed
+           initialSquareSize = sizeDefaults[h].squareSize; // used for saving font
+           ChangeFont(1, &appData.clockFont, CLOCK_FONT, initialSquareSize, CLOCK_FONT_NAME, 2*(sizeDefaults[h].clockFontPxlSize+1)/3);
+           ChangeFont(1, &appData.font, MESSAGE_FONT, initialSquareSize, DEFAULT_FONT_NAME, sizeDefaults[h].coordFontPxlSize);
+           ChangeFont(0, &appData.icsFont, CONSOLE_FONT, initialSquareSize, CONSOLE_FONT_NAME, sizeDefaults[h].coordFontPxlSize);
+           ChangeFont(0, &appData.tagsFont, EDITTAGS_FONT, initialSquareSize, TAGS_FONT_NAME, sizeDefaults[h].coordFontPxlSize);
+           ChangeFont(0, &appData.commentFont, COMMENT_FONT, initialSquareSize, COMMENT_FONT_NAME, sizeDefaults[h].coordFontPxlSize);
+           ChangeFont(0, &appData.gameListFont, GAMELIST_FONT, initialSquareSize, GAMELIST_FONT_NAME, sizeDefaults[h].coordFontPxlSize);
+           ChangeFont(0, &appData.historyFont, MOVEHISTORY_FONT, initialSquareSize, HISTORY_FONT_NAME, sizeDefaults[h].coordFontPxlSize);
+           DisplayBothClocks();
+           ApplyFont(&mainOptions[W_MESSG], NULL);
+           for(i=1; i<6; i++) ApplyFont(&mainOptions[W_BUTTON+i], NULL);
+           ApplyFont(&tagsOptions[1], NULL);
+           ApplyFont(&commentOptions[0], NULL);
+           ApplyFont(&historyOptions[0], NULL);
+           ApplyFont(&engoutOptions[5], NULL);
+           ApplyFont(&engoutOptions[12], NULL);
+           ApplyFont(&chatOptions[11], appData.icsFont);
+           AppendColorized(&chatOptions[6], NULL, 0); // kludge to replace font tag
+       }
+       if(!strchr(appData.boardSize, ',')) {
+           ASSIGN(appData.boardSize, sizeDefaults[h].name);
+       }
+#ifndef OSXAPP
+       if(sizeDefaults[h].tinyLayout != tinyLayout) { // alter clipping of menu names to conform to board width
+           int clip = (tinyLayout = sizeDefaults[h].tinyLayout) + 1;
+           char text[MSG_SIZ];
+           for(h=1; mainOptions[h].type == DropDown; h++) {
+               strncpy(text, _(mainOptions[h].name), MSG_SIZ);
+               if(clip != 1) text[clip + (text[clip-1] == '_')] = NULLCHAR;
+               gtk_menu_item_set_label((GtkMenuItem *) mainOptions[h].handle, text);
+           }
+       }
+#endif
        if(sqx != squareSize && !first) {
            squareSize = sqx; // adopt new square size
            CreatePNGPieces(); // make newly scaled pieces
@@ -1698,6 +1746,12 @@ ReSize (WindowPlacement *wp)
        if(optList[W_BOARD].max   > w) optList[W_BOARD].max = w;
        if(optList[W_BOARD].value > h) optList[W_BOARD].value = h;
        first = appData.fixedSize;
+       if(twoBoards && shellUp[DummyDlg]) {
+           SlavePopUp(); dualOptions[3].max = 0; DoEvents(); // calls SlaveResize, kludge to force assigning new canvas
+           partnerUp = !partnerUp; flipView = !flipView;
+           DrawPosition(True, NULL);
+           partnerUp = !partnerUp; flipView = !flipView;
+       }
 }
 
 static guint delayedDragTag = 0;
@@ -1705,16 +1759,19 @@ static guint delayedDragTag = 0;
 void
 DragProc ()
 {
-       static int busy;
-       if(busy) return;
+    static int busy;
+    if(busy++) return; // prevent recursive calling, but remember we missed an event in 'busy'
 
-       busy = 1;
+    if(delayedDragTag) g_source_remove(delayedDragTag); // no more timer interrupts from same event!
+    delayedDragTag = 0;
+
+    do {
        GetActualPlacement(shellWidget, &wpNew);
        if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
           wpNew.width == wpMain.width && wpNew.height == wpMain.height) { // not sized
-           busy = 0; return; // false alarm
+           busy = 0; break; // false alarm
        }
-       ReSize(&wpNew);
+       ReSize(&wpNew); // this can be interrupted by other events
        if(appData.useStickyWindows) {
            if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
            if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
@@ -1724,9 +1781,8 @@ DragProc ()
         }
        wpMain = wpNew;
        DrawPosition(True, NULL);
-       if(delayedDragTag) g_source_remove(delayedDragTag);
-       delayedDragTag = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
-       busy = 0;
+       if(busy > 2) busy = 2; // if multiple events were backlogged, only do one more
+    } while(--busy);
 }
 
 void
@@ -1747,6 +1803,7 @@ EventProc (GtkWidget *widget, GdkEvent *event, gpointer g)
     wpNew.y = event->configure.y;
     wpNew.width  = event->configure.width;
     wpNew.height = event->configure.height;
+//    SetWidgetLabel(&mainOptions[W_WHITE], ""); SetWidgetLabel(&mainOptions[W_BLACK], "");
     DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
     return FALSE;
 }
@@ -2050,13 +2107,33 @@ DisplayIcsInteractionTitle (String message)
 #endif
 }
 
+void
+LockBoardSize (int after)
+{
+    static char *oldClockFont, *oldMessgFont;
+    int w, h;
+    if(oldMessgFont && !strcmp(oldMessgFont, appData.font) &&
+       oldClockFont && !strcmp(oldClockFont, appData.clockFont) && after < 2) return; // only do something when font changed
+    w = BOARD_WIDTH*(squareSize + lineGap) + lineGap;
+    h = BOARD_HEIGHT*(squareSize + lineGap) + lineGap;
+    if(after & 1) {
+       ASSIGN(oldClockFont, appData.clockFont);
+       ASSIGN(oldMessgFont, appData.font);
+       gtk_window_resize(GTK_WINDOW(shellWidget), w, h);
+       DoEvents();
+       gtk_widget_set_size_request(optList[W_BOARD].handle, -1, -1); // liberate board
+    } else { // before
+       gtk_widget_set_size_request(optList[W_BOARD].handle, w, h);   // protect board widget
+    }
+}
 
 void
 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
 {
+    static int twoLines = -1;
     GtkWidget *w = (GtkWidget *) opt->handle;
     GdkColor col;
-    char *markup;
+    char *markup, two = (appData.logoSize != 0);
     char bgcolor[10];
     char fgcolor[10];
 
@@ -2073,12 +2150,14 @@ DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
         strcpy(fgcolor, appData.lowTimeWarningColor);
     }
 
+    if(! partnerUp && two != twoLines) LockBoardSize(2); // lock board size if clock height changes
+
     gdk_color_parse( bgcolor, &col );
     gtk_widget_modify_bg(gtk_widget_get_parent(opt->handle), GTK_STATE_NORMAL, &col);
 
     if (appData.clockMode) {
         markup = g_markup_printf_escaped("<span font=\"%s\" background=\"%s\" foreground=\"%s\">%s:%s%s</span>", appData.clockFont,
-                                        bgcolor, fgcolor, color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
+                                        bgcolor, fgcolor, color, two ? "\n" : " ", TimeString(timer));
 //        markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s:%s%s</span>",
 //                                      bgcolor, fgcolor, color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
     } else {
@@ -2089,6 +2168,8 @@ DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
     }
     gtk_label_set_markup(GTK_LABEL(w), markup);
     g_free(markup);
+
+    if(!partnerUp && two != twoLines) LockBoardSize(3), twoLines = two;
 }
 
 static GdkPixbuf **clockIcons[] = { &WhiteIcon, &BlackIcon };
@@ -2322,15 +2403,15 @@ UpdateLogos (int displ)
     return;
 }
 
-void FileNamePopUpWrapper(label, def, filter, proc, pathFlag, openMode, name, fp)
-     char *label;
-     char *def;
-     char *filter;
-     FileProc proc;
-     char *openMode;
-     Boolean pathFlag;
-     char **name;
-     FILE **fp;
+void FileNamePopUpWrapper(
+     char *label,
+     char *def,
+     char *filter,
+     FileProc proc,
+     Boolean pathFlag,
+     char *openMode,
+     char **name,
+     FILE **fp)
 {
   GtkWidget     *dialog;
   GtkFileFilter *gtkfilter;