1 /*
2 
3     eboard - chess client
4     http://www.bergo.eng.br/eboard
5     https://github.com/fbergo/eboard
6     Copyright (C) 2000-2016 Felipe Bergo
7     fbergo/at/gmail/dot/com
8 
9     This program is free software; you can redistribute it and/or modify
10     it under the terms of the GNU General Public License as published by
11     the Free Software Foundation; either version 2 of the License, or
12     (at your option) any later version.
13 
14     This program is distributed in the hope that it will be useful,
15     but WITHOUT ANY WARRANTY; without even the implied warranty of
16     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17     GNU General Public License for more details.
18 
19     You should have received a copy of the GNU General Public License
20     along with this program; if not, write to the Free Software
21     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22 
23 */
24 
25 #include <iostream>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include "eboard.h"
30 #include "board.h"
31 #include "chess.h"
32 #include "global.h"
33 #include "text.h"
34 #include "protocol.h"
35 #include "dgtboard.h"
36 
37 #include "mainwindow.h"
38 
39 Board * Board::PopupOwner = 0;
40 
41 static GdkEventMotion LastMotionEvent;
42 
43 // --- Board --------------------------
44 
45 #if MAEMO
tap_and_hold_cb(GtkWidget * widget,gpointer user_data)46 void tap_and_hold_cb(GtkWidget *widget, gpointer user_data) {
47   ((Board*)user_data)->popupProtocolMenu(100,100,0);
48 }
49 #endif
50 
Board()51 Board::Board() : WidgetProxy() {
52   int i;
53 
54   global.debug("Board","Board");
55 
56   global.addPieceClient(this);
57 
58   cur=0;
59   clock.setHost(this);
60 
61   premoveq=0;
62   mygame=0;
63   clock_ch=1;
64   hilite_ch=1;
65   allowMove=true;
66   gotAnimationLoop=0;
67   update_queue=false;
68   LockAnimate=0;
69   FreeMove=false;
70   dr_active=false;
71   FlipInvert=false;
72   EditMode=false;
73   UpdateClockAfterConfigure = false;
74 
75   AnimationTimeout = -1;
76 
77   for(i=0;i<6;i++)
78     info[i].erase();
79 
80   currently_rendered.setPiece(0,0,EMPTY);
81 
82   repaint_due=false;
83   frozen_lvl=0;
84 
85   if (global.ShowCoordinates) { borx=20; bory=20; } else { borx=bory=0; }
86   morey=0;
87 
88   global.BoardList.push_back(this);
89 
90   canselect=true;
91   sel_color=0xffff00;
92   sp=0;
93   flipped=false;
94 
95   widget=yidget=gtk_drawing_area_new();
96   gtk_widget_set_events(yidget,GDK_EXPOSURE_MASK|GDK_BUTTON_PRESS_MASK|
97 			GDK_BUTTON_RELEASE_MASK|GDK_BUTTON1_MOTION_MASK|
98 			GDK_POINTER_MOTION_HINT_MASK);
99 
100   sqside=59;
101   sqw = 2*borx + 8*sqside + 1;
102   sqh = 2*bory + morey + 8*sqside + 1;
103 
104   buffer=gdk_pixmap_new(MainWindow::RefWindow,sqw,sqh,-1);
105   clkbar=gdk_pixmap_new(MainWindow::RefWindow,250,sqh,-1);
106 
107   clkgc=gdk_gc_new(clkbar);
108   wgc=0;
109 
110   scrap=(rgbptr)g_malloc(sqw*sqh*3);
111   memset(scrap,0,sqw*sqh*3);
112 
113   gtk_signal_connect (GTK_OBJECT (yidget), "expose_event",
114                       (GtkSignalFunc) board_expose_event, (gpointer) this);
115   gtk_signal_connect (GTK_OBJECT (yidget), "configure_event",
116                       (GtkSignalFunc) board_configure_event, (gpointer) this);
117   gtk_signal_connect (GTK_OBJECT (yidget), "button_press_event",
118                       (GtkSignalFunc) board_button_press_event,
119 		      (gpointer) this);
120   gtk_signal_connect (GTK_OBJECT (yidget), "button_release_event",
121                       (GtkSignalFunc) board_button_release_event,
122 		      (gpointer) this);
123   gtk_signal_connect (GTK_OBJECT (yidget), "motion_notify_event",
124                       (GtkSignalFunc) board_motion_event,
125 		      (gpointer) this);
126 
127 #if MAEMO
128   // Note: the following code does not work
129   // I get no events :-/
130   gtk_widget_tap_and_hold_setup (yidget, NULL, NULL, GtkWidgetTapAndHoldFlags(0));
131 #if 0
132   gtk_signal_connect (GTK_OBJECT (yidget), "tap-and-hold",
133 		      //(GtkSignalFunc) board_button_press_event,
134 		      (GtkSignalFunc) tap_and_hold_cb ,
135 		      (gpointer) this);
136 #else
137   g_signal_connect (G_OBJECT (yidget), "tap-and-hold", G_CALLBACK (tap_and_hold_cb), (gpointer) this);
138 #endif
139 #endif
140 
141   gshow(widget);
142 }
143 
hasGame()144 bool Board::hasGame() {
145   return(mygame!=0);
146 }
147 
~Board()148 Board::~Board() {
149   unsigned int i;
150   global.debug("Board","~Board");
151 
152   if (mygame!=NULL)
153     if (mygame->getBoard() == this)
154       mygame->setBoard(NULL);
155 
156   global.removeBoard(this);
157   global.removePieceClient(this);
158   for(i=0;i<LastMove.size();i++)
159     delete(LastMove[i]);
160   LastMove.clear();
161   if (cur) {
162     delete cur;
163     cur = NULL;
164   }
165   if (wgc)
166     gdk_gc_destroy(wgc);
167   if (scrap!=NULL) {
168     g_free(scrap);
169     scrap = NULL;
170   }
171   gdk_gc_destroy(clkgc);
172   gdk_pixmap_unref(clkbar);
173   gdk_pixmap_unref(buffer);
174   if (AnimationTimeout >= 0)
175     gtk_timeout_remove(AnimationTimeout);
176 }
177 
reloadFonts()178 void Board::reloadFonts() {
179   clock_ch=1;
180   queueRepaint();
181 }
182 
183 /*
184   Resets the board state regarding any previous game played
185   here.
186 
187   This method is called by the protocol classes when a new
188   game is being attached to the board (e.g. when a engine is
189   run, when a new game starts on FICS, etc.
190 */
191 
reset()192 void Board::reset() {
193   unsigned int i;
194   global.debug("Board","reset");
195   while(gotAnimationLoop)
196     global.WrappedMainIteration();
197 
198   if (mygame) {
199     if ( (mygame->getBoard() == this) && (mygame->isOver()) )
200       mygame->setBoard(0);
201   }
202 
203   update_queue=false;
204 
205   while(!UpdateStack.empty())
206     UpdateStack.pop();
207 
208   sp=0;
209   mygame=0;
210   premoveq=0;
211   flipped=false;
212   clock_ch=1;
213   hilite_ch=1;
214   allowMove=true;
215   dr_active=false;
216   LockAnimate=0;
217   FlipInvert=false;
218   for(i=0;i<6;i++)
219     info[i].erase();
220   clock.setMirror(0);
221   clock.setClock2(0,0,0,1);
222   currently_rendered.invalidate();
223   position.setStartPos();
224   for(i=0;i<LastMove.size();i++)
225     delete(LastMove[i]);
226   LastMove.clear();
227   cleanTargets();
228   queueRepaint();
229 }
230 
setFlipInversion(bool v)231 void Board::setFlipInversion(bool v) {
232   FlipInvert=v;
233   currently_rendered.invalidate();
234   clock_ch=1;
235   hilite_ch=1;
236   queueRepaint();
237 }
238 
getFlipInversion()239 bool Board::getFlipInversion() {
240   return(FlipInvert);
241 }
242 
pieceSetChanged()243 void Board::pieceSetChanged() {
244  if (cur)
245     delete cur;
246   cur=new PieceSet(global.pieceset);
247   currently_rendered.invalidate();
248   if (global.ShowCoordinates) { borx=20; bory=20; } else { borx=bory=0; }
249   morey=0;
250   UpdateClockAfterConfigure = true;
251   queueRepaint();
252 }
253 
getGame()254 ChessGame * Board::getGame() {
255   return(mygame);
256 }
257 
setGame(ChessGame * game)258 void Board::setGame(ChessGame *game) {
259   global.debug("Board","setGame");
260   mygame=game;
261   clock_ch=1;
262   queueRepaint();
263 
264   // update button enabling/disabling
265   global.ebook->pretendPaneChanged();
266 }
267 
show()268 void Board::show() {
269   // already called on creation
270 }
271 
setSensitive(bool state)272 void Board::setSensitive(bool state) {
273   canselect=state;
274   if ((!canselect)&&(sp)) {
275     sp=0;
276     hilite_ch=1;
277     queueRepaint();
278   }
279 }
280 
clearSel()281 void Board::clearSel() {
282   if (sp) {
283     sp=0;
284     hilite_ch=1;
285     queueRepaint();
286   }
287 }
288 
setSelColor(int color)289 void Board::setSelColor(int color) {
290   sel_color=color;
291   if (sp) {
292     hilite_ch=1;
293     queueRepaint();
294   }
295 }
296 
setFlipped(bool flip)297 void Board::setFlipped(bool flip) {
298   bool ov;
299   ov=flipped;
300   flipped=flip;
301   if (flipped!=ov) {
302     currently_rendered.invalidate();
303     clock_ch=1;
304     queueRepaint();
305   }
306 }
307 
supressNextAnimation()308 void Board::supressNextAnimation() {
309   ++LockAnimate;
310 }
311 
updateClock()312 void Board::updateClock() {
313   if (yidget->window) {
314     clock_ch=1;
315     renderClock();
316     gdk_draw_pixmap(yidget->window,
317 		    yidget->style->fg_gc[GTK_WIDGET_STATE (yidget)],
318 		    clkbar,
319 		    0, 0,
320 		    Width(), 0,250, Height());
321     if (dr_active)
322       board_motion_event(widget,&LastMotionEvent,(gpointer) this);
323   }
324 }
325 
clockString(int val,char * z,int maxlen)326 void RootBoard::clockString(int val,char *z,int maxlen) {
327   static const char *minus = "-";
328   const int mrdiv[4] = { 1, 100, 10, 1 };
329   char fmt[32];
330   int cv,mr,neg,H,M,S,TS, ed;
331   cv=val;
332   if (cv<0) {
333     cv=-cv;
334     neg=1;
335   } else
336     neg=0;
337 
338   mr = cv%1000;
339   cv /= 1000;
340 
341   H=cv/3600;
342   M=(cv-3600*H)/60;
343   S=(cv-3600*H)%60;
344 
345   TS = H*3600 + M*60 + S;
346   ed = global.MsecDigits;
347   if (ed > 3) ed = 3; if (ed<1) mr=0;
348   if (ed < 0) ed = 0;
349   mr /= mrdiv[ed];
350 
351   if (TS >= global.MsecThreshold || mr==0) {
352     if (H!=0) {
353       snprintf(z,maxlen,"%s%d:%.2d:%.2d",
354 	       neg?minus:&minus[1],H,M,S);
355     } else {
356       snprintf(z,maxlen,"%s%.2d:%.2d",
357 	       neg?minus:&minus[1],M,S);
358     }
359   } else {
360     if (H==0) {
361       snprintf(fmt,32,"%%s%%.2d:%%.2d.%%.%dd", ed);
362       snprintf(z,maxlen,fmt,neg?minus:&minus[1],M,S,mr);
363     } else {
364       snprintf(fmt,32,"%%s%%d:%%.2d:%%.2d.%%.%dd", ed);
365       snprintf(z,maxlen,fmt,neg?minus:&minus[1],H,M,S,mr);
366     }
367   }
368 }
369 
repaint()370 void Board::repaint() {
371   global.debug("Board","repaint");
372   WidgetProxy::repaint();
373 }
374 
renderClock()375 void Board::renderClock() {
376   GdkGC *gc=clkgc;
377   int i,j,k,sq,bh,ch,th;
378   char z[64];
379   bool ef, lowtime=false;
380 
381   if (!clock_ch)
382     return;
383 
384   if (mygame)
385     if (mygame->AmPlaying)
386       lowtime=clock.lowTimeWarning(mygame->MyColor);
387 
388   sq=sqside;
389 
390   C.prepare(widget,clkbar,gc,0,0,250,sqh);
391   C.setColor(0);
392   C.drawRect(0,0,C.W,C.H,true);
393   C.H = Height();
394 
395   C.setFont(0,global.PlayerFont);
396   C.setFont(1,global.ClockFont);
397   C.setFont(2,global.InfoFont);
398 
399   ch = C.fontHeight(1);
400   th = C.fontHeight(0);
401   bh = ch + th;
402 
403   if (sq*3 < bh) {
404     C.setFont(0,global.InfoFont);
405     C.setFont(1,global.InfoFont);
406     C.setFont(2,global.InfoFont);
407     ch = C.fontHeight(1);
408     th = C.fontHeight(0);
409     bh = ch + th;
410   }
411 
412   // draw the clock time and White/Black labels
413 
414   ef=effectiveFlip();
415   if (ef) i= C.H - bh; else i = 0;
416   j=16;
417 
418   C.setColor(0xffffff);
419   if (clock.getActive()>0) {
420     if (lowtime) C.setColor(0xff8080);
421     C.drawRect(15, i, 200-30, bh+4, true);
422     C.setColor(0);
423   }
424 
425   C.drawString(j,i,0,_("Black"));
426   clockString(clock.getValue2(1),z,64);
427   C.drawString(j,i+th,1,z);
428 
429   if (ef) i=0; else i= C.H - bh;
430 
431   C.setColor(0xffffff);
432   if (clock.getActive()<0) {
433     if (lowtime) C.setColor(0xff8080);
434     C.drawRect(15, i, 200-30, bh+4, true);
435     C.setColor(0);
436   }
437   C.drawString(j,i,0,_("White"));
438   clockString(clock.getValue2(0),z,64);
439   C.drawString(j,i+th,1,z);
440 
441   C.consumeTop(bh+10);
442   C.consumeBottom(bh+10);
443 
444   // if we have enough space, render player names too
445   if ( (mygame) && (C.H > 2*th) ) {
446     C.setColor(0xffffff);
447     C.drawString(j,C.Y,0, mygame->getPlayerString(ef?0:1) );
448     C.drawString(j,C.Y + C.H - th,0, mygame->getPlayerString(ef?1:0) );
449     C.consumeTop(th+8);
450     C.consumeBottom(th+8);
451   }
452 
453   cleanTargets();
454 
455   // now render the information lines
456 
457   if (mygame) {
458     C.setColor(0xffffff);
459 
460     for(k=0;k<5;k++) {
461       if (C.H < 16) break;
462       C.drawString(j, C.Y, 2, info[k] );
463       C.consumeTop(16);
464     }
465 
466     // house string is info[5]
467 
468     if (!info[5].empty()) {
469       if ((!global.DrawHouseStock)||(C.H < 96)||(EditMode)) {
470 	if (C.H >= 16) {
471 	  C.drawString(j,C.Y,2,info[5]);
472 	  C.consumeTop(16);
473 	}
474       } else {
475 	// draw pieces in stock
476 	piece pk[5]={PAWN,ROOK,KNIGHT,BISHOP,QUEEN};
477 	int st[5][2];
478 	int si,sj,we,be;
479 
480 	// build stock info from string
481 	for(si=0;si<5;si++) { st[si][0]=st[si][1]=0; }
482 	sj=0;
483 	k=info[5].length();
484 	for(i=0;i<k;i++) {
485 	  switch(info[5][i]) {
486 	  case 'P': st[0][sj]++; break;
487 	  case 'R': st[1][sj]++; break;
488 	  case 'N': st[2][sj]++; break;
489 	  case 'B': st[3][sj]++; break;
490 	  case 'Q': st[4][sj]++; break;
491 	  case ']': sj=1; break;
492 	  }
493 	}
494 
495 	// draw stock pieces
496 	int eph, my, fh2, phs, pcsw;
497 
498 	if (global.UseVectorPieces)
499 	  eph=40+8;
500 	else
501 	  eph=cur->side + 8;
502 
503 	if (ef) { we=0; be=eph; } else { we=eph; be=0; }
504 	fh2 = C.fontHeight(2);
505 	if (!global.UseVectorPieces)
506 	  phs = cur->side/2;
507 
508 	my=2*eph;
509 	C.setColor(0x505050);
510 	for(si=2;si<my+2;si+=2)
511 	  C.drawLine(5,C.Y+si,235,C.Y+si);
512 
513 	for(si=3;si<my+2;si+=2) {
514 	  int lv;
515 	  lv=my/2 - si;
516 	  if (lv<0) lv=-lv;
517 	  lv*=2;
518 	  if (lv>0xc0) lv=0xc0;
519 	  lv=0xc0-lv;
520 	  lv=(lv<<16)|(lv<<8)|lv;
521 	  C.setColor(lv);
522 
523 	  C.drawLine(5,C.Y+si,235,C.Y+si);
524 	}
525 
526 	C.setColor(0x555555);
527 	for(si=0;si<5;si++)
528 	  C.drawLine(40*(si+1),C.Y+2,40*(si+1),C.Y+my+1);
529 
530 	for(si=0;si<5;si++) {
531 	  if (st[si][0]) {
532 
533 	    if (global.UseVectorPieces)
534 	      global.vpieces.drawPiece(clkbar,gc,40,40*si,C.Y+we,pk[si]|WHITE);
535 	    else
536 	      cur->drawPiece(pk[si]|WHITE,clkbar,gc,40*si+20-phs, C.Y+we);
537 
538 	    snprintf(z,64,"%d",st[si][0]);
539 
540 	    pcsw = C.stringWidth(2,z);
541 	    shadyText(40*si+20-pcsw/2, C.Y+eph+we-fh2, 2, z);
542 
543 	    if (mygame->MyColor==WHITE)
544 	      addTarget(new DropSource(pk[si]|WHITE,Width()+40*si,C.Y+we,40,eph));
545 
546 	    /*
547 	    C.setColor(0xffff00);
548 	    C.drawRect(40*si,C.Y+we,40,eph,false);
549 	    */
550 	  }
551 	  if (st[si][1]) {
552 
553 	    if (global.UseVectorPieces)
554 	      global.vpieces.drawPiece(clkbar,gc,40,40*si,C.Y+be,pk[si]|BLACK);
555 	    else
556 	      cur->drawPiece(pk[si]|BLACK,clkbar,gc,40*si+20-phs, C.Y+be);
557 
558 	    snprintf(z,64,"%d",st[si][1]);
559 
560 	    pcsw = C.stringWidth(2,z);
561 	    shadyText(40*si+20-pcsw/2, C.Y+eph+be-fh2, 2, z);
562 
563 	    if (mygame->MyColor==BLACK)
564 	      addTarget(new DropSource(pk[si]|BLACK,Width()+40*si,C.Y+be,40,eph));
565 
566 	    /*
567 	    C.setColor(0xffff00);
568 	    C.drawRect(40*si,C.Y+be,40,eph,false);
569 	    */
570 	  }
571 	} // for
572 
573 	C.consumeTop(2*eph+8);
574       } // text | graphic stock
575     } // if !info[5].empty
576 
577     // draw mouse if protocol menu available
578     if (global.protocol)
579       if (global.protocol->getPlayerActions()) {
580 	C.setColor(0xffffff);
581 	C.drawRect(170,C.Y+8,16,18,true);
582 	C.drawLine(177,C.Y+8,177,C.Y+4);
583 	C.setColor(0xffcc00);
584 	C.drawRect(180,C.Y+8,6,8,true);
585 	C.setColor(0);
586 	C.drawLine(170,C.Y+16,186,C.Y+16);
587 	C.drawLine(175,C.Y+8,175,C.Y+16);
588 	C.drawLine(180,C.Y+8,180,C.Y+16);
589 	C.consumeTop(20);
590       }
591   }
592 
593   if (C.H > 32 && !EditMode) {
594     if (position.getAnnotation()) {
595       C.consumeTop(16);
596       j=16;
597 
598       C.setColor(0x252535);
599       C.drawRect(j-5,C.Y,170,C.H,true);
600       C.setColor(0x4545ff);
601       C.drawRect(j-5,C.Y,170,C.H,false);
602 
603       C.setColor(0xffffff);
604       C.drawBoxedString(j,C.Y,160,C.H,2, position.getAnnotation(), " ");
605     }
606   }
607 
608   clock_ch=0;
609 }
610 
getClock()611 ChessClock * Board::getClock() {
612   return(&clock);
613 }
614 
getClockValue2(bool white)615 int Board::getClockValue2(bool white) {
616   return(clock.getValue2(white?0:1));
617 }
618 
setClock2(int wmsecs,int bmsecs,int actv,int cdown)619 void Board::setClock2(int wmsecs,int bmsecs,int actv,int cdown) {
620   global.debug("Board","setClock");
621   clock_ch=1;
622   clock.setClock2(wmsecs,bmsecs,actv,cdown);
623   queueRepaint();
624 }
625 
shadyText(int x,int y,int f,char * z)626 void Board::shadyText(int x,int y, int f, char *z) {
627   int i,j;
628 
629   C.setColor(0);
630   for(i=-2;i<=3;i++)
631     for(j=-2;j<=2;j++)
632       C.drawString(x+i,y+j,f,z);
633 
634   C.setColor(0x808080);
635   C.drawString(x,y,f,z);
636   C.setColor(0xffffff);
637   C.drawString(x+1,y,f,z);
638 }
639 
freeze()640 void Board::freeze() {
641   frozen_lvl++;
642 }
643 
thaw()644 void Board::thaw() {
645   frozen_lvl--;
646   if (frozen_lvl < 0) frozen_lvl=0;
647   if ((!frozen_lvl)&&(repaint_due)) {
648     repaint();
649     repaint_due=false;
650   }
651 }
652 
invalidate()653 void Board::invalidate() {
654   currently_rendered.invalidate();
655   queueRepaint();
656 }
657 
queueRepaint()658 void Board::queueRepaint() {
659   global.debug("Board","queueRepaint");
660   if (frozen_lvl)
661     repaint_due=true;
662   else
663     repaint();
664 }
665 
setInfo(int idx,const char * s)666 void Board::setInfo(int idx,const char *s) {
667   global.debug("Board","setInfo",s);
668   info[idx%6]=s;
669   clock_ch=1;
670   queueRepaint();
671 }
672 
setInfo(int idx,string & s)673 void Board::setInfo(int idx,string &s) {
674   global.debug("Board","setInfo/2");
675   info[idx%6] = s;
676   clock_ch=1;
677   queueRepaint();
678 }
679 
update(bool sndflag)680 void Board::update(bool sndflag) {
681   unsigned int i,rate;
682   AnimatedPiece *ap;
683   Position rpv;
684   bool ef;
685   bool sok=false;
686 
687   global.debug("Board","update");
688 
689   if (!mygame)
690     return;
691 
692   if ((global.AnimateMoves)&&(gotAnimationLoop)) {
693     update_queue=true;
694     UpdateStack.push(sndflag);
695     return;
696   }
697 
698   position = mygame->getCurrentPosition();
699   rpv = mygame->getPreviousPosition();
700   ef=effectiveFlip();
701 
702   if ((global.HilightLastMove)||(global.AnimateMoves)) {
703     rpv.diff(position,LastMove);
704     if (LockAnimate)
705       --LockAnimate;
706     else
707       if (currently_rendered != position)
708 	if ((global.AnimateMoves)&&(gdk_window_is_viewable(yidget->window))) {
709 	  sok=true; // sound ok
710 	  fake=position;
711 	  fake.intersection(rpv);
712 	  rate = global.SmootherAnimation ? 8 : 4;
713 	  for(i=0;i<LastMove.size();i++) {
714 	    if (ef)
715 	      ap=new FlatAnimatedPiece(LastMove[i]->Piece,
716 				       borx+sqside*(7-LastMove[i]->SX),
717 				       morey+bory+sqside*(LastMove[i]->SY),
718 				       borx+sqside*(7-LastMove[i]->DX),
719 				       morey+bory+sqside*(LastMove[i]->DY),
720 				       rate*LastMove[i]->distance(),sndflag);
721 	    else
722 	      ap=new FlatAnimatedPiece(LastMove[i]->Piece,
723 				       borx+sqside*(LastMove[i]->SX),
724 				       morey+bory+sqside*(7-LastMove[i]->SY),
725 				       borx+sqside*(LastMove[i]->DX),
726 				       morey+bory+sqside*(7-LastMove[i]->DY),
727 				       rate*LastMove[i]->distance(),sndflag);
728 	    ap->create(yidget->window,cur,sqside);
729 	    animes.push_back(ap);
730 	  }
731 	  if ((!LastMove.size())&&(sndflag))
732 	    global.flushSound();
733 	  if ((!animes.empty())&&(!gotAnimationLoop)) {
734 	    int tag;
735 	    gotAnimationLoop=1;
736 	    currently_rendered.invalidate();
737 	    board_configure_event(yidget,0,(gpointer)this);
738 	    if (rate==4)
739 	      tag=gtk_timeout_add(30,board_animate,this);
740 	    else
741 	      tag=gtk_timeout_add(15,board_animate,this);
742 	    AnimationTimeout = tag;
743 	  }
744 	}
745   }
746   if (!global.AnimateMoves)
747     queueRepaint();
748 
749   /* force redraw of background of dragging */
750   if (dr_active)
751     dr_step=0;
752 
753   update_queue=false;
754 
755   if ((!sok)&&(global.BeepWhenOppMoves)&&(sndflag))
756     global.flushSound();
757 }
758 
sendMove()759 void Board::sendMove() {
760   global.debug("Board","sendMove");
761   if ((sp<2)||(!mygame)) return;
762   if ((allowMove)||(FreeMove)) {
763     if (global.effectiveLegalityChecking() && !FreeMove)
764       if (!position.isMoveLegalCartesian(ax[0],ay[0],ax[1],ay[1],
765 					 mygame->MyColor,mygame->Variant)) {
766 	char z[128];
767 	snprintf(z,128,_("Illegal Move %c%d%c%d (Legality Checking On)"),
768 		 'a'+ax[0],ay[0]+1,'a'+ax[1],ay[1]+1);
769 	global.status->setText(z,15);
770 	goto sendmove_cleanup;
771       }
772     position.moveCartesian(ax[0],ay[0],ax[1],ay[1],REGULAR,true);
773     mygame->sendMove(ax[0],ay[0],ax[1],ay[1]);
774     allowMove=false;
775   } else if (global.Premove) {
776     premoveq=1;
777     pmx[0]=ax[0];
778     pmy[0]=ay[0];
779     pmx[1]=ax[1];
780     pmy[1]=ay[1];
781     pmp=EMPTY;
782   }
783  sendmove_cleanup:
784   sp=0;
785   hilite_ch=1;
786   repaint();
787 }
788 
setCanMove(bool value)789 void Board::setCanMove(bool value) {
790   bool oldv;
791   global.debug("Board","setCanMove");
792 
793   oldv=allowMove;
794   allowMove=value;
795 
796   // commit premove if any
797   if ((mygame)&&(global.Premove)&&(!oldv)&&(value)&&(premoveq)) {
798     repaint();
799 
800     if (pmp==EMPTY) {
801       if (position.isMoveLegalCartesian(pmx[0],pmy[0],pmx[1],pmy[1],
802 					mygame->MyColor,mygame->Variant)) {
803 	position.moveCartesian(pmx[0],pmy[0],pmx[1],pmy[1],REGULAR,true);
804 	mygame->sendMove(pmx[0],pmy[0],pmx[1],pmy[1]);
805       }
806     } else {
807       if (position.isDropLegal(pmp,pmx[1],pmy[1],
808 			       mygame->MyColor,mygame->Variant)) {
809 	if (pmp&COLOR_MASK == 0) pmp|=mygame->MyColor;
810 	position.moveDrop(pmp, pmx[1], pmy[1]);
811 	mygame->sendDrop(pmp, pmx[1], pmy[1]);
812       }
813     }
814     premoveq=0;
815     sp=0;
816     hilite_ch=1;
817     repaint();
818   }
819 }
820 
stopClock()821 void Board::stopClock() {
822   clock_ch=1;
823   clock.setClock2(clock.getValue2(0),clock.getValue2(1),0,1);
824   queueRepaint();
825 }
826 
openMovelist()827 void Board::openMovelist() {
828   global.debug("Board","openMovelist");
829   if (!mygame)
830     return;
831   mygame->openMoveList();
832 }
833 
walkBack1()834 void Board::walkBack1() {
835   if (!mygame)
836     return;
837   freeze();
838   mygame->goBack1();
839   position= mygame->getCurrentPosition();
840   setInfo(2,position.getLastMove());
841   setInfo(4,position.getMaterialString(mygame->Variant));
842   setInfo(5,position.getHouseString());
843   queueRepaint();
844   thaw();
845 }
846 
walkBackAll()847 void Board::walkBackAll() {
848   if (!mygame)
849     return;
850   freeze();
851   mygame->goBackAll();
852   position= mygame->getCurrentPosition();
853   setInfo(2,position.getLastMove());
854   setInfo(4,position.getMaterialString(mygame->Variant));
855   setInfo(5,position.getHouseString());
856   queueRepaint();
857   thaw();
858 }
859 
walkForward1()860 void Board::walkForward1() {
861   if (!mygame)
862     return;
863   freeze();
864   mygame->goForward1();
865   position= mygame->getCurrentPosition();
866   setInfo(2,position.getLastMove());
867   setInfo(4,position.getMaterialString(mygame->Variant));
868   setInfo(5,position.getHouseString());
869   queueRepaint();
870   thaw();
871 }
872 
walkForwardAll()873 void Board::walkForwardAll() {
874   if (!mygame)
875     return;
876   freeze();
877   mygame->goForwardAll();
878   position=mygame->getCurrentPosition();
879   setInfo(2,position.getLastMove());
880   setInfo(4,position.getMaterialString(mygame->Variant));
881   setInfo(5,position.getHouseString());
882   queueRepaint();
883   thaw();
884 }
885 
outlineRectangle(GdkGC * gc,int x,int y,int color,int pen)886 void Board::outlineRectangle(GdkGC *gc, int x,int y,int color,int pen) {
887   int i,j,k,X,Y,S;
888   gdk_rgb_gc_set_foreground(gc,color);
889   j=x; k=y; if (effectiveFlip()) j=7-j; else k=7-k;
890   if ((global.UseVectorPieces)||(!cur->extruded))
891     for(i=0;i<pen;i++)
892       gdk_draw_rectangle(yidget->window,gc,FALSE,borx+j*sqside+i,
893 			 morey+bory+k*sqside+i,
894 			 sqside-2*i,sqside-2*i);
895   else {
896     X=borx+j*sqside; Y=morey+bory+k*sqside; S=sqside;
897     gdk_draw_rectangle(yidget->window,gc,TRUE,X,Y,pen,sqside);
898     gdk_draw_rectangle(yidget->window,gc,TRUE,X+S-pen,Y,pen,sqside);
899     gdk_draw_rectangle(yidget->window,gc,TRUE,X,Y,3*pen,pen);
900     gdk_draw_rectangle(yidget->window,gc,TRUE,X+S-3*pen,Y,3*pen,pen);
901     gdk_draw_rectangle(yidget->window,gc,TRUE,X,Y+S-pen,3*pen,pen);
902     gdk_draw_rectangle(yidget->window,gc,TRUE,X+S-3*pen,Y+S-pen,3*pen,pen);
903   }
904 
905 }
906 
drawBall(GdkGC * gc,int x,int y,int color,int radius)907 void Board::drawBall(GdkGC *gc, int x,int y,int color,int radius) {
908   int j,k;
909   j=x; k=y; if (effectiveFlip()) j=7-j; else k=7-k;
910   gdk_rgb_gc_set_foreground(gc,color);
911   gdk_draw_arc(yidget->window,gc,FALSE,
912 	       borx+j*sqside+sqside/2-radius,
913 	       morey+bory+k*sqside+sqside/2-radius,
914 	       radius*2, radius*2, 0, 360*64);
915 }
916 
drop(piece p)917 void Board::drop(piece p) {
918   global.debug("Board","drop");
919   if (!mygame) return;
920   if (p&COLOR_MASK == 0) p|=mygame->MyColor;
921   if ((!allowMove)&&(!FreeMove)) {
922     if (global.Premove) {
923       premoveq=1;
924       pmx[0]=-1;
925       pmx[1]=dropsq[0];
926       pmy[1]=dropsq[1];
927       pmp=p;
928     } else
929       return;
930   } else {
931     if ((global.CheckLegality)&&(!FreeMove))
932       if (!position.isDropLegal(p&PIECE_MASK, dropsq[0], dropsq[1],
933 				mygame->MyColor,mygame->Variant))
934 	{
935 	  char z[128];
936 	  snprintf(z,128,_("Illegal Drop on %c%d (Legality Checking On)"),
937 		   'a'+dropsq[0],dropsq[1]+1);
938 	  global.status->setText(z,15);
939 	  goto drop_cleanup;
940 	}
941     position.moveDrop(p, dropsq[0], dropsq[1]);
942     mygame->sendDrop(p, dropsq[0], dropsq[1]);
943   }
944 
945  drop_cleanup:
946   sp=0;
947   hilite_ch=1;
948   repaint();
949 }
950 
951 // for crazyhouse/bughouse
popupDropMenu(int col,int row,int sx,int sy,guint32 etime)952 void Board::popupDropMenu(int col,int row,int sx,int sy,guint32 etime) {
953   GtkWidget *popmenu;
954   GtkWidget *mi[7];
955   Position pos;
956   int i;
957   char z[64];
958 
959   // sanity checks
960   if (!allowMove)       return;
961   if (!mygame)          return;
962   if (mygame->isOver()) return;
963   if ( (mygame->Variant != CRAZYHOUSE)&&(mygame->Variant != BUGHOUSE) )
964     return;
965 
966   dropsq[0]=col;
967   dropsq[1]=row;
968 
969   popmenu=gtk_menu_new();
970 
971   pos=mygame->getLastPosition();
972 
973   mi[0]=gtk_menu_item_new_with_label(_("Drop Piece:"));
974   gtk_widget_set_sensitive(mi[0],FALSE);
975   mi[1]=gtk_separator_menu_item_new();
976 
977   snprintf(z,64,_("Pawn    %d"),i=pos.getStockCount(PAWN|mygame->MyColor));
978   mi[2]=gtk_menu_item_new_with_label(z);
979   if (!i) gtk_widget_set_sensitive(mi[2],FALSE);
980 
981   snprintf(z,64,_("Rook    %d"),i=pos.getStockCount(ROOK|mygame->MyColor));
982   mi[3]=gtk_menu_item_new_with_label(z);
983   if (!i) gtk_widget_set_sensitive(mi[3],FALSE);
984 
985   snprintf(z,64,_("Knight  %d"),i=pos.getStockCount(KNIGHT|mygame->MyColor));
986   mi[4]=gtk_menu_item_new_with_label(z);
987   if (!i) gtk_widget_set_sensitive(mi[4],FALSE);
988 
989   snprintf(z,64,_("Bishop  %d"),i=pos.getStockCount(BISHOP|mygame->MyColor));
990   mi[5]=gtk_menu_item_new_with_label(z);
991   if (!i) gtk_widget_set_sensitive(mi[5],FALSE);
992 
993   snprintf(z,64,_("Queen   %d"),i=pos.getStockCount(QUEEN|mygame->MyColor));
994   mi[6]=gtk_menu_item_new_with_label(z);
995   if (!i) gtk_widget_set_sensitive(mi[6],FALSE);
996 
997   for(i=0;i<7;i++) {
998     gshow(mi[i]);
999     gtk_menu_shell_append(GTK_MENU_SHELL(popmenu),mi[i]);
1000   }
1001 
1002   gtk_signal_connect(GTK_OBJECT(mi[2]),"activate",
1003 		     GTK_SIGNAL_FUNC(drop_callbackP),
1004 		     (gpointer)this);
1005   gtk_signal_connect(GTK_OBJECT(mi[3]),"activate",
1006 		     GTK_SIGNAL_FUNC(drop_callbackR),
1007 		     (gpointer)this);
1008   gtk_signal_connect(GTK_OBJECT(mi[4]),"activate",
1009 		     GTK_SIGNAL_FUNC(drop_callbackN),
1010 		     (gpointer)this);
1011   gtk_signal_connect(GTK_OBJECT(mi[5]),"activate",
1012 		     GTK_SIGNAL_FUNC(drop_callbackB),
1013 		     (gpointer)this);
1014   gtk_signal_connect(GTK_OBJECT(mi[6]),"activate",
1015 		     GTK_SIGNAL_FUNC(drop_callbackQ),
1016 		     (gpointer)this);
1017 
1018   gtk_menu_popup(GTK_MENU(popmenu),0,0,0,0,3,etime);
1019 }
1020 
popupProtocolMenu(int x,int y,guint32 etime)1021 void Board::popupProtocolMenu(int x,int y, guint32 etime) {
1022   GtkWidget *mi, *popmenu;
1023   vector<string *> *v;
1024   char z[64];
1025   unsigned int i;
1026 
1027   if (!global.protocol) return;
1028   if (!mygame) return;
1029   if (global.protocol->getPlayerActions() == NULL) return;
1030 
1031   popmenu=gtk_menu_new();
1032   PopupOwner = this;
1033 
1034   // players
1035   v=global.protocol->getPlayerActions();
1036 
1037   for(i=0;i<v->size();i++) {
1038     snprintf(z,64,"%s: %s",mygame->PlayerName[0], ((*v)[i])->c_str() );
1039     mi=gtk_menu_item_new_with_label(z);
1040     gshow(mi);
1041     gtk_menu_shell_append(GTK_MENU_SHELL(popmenu),mi);
1042 
1043     gtk_signal_connect(GTK_OBJECT(mi),"activate",
1044 		       GTK_SIGNAL_FUNC(menu_whitep),
1045 		       (gpointer) ( (*v)[i] ) );
1046   }
1047 
1048   mi=gtk_separator_menu_item_new();
1049   gshow(mi);
1050   gtk_menu_shell_append(GTK_MENU_SHELL(popmenu),mi);
1051 
1052   for(i=0;i<v->size();i++) {
1053     snprintf(z,64,"%s: %s",mygame->PlayerName[1], ((*v)[i])->c_str() );
1054     mi=gtk_menu_item_new_with_label(z);
1055     gshow(mi);
1056     gtk_menu_shell_append(GTK_MENU_SHELL(popmenu),mi);
1057 
1058     gtk_signal_connect(GTK_OBJECT(mi),"activate",
1059 		       GTK_SIGNAL_FUNC(menu_blackp),
1060 		       (gpointer) ( (*v)[i] ) );
1061   }
1062 
1063   if (global.protocol->getGameActions() != 0) {
1064     mi=gtk_separator_menu_item_new();
1065     gshow(mi);
1066     gtk_menu_shell_append(GTK_MENU_SHELL(popmenu),mi);
1067 
1068     v=global.protocol->getGameActions();
1069 
1070     for(i=0;i<v->size();i++) {
1071       snprintf(z,64,_("Game #%d: %s"),mygame->GameNumber, ((*v)[i])->c_str() );
1072       mi=gtk_menu_item_new_with_label(z);
1073       gshow(mi);
1074       gtk_menu_shell_append(GTK_MENU_SHELL(popmenu),mi);
1075 
1076       gtk_signal_connect(GTK_OBJECT(mi),"activate",
1077 			 GTK_SIGNAL_FUNC(menu_gamep),
1078 			 (gpointer) ( (*v)[i] ) );
1079     }
1080   }
1081 
1082 #if MAEMO
1083   gtk_menu_popup(GTK_MENU(popmenu),0,0,0,0,1,etime);
1084 #else
1085   gtk_menu_popup(GTK_MENU(popmenu),0,0,0,0,3,etime);
1086 #endif
1087 }
1088 
dump()1089 void Board::dump() {
1090   cerr.setf(ios::hex,ios::basefield);
1091   cerr.setf(ios::showbase);
1092 
1093   cerr << "[board " << ((uint64_t) this) << "] ";
1094   cerr << "game=[" << ((uint64_t) mygame) << "] ";
1095 
1096   cerr.setf(ios::dec,ios::basefield);
1097   cerr << "paneid=" << getPageId() << endl;
1098 }
1099 
1100 /* callbacks */
1101 
drop_callbackP(GtkMenuItem * item,gpointer data)1102 void drop_callbackP(GtkMenuItem *item,gpointer data) {
1103   Board *b; b=(Board *)data; b->drop(PAWN);
1104 }
1105 
drop_callbackR(GtkMenuItem * item,gpointer data)1106 void drop_callbackR(GtkMenuItem *item,gpointer data) {
1107   Board *b; b=(Board *)data; b->drop(ROOK);
1108 }
1109 
drop_callbackN(GtkMenuItem * item,gpointer data)1110 void drop_callbackN(GtkMenuItem *item,gpointer data) {
1111   Board *b; b=(Board *)data; b->drop(KNIGHT);
1112 }
1113 
drop_callbackB(GtkMenuItem * item,gpointer data)1114 void drop_callbackB(GtkMenuItem *item,gpointer data) {
1115   Board *b; b=(Board *)data; b->drop(BISHOP);
1116 }
1117 
drop_callbackQ(GtkMenuItem * item,gpointer data)1118 void drop_callbackQ(GtkMenuItem *item,gpointer data) {
1119   Board *b; b=(Board *)data; b->drop(QUEEN);
1120 }
1121 
menu_whitep(GtkMenuItem * item,gpointer data)1122 void menu_whitep(GtkMenuItem *item, gpointer data) {
1123   Board *me;
1124   me=Board::PopupOwner;
1125   Board::PopupOwner = 0;
1126   if (!me) return;
1127   if (!me->mygame) return;
1128   if (!global.protocol) return;
1129   global.protocol->callPlayerAction(me->mygame->PlayerName[0],(string *) data);
1130 }
1131 
menu_blackp(GtkMenuItem * item,gpointer data)1132 void menu_blackp(GtkMenuItem *item, gpointer data) {
1133   Board *me;
1134   me=Board::PopupOwner;
1135   Board::PopupOwner = 0;
1136   if (!me) return;
1137   if (!me->mygame) return;
1138   if (!global.protocol) return;
1139   global.protocol->callPlayerAction(me->mygame->PlayerName[1],(string *) data);
1140 }
1141 
menu_gamep(GtkMenuItem * item,gpointer data)1142 void menu_gamep(GtkMenuItem *item, gpointer data) {
1143   Board *me;
1144   me=Board::PopupOwner;
1145   Board::PopupOwner = 0;
1146   if (!me) return;
1147   if (!me->mygame) return;
1148   if (!global.protocol) return;
1149   global.protocol->callGameAction(me->mygame->GameNumber,(string *) data);
1150 }
1151 
drawCoordinates(GdkPixmap * dest,GdkGC * gc,int side)1152 void Board::drawCoordinates(GdkPixmap *dest,GdkGC *gc,int side) {
1153   bool ef;
1154   int i;
1155   char z[2];
1156   PangoLayout *pl;
1157   PangoFontDescription *pfd;
1158 
1159   z[1]=0;
1160   ef=effectiveFlip();
1161 
1162   if ((borx)||(bory)) {
1163     gdk_rgb_gc_set_foreground(gc,0);
1164     gdk_draw_rectangle(dest,gc,TRUE,0,0,Width(),bory);
1165     gdk_draw_rectangle(dest,gc,TRUE,0,Height()-bory,Width(),bory);
1166     gdk_draw_rectangle(dest,gc,TRUE,0,0,borx,Height());
1167     gdk_draw_rectangle(dest,gc,TRUE,Width()-borx,0,borx,Height());
1168   }
1169 
1170   gdk_rgb_gc_set_foreground(gc,0x404040);
1171   gdk_draw_rectangle(dest,gc,TRUE,borx,2,side*8,bory-4);
1172   gdk_draw_rectangle(dest,gc,TRUE,borx,2+side*8+bory+morey,side*8,bory-4);
1173   gdk_draw_rectangle(dest,gc,TRUE,2,bory+1,borx-4,morey+side*8-1);
1174   gdk_draw_rectangle(dest,gc,TRUE,2+side*8+borx,bory+1,borx-4,morey+side*8-1);
1175 
1176   gdk_rgb_gc_set_foreground(gc,0xffffff);
1177 
1178   pl = gtk_widget_create_pango_layout(widget, NULL);
1179   pfd = pango_font_description_from_string(global.InfoFont);
1180   pango_layout_set_font_description(pl, pfd);
1181 
1182   for(i=0;i<8;i++) {
1183     z[0]=ef?'h'-i:'a'+i;
1184 
1185     pango_layout_set_text(pl,z,-1);
1186     gdk_draw_layout(dest,gc,borx+side*i+side/2, 2,pl);
1187     gdk_draw_layout(dest,gc,borx+side*i+side/2, morey+8*side+bory,pl);
1188     z[0]=ef?'1'+i:'8'-i;
1189     pango_layout_set_text(pl,z,-1);
1190 
1191     gdk_draw_layout(dest,gc, 6, bory+morey+side*i+side/2, pl);
1192     gdk_draw_layout(dest,gc, side*8+borx+6, bory+morey+side*i+side/2, pl);
1193   }
1194 
1195   pango_font_description_free(pfd);
1196   g_object_unref(G_OBJECT(pl));
1197 }
1198 
composeVecBoard(GdkGC * gc)1199 void Board::composeVecBoard(GdkGC *gc) {
1200   Position *joker;
1201   int i,j;
1202   global.vpieces.drawSquares(buffer,gc,sqside, borx, bory);
1203 
1204   if (gotAnimationLoop) joker=&fake; else joker=&position;
1205   if (effectiveFlip())
1206     for(i=0;i<8;i++)
1207       for(j=0;j<8;j++)
1208 	global.vpieces.drawPiece(buffer,gc,sqside,borx+i*sqside,bory+j*sqside,
1209 				 joker->getPiece(7-i,j));
1210   else
1211     for(i=0;i<8;i++)
1212       for(j=0;j<8;j++)
1213 	global.vpieces.drawPiece(buffer,gc,sqside,borx+i*sqside,bory+j*sqside,
1214 				 joker->getPiece(i,7-j));
1215 
1216   if (global.ShowCoordinates)
1217     drawCoordinates(buffer,gc,sqside);
1218 
1219   i=Width();
1220   gdk_rgb_gc_set_foreground(gc,0);
1221   gdk_draw_rectangle(buffer,gc,TRUE,0,i,i,sqh-Height());
1222 
1223   currently_rendered=*(joker);
1224 }
1225 
pasteVecBoard()1226 void Board::pasteVecBoard() {
1227   gdk_draw_pixmap(yidget->window,
1228 		  yidget->style->fg_gc[GTK_WIDGET_STATE (yidget)],
1229 		  buffer,
1230 		  0, 0,
1231 		  0, 0,Width(),Height());
1232 }
1233 
1234 /* ******************************************************************
1235    board_expose_event
1236 
1237    called on: repaints (both generated by the program and by the
1238                         windowing system).
1239 
1240    description:
1241    paints straight on window. paints external borders
1242    black if window size requires it.
1243 
1244    Board:
1245    If using vectorized pieces, calls [composeVecBoard] to update
1246    the [buffer] variable.
1247    Paints [buffer] (board) on window.
1248 
1249    Clock:
1250    If [clock_ch] is set, call renderClock.
1251    Draws [clkbar] on window.
1252 
1253    Highlights:
1254    Paints highlights, selection, premove indicators straight
1255    to window.
1256 
1257    Dragging:
1258    If dragging, call [board_motion_event] to update the
1259    dragging buffers.
1260 
1261    ********************************************************** */
board_expose_event(GtkWidget * widget,GdkEventExpose * ee,gpointer data)1262 gboolean board_expose_event(GtkWidget *widget,GdkEventExpose *ee,
1263                             gpointer data) {
1264   Board *me;
1265   GdkGC *gc;
1266   unsigned int i;
1267   int w,h,sq,pw,fw,fh;
1268 
1269   global.debug("Board","board_expose_event");
1270 
1271   me=(Board *)data;
1272   w=ee->area.width;
1273   h=ee->area.height;
1274   fw=ee->area.x+ee->area.width;
1275   fh=ee->area.y+ee->area.height;
1276 
1277   sq=me->sqside;
1278 
1279   if (!me->wgc)
1280     me->wgc=gdk_gc_new(widget->window);
1281   gc=me->wgc;
1282 
1283   if (fw> ( me->Width() + 250) )
1284     gdk_draw_rectangle(widget->window,widget->style->black_gc,TRUE,
1285 		       me->Width()+250,0,fw-me->Width()-250,fh);
1286 
1287   if (fh > me->sqh) {
1288     gdk_draw_rectangle(widget->window,widget->style->black_gc,TRUE,
1289 		       0,me->sqh,fw,fh-me->sqh);
1290     fh=me->sqh;
1291   }
1292 
1293   if (fw > me->sqw)
1294     fw=me->sqw;
1295 
1296   pw=fw;
1297   if (fw> me->Width() ) pw=me->Width();
1298 
1299   // draw board+pieces
1300 
1301   if (global.UseVectorPieces)
1302     me->composeVecBoard(gc);
1303 
1304   gdk_draw_pixmap(widget->window,
1305 		  widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
1306 		  me->buffer,
1307 		  0, 0,
1308 		  0, 0,pw,fh);
1309 
1310   // draw clock
1311 
1312   if (me->clock_ch)
1313     me->renderClock();
1314   gdk_draw_pixmap(widget->window,
1315                   widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
1316                   me->clkbar,
1317                   0, 0,
1318                   me->Width(), 0,250,fh);
1319 
1320   // draw highlighted squares
1321 
1322   if (!me->gotAnimationLoop)
1323     if ((me->sp)||(!me->LastMove.empty())||(me->premoveq)) {
1324 
1325       if (!me->EditMode)
1326 	if ((global.HilightLastMove)&&(!me->LastMove.empty()))
1327 	  for(i=0;i<me->LastMove.size();i++) {
1328 	    me->outlineRectangle(gc,me->LastMove[i]->SX,me->LastMove[i]->SY,
1329 				 0x0000ff,3);
1330 	    me->outlineRectangle(gc,me->LastMove[i]->DX,me->LastMove[i]->DY,
1331 				 0x0000ff,3);
1332 	}
1333 
1334       if (me->sp)
1335 	for(i=0;i < (unsigned int) (me->sp);i++)
1336 	  me->outlineRectangle(gc,me->ax[i],me->ay[i],me->sel_color,4);
1337 
1338       if (me->premoveq) {
1339 	me->outlineRectangle(gc,me->pmx[1],me->pmy[1],0xff0000,3);
1340 	me->drawBall(gc,me->pmx[1],me->pmy[1],0xff0000,5);
1341 	if (me->pmx[0]>=0) {
1342 	  me->outlineRectangle(gc,me->pmx[0],me->pmy[0],0xff0000,3);
1343 	  me->drawBall(gc,me->pmx[0],me->pmy[0],0xff0000,5);
1344 	}
1345       }
1346     }
1347 
1348   /* if dragging... */
1349   if (me->dr_active) {
1350     me->dr_step=0;
1351     board_motion_event(widget,&LastMotionEvent,data);
1352   }
1353 
1354   return 1;
1355 }
1356 
1357 /* ******************************************************************
1358    board_configure_event
1359 
1360    called on: resizes, piece changes, program-generated repaints.
1361 
1362    Updates the [buffer] pixmap.
1363 
1364    Description:
1365 
1366    Square size/Pieces:
1367    Recalculates square size. Resizes piece set [Board::cur] if needed.
1368 
1369    Return immediately if dealing with vectorized pieces.
1370 
1371    Compose [buffer]:
1372    Render position to [scrap]. If animating a move, renders
1373    [Board::fake], else renders [Board::position].
1374 
1375    Copy [scrap] to [buffer]. (X programming gizmo, scrap is an image,
1376    buffer is a pixmap, and bitmap pieces can't be rendered on
1377    pixmaps directly)
1378 
1379    Coordinates:
1380    Draw coordinates to [buffer] if global.ShowCoordinates set.
1381 
1382    updates [currently_rendered] to reflect the current situation.
1383 
1384    ************************************************************* */
board_configure_event(GtkWidget * widget,GdkEventConfigure * ce,gpointer data)1385 gboolean board_configure_event(GtkWidget *widget,GdkEventConfigure *ce,
1386 			       gpointer data) {
1387   Board *me;
1388   int optimal_square,sq;
1389   int ww,wh,i,j;
1390   int oldsqw, oldsqh;
1391   Position *joker;
1392 
1393   me=(Board *)data;
1394 
1395   global.debug("Board","board_configure_event");
1396 
1397   gdk_window_get_size(widget->window,&ww,&wh);
1398   ww-=200;
1399 
1400   if ( (ww-2*me->borx) > (wh-2*me->bory) ) {
1401     if ( (global.UseVectorPieces) || (! global.pieceset->extruded) ) {
1402       optimal_square=(wh - 2 * me->bory)/8;
1403       me->morey=0;
1404     } else {
1405       optimal_square=(int) ((wh - 2 * me->bory)/8.50);
1406       me->morey = optimal_square / 2;
1407     }
1408   } else {
1409     optimal_square=(ww - 2 * me->borx)/8;
1410     if ( (global.UseVectorPieces) || (! global.pieceset->extruded) )
1411       me->morey = 0;
1412     else
1413       me->morey = optimal_square / 2;
1414   }
1415 
1416   if (optimal_square < 10) return FALSE;
1417 
1418   if (me->cur == 0)
1419     me->cur=new PieceSet(global.pieceset);
1420   if (me->cur->side != optimal_square) {
1421     if (me->cur->side != global.pieceset->side) {
1422       delete me->cur;
1423       me->cur=new PieceSet(global.pieceset);
1424     }
1425     me->cur->scale(optimal_square);
1426     me->currently_rendered.invalidate();
1427   }
1428   sq=optimal_square;
1429   if (sq!=me->sqside) {
1430     me->currently_rendered.invalidate();
1431     me->clock_ch=1;
1432   }
1433 
1434   oldsqw=me->sqw;
1435   oldsqh=me->sqh;
1436   me->sqside=sq;
1437   me->sqw = 2*me->borx + 8 * me->sqside + 1;
1438   me->sqh = 2*me->bory + me->morey + 8 * me->sqside + 1;
1439 
1440   if ((me->currently_rendered == me->position)&&(!me->update_queue)) {
1441     return TRUE;
1442   }
1443 
1444   if (oldsqw!= me->sqw || oldsqh != me->sqh) {
1445     gdk_pixmap_unref(me->clkbar);
1446     gdk_pixmap_unref(me->buffer);
1447     if (me->clkgc) { gdk_gc_destroy(me->clkgc); me->clkgc=0; }
1448     if (me->scrap) { g_free(me->scrap); me->scrap=0; }
1449 
1450     me->buffer=gdk_pixmap_new(MainWindow::RefWindow,
1451 			      me->sqw,me->sqh,-1);
1452     me->clkbar=gdk_pixmap_new(MainWindow::RefWindow,
1453 			      250,me->sqh,-1);
1454     me->clkgc=gdk_gc_new(me->clkbar);
1455     me->scrap=(rgbptr)g_malloc(me->sqw * me->sqh * 3);
1456   }
1457 
1458   memset(me->scrap,0,me->sqw * me->sqh * 3);
1459 
1460   if (me->UpdateClockAfterConfigure) {
1461     me->updateClock();
1462     me->UpdateClockAfterConfigure = false;
1463   }
1464 
1465   if (global.UseVectorPieces)
1466     return TRUE;
1467 
1468   if (me->gotAnimationLoop)
1469     joker=&(me->fake);
1470   else
1471     joker=&(me->position);
1472 
1473   me->cur->beginQueueing();
1474 
1475   if (me->effectiveFlip())
1476     for(i=0;i<8;i++)
1477       for(j=0;j<8;j++)
1478 	me->cur->drawPieceAndSquare(joker->getPiece(7-i,j),
1479 				    me->scrap,me->borx+i*sq,
1480 				    me->morey+me->bory+j*sq,me->sqw,
1481 				    (i+j)%2);
1482   else
1483     for(i=0;i<8;i++)
1484       for(j=0;j<8;j++)
1485 	me->cur->drawPieceAndSquare(joker->getPiece(i,7-j),
1486 				    me->scrap,me->borx+i*sq,
1487 				    me->morey+me->bory+j*sq,me->sqw,
1488 				    (i+j)%2);
1489 
1490   me->cur->endQueueing();
1491   // and doing without curly braces just adds to the general perplexity
1492 
1493   // copy scrap to pixmap buffer
1494   gdk_draw_rgb_image(me->buffer,widget->style->black_gc,0,0,
1495 		     me->sqw, me->sqh,
1496 		     GDK_RGB_DITHER_NORMAL,me->scrap,me->sqw*3);
1497 
1498   if (global.ShowCoordinates) {
1499     if (!me->wgc)
1500       me->wgc=gdk_gc_new(widget->window);
1501     me->drawCoordinates(me->buffer,me->wgc,sq);
1502   }
1503 
1504   me->currently_rendered=*(joker);
1505 
1506   return TRUE;
1507 }
1508 
1509 /*
1510 static bool not_disjoint_intervals(int a, int b, int c, int d) {
1511   return( (c<=b) && (d>=a) );
1512 }
1513 
1514 static bool rects_overlap(int x1,int y1,int w1,int h1,
1515 			  int x2,int y2,int w2,int h2) {
1516   return( not_disjoint_intervals(x1,x1+w1, x2, x2+w2) &&
1517 	  not_disjoint_intervals(y1,y1+h1, y2, y2+h2) );
1518 }
1519 */
1520 
1521 static GdkPixmap *GCPbuffer=0;
1522 static int gcpw=0,gcph=0;
1523 
1524 /* ***************************************************************
1525    board_motion_event
1526 
1527    called on: mouse moved during drag, also called by
1528               [board_expose_event]
1529 
1530    depends on all Board::dr_* fields.
1531    uses [GCPbuffer] (static to this module) as drawing buffer.
1532 
1533    Startup Sanity Checking:
1534    Return immediately if the after-click timeout hasn't
1535    expired [dr_fto], not dragging a piece [dr_active],
1536    no event [em], or dragging not with left mouse button.
1537 
1538    GCPbuffer allocation:
1539    If current [GCPbuffer] is smaller than needed, dump the
1540    current one and allocate another.
1541 
1542    Step 0: Prepare background
1543    If first call on this drag ([dr_step] = 0), copy [buffer] to
1544    [GCPbuffer]
1545 
1546    Step 1: Erase previous dragging frame
1547    Only if not first call on this drag ([dr_step] != 0).
1548    copy square from [buffer] to [GCPbuffer],
1549    copy clock from [clkbar] to [GCPbuffer] if needed.
1550    Extruded sets: be sure to erase enough to cover [PieceSet::height]
1551 
1552    Step 2: Erase source square
1553    Only if dragging from the board (instead of zhouse stock,
1554    [dr_fromstock]).
1555    Vectorized pieces: draw square and coordinates directly to
1556     [GCPbuffer].
1557    Bitmapped pieces: allocate new image (M[0]), draw empty square on
1558     it, copy [M[0]] to [GCPbuffer].
1559    Extruded Bitmapped pieces: redraw piece on square right above the
1560     source square.
1561 
1562    Step 3: Draw dragged piece
1563    Vectorized pieces: just draw it on [GCPbuffer]
1564    Bitmap pieces: paint alpha'ed piece (more comments to come, experimental
1565     code)
1566 
1567    Step 3 1/2: Draw coordinates (bitmap-mode only)
1568    Redraw the coordinates (if global.ShowCoordinates set) on
1569    [GCPbuffer]
1570 
1571    Step 4: Commit
1572    Draw [GCPbuffer] on window, update Board::dr_* about what has
1573    been done, deallocate all local storage as needed.
1574 
1575    *************************************************************** */
board_motion_event(GtkWidget * widget,GdkEventMotion * em,gpointer data)1576 gboolean board_motion_event(GtkWidget *widget,
1577 			    GdkEventMotion *em,
1578 			    gpointer data) {
1579   Board *me;
1580   GdkGC *gcpgc;
1581   int rw,rh;
1582   int ww,wh,sw,sh;
1583   int sq,he,xc,xr;
1584   int bw,bh;
1585 
1586   if (em!=NULL) {
1587     if (!GDK_IS_WINDOW(em->window))
1588       return TRUE;
1589     memcpy(&LastMotionEvent,em,sizeof(GdkEventMotion));
1590   }
1591 
1592   me=(Board *)data;
1593 
1594   if ((!me->dr_fto)||(!me->dr_active)||(em==NULL)||(!(em->state & GDK_BUTTON1_MASK)))
1595     return TRUE;
1596 
1597   sq=he=me->sqside;
1598   if ((me->cur->extruded)&&(!global.UseVectorPieces)) he=me->cur->height;
1599 
1600   // STEP 0: prepare double buffer for drawing
1601 
1602   gdk_window_get_size(widget->window,&ww,&wh);
1603 
1604   if (!me->wgc)
1605     me->wgc=gdk_gc_new(widget->window);
1606   gcpgc=me->wgc;
1607   gdk_rgb_gc_set_foreground(gcpgc,0);
1608 
1609   sw=ww; sh=wh;
1610   if (sw> me->sqw) sw=me->sqw;
1611   if (sh> me->sqh) sh=me->sqh;
1612 
1613   if ( (gcpw < ww)|| (gcph < wh) ) {
1614     gcpw=ww; gcph=wh;
1615     if (GCPbuffer) gdk_pixmap_unref(GCPbuffer);
1616     GCPbuffer=gdk_pixmap_new(widget->window, gcpw, gcph, -1);
1617     gdk_draw_rectangle(GCPbuffer,gcpgc,TRUE,0,0,gcpw,gcph);
1618     gdk_draw_pixmap(GCPbuffer,gcpgc,me->buffer,0,0,0,0,sw,sh);
1619   } else
1620     if (! me->dr_step) {
1621       gdk_draw_rectangle(GCPbuffer,gcpgc,TRUE,0,0,gcpw,gcph);
1622       gdk_draw_pixmap(GCPbuffer,gcpgc,me->buffer,0,0,0,0,sw,sh);
1623     }
1624 
1625   bw=me->Width();
1626   bh=me->Height();
1627 
1628   // STEP 1: erase dirty animation square
1629 
1630   if (me->dr_step) {
1631     rw= bw - me->dr_dirty[0];
1632     rh= bh - me->dr_dirty[1];
1633     if (rw > sq) rw = sq;
1634     if (rh > he) rh = he;
1635 
1636     if ((rw>0)&&(rh>0))
1637       gdk_draw_pixmap(GCPbuffer,gcpgc,
1638 		      me->buffer,
1639 		      me->dr_dirty[0],me->dr_dirty[1],
1640 		      me->dr_dirty[0],me->dr_dirty[1],
1641 		      rw,rh);
1642 
1643     //    gdk_rgb_gc_set_foreground(gcpgc,0xffff00);
1644     gdk_draw_rectangle(GCPbuffer,gcpgc,TRUE,bw+250,0,ww-(bw+250),wh);
1645     gdk_draw_rectangle(GCPbuffer,gcpgc,TRUE,0,bh,bw+250,wh-bh);
1646     //    gdk_rgb_gc_set_foreground(gcpgc,0);
1647 
1648   }
1649 
1650   gdk_draw_pixmap(GCPbuffer,
1651 		  widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
1652 		  me->clkbar,
1653 		  0, 0,
1654 		  bw, 0,250, bh );
1655 
1656   // STEP 2: erase source square (if not dragging from zhouse stock)
1657 
1658   if (! me->dr_fromstock) {
1659     if (global.UseVectorPieces) {
1660 
1661       gdk_rgb_gc_set_foreground(gcpgc,((me->dr_c+me->dr_r)&0x01)?global.DarkSqColor:global.LightSqColor);
1662       gdk_draw_rectangle(GCPbuffer,gcpgc,TRUE,
1663 			 me->borx + me->dr_c*sq,
1664 			 me->morey + me->bory + me->dr_r*sq,
1665 			 sq,sq);
1666       gdk_rgb_gc_set_foreground(gcpgc,0);
1667       gdk_draw_rectangle(GCPbuffer,gcpgc,FALSE,
1668 			 me->borx + me->dr_c*sq,
1669 			 me->morey + me->bory + me->dr_r*sq,
1670 			 sq,sq);
1671     } else {
1672 
1673       // I only need *3, but glibc has some weird glitches when it
1674       // comes to allocating not padded to 32-bit words
1675 
1676       me->M[0].ensure(sq*he*4);
1677       me->cur->drawSquare( (me->dr_c+me->dr_r)&0x01, me->M[0].data , 0, 0, sq );
1678       gdk_draw_rgb_image(GCPbuffer,widget->style->black_gc,
1679 			 me->borx + me->dr_c * sq,
1680 			 me->morey + me->bory + me->dr_r * sq,
1681 			 sq, sq,
1682 			 GDK_RGB_DITHER_NORMAL, me->M[0].data ,3 * sq);
1683 
1684       // erase upper portion of extruded piece
1685       if (me->cur->extruded) {
1686 	if (me->dr_r > 0) { // there's a square above it
1687 	  me->M[1].ensure(sq*sq*4);
1688 	  xc = me->effectiveFlip() ? 7 - me->dr_c : me->dr_c;
1689 	  xr = me->effectiveFlip() ? me->dr_r - 1 : 8 - me->dr_r ;
1690 	  me->cur->drawPieceAndSquare(
1691 				      me->position.getPiece(xc, xr) ,
1692 				      me->M[0].data, 0, 0, sq, ((xc+xr)%2 == 0) );
1693 	  memcpy(me->M[1].data , me->M[0].data, sq*sq*3);
1694 
1695 	  gdk_draw_rgb_image(GCPbuffer,widget->style->black_gc,
1696 			     me->borx + me->dr_c * sq,
1697 			     me->morey + me->bory + (me->dr_r - 1) * sq,
1698 			     sq, sq,
1699 			     GDK_RGB_DITHER_NORMAL,me->M[0].data,3 * sq);
1700 	} else { // there is nothing above it, just paint it black
1701 	  gdk_draw_rectangle(GCPbuffer,widget->style->black_gc,TRUE,
1702 			     me->borx + me->dr_c * sq,
1703 			     me->bory, sq, me->morey);
1704 	}
1705       }
1706 
1707     }
1708   }
1709 
1710   // STEP 3: draw dragged piece
1711 
1712   int dx,dy;
1713   int x,y;
1714   GdkModifierType state;
1715 
1716   if (em->is_hint){
1717 
1718     x=(int)(em->x);
1719     y=(int)(em->y);
1720 
1721     if (em->window != NULL) {
1722       if (GDK_IS_WINDOW(em->window))
1723 	gdk_window_get_pointer(em->window, &x, &y, &state);
1724       else
1725 	cout << "[ case 1: em->window is NOT a GDK_WINDOW ]\n";
1726     } else {
1727       if (me->widget->window==NULL) {
1728 	cout << "[ case 3: both window pointers are NULL ]\n";
1729       } else if (GDK_IS_WINDOW(me->widget->window))
1730 	gdk_window_get_pointer(me->widget->window, &x, &y, &state);
1731       else
1732 	cout << "[ case 2: me->widget->window is NOT a GDK_WINDOW ]\n";
1733       cout << "[ And this is where it used to print a warning ]\n"; // FIXME
1734     }
1735 
1736   } else {
1737     x=(int)(em->x);
1738     y=(int)(em->y);
1739   }
1740 
1741   // prevent segfaults (bug sf#459164)
1742   if (x < 0) x=0;
1743   if (y < 0) y=0;
1744   if (x > ww) x=ww;
1745   if (y > wh) y=wh;
1746 
1747   dx = x - me->dr_ox;
1748   dy = y - me->dr_oy;
1749 
1750   // yet more segfault prevention
1751   if (dx < 0) { x-=dx; dx=0; }
1752   if (dy < 0) { y-=dy; dy=0; }
1753 
1754   if (global.UseVectorPieces) {
1755     global.vpieces.drawPiece((GdkPixmap *)(GCPbuffer),
1756 			     gcpgc,sq,dx,dy,me->dr_p);
1757     xr=dy;
1758     //  } else if (dx+sq > me->Width() ) {
1759   } else {
1760     // segment-by-segment masked render, piece overlaps clock area
1761 
1762     if (me->cur->extruded) {
1763       xr=dy-he+sq; xc=he-sq;
1764       if (xr < 0) { xc+=xr; xr=0; }
1765       if (xc > me->morey) { --xc; ++xr; }
1766     } else { xc=0; xr=dy; }
1767 
1768     me->cur->drawPiece(me->dr_p,GCPbuffer,gcpgc,dx,dy);
1769   }
1770 
1771   // STEP 3 1/2: draw coordinates if not in vector mode
1772   if ((!global.UseVectorPieces)&&(global.ShowCoordinates))
1773     me->drawCoordinates(GCPbuffer,gcpgc,sq);
1774 
1775   // STEP 4: commit double buffer to window
1776 
1777   gdk_draw_pixmap(widget->window,gcpgc,GCPbuffer,0,0,0,0,ww,wh);
1778 
1779   me->dr_dirty[0]=dx;
1780   me->dr_dirty[1]=xr;
1781   me->dr_step++;
1782 
1783   return TRUE;
1784 }
1785 
board_button_release_event(GtkWidget * widget,GdkEventButton * be,gpointer data)1786 gboolean board_button_release_event(GtkWidget *widget,
1787 				    GdkEventButton *be,
1788 				    gpointer data) {
1789   Board *me;
1790   int x,y;
1791   bool must_repaint=false;
1792   bool drop_from_drag=false;
1793 
1794   if (be==0) return 0;
1795   me=(Board *)data;
1796 
1797   if ((be->button==1)&&(me->dr_active)) {
1798     if (me->dr_step)
1799       must_repaint=true;
1800     me->dr_active=false;
1801 
1802     if (me->dr_fromstock)
1803       drop_from_drag=true;
1804   }
1805 
1806   if ( (be->x < me->borx) || (be->y < me->morey + me->bory ) )
1807     goto b_rele_nothing;
1808 
1809   x=((int)(be->x)-me->borx)/me->sqside;
1810   y=((int)(be->y)-me->bory-me->morey)/me->sqside;
1811 
1812   if ((x>7)||(y>7)) goto b_rele_nothing;
1813   if (me->effectiveFlip()) x=7-x; else y=7-y;
1814   //
1815   if (be->button==1) {
1816 
1817     // piece dragged from stock
1818     if (drop_from_drag) {
1819       me->dropsq[0]=x;
1820       me->dropsq[1]=y;
1821       if (me->mygame) {
1822 	me->drop(me->dr_p);
1823 	return 1;
1824       }
1825     }
1826 
1827     if ( (me->sp==1) && ( (x!=me->ax[0]) || (y!=me->ay[0]) ) ) {
1828       me->ax[me->sp]=x; me->ay[me->sp]=y;
1829       me->sp=2;
1830       if (me->mygame)
1831 	me->sendMove();
1832       else {
1833 	me->sp=0;
1834 	me->hilite_ch=1;
1835 	me->repaint();
1836       }
1837       return 1;
1838     }
1839   }
1840 
1841  b_rele_nothing:
1842   if (must_repaint) {
1843     me->clock_ch=1;
1844     me->hilite_ch=1;
1845     me->repaint();
1846   }
1847   return 1;
1848 }
1849 
board_button_press_event(GtkWidget * widget,GdkEventButton * be,gpointer data)1850 gboolean board_button_press_event(GtkWidget *widget,GdkEventButton *be,
1851 			    gpointer data) {
1852   Board *me;
1853   int x,y;
1854   me=(Board *)data;
1855   if (be==NULL) return 0;
1856   if ((!me->canselect)&&(be->button!=3)) return 1;
1857 
1858   if ( (be->x < me->borx) || (be->y < me->bory + me->morey ) ) return 1;
1859 
1860   x=((int)(be->x)- me->borx)/me->sqside;
1861   y=((int)(be->y)- me->bory - me->morey )/me->sqside;
1862 
1863   if ((x<8)&&(y<8)) {
1864     me->dr_ox= ((int)(be->x) - me->borx ) % me->sqside;
1865     me->dr_oy= ((int)(be->y) - me->bory - me->morey ) % me->sqside;
1866     me->dr_c=x;
1867     me->dr_r=y;
1868   }
1869 
1870   if ((x>7)||(y>7)) {
1871     DropSource *ds;
1872     if (be->button == 1) {
1873       ds=me->hitTarget((int)(be->x),(int)(be->y));
1874       if (ds!=NULL) {
1875 	if (ds->dragged) {
1876 	  me->dr_active=true;
1877 	  me->dr_fto=true;
1878 	  me->dr_fromstock=true;
1879 	  me->dr_step=0;
1880 	  me->dr_p=ds->P;
1881 	  me->dr_ox = me->sqside / 2;
1882 	  me->dr_oy = me->sqside / 2;
1883 	  me->dr_c=0; // non-sense
1884 	  me->dr_r=0;
1885 	} else {
1886 	  EditBoard *eme;
1887 	  eme=(EditBoard *)data;
1888 
1889 	  // empty / start pos buttons
1890 	  if ((me->EditMode)&&(me->mygame)) {
1891 	    switch(ds->P) {
1892 	    case EMPTY: me->mygame->editEmpty(); break;
1893 	    case WHITE|BLACK: me->mygame->editStartPos(); break;
1894 	    case WHITE: eme->popRunEngine(ds->X,ds->Y2); break;
1895 	    case BLACK: eme->flipSide(); break;
1896 	    case WASPAWN: eme->getFEN(); break;
1897 	    }
1898 	  }
1899 	}
1900       }
1901     }
1902     if (be->button == 3) {
1903       me->popupProtocolMenu( (int) be->x, (int) be->y, be->time );
1904     }
1905     return 1;
1906   }
1907 
1908   if (me->effectiveFlip()) x=7-x; else y=7-y;
1909 
1910   if (be->button == 1) {
1911 
1912     me->dr_active=true;
1913     me->dr_fto=true;
1914     me->dr_fromstock=false;
1915     me->dr_step=0;
1916     me->dr_p=me->position.getPiece(x,y);
1917     if (me->dr_p == EMPTY)
1918       me->dr_active=false;
1919 
1920     if (me->sp > 1) me->sp=0;
1921     if ( (me->sp == 1) && (x==me->ax[0]) && (y==me->ay[0]) )
1922       me->sp=0;
1923     else {
1924 
1925       if ((me->sp == 1) &&
1926 	  ( (me->position.getPiece(me->ax[0],me->ay[0])&COLOR_MASK) ==
1927 	    (me->position.getPiece(x,y)&COLOR_MASK)) &&
1928 	  (me->position.getPiece(x,y)!=EMPTY) && (me->allowMove) )
1929 	me->sp=0;
1930 
1931       me->ax[me->sp]=x; me->ay[me->sp]=y;
1932       ++(me->sp);
1933     }
1934 
1935     if ((me->sp==2)&&(me->mygame)) {
1936       me->sendMove();
1937     } else {
1938       me->premoveq=0;
1939       me->hilite_ch=1;
1940       me->repaint();
1941     }
1942   }
1943   if ((be->button == 3)&&(me->canselect)) {
1944     me->popupDropMenu(x,y,0,0,be->time);
1945   }
1946 
1947   return 1;
1948 }
1949 
vec_board_animate(gpointer data)1950 gboolean vec_board_animate(gpointer data) {
1951   list<AnimatedPiece *>::iterator li;
1952   int not_over=0, nonover=0;
1953   Board *me;
1954   GdkGC *gc;
1955   GdkEventExpose ee;
1956   int w,h;
1957   int sh0=0,sh1=0;
1958 
1959   me=(Board *)data;
1960 
1961   for(li=me->animes.begin();li!=me->animes.end();li++) {
1962     if (! (*li)->over() )
1963       nonover++; // to paint overs when nonovers are in the queue
1964     if ( (*li)->getSoundHold() )
1965       sh0++;
1966   }
1967 
1968   gc=gdk_gc_new(me->yidget->window);
1969   gdk_window_get_size(me->yidget->window,&w,&h);
1970   ee.area.width=w;
1971   ee.area.height=h;
1972   ee.area.x=0;
1973   ee.area.y=0;
1974   me->composeVecBoard(gc);
1975   for(li=me->animes.begin();li!=me->animes.end();li++)
1976     if (nonover) {
1977       (*li)->step();
1978       global.vpieces.drawPiece(me->buffer,gc,me->sqside,
1979 			       (*li)->getX(),(*li)->getY(),(*li)->getPiece());
1980     }
1981   me->pasteVecBoard();
1982   gdk_gc_destroy(gc);
1983 
1984   // count remaining
1985   for(li=me->animes.begin();li!=me->animes.end();li++) {
1986     if (! (*li)->over())
1987       not_over++;
1988     if ( (*li)->getSoundHold() )
1989       sh1++;
1990   }
1991 
1992   // if none remain, finish them all
1993   if (!not_over) {
1994     for(li=me->animes.begin();li!=me->animes.end();li++)
1995       (*li)->lemming();
1996     me->animes.clear();
1997   }
1998 
1999   // if there was at least one animation waiting to release the sound
2000   // and now there is none, flush the sound stack
2001   if ((sh0)&&(!sh1))
2002     global.flushSound();
2003 
2004   if (not_over)
2005     return TRUE;
2006   else {
2007     me->gotAnimationLoop=0;
2008     me->AnimationTimeout=-1;
2009     if (me->update_queue) {
2010       me->update(me->UpdateStack.top());
2011       me->UpdateStack.pop();
2012     }
2013     me->queueRepaint();
2014     return FALSE;
2015   }
2016 }
2017 
board_animate(gpointer data)2018 gboolean board_animate(gpointer data) {
2019   list<AnimatedPiece *>::iterator li;
2020   Board *me;
2021   Rect *r;
2022   int dy,nonover=0;
2023   int sh0=0,sh1=0;
2024 
2025   global.debug("Board","board_animate");
2026   if (global.UseVectorPieces)
2027     return(vec_board_animate(data));
2028 
2029   int not_over=0;
2030 
2031   me=(Board *)data;
2032   if (me->cur == NULL)
2033     return FALSE;
2034 
2035   dy=me->cur->side - me->cur->height;
2036 
2037   for(li=me->animes.begin();li!=me->animes.end();li++) {
2038     if (! (*li)->over() )
2039       nonover++; // to paint overs when nonovers are in the queue
2040     if ( (*li)->getSoundHold() )
2041       sh0++;
2042   }
2043 
2044   // clear previous frames
2045 
2046   while(! me->adirty.empty() ) {
2047     r=me->adirty.front();
2048 
2049     gdk_draw_pixmap(me->yidget->window,
2050 		    me->yidget->style->black_gc,
2051 		    me->buffer,
2052 		    r->x, r->y,
2053 		    r->x, r->y,
2054 		    r->w, r->h);
2055 
2056     delete r;
2057     me->adirty.pop_front();
2058   }
2059 
2060   // draw current frames
2061 
2062   for(li=me->animes.begin();li!=me->animes.end();li++)
2063     if (nonover) {
2064 
2065       (*li)->step();
2066 
2067       r=new Rect((*li)->getX(), (*li)->getY(),
2068 		 me->cur->side, me->cur->height);
2069 
2070       me->cur->drawPiece((*li)->getPiece(),
2071 			 me->yidget->window,
2072 			 me->yidget->style->black_gc,
2073 			 r->x, r->y);
2074       r->translate(0,dy);
2075       me->adirty.push_back(r);
2076     }
2077 
2078   // count remaining
2079   for(li=me->animes.begin();li!=me->animes.end();li++) {
2080     if (! (*li)->over())
2081       not_over++;
2082     if ( (*li)->getSoundHold() )
2083       sh1++;
2084   }
2085 
2086   // if none remain, finish them all
2087   if (!not_over) {
2088     for(li=me->animes.begin();li!=me->animes.end();li++)
2089       (*li)->lemming();
2090     me->animes.clear();
2091   }
2092 
2093   // if there was at least one animation waiting to release the sound
2094   // and now there is none, flush the sound stack
2095   if ((sh0)&&(!sh1))
2096     global.flushSound();
2097 
2098   if (not_over)
2099     return TRUE;
2100   else {
2101     me->gotAnimationLoop=0;
2102     me->AnimationTimeout=-1;
2103     if (me->update_queue) {
2104       me->update(me->UpdateStack.top());
2105       me->UpdateStack.pop();
2106     }
2107     me->queueRepaint();
2108     return FALSE;
2109   }
2110 }
2111 
2112 // ------------- targets
2113 
DropSource(piece p,int x1,int y1,int w,int h,bool d)2114 DropSource::DropSource(piece p, int x1,int y1,int w,int h,bool d) {
2115   X=x1; Y=y1;
2116   X2=X+w; Y2=Y+h;
2117   P=p;
2118   dragged = d;
2119 }
2120 
hit(int x,int y)2121 bool DropSource::hit(int x,int y) {
2122   return((x>=X)&&(x<=X2)&&(y>Y)&&(y<Y2));
2123 }
2124 
cleanTargets()2125 void TargetManager::cleanTargets() {
2126   int i,j;
2127   j=targets.size();
2128   for(i=0;i<j;i++)
2129     delete(targets[i]);
2130   targets.clear();
2131 }
2132 
addTarget(DropSource * ds)2133 void TargetManager::addTarget(DropSource *ds) {
2134   targets.push_back(ds);
2135 }
2136 
hitTarget(int x,int y)2137 DropSource * TargetManager::hitTarget(int x,int y) {
2138   int i,j;
2139   j=targets.size();
2140   for(i=0;i<j;i++)
2141     if (targets[i]->hit(x,y))
2142       return(targets[i]);
2143   return NULL;
2144 }
2145 
EditBoard(ChessGame * cg)2146 EditBoard::EditBoard(ChessGame *cg) : Board() {
2147   EditMode = true;
2148   FreeMove = true;
2149   setGame(cg);
2150 }
2151 
2152 static EditBoard *eb_last=0;
2153 
eb_runengine_nobm(GtkMenuItem * item,gpointer data)2154 void eb_runengine_nobm(GtkMenuItem *item,gpointer data) {
2155   int *i;
2156   EngineProtocol *ep;
2157   Position p;
2158 
2159   if (!eb_last) return;
2160   if (!eb_last->mygame) return;
2161 
2162   i=(int *)data;
2163 
2164   switch(*i) {
2165   case 0: ep=new GnuChess4Protocol(); break;
2166   case 1: ep=new CraftyProtocol();    break;
2167   case 2: ep=new SjengProtocol();     break;
2168   case 3: ep=new XBoardProtocol();    break;
2169   default: return;
2170   }
2171 
2172   p=eb_last->mygame->getCurrentPosition();
2173   ep->setInitialPosition(&p);
2174   global.chandler->openEngine(ep,0);
2175 }
2176 
eb_runengine_bm(GtkMenuItem * item,gpointer data)2177 void eb_runengine_bm(GtkMenuItem *item,gpointer data) {
2178   EngineBookmark *ebm;
2179   EngineProtocol *ep;
2180   Position p;
2181 
2182   if (!eb_last) return;
2183   if (!eb_last->mygame) return;
2184 
2185   ebm=(EngineBookmark *)data;
2186 
2187   if (!ebm) return;
2188 
2189   switch(ebm->proto) {
2190   case 0: ep=new XBoardProtocol();    break;
2191   case 1: ep=new CraftyProtocol();    break;
2192   case 2: ep=new SjengProtocol();     break;
2193   case 3: ep=new GnuChess4Protocol(); break;
2194   default: return;
2195   }
2196 
2197   p=eb_last->mygame->getCurrentPosition();
2198   ep->setInitialPosition(&p);
2199   global.chandler->openEngine(ep,ebm);
2200 }
2201 
popRunEngine(int x,int y)2202 void EditBoard::popRunEngine(int x,int y) {
2203   GtkWidget *popmenu, *note=0, *e;
2204   vector<GtkWidget *> mi;
2205   list<EngineBookmark *>::iterator ei;
2206   unsigned int i;
2207   static int sindex[4]={0,1,2,3};
2208 
2209   eb_last=this;
2210 
2211   popmenu=gtk_menu_new();
2212 
2213   mi.push_back(gtk_menu_item_new_with_label("GNU Chess 4..."));
2214   mi.push_back(gtk_menu_item_new_with_label("Crafty..."));
2215   mi.push_back(gtk_menu_item_new_with_label("Sjeng..."));
2216   mi.push_back(gtk_menu_item_new_with_label(_("Generic XBoard Engine...")));
2217   mi.push_back(gtk_separator_menu_item_new());
2218 
2219   for(i=0;i<4;i++)
2220     gtk_signal_connect(GTK_OBJECT(mi[i]),"activate",
2221 		       GTK_SIGNAL_FUNC(eb_runengine_nobm),
2222 		       (gpointer) (&sindex[i]) );
2223 
2224   if (!global.EnginePresets.empty()) {
2225     note=gtk_menu_item_new_with_label(_("If you pick a bookmark, the engine\n"\
2226 				      "will play the next move, ignoring\n"\
2227 				      "the side setting in the bookmark."));
2228     gtk_widget_set_sensitive(note,FALSE);
2229   }
2230 
2231   for(ei=global.EnginePresets.begin();ei!=global.EnginePresets.end();ei++) {
2232     e=gtk_menu_item_new_with_label((*ei)->caption.c_str());
2233     mi.push_back(e);
2234 
2235     gtk_signal_connect(GTK_OBJECT(e),"activate",
2236 		       GTK_SIGNAL_FUNC(eb_runengine_bm),
2237 		       (gpointer)(*ei));
2238   }
2239 
2240   if (note)
2241     mi.push_back(gtk_separator_menu_item_new());
2242 
2243   for(i=0;i<mi.size();i++) {
2244     gshow(mi[i]);
2245     gtk_menu_shell_append(GTK_MENU_SHELL(popmenu),mi[i]);
2246   }
2247   if (note) {
2248     gshow(note);
2249     gtk_menu_shell_append(GTK_MENU_SHELL(popmenu), note);
2250   }
2251 
2252   gtk_menu_popup(GTK_MENU(popmenu),NULL,NULL,NULL,NULL,1,gtk_get_current_event_time());
2253 }
2254 
renderClock()2255 void EditBoard::renderClock() {
2256   GdkGC *gc=clkgc;
2257   int lch=clock_ch;
2258   int i,j,k;
2259 
2260   Board::renderClock();
2261   if (!lch) return;
2262   if (!mygame) return;
2263 
2264   // scratch-board pieces and buttons
2265 
2266   C.consumeTop(10);
2267 
2268   if (C.H > 80+48) {
2269     i = 32;
2270     for(k=0;k<6;k++) {
2271       C.drawButton(10+i*k, C.Y, i, i, NULL, 0, 0xffffff, 0x505070);
2272       C.drawButton(10+i*k, C.Y+i+8, i, i, NULL, 0, 0xffffff, 0x505070);
2273 
2274       global.vpieces.drawPiece(clkbar,gc, i, 10+i*k, C.Y,WHITE|(k+1));
2275       global.vpieces.drawPiece(clkbar,gc, i, 10+i*k, C.Y+i+8,BLACK|(k+1));
2276       addTarget(new DropSource(WHITE|(k+1),Width()+10+i*k,C.Y,i,i));
2277       addTarget(new DropSource(BLACK|(k+1),Width()+10+i*k,C.Y+i+8,i,i));
2278     }
2279     C.consumeTop(i*2+16);
2280   } else if (C.H > 24) {
2281     i = 16;
2282     for(k=0;k<6;k++) {
2283       C.drawButton(10+i*k, C.Y, i, i, NULL, 0, 0xffffff, 0x505070);
2284       C.drawButton(10+i*(6+k), C.Y, i, i, NULL, 0, 0xffffff, 0x505070);
2285 
2286       global.vpieces.drawPiece(clkbar,gc, i, 10+i*k, C.Y,WHITE|(k+1));
2287       global.vpieces.drawPiece(clkbar,gc, i, 10+i*(6+k), C.Y,BLACK|(k+1));
2288       addTarget(new DropSource(WHITE|(k+1),Width()+10+i*k,C.Y,i,i));
2289       addTarget(new DropSource(BLACK|(k+1),Width()+10+i*(6+k),C.Y,i,i));
2290     }
2291     C.consumeTop(24);
2292   }
2293 
2294   if (C.H > 28) {
2295     j=C.drawButton(10,C.Y,-1,20,_("Empty"),2,0xffffff, 0x505070);
2296     addTarget(new DropSource(EMPTY,Width()+10,C.Y,j,20,false));
2297 
2298     k=10+j+10;
2299     j=C.drawButton(k,C.Y,-1,20,_("Initial Position"),2,0xffffff,0x505070);
2300 
2301     addTarget(new DropSource(WHITE|BLACK,Width()+k,C.Y,j,20,false));
2302 
2303     k+=10+j;
2304     j=C.drawButton(k,C.Y,-1,20,_("From FEN"),2,0xffffff,0x505070);
2305 
2306     addTarget(new DropSource(WASPAWN,Width()+k,C.Y,j,20,false));
2307 
2308     C.consumeTop(28);
2309   }
2310   if (C.H > 20) {
2311     j=C.drawButton(10,C.Y,-1,20,_("Run Engine..."),
2312 		   2,0xffffff,0xa07050);
2313     addTarget(new DropSource(WHITE,Width()+10,C.Y,j,20,false));
2314 
2315     k=j+20;
2316     j+=20+C.stringWidth(2,_("Side to move: "));
2317 
2318     C.drawButton( k,C.Y,j-k+10+24,20,NULL,2,0xffffff,0x505070);
2319     addTarget(new DropSource(BLACK,Width()+k,C.Y,j-k+10+24,20,false));
2320 
2321     C.drawString( k+10, C.Y+2, 2, _("Side to move: "));
2322 
2323     C.setColor( mygame->getSideHint() ? 0xffffff : 0);
2324     C.drawEllipse( 10+j+3, C.Y+3, 14, 14, true);
2325     C.setColor(0xffffff);
2326     C.drawEllipse( 10+j+3, C.Y+3, 14, 14, false);
2327 
2328     C.consumeTop(20);
2329   }
2330 
2331 }
2332 
flipSide()2333 void EditBoard::flipSide() {
2334   if (mygame) {
2335     mygame->setSideHint( ! mygame->getSideHint() );
2336     clock_ch=1;
2337     queueRepaint();
2338   }
2339 }
2340 
getFEN()2341 void EditBoard::getFEN() {
2342   (new GetFENDialog(this))->show();
2343 }
2344 
applyFEN(string & s)2345 void EditBoard::applyFEN(string &s) {
2346   Position p;
2347 
2348   p.setFEN(s.c_str());
2349 
2350   mygame->editEmpty();
2351   mygame->updatePosition2(p,1,p.sidehint ? 0 : 1,
2352 			  0,0,"<FEN>",false);
2353 }
2354 
GetFENDialog(EditBoard * _owner)2355 GetFENDialog::GetFENDialog(EditBoard *_owner)
2356   : ModalDialog(N_("Enter FEN Position"))
2357 {
2358   GtkWidget *v,*hs,*hb,*ok,*cancel;
2359 
2360   owner=_owner;
2361 
2362   gtk_window_set_default_size(GTK_WINDOW(widget), 450, 100);
2363 
2364   v=gtk_vbox_new(FALSE,4);
2365 
2366   gtk_container_add(GTK_CONTAINER(widget),v);
2367 
2368   e=gtk_entry_new();
2369   gtk_box_pack_start(GTK_BOX(v),e,FALSE,TRUE,4);
2370 
2371   hs=gtk_hseparator_new();
2372   gtk_box_pack_start(GTK_BOX(v),hs,FALSE,TRUE,4);
2373 
2374   hb=gtk_hbutton_box_new();
2375   gtk_button_box_set_layout(GTK_BUTTON_BOX(hb), GTK_BUTTONBOX_END);
2376   gtk_button_box_set_spacing(GTK_BUTTON_BOX(hb), 5);
2377   gtk_box_pack_start(GTK_BOX(v), hb, FALSE, TRUE, 2);
2378 
2379   ok=gtk_button_new_with_label(_("Ok"));
2380   GTK_WIDGET_SET_FLAGS(ok,GTK_CAN_DEFAULT);
2381   cancel=gtk_button_new_with_label(_("Cancel"));
2382   GTK_WIDGET_SET_FLAGS(cancel,GTK_CAN_DEFAULT);
2383   gtk_box_pack_start(GTK_BOX(hb),ok,TRUE,TRUE,0);
2384   gtk_box_pack_start(GTK_BOX(hb),cancel,TRUE,TRUE,0);
2385   gtk_widget_grab_default(ok);
2386 
2387   Gtk::show(ok,cancel,hb,hs,e,v,NULL);
2388   setDismiss(GTK_OBJECT(cancel),"clicked");
2389 
2390   gtk_signal_connect(GTK_OBJECT(ok),"clicked",
2391 		     GTK_SIGNAL_FUNC(getfen_ok),(gpointer)this);
2392 
2393   focused_widget=e;
2394 }
2395 
getfen_ok(GtkWidget * w,gpointer data)2396 void getfen_ok(GtkWidget *w, gpointer data) {
2397   string s;
2398   GetFENDialog *me;
2399   me=(GetFENDialog *)data;
2400   s=gtk_entry_get_text(GTK_ENTRY(me->e));
2401   me->owner->applyFEN(s);
2402   me->release();
2403 }
2404 
2405 // former animate.cc
2406 
over()2407 int AnimatedPiece::over() { return(ower<=0); }
2408 
getX()2409 int AnimatedPiece::getX() { return X; }
2410 
getY()2411 int AnimatedPiece::getY() { return Y; }
2412 
getPiece()2413 piece AnimatedPiece::getPiece() { return Piece; }
2414 
getSoundHold()2415 bool AnimatedPiece::getSoundHold() { return((SoundHold)&&(ower>0)); }
2416 
2417 // ================================================
2418 // flat implementation
2419 // ================================================
2420 
FlatAnimatedPiece(piece p,int x0,int y0,int xf,int yf,int s,bool sndhold)2421 FlatAnimatedPiece::FlatAnimatedPiece(piece p,int x0,int y0,
2422 				     int xf,int yf,int s,bool sndhold) :
2423   AnimatedPiece()
2424 {
2425   Piece=p;
2426   X0=x0; Y0=y0; XF=xf; YF=yf;
2427   steps=s; X=X0; Y=Y0;
2428   DX=(XF-X0)/steps;
2429   DY=(YF-Y0)/steps;
2430   ower=steps;
2431   SoundHold=sndhold;
2432 }
2433 
create(GdkWindow * parent,PieceSet * set,int sqside)2434 void FlatAnimatedPiece::create(GdkWindow *parent,PieceSet *set,int sqside) {
2435   square=sqside;
2436 }
2437 
lemming()2438 void FlatAnimatedPiece::lemming() {
2439   delete this;
2440 }
2441 
destroy()2442 void FlatAnimatedPiece::destroy() {
2443 
2444 }
2445 
step()2446 void FlatAnimatedPiece::step() {
2447   if (ower > 1) {
2448     X+=DX;
2449     Y+=DY;
2450   }
2451   if (ower==1) { X=XF; Y=YF; }
2452   --ower;
2453 }
2454