652db01bb01e218a610979bc5e07a74ed00206d0
[xboard.git] / callback.c
1 #include <gtk/gtk.h>
2 #include "common.h"
3 #include "xboard.h"
4 #include <errno.h>
5 #include "backend.h"
6
7 #ifdef ENABLE_NLS
8 # define  _(s) gettext (s)
9 # define N_(s) gettext_noop (s)
10 #else
11 # define  _(s) (s)
12 # define N_(s)  s
13 #endif
14
15 extern GtkWidget  *about;
16 extern GtkWidget  *GUI_Window;
17 extern GtkWidget  *GUI_Aspect;
18 extern GtkWidget  *GUI_Menubar;
19 extern GtkWidget  *GUI_Timer;
20 extern GtkWidget  *GUI_Buttonbar;
21 extern GtkWidget  *GUI_Board;
22
23 extern char *programVersion;
24 extern int errorExitStatus;
25 extern int promotionUp;
26 extern int fromX;
27 extern int fromY;
28 extern int toX;
29 extern int toY;
30 extern int squareSize,lineGap;
31
32 extern int LoadGamePopUp P((FILE *f, int gameNumber, char *title));
33 extern int LoadPosition P((FILE *f, int gameNumber, char *title));
34 extern int SaveGame P((FILE *f, int gameNumber, char *title));
35 extern int SavePosition P((FILE *f, int gameNumber, char *title));
36
37 gboolean
38 ExposeProc(object, user_data)
39      GtkObject *object;
40      gpointer user_data;
41 {
42   /* do resizing to a fixed aspect ratio */
43   GtkRequisition w;
44   int totalh=0,nw,nh;
45   float ratio;
46   int boardWidth,boardHeight,old,new;
47
48   nw=GTK_WIDGET(object)->allocation.width;
49   nh=GTK_WIDGET(object)->allocation.height;
50
51   old=squareSize;
52   squareSize  = nw/(BOARD_WIDTH*1.05+0.05);
53
54   if(old!=squareSize)
55     {
56       lineGap = squareSize*0.05;
57
58       boardWidth  = lineGap + BOARD_WIDTH  * (squareSize + lineGap);
59       boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
60
61       /* get the height of the menus, etc. and calculate the aspect ratio */
62       gtk_widget_size_request(GTK_WIDGET(GUI_Menubar),   &w);
63       totalh += w.height;
64       gtk_widget_size_request(GTK_WIDGET(GUI_Timer),   &w);
65       totalh += w.height;
66       gtk_widget_size_request(GTK_WIDGET(GUI_Buttonbar),   &w);
67       totalh += w.height;
68
69       ratio  = ((float)totalh+boardHeight)/((float)boardWidth) ;
70
71       gtk_widget_set_size_request(GTK_WIDGET(GUI_Board),
72                                   boardWidth,boardHeight);
73
74       gtk_aspect_frame_set (GTK_ASPECT_FRAME(GUI_Aspect),0,0,ratio,TRUE);
75
76       /* recreate pieces with new size... TODO: keep svg in memory and just recreate pixmap instead of reloading files */
77       CreatePieces();
78     }
79   return FALSE; /* return false, so that other expose events are called too */
80 }
81
82 void
83 QuitProc (object, user_data)
84      GtkObject *object;
85      gpointer user_data;
86 {
87   gtk_main_quit();
88   ExitEvent(0);
89 }
90
91 /* Help Menu */
92 void InfoProc(object, user_data)
93      GtkObject *object;
94      gpointer user_data;
95 {
96     char buf[MSG_SIZ];
97     snprintf(buf, sizeof(buf), "xterm -e info --directory %s --directory . -f %s &",
98             INFODIR, INFOFILE);
99     system(buf);
100     return;
101 }
102
103 void ManProc(object, user_data)
104      GtkObject *object;
105      gpointer user_data;
106 {
107     char buf[MSG_SIZ];
108     snprintf(buf, sizeof(buf), "xterm -e man xboard &");
109     system(buf);
110     return;
111 }
112
113 void HintProc(object, user_data)
114      GtkObject *object;
115      gpointer user_data;
116 {
117     HintEvent();
118     return;
119 }
120
121 void BookProc(object, user_data)
122      GtkObject *object;
123      gpointer user_data;
124 {
125     BookEvent();
126     return;
127 }
128
129 void AboutProc (object, user_data)
130      GtkObject *object;
131      gpointer user_data;
132 {
133   GtkWidget               *about;
134
135   const gchar *authors[] = {"Tim Mann <tim@tim-mann.org>",
136                             "John Chanak",
137                             "Evan Welsh <Evan.Welsh@msdw.com>",
138                             "Elmar Bartel <bartel@informatik.tu-muenchen.de>",
139                             "Jochen Wiedmann",
140                             "Frank McIngvale",
141                             "Hugh Fisher <Hugh.Fisher@cs.anu.edu.au>",
142                             "Allessandro Scotti",
143                             "H.G. Muller <h.g.muller AT hccnet DOT nl>",
144                             "Eric Mullins <emwine AT earthlink DOT net>",
145                             "Arun Persaud <arun@nubati.net>"};
146
147   /* set up about window */
148   about =  GTK_WIDGET(gtk_about_dialog_new());
149
150   /* fill in some information */
151   char buf[MSG_SIZ];
152 #if ZIPPY
153   char *zippy = " (with Zippy code)";
154 #else
155   char *zippy = "";
156 #endif
157   sprintf(buf, "%s%s",  programVersion, zippy);
158
159   gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(about),buf);
160
161   gtk_about_dialog_set_copyright(GTK_ABOUT_DIALOG(about),
162                                  "Copyright 1991 Digital Equipment Corporation\n"
163                                  "Enhancements Copyright 1992-2009 Free Software Foundation\n"
164                                  "Enhancements Copyright 2005 Alessandro Scotti");
165   gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(about),"http://www.gnu.org/software/xboard/");
166   gtk_about_dialog_set_authors(GTK_ABOUT_DIALOG(about),authors);
167   gtk_about_dialog_set_translator_credits(GTK_ABOUT_DIALOG(about),
168                                           " A. Alper (turkish)\n"
169                                           " A. Persaud (german)\n");
170
171   /* end set up about window */
172   gtk_dialog_run(GTK_DIALOG (about));
173   gtk_widget_destroy(about);
174 }
175
176 /* End Help Menu */
177
178 void IcsClientProc(object, user_data)
179      GtkObject *object;
180      gpointer user_data;
181 {
182     IcsClientEvent();
183     return;
184 }
185
186 /*
187  * File menu
188  */
189
190 void
191 LoadNextGameProc(object, user_data)
192      GtkObject *object;
193      gpointer user_data;
194 {
195     ReloadGame(1);
196     return;
197 }
198
199 void
200 LoadPrevGameProc(object, user_data)
201      GtkObject *object;
202      gpointer user_data;
203 {
204     ReloadGame(-1);
205     return;
206 }
207
208 void
209 ReloadGameProc(object, user_data)
210      GtkObject *object;
211      gpointer user_data;
212 {
213     ReloadGame(0);
214     return;
215 }
216
217 void
218 LoadNextPositionProc(object, user_data)
219      GtkObject *object;
220      gpointer user_data;
221 {
222     ReloadPosition(1);
223     return;
224 }
225
226 void
227 LoadPrevPositionProc(object, user_data)
228      GtkObject *object;
229      gpointer user_data;
230 {
231     ReloadPosition(-1);
232     return;
233 }
234
235 void
236 ReloadPositionProc(object, user_data)
237      GtkObject *object;
238      gpointer user_data;
239 {
240     ReloadPosition(0);
241     return;
242 }
243
244 void
245 LoadPositionProc(object, user_data)
246      GtkObject *object;
247      gpointer user_data;
248 {
249   if (gameMode == AnalyzeMode || gameMode == AnalyzeFile)
250     {
251       Reset(FALSE, TRUE);
252     };
253
254   FileNamePopUp(_("Load position file name?"), "", LoadPosition, "rb");
255   return;
256 }
257
258 void
259 SaveGameProc(object, user_data)
260      GtkObject *object;
261      gpointer user_data;
262 {
263   FileNamePopUp(_("Save game file name?"),
264                 DefaultFileName(appData.oldSaveStyle ? "game" : "pgn"),
265                 SaveGame, "a");
266   return;
267 }
268
269 void
270 SavePositionProc(object, user_data)
271      GtkObject *object;
272      gpointer user_data;
273 {
274   FileNamePopUp(_("Save position file name?"),
275                 DefaultFileName(appData.oldSaveStyle ? "pos" : "fen"),
276                 SavePosition, "a");
277   return;
278 }
279
280 void
281 AnalyzeFileProc(object, user_data)
282      GtkObject *object;
283      gpointer user_data;
284 {
285   if (!first.analysisSupport)
286     {
287       char buf[MSG_SIZ];
288       snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
289       DisplayError(buf, 0);
290       return;
291     };
292   Reset(FALSE, TRUE);
293
294   if (!appData.showThinking)
295     ShowThinkingProc(NULL,NULL);
296
297   AnalyzeFileEvent();
298   FileNamePopUp(_("File to analyze"), "", LoadGamePopUp, "rb");
299   AnalysisPeriodicEvent(1);
300   return;
301 }
302
303
304 /* End File Menu */
305
306 void MachineWhiteProc(object, user_data)
307      GtkObject *object;
308      gpointer user_data;
309 {
310     MachineWhiteEvent();
311     return;
312 }
313
314 void MachineBlackProc(object, user_data)
315      GtkObject *object;
316      gpointer user_data;
317 {
318     MachineBlackEvent();
319     return;
320 }
321
322 void TwoMachinesProc(object, user_data)
323      GtkObject *object;
324      gpointer user_data;
325 {
326     TwoMachinesEvent();
327     return;
328 }
329
330 void AcceptProc(object, user_data)
331      GtkObject *object;
332      gpointer user_data;
333 {
334     AcceptEvent();
335     return;
336 }
337
338 void DeclineProc(object, user_data)
339      GtkObject *object;
340      gpointer user_data;
341 {
342     DeclineEvent();
343     return;
344 }
345
346 void RematchProc(object, user_data)
347      GtkObject *object;
348      gpointer user_data;
349 {
350     RematchEvent();
351     return;
352 }
353
354 void CallFlagProc(object, user_data)
355      GtkObject *object;
356      gpointer user_data;
357 {
358     CallFlagEvent();
359     return;
360 }
361
362 void DrawProc(object, user_data)
363      GtkObject *object;
364      gpointer user_data;
365 {
366     DrawEvent();
367     return;
368 }
369
370 void AbortProc(object, user_data)
371      GtkObject *object;
372      gpointer user_data;
373 {
374     AbortEvent();
375     return;
376 }
377
378 void AdjournProc(object, user_data)
379      GtkObject *object;
380      gpointer user_data;
381 {
382     AdjournEvent();
383     return;
384 }
385
386 void ResignProc(object, user_data)
387      GtkObject *object;
388      gpointer user_data;
389 {
390     ResignEvent();
391     return;
392 }
393
394 void StopObservingProc(object, user_data)
395      GtkObject *object;
396      gpointer user_data;
397 {
398     StopObservingEvent();
399     return;
400 }
401
402 void StopExaminingProc(object, user_data)
403      GtkObject *object;
404      gpointer user_data;
405 {
406     StopExaminingEvent();
407     return;
408 }
409
410 void AdjuWhiteProc(object, user_data)
411      GtkObject *object;
412      gpointer user_data;
413 {
414     UserAdjudicationEvent(+1);
415     return;
416 }
417
418 void AdjuBlackProc(object, user_data)
419      GtkObject *object;
420      gpointer user_data;
421 {
422     UserAdjudicationEvent(-1);
423     return;
424 }
425
426 void AdjuDrawProc(object, user_data)
427      GtkObject *object;
428      gpointer user_data;
429 {
430     UserAdjudicationEvent(0);
431     return;
432 }
433
434 void BackwardProc(object, user_data)
435      GtkObject *object;
436      gpointer user_data;
437 {
438     BackwardEvent();
439     return;
440 }
441
442 void ForwardProc(object, user_data)
443      GtkObject *object;
444      gpointer user_data;
445 {
446     ForwardEvent();
447     return;
448 }
449
450 void ToStartProc(object, user_data)
451      GtkObject *object;
452      gpointer user_data;
453 {
454     ToStartEvent();
455     return;
456 }
457
458 void ToEndProc(object, user_data)
459      GtkObject *object;
460      gpointer user_data;
461 {
462     ToEndEvent();
463     return;
464 }
465
466 void RevertProc(object, user_data)
467      GtkObject *object;
468      gpointer user_data;
469 {
470     RevertEvent();
471     return;
472 }
473
474 void TruncateGameProc(object, user_data)
475      GtkObject *object;
476      gpointer user_data;
477 {
478     TruncateGameEvent();
479     return;
480 }
481
482 void MoveNowProc(object, user_data)
483      GtkObject *object;
484      gpointer user_data;
485 {
486     MoveNowEvent();
487     return;
488 }
489
490 void RetractMoveProc(object, user_data)
491      GtkObject *object;
492      gpointer user_data;
493 {
494     RetractMoveEvent();
495     return;
496 }
497
498 /* Option Menu */
499 void AutocommProc(object, user_data)
500      GtkObject *object;
501      gpointer user_data;
502 {
503     appData.autoComment = !appData.autoComment;
504     return;
505 }
506
507 void AutoflagProc(object, user_data)
508      GtkObject *object;
509      gpointer user_data;
510 {
511     appData.autoCallFlag = !appData.autoCallFlag;
512     return;
513 }
514
515 void AutoflipProc(object, user_data)
516      GtkObject *object;
517      gpointer user_data;
518 {
519     appData.autoFlipView = !appData.autoFlipView;
520     return;
521 }
522
523 void ShowThinkingProc(object, user_data)
524      GtkObject *object;
525      gpointer user_data;
526 {
527     appData.showThinking = !appData.showThinking;
528     ShowThinkingEvent();
529
530     return;
531 }
532
533 void HideThinkingProc(object, user_data)
534      GtkObject *object;
535      gpointer user_data;
536 {
537     appData.hideThinkingFromHuman = !appData.hideThinkingFromHuman;
538     ShowThinkingEvent();
539
540     return;
541 }
542
543 void FlipViewProc(object, user_data)
544      GtkObject *object;
545      gpointer user_data;
546 {
547     flipView = !flipView;
548     DrawPosition(True, NULL);
549     return;
550 }
551
552
553 gboolean CloseWindowProc(GtkWidget *button)
554 {
555     gtk_widget_destroy(gtk_widget_get_toplevel(button));
556     return TRUE;
557 }
558
559 void
560 ResetProc (object, user_data)
561      GtkObject *object;
562      gpointer user_data;
563 {
564   ResetGameEvent();
565   EngineOutputPopDown();
566 }
567
568 void WhiteClockProc(object, user_data)
569      GtkObject *object;
570      gpointer user_data;
571 {
572     if (gameMode == EditPosition || gameMode == IcsExamining) {
573         SetWhiteToPlayEvent();
574     } else if (gameMode == IcsPlayingBlack || gameMode == MachinePlaysWhite) {
575         CallFlagEvent();
576     }
577 }
578
579 void BlackClockProc(object, user_data)
580      GtkObject *object;
581      gpointer user_data;
582 {
583     if (gameMode == EditPosition || gameMode == IcsExamining) {
584         SetBlackToPlayEvent();
585     } else if (gameMode == IcsPlayingWhite || gameMode == MachinePlaysBlack) {
586         CallFlagEvent();
587     }
588 }
589
590
591 void ShowCoordsProc(object, user_data)
592      GtkObject *object;
593      gpointer user_data;
594 {
595     appData.showCoords = !appData.showCoords;
596
597     DrawPosition(True, NULL);
598 }
599
600 void ErrorPopDownProc(object, user_data)
601      GtkObject *object;
602      gpointer user_data;
603 {
604   gtk_widget_destroy(GTK_WIDGET(object));
605   ErrorPopDown();
606 }
607
608 void PauseProc(object, user_data)
609      GtkObject *object;
610      gpointer user_data;
611 {
612     // todo this toggling of the pause button doesn't seem to work?
613     // e.g. select pause from buttonbar doesn't activate menumode.pause
614   PauseEvent();
615 }
616
617
618 void LoadGameProc(object, user_data)
619      GtkObject *object;
620      gpointer user_data;
621 {
622   FileNamePopUp(_("Load game file name?"),"",LoadGamePopUp,"rb");
623   return;
624 }
625
626
627 /*************
628  * EVENTS
629  *************/
630
631 void EventProc(window, event, data)
632      GtkWindow *window;
633      GdkEvent *event;
634      gpointer data;
635 {
636   /* todo do we still need this?
637     if (!XtIsRealized(widget))
638       return;
639   */
640
641     switch (event->type) {
642       case GDK_EXPOSE:
643         if (event->expose.count > 0) return;  /* no clipping is done */
644         DrawPosition(True, NULL);
645         break;
646       default:
647         return;
648     }
649 }
650
651
652 /*
653  * event handler for parsing user moves
654  */
655 void UserMoveProc(window, event, data)
656      GtkWindow *window;
657      GdkEvent *event;
658      gpointer data;
659 {
660     int x, y;
661     Boolean saveAnimate;
662     static int second = 0;
663
664     if (errorExitStatus != -1) return;
665
666     if (event->type == GDK_BUTTON_PRESS) ErrorPopDown();
667
668     if (promotionUp)
669       {
670         if (event->type == GDK_BUTTON_PRESS)
671           {
672             /* todo add promotionshellwidget
673                XtPopdown(promotionShell);
674                XtDestroyWidget(promotionShell); */
675             promotionUp = False;
676             ClearHighlights();
677             fromX = fromY = -1;
678           }
679         else
680           {
681             return;
682           }
683       }
684
685     x = EventToSquare( (int)event->button.x, BOARD_WIDTH  );
686     y = EventToSquare( (int)event->button.y, BOARD_HEIGHT );
687     if (!flipView && y >= 0)
688       {
689         y = BOARD_HEIGHT - 1 - y;
690       }
691     if (flipView && x >= 0)
692       {
693         x = BOARD_WIDTH - 1 - x;
694       }
695
696     if (fromX == -1)
697       {
698         if (event->type == ButtonPress)
699           {
700             /* First square */
701             if (OKToStartUserMove(x, y))
702               {
703                 fromX = x;
704                 fromY = y;
705                 second = 0;
706                 DragPieceBegin(event->button.x, event->button.y);
707                 if (appData.highlightDragging)
708                   {
709                     SetHighlights(x, y, -1, -1);
710                   }
711               }
712           }
713         return;
714       }
715
716     /* fromX != -1 */
717     if (event->type == GDK_BUTTON_PRESS && gameMode != EditPosition &&
718         x >= 0 && y >= 0) {
719         ChessSquare fromP;
720         ChessSquare toP;
721         /* Check if clicking again on the same color piece */
722         fromP = boards[currentMove][fromY][fromX];
723         toP = boards[currentMove][y][x];
724         if ((WhitePawn <= fromP && fromP <= WhiteKing &&
725              WhitePawn <= toP && toP <= WhiteKing) ||
726             (BlackPawn <= fromP && fromP <= BlackKing &&
727              BlackPawn <= toP && toP <= BlackKing)) {
728             /* Clicked again on same color piece -- changed his mind */
729             second = (x == fromX && y == fromY);
730             if (appData.highlightDragging) {
731                 SetHighlights(x, y, -1, -1);
732             } else {
733                 ClearHighlights();
734             }
735             if (OKToStartUserMove(x, y)) {
736                 fromX = x;
737                 fromY = y;
738                 DragPieceBegin(event->button.x, event->button.y);
739             }
740             return;
741         }
742     }
743
744     if (event->type == GDK_BUTTON_RELEASE &&    x == fromX && y == fromY)
745       {
746         DragPieceEnd(event->button.x, event->button.y);
747         if (appData.animateDragging)
748           {
749             /* Undo animation damage if any */
750             DrawPosition(FALSE, NULL);
751           }
752         if (second)
753           {
754             /* Second up/down in same square; just abort move */
755             second = 0;
756             fromX = fromY = -1;
757             ClearHighlights();
758             gotPremove = 0;
759             ClearPremoveHighlights();
760           }
761         else
762           {
763             /* First upclick in same square; start click-click mode */
764             SetHighlights(x, y, -1, -1);
765           }
766         return;
767       }
768
769     /* Completed move */
770     toX = x;
771     toY = y;
772     saveAnimate = appData.animate;
773
774     if (event->type == GDK_BUTTON_PRESS)
775       {
776         /* Finish clickclick move */
777         if (appData.animate || appData.highlightLastMove)
778           {
779             SetHighlights(fromX, fromY, toX, toY);
780           }
781         else
782           {
783             ClearHighlights();
784           }
785       }
786     else
787       {
788         /* Finish drag move */
789         if (appData.highlightLastMove)
790           {
791             SetHighlights(fromX, fromY, toX, toY);
792           }
793         else
794           {
795             ClearHighlights();
796           }
797         DragPieceEnd(event->button.x, event->button.y);
798         /* Don't animate move and drag both */
799         appData.animate = FALSE;
800       }
801
802     if (IsPromotion(fromX, fromY, toX, toY))
803       {
804         if (appData.alwaysPromoteToQueen)
805           {
806             UserMoveEvent(fromX, fromY, toX, toY, 'q');
807             if (!appData.highlightLastMove || gotPremove) ClearHighlights();
808             if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
809             fromX = fromY = -1;
810           }
811         else
812           {
813             SetHighlights(fromX, fromY, toX, toY);
814             PromotionPopUp();
815           }
816       }
817     else
818       {
819         UserMoveEvent(fromX, fromY, toX, toY, NULLCHAR);
820
821         if (!appData.highlightLastMove || gotPremove) ClearHighlights();
822         if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
823         fromX = fromY = -1;
824       }
825
826     appData.animate = saveAnimate;
827     if (appData.animate || appData.animateDragging) {
828         /* Undo animation damage if needed */
829         DrawPosition(FALSE, NULL);
830     }
831
832     return;
833 }
834
835 void GetMoveListProc(object, user_data)
836      GtkObject *object;
837      gpointer user_data;
838 {
839   appData.getMoveList = !appData.getMoveList;
840
841   if (appData.getMoveList)
842     {
843       GetMoveListEvent();
844     }
845
846   // gets set automatically? if we set it with set_active we end up in an endless loop switching between 0 and 1
847   //  gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (object),(gboolean) appData.getMoveList );
848
849   return;
850 }