Updated copyright notice to 2012
[xboard.git] / xboard.c
index 4d10b37..f29eb15 100644 (file)
--- a/xboard.c
+++ b/xboard.c
@@ -5,7 +5,7 @@
  * Massachusetts.
  *
  * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
- * 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
+ * 2007, 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
  *
  * The following terms apply to Digital Equipment Corporation's copyright
  * interest in XBoard:
@@ -238,6 +238,8 @@ typedef struct {
     String name;
     String ref;
     MenuItem *mi;
+    int textWidth;
+    Widget subMenu;
 } Menu;
 
 int main P((int argc, char **argv));
@@ -253,7 +255,7 @@ void CreateXPMPieces P((void));
 void CreateXPMBoard P((char *s, int n));
 void CreatePieces P((void));
 void CreatePieceMenus P((void));
-Widget CreateMenuBar P((Menu *mb));
+Widget CreateMenuBar P((Menu *mb, int boardWidth));
 Widget CreateButtonBar P ((MenuItem *mi));
 #if ENABLE_NLS
 char *InsertPxlSize P((char *pattern, int targetPxlSize));
@@ -387,6 +389,9 @@ void StopExaminingProc P((Widget w, XEvent *event, String *prms,
 void UploadProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
 void BackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
 void ForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
+void TempBackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
+void TempForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
+Boolean TempBackwardActive = False;
 void ToStartProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
 void ToEndProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
 void RevertProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
@@ -444,7 +449,6 @@ void AboutGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
 void AboutProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
 void DebugProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
 void NothingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
-void Iconify P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
 void DisplayMove P((int moveNumber));
 void DisplayTitle P((char *title));
 void ICSInitScript P((void));
@@ -921,7 +925,6 @@ XtActionsRec boardActions[] = {
     { "PieceMenuPopup", PieceMenuPopup },
     { "WhiteClock", WhiteClock },
     { "BlackClock", BlackClock },
-    { "Iconify", Iconify },
     { "ResetProc", ResetProc },
     { "NewVariantProc", NewVariantProc },
     { "LoadGameProc", LoadGameProc },
@@ -982,6 +985,8 @@ XtActionsRec boardActions[] = {
     { "UploadProc", UploadProc },
     { "BackwardProc", BackwardProc },
     { "ForwardProc", ForwardProc },
+    { "TempBackwardProc", TempBackwardProc },
+    { "TempForwardProc", TempForwardProc },
     { "ToStartProc", ToStartProc },
     { "ToEndProc", ToEndProc },
     { "RevertProc", RevertProc },
@@ -1108,14 +1113,13 @@ char globalTranslations[] =
    :Ctrl<Key>H: HideThinkingProc() \n "
 #endif
    "\
-   :<Key>-: Iconify() \n \
    :<Key>F1: ManProc() \n \
    :<Key>F2: FlipViewProc() \n \
-   <KeyDown>.: BackwardProc() \n \
-   <KeyUp>.: ForwardProc() \n \
-   Shift<Key>1: AskQuestionProc(\"Direct command\",\
+   :<KeyDown>Return: TempBackwardProc() \n \
+   :<KeyUp>Return: TempForwardProc() \n \
+   :Ctrl<Key>1: AskQuestionProc(\"Direct command\",\
                                 \"Send to chess program:\",,1) \n \
-   Shift<Key>2: AskQuestionProc(\"Direct command\",\
+   :Ctrl<Key>2: AskQuestionProc(\"Direct command\",\
                                 \"Send to second chess program:\",,2) \n";
 
 char boardTranslations[] =
@@ -1684,7 +1688,7 @@ void InitDrawingSizes(BoardSize boardSize, int flags)
     int i;
     static Dimension oldWidth, oldHeight;
     static VariantClass oldVariant;
-    static int oldDual = -1;
+    static int oldDual = -1, oldMono = -1;
 
     if(!formWidget) return;
 
@@ -1763,7 +1767,7 @@ void InitDrawingSizes(BoardSize boardSize, int flags)
     // [HGM] pieces: tailor piece bitmaps to needs of specific variant
     // (only for xpm)
 
-    if(gameInfo.variant == oldVariant) return; // and only if variant changed
+  if(gameInfo.variant != oldVariant) { // and only if variant changed
 
     if(useImages) {
       for(i=0; i<4; i++) {
@@ -1831,9 +1835,12 @@ void InitDrawingSizes(BoardSize boardSize, int flags)
        }
       }
     }
+  }
 #if HAVE_LIBXPM
+  if(appData.monoMode == oldMono)
     CreateAnimVars();
 #endif
+  oldMono = appData.monoMode;
 }
 #endif
 
@@ -1941,7 +1948,7 @@ CreateAnyPieces()
 #if HAVE_LIBXPM
     if (appData.monoMode && // [HGM] no sense to go on to certain doom
        (appData.bitmapDirectory == NULL || appData.bitmapDirectory[0] == NULLCHAR))
-           appData.bitmapDirectory = DEF_BITMAP_DIR;
+           appData.bitmapDirectory = strdup(DEF_BITMAP_DIR);
 
     if (appData.bitmapDirectory[0] != NULLCHAR) {
       CreatePieces();
@@ -2273,7 +2280,7 @@ XBoard square size (hint): %d\n\
     XtGetValues(formWidget, args, 1);
 
     j = 0;
-    widgetList[j++] = menuBarWidget = CreateMenuBar(menuBar);
+    widgetList[j++] = menuBarWidget = CreateMenuBar(menuBar, boardWidth);
     XtSetArg(args[0], XtNtop,    XtChainTop);
     XtSetArg(args[1], XtNbottom, XtChainTop);
     XtSetArg(args[2], XtNright,  XtChainLeft);
@@ -3944,13 +3951,16 @@ void CreateMenuBarPopup(parent, name, mb)
     }
 }
 
-Widget CreateMenuBar(mb)
+Widget CreateMenuBar(mb, boardWidth)
      Menu *mb;
+     int boardWidth;
 {
-    int j;
-    Widget anchor, menuBar;
+    int i, j, nr = 0, wtot = 0, widths[10];
+    Widget menuBar;
     Arg args[16];
     char menuName[MSG_SIZ];
+    Dimension w;
+    Menu *ma = mb;
 
     j = 0;
     XtSetArg(args[j], XtNorientation, XtorientHorizontal);  j++;
@@ -3964,22 +3974,28 @@ Widget CreateMenuBar(mb)
        strncat(menuName, mb->ref, MSG_SIZ - strlen(menuName) - 1);
        j = 0;
        XtSetArg(args[j], XtNmenuName, XtNewString(menuName));  j++;
-       if (tinyLayout) {
-           char shortName[2];
-            shortName[0] = mb->name[0];
-           shortName[1] = NULLCHAR;
-           XtSetArg(args[j], XtNlabel, XtNewString(shortName)); j++;
-       }
-      else {
-       XtSetArg(args[j], XtNlabel, XtNewString(_(mb->name))); j++;
-      }
-
+       XtSetArg(args[j], XtNlabel, XtNewString(_(mb->name)));  j++;
        XtSetArg(args[j], XtNborderWidth, 0);                   j++;
-       anchor = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
+       mb->subMenu = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
                                       menuBar, args, j);
        CreateMenuBarPopup(menuBar, menuName, mb);
+       j = 0;
+       XtSetArg(args[j], XtNwidth, &w);                   j++;
+       XtGetValues(mb->subMenu, args, j);
+       wtot += mb->textWidth = widths[nr++] = w;
        mb++;
     }
+    while(wtot > boardWidth - 40) {
+       int wmax=0, imax=0;
+       for(i=0; i<nr; i++) if(widths[i] > wmax) wmax = widths[imax=i];
+       widths[imax]--;
+       wtot--;
+    }
+    for(i=0; i<nr; i++) if(widths[i] != ma[i].textWidth) {
+       j = 0;
+       XtSetArg(args[j], XtNwidth, widths[i]);                   j++;
+       XtSetValues(ma[i].subMenu, args, j);
+    }
     return menuBar;
 }
 
@@ -4637,6 +4653,12 @@ void DrawSquare(row, column, piece, do_flash)
        }
     }
     if(!partnerUp && marker[row][column]) {
+       if(appData.monoMode) {
+           XFillArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? darkSquareGC : lightSquareGC,
+                   x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
+           XDrawArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? lightSquareGC : darkSquareGC,
+                   x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
+       } else
        XFillArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? prelineGC : highlineGC,
                x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
     }
@@ -5122,6 +5144,7 @@ void PromotionPopUp()
     Position x, y;
     Dimension bw_width, pw_width;
     int j;
+    char *PromoChars = "wglcqrbnkac+=\0";
 
     j = 0;
     XtSetArg(args[j], XtNwidth, &bw_width); j++;
@@ -5145,47 +5168,33 @@ void PromotionPopUp()
 
   if(gameInfo.variant != VariantShogi) {
    if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) {
-      XawDialogAddButton(dialog, _("Warlord"), PromotionCallback,
-                        (XtPointer) dialog);
-      XawDialogAddButton(dialog, _("General"), PromotionCallback,
-                        (XtPointer) dialog);
-      XawDialogAddButton(dialog, _("Lieutenant"), PromotionCallback,
-                        (XtPointer) dialog);
-      XawDialogAddButton(dialog, _("Captain"), PromotionCallback,
-                        (XtPointer) dialog);
+      XawDialogAddButton(dialog, _("Warlord"), PromotionCallback, PromoChars + 0);
+      XawDialogAddButton(dialog, _("General"), PromotionCallback, PromoChars + 1);
+      XawDialogAddButton(dialog, _("Lieutenant"), PromotionCallback, PromoChars + 2);
+      XawDialogAddButton(dialog, _("Captain"), PromotionCallback, PromoChars + 3);
     } else {
-    XawDialogAddButton(dialog, _("Queen"), PromotionCallback,
-                      (XtPointer) dialog);
-    XawDialogAddButton(dialog, _("Rook"), PromotionCallback,
-                      (XtPointer) dialog);
-    XawDialogAddButton(dialog, _("Bishop"), PromotionCallback,
-                      (XtPointer) dialog);
-    XawDialogAddButton(dialog, _("Knight"), PromotionCallback,
-                      (XtPointer) dialog);
+    XawDialogAddButton(dialog, _("Queen"), PromotionCallback, PromoChars + 4);
+    XawDialogAddButton(dialog, _("Rook"), PromotionCallback, PromoChars + 5);
+    XawDialogAddButton(dialog, _("Bishop"), PromotionCallback, PromoChars + 6);
+    XawDialogAddButton(dialog, _("Knight"), PromotionCallback, PromoChars + 7);
     }
     if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
         gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
         gameInfo.variant == VariantGiveaway) {
-      XawDialogAddButton(dialog, _("King"), PromotionCallback,
-                        (XtPointer) dialog);
+      XawDialogAddButton(dialog, _("King"), PromotionCallback, PromoChars + 8);
     }
     if(gameInfo.variant == VariantCapablanca ||
        gameInfo.variant == VariantGothic ||
        gameInfo.variant == VariantCapaRandom) {
-      XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback,
-                        (XtPointer) dialog);
-      XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback,
-                        (XtPointer) dialog);
+      XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback, PromoChars + 9);
+      XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback, PromoChars + 10);
     }
   } else // [HGM] shogi
   {
-      XawDialogAddButton(dialog, _("Promote"), PromotionCallback,
-                        (XtPointer) dialog);
-      XawDialogAddButton(dialog, _("Defer"), PromotionCallback,
-                        (XtPointer) dialog);
+      XawDialogAddButton(dialog, _("Promote"), PromotionCallback, PromoChars + 11);
+      XawDialogAddButton(dialog, _("Defer"), PromotionCallback, PromoChars + 12);
   }
-    XawDialogAddButton(dialog, _("cancel"), PromotionCallback,
-                      (XtPointer) dialog);
+    XawDialogAddButton(dialog, _("cancel"), PromotionCallback, PromoChars + 13);
 
     XtRealizeWidget(promotionShell);
     CatchDeleteWindow(promotionShell, "PromotionPopDown");
@@ -5221,31 +5230,17 @@ void PromotionCallback(w, client_data, call_data)
      Widget w;
      XtPointer client_data, call_data;
 {
-    String name;
-    Arg args[16];
-    int promoChar;
-
-    XtSetArg(args[0], XtNlabel, &name);
-    XtGetValues(w, args, 1);
+    int promoChar = * (const char *) client_data;
 
     PromotionPopDown();
 
     if (fromX == -1) return;
 
-    if (strcmp(name, _("cancel")) == 0) {
+    if (! promoChar) {
        fromX = fromY = -1;
        ClearHighlights();
        return;
-    } else if (strcmp(name, _("Knight")) == 0) {
-       promoChar = 'n';
-    } else if (strcmp(name, _("Promote")) == 0) {
-       promoChar = '+';
-    } else if (strcmp(name, _("Defer")) == 0) {
-       promoChar = '=';
-    } else {
-       promoChar = ToLower(name[0]);
     }
-
     UserMoveEvent(fromX, fromY, toX, toY, promoChar);
 
     if (!appData.highlightLastMove || gotPremove) ClearHighlights();
@@ -5641,11 +5636,19 @@ SendPositionSelection(Widget w, Atom *selection, Atom *target,
     *value_return = targets_tmp;
     *type_return = XA_ATOM;
     *length_return = 2;
+#if 0
+    // This code leads to a read of value_return out of bounds on 64-bit systems.
+    // Other code which I have seen always sets *format_return to 32 independent of
+    // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
+    // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
     *format_return = 8 * sizeof(Atom);
     if (*format_return > 32) {
       *length_return *= *format_return / 32;
       *format_return = 32;
     }
+#else
+    *format_return = 32;
+#endif
     return True;
   } else {
     return False;
@@ -5750,11 +5753,19 @@ SendGameSelection(Widget w, Atom *selection, Atom *target,
     *value_return = targets_tmp;
     *type_return = XA_ATOM;
     *length_return = 2;
+#if 0
+    // This code leads to a read of value_return out of bounds on 64-bit systems.
+    // Other code which I have seen always sets *format_return to 32 independent of
+    // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
+    // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
     *format_return = 8 * sizeof(Atom);
     if (*format_return > 32) {
       *length_return *= *format_return / 32;
       *format_return = 32;
     }
+#else
+    *format_return = 32;
+#endif
     return True;
   } else {
     return False;
@@ -6244,6 +6255,37 @@ void BackwardProc(w, event, prms, nprms)
     BackwardEvent();
 }
 
+void TempBackwardProc(w, event, prms, nprms)
+     Widget w;
+     XEvent *event;
+     String *prms;
+     Cardinal *nprms;
+{
+       if (!TempBackwardActive) {
+               TempBackwardActive = True;
+               BackwardEvent();
+       }
+}
+
+void TempForwardProc(w, event, prms, nprms)
+     Widget w;
+     XEvent *event;
+     String *prms;
+     Cardinal *nprms;
+{
+       /* Check to see if triggered by a key release event for a repeating key.
+        * If so the next queued event will be a key press of the same key at the same time */
+       if (XEventsQueued(xDisplay, QueuedAfterReading)) {
+               XEvent next;
+               XPeekEvent(xDisplay, &next);
+               if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
+                       next.xkey.keycode == event->xkey.keycode)
+                               return;
+       }
+    ForwardEvent();
+       TempBackwardActive = False;
+}
+
 void ToStartProc(w, event, prms, nprms)
      Widget w;
      XEvent *event;
@@ -6870,19 +6912,6 @@ void NothingProc(w, event, prms, nprms)
     return;
 }
 
-void Iconify(w, event, prms, nprms)
-     Widget w;
-     XEvent *event;
-     String *prms;
-     Cardinal *nprms;
-{
-    Arg args[16];
-
-    fromX = fromY = -1;
-    XtSetArg(args[0], XtNiconic, True);
-    XtSetValues(shellWidget, args, 1);
-}
-
 void DisplayMessage(message, extMessage)
      char *message, *extMessage;
 {
@@ -8888,7 +8917,7 @@ void SquareToPos(int rank, int file, int *x, int *y)
 /* Draw an arrow between two points using current settings */
 void DrawArrowBetweenPoints( int s_x, int s_y, int d_x, int d_y )
 {
-    XPoint arrow[7];
+    XPoint arrow[8];
     double dx, dy, j, k, x, y;
 
     if( d_x == s_x ) {
@@ -8985,6 +9014,7 @@ void DrawArrowBetweenPoints( int s_x, int s_y, int d_x, int d_y )
     }
 
     XFillPolygon(xDisplay, xBoardWindow, highlineGC, arrow, 7, Nonconvex, CoordModeOrigin);
+    if(appData.monoMode) arrow[7] = arrow[0], XDrawLines(xDisplay, xBoardWindow, darkSquareGC, arrow, 8, CoordModeOrigin);
 //    Polygon( hdc, arrow, 7 );
 }