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