1 // NetHack 3.6	qt_win.cpp	$NHDT-Date: 1524684508 2018/04/25 19:28:28 $  $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.77 $
2 // Copyright (c) Warwick Allison, 1999.
3 // NetHack may be freely redistributed.  See license for details.
4 
5 // Qt Binding for NetHack 3.4
6 //
7 // Copyright (C) 1996-2001 by Warwick W. Allison (warwick@troll.no)
8 //
9 // Contributors:
10 //    Michael Hohmuth <hohmuth@inf.tu-dresden.de>
11 //       - Userid control
12 //    Svante Gerhard <svante@algonet.se>
13 //       - .nethackrc tile and font size settings
14 //    Dirk Schoenberger <schoenberger@signsoft.com>
15 //       - KDE support
16 //       - SlashEm support
17 //    and many others for bug reports.
18 //
19 // Unfortunately, this doesn't use Qt as well as I would like,
20 // primarily because NetHack is fundamentally a getkey-type program
21 // rather than being event driven (hence the ugly key and click buffer)
22 // and also because this is my first major application of Qt.
23 //
24 // The problem of NetHack's getkey requirement is solved by intercepting
25 // key events by overiding QApplicion::notify(...), and putting them in
26 // a buffer.  Mouse clicks on the map window are treated with a similar
27 // buffer.  When the NetHack engine calls for a key, one is taken from
28 // the buffer, or if that is empty, QApplication::enter_loop() is called.
29 // Whenever keys or clicks go into the buffer, QApplication::exit_loop()
30 // is called.
31 //
32 // Another problem is that some NetHack players are decade-long players who
33 // demand complete keyboard control (while Qt and X11 conspire to make this
34 // difficult by having widget-based focus rather than application based -
35 // a good thing in general).  This problem is solved by again using the key
36 // event buffer.
37 //
38 // Out of all this hackery comes a silver lining however, as macros for
39 // the super-expert and menus for the ultra-newbie are also made possible
40 // by the key event buffer.
41 //
42 
43 extern "C" {
44 
45 // This includes all the definitions we need from the NetHack main
46 // engine.  We pretend MSC is a STDC compiler, because C++ is close
47 // enough, and we undefine NetHack macros which conflict with Qt
48 // identifiers.
49 
50 #define alloc hide_alloc // avoid treading on STL symbol
51 #define lock hide_lock // avoid treading on STL symbol
52 #ifdef _MSC_VER
53 #define NHSTDC
54 #endif
55 #include "hack.h"
56 #include "func_tab.h"
57 #include "dlb.h"
58 #include "patchlevel.h"
59 #include "tile2x11.h"
60 #undef Invisible
61 #undef Warning
62 #undef red
63 #undef green
64 #undef blue
65 #undef Black
66 #undef curs
67 #undef TRUE
68 #undef FALSE
69 #undef min
70 #undef max
71 #undef alloc
72 #undef lock
73 #undef yn
74 
75 }
76 
77 #ifdef Invisible
78 /* Invisible was added to an enum in Qt 3.2, #defined in youprop.h */
79 #undef Invisible
80 #endif
81 
82 #include "qt_win.h"
83 #include <qregexp.h>
84 #include <qpainter.h>
85 #include <qdir.h>
86 #include <qbitmap.h>
87 #include <qkeycode.h>
88 #include <qmenubar.h>
89 #include <qpopupmenu.h>
90 #include <qlayout.h>
91 #include <qheader.h>
92 #include <qradiobutton.h>
93 #include <qtoolbar.h>
94 #include <qtoolbutton.h>
95 #include <qcombobox.h>
96 #include <qvbox.h>
97 #include <qdragobject.h>
98 #include <qtextbrowser.h>
99 #include <qhbox.h>
100 #include <qsignalmapper.h>
101 //#include <qgrid.h>
102 //#include <qlabelled.h>
103 
104 #include <ctype.h>
105 
106 #include "qt_clust.h"
107 #include "qt_xpms.h"
108 
109 #include <dirent.h>
110 #ifdef Q_WS_MACX
111 #  include <sys/malloc.h>
112 #else
113 #  include <stdlib.h>
114 #endif
115 
116 #ifdef _WS_X11_
117 // For userid control
118 #include <unistd.h>
119 #endif
120 
121 // Some distributors released Qt 2.1.0beta4
122 #if QT_VERSION < 220
123 # define nh_WX11BypassWM 0x01000000
124 #else
125 # define nh_WX11BypassWM WX11BypassWM
126 #endif
127 
128 #ifdef USER_SOUNDS
129 # if QT_VERSION < 220
130 #  undef USER_SOUNDS
131 # else
132 #  include <qsound.h>
133 # endif
134 #endif
135 
136 
137 #ifdef USER_SOUNDS
138 extern "C" void play_sound_for_message(const char* str);
139 #endif
140 
141 #ifdef SAFERHANGUP
142 #include <qtimer.h>
143 #endif
144 
145 // Warwick prefers it this way...
146 #define QT_CHOOSE_RACE_FIRST
147 
148 static const char nh_attribution[] = "<center><big>NetHack</big>"
149 	"<br><small>by the NetHack DevTeam</small></center>";
150 
151 static QString
aboutMsg()152 aboutMsg()
153 {
154     QString msg;
155     msg.sprintf(
156     "Qt NetHack is a version of NetHack built\n"
157 #ifdef KDE
158     "using KDE and the Qt GUI toolkit.\n"
159 #else
160     "using the Qt GUI toolkit.\n"
161 #endif
162     "This is version %d.%d.%d\n\n"
163     "Homepage:\n     http://trolls.troll.no/warwick/nethack/\n\n"
164 #ifdef KDE
165 	  "KDE:\n     http://www.kde.org\n"
166 #endif
167 	  "Qt:\n     http://www.troll.no",
168 	VERSION_MAJOR,
169 	VERSION_MINOR,
170 	PATCHLEVEL);
171     return msg;
172 }
173 
174 static void
centerOnMain(QWidget * w)175 centerOnMain( QWidget* w )
176 {
177     QWidget* m = qApp->mainWidget();
178     if (!m) m = qApp->desktop();
179     QPoint p = m->mapToGlobal(QPoint(0,0));
180     w->move( p.x() + m->width()/2  - w->width()/2,
181               p.y() + m->height()/2 - w->height()/2 );
182 }
183 
NetHackQtLineEdit()184 NetHackQtLineEdit::NetHackQtLineEdit() :
185     QLineEdit(0)
186 {
187 }
188 
NetHackQtLineEdit(QWidget * parent,const char * name)189 NetHackQtLineEdit::NetHackQtLineEdit(QWidget* parent, const char* name) :
190     QLineEdit(parent,name)
191 {
192 }
193 
fakeEvent(int key,int ascii,int state)194 void NetHackQtLineEdit::fakeEvent(int key, int ascii, int state)
195 {
196     QKeyEvent fake(QEvent::KeyPress,key,ascii,state);
197     keyPressEvent(&fake);
198 }
199 
200 extern "C" {
201 /* Used by tile/font-size patch below and in ../../src/files.c */
202 char *qt_tilewidth=NULL;
203 char *qt_tileheight=NULL;
204 char *qt_fontsize=NULL;
205 #if defined(QWS)
206 int qt_compact_mode = 1;
207 #else
208 int qt_compact_mode = 0;
209 #endif
210 extern const char *enc_stat[]; /* from botl.c */
211 extern const char *hu_stat[]; /* from eat.c */
212 extern int total_tiles_used; // from tile.c
213 extern short glyph2tile[]; // from tile.c
214 }
215 
216 static int tilefile_tile_W=16;
217 static int tilefile_tile_H=16;
218 
219 #define TILEWMIN 1
220 #define TILEHMIN 1
221 
222 
223 /* XPM */
224 static const char * nh_icon[] = {
225 "40 40 6 1",
226 " 	s None c none",
227 ".	c #ffffff",
228 "X	c #dadab6",
229 "o	c #6c91b6",
230 "O	c #476c6c",
231 "+	c #000000",
232 "                                        ",
233 "                                        ",
234 "                                        ",
235 "        .      .X..XX.XX      X         ",
236 "        ..   .....X.XXXXXX   XX         ",
237 "        ... ....X..XX.XXXXX XXX         ",
238 "   ..   ..........X.XXXXXXXXXXX   XX    ",
239 "   .... ........X..XX.XXXXXXXXX XXXX    ",
240 "   .... ..........X.XXXXXXXXXXX XXXX    ",
241 "   ooOOO..ooooooOooOOoOOOOOOOXX+++OO++  ",
242 "   ooOOO..ooooooooOoOOOOOOOOOXX+++OO++  ",
243 "   ....O..ooooooOooOOoOOOOOOOXX+XXXX++  ",
244 "   ....O..ooooooooOoOOOOOOOOOXX+XXXX++  ",
245 "   ..OOO..ooooooOooOOoOOOOOOOXX+++XX++  ",
246 "    ++++..ooooooooOoOOOOOOOOOXX+++ +++  ",
247 "     +++..ooooooOooOOoOOOOOOOXX+++  +   ",
248 "      ++..ooooooooOoOOOOOOOOOXX+++      ",
249 "        ..ooooooOooOOoOOOOOOOXX+++      ",
250 "        ..ooooooooOoOOOOOOOOOXX+++      ",
251 "        ..ooooooOooOOoOOOOOOOXX+++      ",
252 "        ..ooooooooOoOOOOOOOOOXX+++      ",
253 "         ..oooooOooOOoOOOOOOXX+++       ",
254 "         ..oooooooOoOOOOOOOOXX+++       ",
255 "          ..ooooOooOOoOOOOOXX+++        ",
256 "          ..ooooooOoOOOOOOOXX++++       ",
257 "        ..o..oooOooOOoOOOOXX+XX+++      ",
258 "       ...o..oooooOoOOOOOXX++XXX++      ",
259 "      ....OO..ooOooOOoOOXX+++XXXX++     ",
260 "     ...oo..+..oooOoOOOXX++XXooXXX++    ",
261 "    ...ooo..++..OooOOoXX+++XXooOXXX+    ",
262 "   ..oooOOXX+++....XXXX++++XXOOoOOXX+   ",
263 "   ..oooOOXX+++ ...XXX+++++XXOOooOXX++  ",
264 "   ..oooOXXX+++  ..XX+++  +XXOOooOXX++  ",
265 "   .....XXX++++             XXXXXXX++   ",
266 "    ....XX++++              XXXXXXX+    ",
267 "     ...XX+++                XXXXX++    ",
268 "                                        ",
269 "                                        ",
270 "                                        ",
271 "                                        "};
272 /* XPM */
273 static const char * nh_icon_small[] = {
274 /* width height ncolors chars_per_pixel */
275 "16 16 16 1",
276 /* colors */
277 "  c #587070",
278 ". c #D1D5C9",
279 "X c #8B8C84",
280 "o c #2A2A28",
281 "O c #9AABA9",
282 "+ c #6A8FB2",
283 "@ c #C4CAC4",
284 "# c #B6BEB6",
285 "$ c None",
286 "% c #54564E",
287 "& c #476C6C",
288 "* c #ADB2AB",
289 "= c #ABABA2",
290 "- c #5E8295",
291 "; c #8B988F",
292 ": c #E8EAE7",
293 /* pixels */
294 "$$$$$$$$$$$$$$$$",
295 "$$$.$#::.#==*$$$",
296 "$.*:::::....#*=$",
297 "$@#:..@#*==#;XX;",
298 "$@O:+++- &&; X%X",
299 "$#%.+++- &&;% oX",
300 "$$o.++-- &&;%%X$",
301 "$$$:++-- &&;%%$$",
302 "$$$.O++- &&=o $$",
303 "$$$=:++- & XoX$$",
304 "$$*:@O--  ;%Xo$$",
305 "$*:O#$+--;oOOX $",
306 "$:+ =o::=oo=-;%X",
307 "$::.%o$*;X;##@%$",
308 "$$@# ;$$$$$=*;X$",
309 "$$$$$$$$$$$$$$$$"
310 };
311 
312 /* XPM */
313 static const char * map_xpm[] = {
314 "12 13 4 1",
315 ".	c None",
316 " 	c #000000000000",
317 "X	c #0000B6DAFFFF",
318 "o	c #69A69248B6DA",
319 "           .",
320 " XXXXX ooo  ",
321 " XoooX o    ",
322 " XoooX o o  ",
323 " XoooX ooo  ",
324 " XXoXX o    ",
325 "  oooooXXX  ",
326 " oo o oooX  ",
327 "    o XooX  ",
328 " oooo XooX  ",
329 " o  o XXXX  ",
330 "            ",
331 ".           "};
332 /* XPM */
333 static const char * msg_xpm[] = {
334 "12 13 4 1",
335 ".	c None",
336 " 	c #FFFFFFFFFFFF",
337 "X	c #69A69248B6DA",
338 "o	c #000000000000",
339 "           .",
340 " XXX XXX X o",
341 "           o",
342 " XXXXX XX  o",
343 "           o",
344 " XX XXXXX  o",
345 "           o",
346 " XXXXXX    o",
347 "           o",
348 " XX XXX XX o",
349 "           o",
350 "           o",
351 ".ooooooooooo"};
352 /* XPM */
353 static const char * stat_xpm[] = {
354 "12 13 5 1",
355 "  c None",
356 ".	c #FFFF00000000",
357 "X	c #000000000000",
358 "o	c #FFFFFFFF0000",
359 "O	c #69A6FFFF0000",
360 "            ",
361 "            ",
362 "...         ",
363 "...X        ",
364 "...X    ... ",
365 "oooX    oooX",
366 "oooXooo oooX",
367 "OOOXOOOXOOOX",
368 "OOOXOOOXOOOX",
369 "OOOXOOOXOOOX",
370 "OOOXOOOXOOOX",
371 "OOOXOOOXOOOX",
372 " XXXXXXXXXXX"};
373 /* XPM */
374 static const char * info_xpm[] = {
375 "12 13 4 1",
376 "  c None",
377 ".	c #00000000FFFF",
378 "X	c #FFFFFFFFFFFF",
379 "o	c #000000000000",
380 "    ...     ",
381 "  .......   ",
382 " ...XXX...  ",
383 " .........o ",
384 "...XXXX.... ",
385 "....XXX....o",
386 "....XXX....o",
387 "....XXX....o",
388 " ...XXX...oo",
389 " ..XXXXX..o ",
390 "  .......oo ",
391 "   o...ooo  ",
392 "     ooo    "};
393 
394 
395 /* XPM */
396 static const char * again_xpm[] = {
397 "12 13 2 1",
398 " 	c None",
399 ".	c #000000000000",
400 "    ..      ",
401 "     ..     ",
402 "   .....    ",
403 " .......    ",
404 "...  ..  .. ",
405 "..  ..   .. ",
406 "..        ..",
407 "..        ..",
408 "..        ..",
409 " ..      .. ",
410 " .......... ",
411 "   ......   ",
412 "            "};
413 /* XPM */
414 static const char * kick_xpm[] = {
415 "12 13 3 1",
416 " 	c None",
417 ".	c #000000000000",
418 "X	c #FFFF6DB60000",
419 "            ",
420 "            ",
421 "   .  .  .  ",
422 "  ...  .  . ",
423 "   ...  .   ",
424 "    ...  .  ",
425 "     ...    ",
426 "XXX   ...   ",
427 "XXX.  ...   ",
428 "XXX. ...    ",
429 "XXX. ..     ",
430 " ...        ",
431 "            "};
432 /* XPM */
433 static const char * throw_xpm[] = {
434 "12 13 3 1",
435 " 	c None",
436 ".	c #FFFF6DB60000",
437 "X	c #000000000000",
438 "            ",
439 "            ",
440 "            ",
441 "            ",
442 "....     X  ",
443 "....X     X ",
444 "....X XXXXXX",
445 "....X     X ",
446 " XXXX    X  ",
447 "            ",
448 "            ",
449 "            ",
450 "            "};
451 /* XPM */
452 static const char * fire_xpm[] = {
453 "12 13 5 1",
454 " 	c None",
455 ".	c #B6DA45140000",
456 "X	c #FFFFB6DA9658",
457 "o	c #000000000000",
458 "O	c #FFFF6DB60000",
459 " .          ",
460 " X.         ",
461 " X .        ",
462 " X .o       ",
463 " X  .    o  ",
464 " X  .o    o ",
465 "OOOOOOOOoooo",
466 " X  .o    o ",
467 " X . o   o  ",
468 " X .o       ",
469 " X. o       ",
470 " . o        ",
471 "  o         "};
472 /* XPM */
473 static const char * get_xpm[] = {
474 "12 13 3 1",
475 " 	c None",
476 ".	c #000000000000",
477 "X	c #FFFF6DB60000",
478 "            ",
479 "     .      ",
480 "    ...     ",
481 "   . . .    ",
482 "     .      ",
483 "     .      ",
484 "            ",
485 "   XXXXX    ",
486 "   XXXXX.   ",
487 "   XXXXX.   ",
488 "   XXXXX.   ",
489 "    .....   ",
490 "            "};
491 /* XPM */
492 static const char * drop_xpm[] = {
493 "12 13 3 1",
494 " 	c None",
495 ".	c #FFFF6DB60000",
496 "X	c #000000000000",
497 "            ",
498 "   .....    ",
499 "   .....X   ",
500 "   .....X   ",
501 "   .....X   ",
502 "    XXXXX   ",
503 "            ",
504 "      X     ",
505 "      X     ",
506 "    X X X   ",
507 "     XXX    ",
508 "      X     ",
509 "            "};
510 /* XPM */
511 static const char * eat_xpm[] = {
512 "12 13 4 1",
513 " 	c None",
514 ".	c #000000000000",
515 "X	c #FFFFB6DA9658",
516 "o	c #FFFF6DB60000",
517 "  .X.  ..   ",
518 "  .X.  ..   ",
519 "  .X.  ..   ",
520 "  .X.  ..   ",
521 "  ...  ..   ",
522 "   ..  ..   ",
523 "   ..  ..   ",
524 "   oo  oo   ",
525 "   oo  oo   ",
526 "   oo  oo   ",
527 "   oo  oo   ",
528 "   oo  oo   ",
529 "   oo  oo   "};
530 /* XPM */
531 static const char * rest_xpm[] = {
532 "12 13 2 1",
533 " 	c None",
534 ".	c #000000000000",
535 "  .....     ",
536 "     .      ",
537 "    .       ",
538 "   .    ....",
539 "  .....   . ",
540 "         .  ",
541 "        ....",
542 "            ",
543 "     ....   ",
544 "       .    ",
545 "      .     ",
546 "     ....   ",
547 "            "};
548 /* XPM */
549 static const char * cast_a_xpm[] = {
550 "12 13 3 1",
551 " 	c None",
552 ".	c #FFFF6DB60000",
553 "X	c #000000000000",
554 "    .       ",
555 "    .       ",
556 "   ..       ",
557 "   ..       ",
558 "  ..  .     ",
559 "  ..  .     ",
560 " ......     ",
561 " .. ..  XX  ",
562 "    .. X  X ",
563 "   ..  X  X ",
564 "   ..  XXXX ",
565 "   .   X  X ",
566 "   .   X  X "};
567 /* XPM */
568 static const char * cast_b_xpm[] = {
569 "12 13 3 1",
570 " 	c None",
571 ".	c #FFFF6DB60000",
572 "X	c #000000000000",
573 "    .       ",
574 "    .       ",
575 "   ..       ",
576 "   ..       ",
577 "  ..  .     ",
578 "  ..  .     ",
579 " ......     ",
580 " .. .. XXX  ",
581 "    .. X  X ",
582 "   ..  XXX  ",
583 "   ..  X  X ",
584 "   .   X  X ",
585 "   .   XXX  "};
586 /* XPM */
587 static const char * cast_c_xpm[] = {
588 "12 13 3 1",
589 " 	c None",
590 ".	c #FFFF6DB60000",
591 "X	c #000000000000",
592 "    .       ",
593 "    .       ",
594 "   ..       ",
595 "   ..       ",
596 "  ..  .     ",
597 "  ..  .     ",
598 " ......     ",
599 " .. ..  XX  ",
600 "    .. X  X ",
601 "   ..  X    ",
602 "   ..  X    ",
603 "   .   X  X ",
604 "   .    XX  "};
605 
NetHackQtSettings(int w,int h)606 NetHackQtSettings::NetHackQtSettings(int w, int h) :
607     tilewidth(TILEWMIN,64,1,this),
608     tileheight(TILEHMIN,64,1,this),
609     widthlbl(&tilewidth,"&Width:",this),
610     heightlbl(&tileheight,"&Height:",this),
611     whichsize("&Zoomed",this),
612     fontsize(this),
613     normal("times"),
614 #ifdef WS_WIN
615     normalfixed("courier new"),
616 #else
617     normalfixed("fixed"),
618 #endif
619     large("times"),
620     theglyphs(0)
621 
622 {
623     int default_fontsize;
624 
625     if (w<=300) {
626 	// ~240x320
627 	default_fontsize=4;
628 	tilewidth.setValue(8);
629 	tileheight.setValue(12);
630     } else if (w<=700) {
631 	// ~640x480
632 	default_fontsize=3;
633 	tilewidth.setValue(8);
634 	tileheight.setValue(14);
635     } else if (w<=900) {
636 	// ~800x600
637 	default_fontsize=3;
638 	tilewidth.setValue(10);
639 	tileheight.setValue(17);
640     } else if (w<=1100) {
641 	// ~1024x768
642 	default_fontsize=2;
643 	tilewidth.setValue(12);
644 	tileheight.setValue(22);
645     } else if (w<=1200) {
646 	// ~1152x900
647 	default_fontsize=1;
648 	tilewidth.setValue(14);
649 	tileheight.setValue(26);
650     } else {
651 	// ~1280x1024 and larger
652 	default_fontsize=0;
653 	tilewidth.setValue(16);
654 	tileheight.setValue(30);
655     }
656 
657     // Tile/font sizes read from .nethackrc
658     if (qt_tilewidth != NULL) {
659 	tilewidth.setValue(atoi(qt_tilewidth));
660 	free(qt_tilewidth);
661     }
662     if (qt_tileheight != NULL) {
663 	tileheight.setValue(atoi(qt_tileheight));
664 	free(qt_tileheight);
665     }
666     if (qt_fontsize != NULL) {
667 	switch (tolower(qt_fontsize[0])) {
668 	  case 'h': default_fontsize = 0; break;
669 	  case 'l': default_fontsize = 1; break;
670 	  case 'm': default_fontsize = 2; break;
671 	  case 's': default_fontsize = 3; break;
672 	  case 't': default_fontsize = 4; break;
673 	}
674 	free(qt_fontsize);
675     }
676 
677     theglyphs=new NetHackQtGlyphs();
678     resizeTiles();
679 
680     connect(&tilewidth,SIGNAL(valueChanged(int)),this,SLOT(resizeTiles()));
681     connect(&tileheight,SIGNAL(valueChanged(int)),this,SLOT(resizeTiles()));
682     connect(&whichsize,SIGNAL(toggled(bool)),this,SLOT(setGlyphSize(bool)));
683 
684     fontsize.insertItem("Huge");
685     fontsize.insertItem("Large");
686     fontsize.insertItem("Medium");
687     fontsize.insertItem("Small");
688     fontsize.insertItem("Tiny");
689     fontsize.setCurrentItem(default_fontsize);
690     connect(&fontsize,SIGNAL(activated(int)),this,SIGNAL(fontChanged()));
691 
692     QGridLayout* grid = new QGridLayout(this, 5, 2, 8);
693     grid->addMultiCellWidget(&whichsize, 0, 0, 0, 1);
694     grid->addWidget(&tilewidth, 1, 1);  grid->addWidget(&widthlbl, 1, 0);
695     grid->addWidget(&tileheight, 2, 1); grid->addWidget(&heightlbl, 2, 0);
696     QLabel* flabel=new QLabel(&fontsize, "&Font:",this);
697     grid->addWidget(flabel, 3, 0); grid->addWidget(&fontsize, 3, 1);
698     QPushButton* dismiss=new QPushButton("Dismiss",this);
699     dismiss->setDefault(TRUE);
700     grid->addMultiCellWidget(dismiss, 4, 4, 0, 1);
701     grid->setRowStretch(4,0);
702     grid->setColStretch(1,1);
703     grid->setColStretch(2,2);
704     grid->activate();
705 
706     connect(dismiss,SIGNAL(clicked()),this,SLOT(accept()));
707     resize(150,140);
708 }
709 
glyphs()710 NetHackQtGlyphs& NetHackQtSettings::glyphs()
711 {
712     return *theglyphs;
713 }
714 
resizeTiles()715 void NetHackQtSettings::resizeTiles()
716 {
717     int w = tilewidth.value();
718     int h = tileheight.value();
719 
720     theglyphs->setSize(w,h);
721     emit tilesChanged();
722 }
723 
toggleGlyphSize()724 void NetHackQtSettings::toggleGlyphSize()
725 {
726     whichsize.toggle();
727 }
728 
setGlyphSize(bool which)729 void NetHackQtSettings::setGlyphSize(bool which)
730 {
731     QSize n = QSize(tilewidth.value(),tileheight.value());
732     if ( othersize.isValid() ) {
733 	tilewidth.blockSignals(TRUE);
734 	tileheight.blockSignals(TRUE);
735 	tilewidth.setValue(othersize.width());
736 	tileheight.setValue(othersize.height());
737 	tileheight.blockSignals(FALSE);
738 	tilewidth.blockSignals(FALSE);
739 	resizeTiles();
740     }
741     othersize = n;
742 }
743 
normalFont()744 const QFont& NetHackQtSettings::normalFont()
745 {
746     static int size[]={ 18, 14, 12, 10, 8 };
747     normal.setPointSize(size[fontsize.currentItem()]);
748     return normal;
749 }
750 
normalFixedFont()751 const QFont& NetHackQtSettings::normalFixedFont()
752 {
753     static int size[]={ 18, 14, 13, 10, 8 };
754     normalfixed.setPointSize(size[fontsize.currentItem()]);
755     return normalfixed;
756 }
757 
largeFont()758 const QFont& NetHackQtSettings::largeFont()
759 {
760     static int size[]={ 24, 18, 14, 12, 10 };
761     large.setPointSize(size[fontsize.currentItem()]);
762     return large;
763 }
764 
ynInMessages()765 bool NetHackQtSettings::ynInMessages()
766 {
767     return !qt_compact_mode;
768 }
769 
770 
771 NetHackQtSettings* qt_settings;
772 
773 
774 
NetHackQtKeyBuffer()775 NetHackQtKeyBuffer::NetHackQtKeyBuffer() :
776     in(0), out(0)
777 {
778 }
779 
Empty() const780 bool NetHackQtKeyBuffer::Empty() const { return in==out; }
Full() const781 bool NetHackQtKeyBuffer::Full() const { return (in+1)%maxkey==out; }
782 
Put(int k,int a,int state)783 void NetHackQtKeyBuffer::Put(int k, int a, int state)
784 {
785     if ( Full() ) return;	// Safety
786     key[in]=k;
787     ascii[in]=a;
788     in=(in+1)%maxkey;
789 }
790 
Put(char a)791 void NetHackQtKeyBuffer::Put(char a)
792 {
793     Put(0,a,0);
794 }
795 
Put(const char * str)796 void NetHackQtKeyBuffer::Put(const char* str)
797 {
798     while (*str) Put(*str++);
799 }
800 
GetKey()801 int NetHackQtKeyBuffer::GetKey()
802 {
803     if ( Empty() ) return 0;
804     int r=TopKey();
805     out=(out+1)%maxkey;
806     return r;
807 }
808 
GetAscii()809 int NetHackQtKeyBuffer::GetAscii()
810 {
811     if ( Empty() ) return 0; // Safety
812     int r=TopAscii();
813     out=(out+1)%maxkey;
814     return r;
815 }
816 
GetState()817 int NetHackQtKeyBuffer::GetState()
818 {
819     if ( Empty() ) return 0;
820     int r=TopState();
821     out=(out+1)%maxkey;
822     return r;
823 }
824 
TopKey() const825 int NetHackQtKeyBuffer::TopKey() const
826 {
827     if ( Empty() ) return 0;
828     return key[out];
829 }
830 
TopAscii() const831 int NetHackQtKeyBuffer::TopAscii() const
832 {
833     if ( Empty() ) return 0;
834     return ascii[out];
835 }
836 
TopState() const837 int NetHackQtKeyBuffer::TopState() const
838 {
839     if ( Empty() ) return 0;
840     return state[out];
841 }
842 
843 
NetHackQtClickBuffer()844 NetHackQtClickBuffer::NetHackQtClickBuffer() :
845     in(0), out(0)
846 {
847 }
848 
Empty() const849 bool NetHackQtClickBuffer::Empty() const { return in==out; }
Full() const850 bool NetHackQtClickBuffer::Full() const { return (in+1)%maxclick==out; }
851 
Put(int x,int y,int mod)852 void NetHackQtClickBuffer::Put(int x, int y, int mod)
853 {
854     click[in].x=x;
855     click[in].y=y;
856     click[in].mod=mod;
857     in=(in+1)%maxclick;
858 }
859 
NextX() const860 int NetHackQtClickBuffer::NextX() const { return click[out].x; }
NextY() const861 int NetHackQtClickBuffer::NextY() const { return click[out].y; }
NextMod() const862 int NetHackQtClickBuffer::NextMod() const { return click[out].mod; }
863 
Get()864 void NetHackQtClickBuffer::Get()
865 {
866     out=(out+1)%maxclick;
867 }
868 
869 class NhPSListViewItem : public QListViewItem {
870 public:
NhPSListViewItem(QListView * parent,const QString & name)871     NhPSListViewItem( QListView* parent, const QString& name ) :
872 	QListViewItem(parent, name)
873     {
874     }
875 
setGlyph(int g)876     void setGlyph(int g)
877     {
878 	NetHackQtGlyphs& glyphs = qt_settings->glyphs();
879 	int gw = glyphs.width();
880 	int gh = glyphs.height();
881 	QPixmap pm(gw,gh);
882 	QPainter p(&pm);
883 	glyphs.drawGlyph(p, g, 0, 0);
884 	p.end();
885 	setPixmap(0,pm);
886 	setHeight(QMAX(pm.height()+1,height()));
887     }
888 
paintCell(QPainter * p,const QColorGroup & cg,int column,int width,int alignment)889     void paintCell( QPainter *p, const QColorGroup &cg,
890 		    int column, int width, int alignment )
891     {
892 	if ( isSelectable() ) {
893 	    QListViewItem::paintCell( p, cg, column, width, alignment );
894 	} else {
895 	    QColorGroup disabled(
896 		cg.foreground().light(),
897 		cg.button().light(),
898 		cg.light(), cg.dark(), cg.mid(),
899 		gray, cg.base() );
900 	    QListViewItem::paintCell( p, disabled, column, width, alignment );
901 	}
902     }
903 };
904 
905 class NhPSListViewRole : public NhPSListViewItem {
906 public:
NhPSListViewRole(QListView * parent,int id)907     NhPSListViewRole( QListView* parent, int id ) :
908 	NhPSListViewItem(parent,
909 #ifdef QT_CHOOSE_RACE_FIRST // Lowerize - looks better
910 	    QString(QChar(roles[id].name.m[0])).lower()+QString(roles[id].name.m+1)
911 #else
912 	    roles[id].name.m
913 #endif
914 	)
915     {
916 	setGlyph(monnum_to_glyph(roles[id].malenum));
917     }
918 };
919 
920 class NhPSListViewRace : public NhPSListViewItem {
921 public:
NhPSListViewRace(QListView * parent,int id)922     NhPSListViewRace( QListView* parent, int id ) :
923 	NhPSListViewItem(parent,
924 #ifdef QT_CHOOSE_RACE_FIRST // Capitalize - looks better
925 	    QString(QChar(races[id].noun[0])).upper()+QString(races[id].noun+1)
926 #else
927 	    QString(QChar(races[id].noun[0])+QString(races[id].noun+1))
928 #endif
929 	)
930     {
931 	setGlyph(monnum_to_glyph(races[id].malenum));
932     }
933 };
934 
935 class NhPSListView : public QListView {
936 public:
NhPSListView(QWidget * parent)937     NhPSListView( QWidget* parent ) :
938 	QListView(parent)
939     {
940 	setSorting(-1); // order is identity
941 	header()->setClickEnabled(FALSE);
942     }
943 
sizePolicy() const944     QSizePolicy sizePolicy() const
945     {
946 	return QSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
947     }
948 
minimumSizeHint() const949     QSize minimumSizeHint() const
950     {
951 	return sizeHint();
952     }
953 
sizeHint() const954     QSize sizeHint() const
955     {
956 	QListView::sizeHint();
957 	QSize sz = header()->sizeHint();
958 	int h=0;
959 	QListViewItem* c=firstChild();
960 	while (c) h+=c->height(),c = c->nextSibling();
961 	sz += QSize(frameWidth()*2, h+frameWidth()*2);
962 	return sz;
963     }
964 
selectedItemNumber() const965     int selectedItemNumber() const
966     {
967 	int i=0;
968 	QListViewItem* c = firstChild();
969 	while (c) {
970 	    if (c == selectedItem()) {
971 		return i;
972 	    }
973 	    i++;
974 	    c = c->nextSibling();
975 	}
976 	return -1;
977     }
978 
setSelectedItemNumber(int i)979     void setSelectedItemNumber(int i)
980     {
981 	QListViewItem* c=firstChild();
982 	while (i--)
983 	    c = c->nextSibling();
984 	c->setSelected(TRUE);
985     }
986 };
987 
NetHackQtPlayerSelector(NetHackQtKeyBuffer & ks)988 NetHackQtPlayerSelector::NetHackQtPlayerSelector(NetHackQtKeyBuffer& ks) :
989     QDialog(qApp->mainWidget(),"plsel",TRUE),
990     keysource(ks),
991     fully_specified_role(TRUE)
992 {
993     /*
994                0             1             2
995 	  + Name ------------------------------------+
996 	0 |                                          |
997 	  + ---- ------------------------------------+
998 	  + Role ---+   + Race ---+   + Gender ------+
999 	  |         |   |         |   |  * Male      |
1000 	1 |         |   |         |   |  * Female    |
1001 	  |         |   |         |   +--------------+
1002 	  |         |   |         |
1003 	  |         |   |         |   + Alignment ---+
1004 	2 |         |   |         |   |  * Male      |
1005 	  |         |   |         |   |  * Female    |
1006 	  |         |   |         |   +--------------+
1007 	3 |         |   |         |   ...stretch...
1008 	  |         |   |         |
1009 	4 |         |   |         |   [  Play  ]
1010 	5 |         |   |         |   [  Quit  ]
1011 	  +---------+   +---------+
1012     */
1013 
1014     int marg=4;
1015     QGridLayout *l = new QGridLayout(this,6,3,marg,marg);
1016 
1017     QButtonGroup* namebox = new QButtonGroup(1,Horizontal,"Name",this);
1018     QLineEdit* name = new QLineEdit(namebox);
1019     name->setMaxLength(sizeof(plname)-1);
1020     if ( strncmp(plname,"player",6) && strncmp(plname,"games",5) )
1021 	name->setText(plname);
1022     connect(name, SIGNAL(textChanged(const QString&)),
1023 	    this, SLOT(selectName(const QString&)) );
1024     name->setFocus();
1025     QButtonGroup* genderbox = new QButtonGroup("Sex",this);
1026     QButtonGroup* alignbox = new QButtonGroup("Alignment",this);
1027     QVBoxLayout* vbgb = new QVBoxLayout(genderbox,3,1);
1028     vbgb->setAutoAdd(TRUE);
1029     vbgb->addSpacing(fontMetrics().height()*3/4);
1030     QVBoxLayout* vbab = new QVBoxLayout(alignbox,3,1);
1031     vbab->setAutoAdd(TRUE);
1032     vbab->addSpacing(fontMetrics().height());
1033     QLabel* logo = new QLabel(nh_attribution, this);
1034 
1035     l->addMultiCellWidget( namebox, 0,0,0,2 );
1036 #ifdef QT_CHOOSE_RACE_FIRST
1037     race = new NhPSListView(this);
1038     role = new NhPSListView(this);
1039     l->addMultiCellWidget( race, 1,5,0,0 );
1040     l->addMultiCellWidget( role, 1,5,1,1 );
1041 #else
1042     role = new NhPSListView(this);
1043     race = new NhPSListView(this);
1044     l->addMultiCellWidget( role, 1,5,0,0 );
1045     l->addMultiCellWidget( race, 1,5,1,1 );
1046 #endif
1047     role->addColumn("Role");
1048     race->addColumn("Race");
1049 
1050     l->addWidget( genderbox, 1, 2 );
1051     l->addWidget( alignbox, 2, 2 );
1052     l->addWidget( logo, 3, 2, AlignCenter );
1053     l->setRowStretch( 3, 5 );
1054 
1055     int i;
1056     int nrole;
1057 
1058     for (nrole=0; roles[nrole].name.m; nrole++)
1059 	;
1060     for (i=nrole-1; i>=0; i--) { // XXX QListView unsorted goes in rev.
1061 	new NhPSListViewRole( role, i );
1062     }
1063     connect( role, SIGNAL(selectionChanged()), this, SLOT(selectRole()) );
1064 
1065     int nrace;
1066     for (nrace=0; races[nrace].noun; nrace++)
1067 	;
1068     for (i=nrace-1; i>=0; i--) {
1069 	new NhPSListViewRace( race, i );
1070     }
1071     connect( race, SIGNAL(selectionChanged()), this, SLOT(selectRace()) );
1072 
1073     gender = new QRadioButton*[ROLE_GENDERS];
1074     for (i=0; i<ROLE_GENDERS; i++) {
1075 	gender[i] = new QRadioButton( genders[i].adj, genderbox );
1076     }
1077     connect( genderbox, SIGNAL(clicked(int)), this, SLOT(selectGender(int)) );
1078 
1079     alignment = new QRadioButton*[ROLE_ALIGNS];
1080     for (i=0; i<ROLE_ALIGNS; i++) {
1081 	alignment[i] = new QRadioButton( aligns[i].adj, alignbox );
1082     }
1083     connect( alignbox, SIGNAL(clicked(int)), this, SLOT(selectAlignment(int)) );
1084 
1085     QPushButton* ok = new QPushButton("Play",this);
1086     l->addWidget( ok, 4, 2 );
1087     ok->setDefault(TRUE);
1088     connect( ok, SIGNAL(clicked()), this, SLOT(accept()) );
1089 
1090     QPushButton* cancel = new QPushButton("Quit",this);
1091     l->addWidget( cancel, 5, 2 );
1092     connect( cancel, SIGNAL(clicked()), this, SLOT(reject()) );
1093 
1094     // Randomize race and role, unless specified in config
1095     int ro = flags.initrole;
1096     if (ro == ROLE_NONE || ro == ROLE_RANDOM) {
1097 	ro = rn2(nrole);
1098 	if (flags.initrole != ROLE_RANDOM) {
1099 	    fully_specified_role = FALSE;
1100 	}
1101     }
1102     int ra = flags.initrace;
1103     if (ra == ROLE_NONE || ra == ROLE_RANDOM) {
1104 	ra = rn2(nrace);
1105 	if (flags.initrace != ROLE_RANDOM) {
1106 	    fully_specified_role = FALSE;
1107 	}
1108     }
1109 
1110     // make sure we have a valid combination, honoring
1111     // the users request if possible.
1112     bool choose_race_first;
1113 #ifdef QT_CHOOSE_RACE_FIRST
1114     choose_race_first = TRUE;
1115     if (flags.initrole >= 0 && flags.initrace < 0) {
1116 	choose_race_first = FALSE;
1117     }
1118 #else
1119     choose_race_first = FALSE;
1120     if (flags.initrace >= 0 && flags.initrole < 0) {
1121 	choose_race_first = TRUE;
1122     }
1123 #endif
1124     while (!validrace(ro,ra)) {
1125 	if (choose_race_first) {
1126 	    ro = rn2(nrole);
1127 	    if (flags.initrole != ROLE_RANDOM) {
1128 	        fully_specified_role = FALSE;
1129 	    }
1130 	} else {
1131 	    ra = rn2(nrace);
1132 	    if (flags.initrace != ROLE_RANDOM) {
1133 	        fully_specified_role = FALSE;
1134 	    }
1135 	}
1136     }
1137 
1138     int g = flags.initgend;
1139     if (g == -1) {
1140 	g = rn2(ROLE_GENDERS);
1141 	fully_specified_role = FALSE;
1142     }
1143     while (!validgend(ro,ra,g)) {
1144 	g = rn2(ROLE_GENDERS);
1145     }
1146     gender[g]->setChecked(TRUE);
1147     selectGender(g);
1148 
1149     int a = flags.initalign;
1150     if (a == -1) {
1151 	a = rn2(ROLE_ALIGNS);
1152 	fully_specified_role = FALSE;
1153     }
1154     while (!validalign(ro,ra,a)) {
1155 	a = rn2(ROLE_ALIGNS);
1156     }
1157     alignment[a]->setChecked(TRUE);
1158     selectAlignment(a);
1159 
1160     QListViewItem* li;
1161 
1162     li = role->firstChild();
1163     while (ro--) li=li->nextSibling();
1164     role->setSelected(li,TRUE);
1165 
1166     li = race->firstChild();
1167     while (ra--) li=li->nextSibling();
1168     race->setSelected(li,TRUE);
1169 
1170     flags.initrace = race->selectedItemNumber();
1171     flags.initrole = role->selectedItemNumber();
1172 }
1173 
1174 
selectName(const QString & n)1175 void NetHackQtPlayerSelector::selectName(const QString& n)
1176 {
1177     strncpy(plname,n.latin1(),sizeof(plname)-1);
1178 }
1179 
selectRole()1180 void NetHackQtPlayerSelector::selectRole()
1181 {
1182     int ra = race->selectedItemNumber();
1183     int ro = role->selectedItemNumber();
1184     if (ra == -1 || ro == -1) return;
1185 
1186 #ifdef QT_CHOOSE_RACE_FIRST
1187     selectRace();
1188 #else
1189     QListViewItem* i=role->currentItem();
1190     QListViewItem* valid=0;
1191     int j;
1192     NhPSListViewItem* item;
1193     item = (NhPSListViewItem*)role->firstChild();
1194     for (j=0; roles[j].name.m; j++) {
1195 	bool v = validrace(j,ra);
1196 	item->setSelectable(TRUE);
1197 	if ( !valid && v ) valid = item;
1198 	item=(NhPSListViewItem*)item->nextSibling();
1199     }
1200     if ( !validrace(role->selectedItemNumber(),ra) )
1201 	i = valid;
1202     role->setSelected(i,TRUE);
1203     item = (NhPSListViewItem*)role->firstChild();
1204     for (j=0; roles[j].name.m; j++) {
1205 	bool v = validrace(j,ra);
1206 	item->setSelectable(v);
1207 	item->repaint();
1208 	item=(NhPSListViewItem*)item->nextSibling();
1209     }
1210 #endif
1211 
1212     flags.initrole = role->selectedItemNumber();
1213     setupOthers();
1214 }
1215 
selectRace()1216 void NetHackQtPlayerSelector::selectRace()
1217 {
1218     int ra = race->selectedItemNumber();
1219     int ro = role->selectedItemNumber();
1220     if (ra == -1 || ro == -1) return;
1221 
1222 #ifndef QT_CHOOSE_RACE_FIRST
1223     selectRole();
1224 #else
1225     QListViewItem* i=race->currentItem();
1226     QListViewItem* valid=0;
1227     int j;
1228     NhPSListViewItem* item;
1229     item = (NhPSListViewItem*)race->firstChild();
1230     for (j=0; races[j].noun; j++) {
1231 	bool v = validrace(ro,j);
1232 	item->setSelectable(TRUE);
1233 	if ( !valid && v ) valid = item;
1234 	item=(NhPSListViewItem*)item->nextSibling();
1235     }
1236     if ( !validrace(ro,race->selectedItemNumber()) )
1237 	i = valid;
1238     race->setSelected(i,TRUE);
1239     item = (NhPSListViewItem*)race->firstChild();
1240     for (j=0; races[j].noun; j++) {
1241 	bool v = validrace(ro,j);
1242 	item->setSelectable(v);
1243 	item->repaint();
1244 	item=(NhPSListViewItem*)item->nextSibling();
1245     }
1246 #endif
1247 
1248     flags.initrace = race->selectedItemNumber();
1249     setupOthers();
1250 }
1251 
setupOthers()1252 void NetHackQtPlayerSelector::setupOthers()
1253 {
1254     int ro = role->selectedItemNumber();
1255     int ra = race->selectedItemNumber();
1256     int valid=-1;
1257     int c=0;
1258     int j;
1259     for (j=0; j<ROLE_GENDERS; j++) {
1260 	bool v = validgend(ro,ra,j);
1261 	if ( gender[j]->isChecked() )
1262 	    c = j;
1263 	gender[j]->setEnabled(v);
1264 	if ( valid<0 && v ) valid = j;
1265     }
1266     if ( !validgend(ro,ra,c) )
1267 	c = valid;
1268     int k;
1269     for (k=0; k<ROLE_GENDERS; k++) {
1270 	gender[k]->setChecked(c==k);
1271     }
1272     selectGender(c);
1273 
1274     valid=-1;
1275     for (j=0; j<ROLE_ALIGNS; j++) {
1276 	bool v = validalign(ro,ra,j);
1277 	if ( alignment[j]->isChecked() )
1278 	    c = j;
1279 	alignment[j]->setEnabled(v);
1280 	if ( valid<0 && v ) valid = j;
1281     }
1282     if ( !validalign(ro,ra,c) )
1283 	c = valid;
1284     for (k=0; k<ROLE_ALIGNS; k++) {
1285 	alignment[k]->setChecked(c==k);
1286     }
1287     selectAlignment(c);
1288 }
1289 
selectGender(int i)1290 void NetHackQtPlayerSelector::selectGender(int i)
1291 {
1292     flags.initgend = i;
1293 }
1294 
selectAlignment(int i)1295 void NetHackQtPlayerSelector::selectAlignment(int i)
1296 {
1297     flags.initalign = i;
1298 }
1299 
1300 
done(int i)1301 void NetHackQtPlayerSelector::done(int i)
1302 {
1303     setResult(i);
1304     qApp->exit_loop();
1305 }
1306 
Quit()1307 void NetHackQtPlayerSelector::Quit()
1308 {
1309     done(R_Quit);
1310     qApp->exit_loop();
1311 }
1312 
Random()1313 void NetHackQtPlayerSelector::Random()
1314 {
1315     done(R_Rand);
1316     qApp->exit_loop();
1317 }
1318 
Choose()1319 bool NetHackQtPlayerSelector::Choose()
1320 {
1321     if (fully_specified_role) return TRUE;
1322 
1323 #if defined(QWS) // probably safe with Qt 3, too (where show!=exec in QDialog).
1324     if ( qt_compact_mode ) {
1325 	showMaximized();
1326     } else
1327 #endif
1328     {
1329 	adjustSize();
1330 	centerOnMain(this);
1331     }
1332 
1333     if ( exec() ) {
1334 	return TRUE;
1335     } else {
1336 	return FALSE;
1337     }
1338 }
1339 
1340 
NetHackQtStringRequestor(NetHackQtKeyBuffer & ks,const char * p,const char * cancelstr)1341 NetHackQtStringRequestor::NetHackQtStringRequestor(NetHackQtKeyBuffer& ks, const char* p, const char* cancelstr) :
1342     QDialog(qApp->mainWidget(),"string",FALSE),
1343     prompt(p,this,"prompt"),
1344     input(this,"input"),
1345     keysource(ks)
1346 {
1347     cancel=new QPushButton(cancelstr,this);
1348     connect(cancel,SIGNAL(clicked()),this,SLOT(reject()));
1349 
1350     okay=new QPushButton("Okay",this);
1351     connect(okay,SIGNAL(clicked()),this,SLOT(accept()));
1352     connect(&input,SIGNAL(returnPressed()),this,SLOT(accept()));
1353     okay->setDefault(TRUE);
1354 
1355     setFocusPolicy(StrongFocus);
1356 }
1357 
resizeEvent(QResizeEvent *)1358 void NetHackQtStringRequestor::resizeEvent(QResizeEvent*)
1359 {
1360     const int margin=5;
1361     const int gutter=5;
1362 
1363     int h=(height()-margin*2-gutter);
1364 
1365     if (strlen(prompt.text()) > 16) {
1366 	h/=3;
1367 	prompt.setGeometry(margin,margin,width()-margin*2,h);
1368 	input.setGeometry(width()*1/5,margin+h+gutter,
1369 	    (width()-margin-2-gutter)*4/5,h);
1370     } else {
1371 	h/=2;
1372 	prompt.setGeometry(margin,margin,(width()-margin*2-gutter)*2/5,h);
1373 	input.setGeometry(prompt.geometry().right()+gutter,margin,
1374 	    (width()-margin-2-gutter)*3/5,h);
1375     }
1376 
1377     cancel->setGeometry(margin,input.geometry().bottom()+gutter,
1378 	(width()-margin*2-gutter)/2,h);
1379     okay->setGeometry(cancel->geometry().right()+gutter,cancel->geometry().y(),
1380 	cancel->width(),h);
1381 }
1382 
SetDefault(const char * d)1383 void NetHackQtStringRequestor::SetDefault(const char* d)
1384 {
1385     input.setText(d);
1386 }
1387 
Get(char * buffer,int maxchar)1388 bool NetHackQtStringRequestor::Get(char* buffer, int maxchar)
1389 {
1390     input.setMaxLength(maxchar);
1391     if (strlen(prompt.text()) > 16) {
1392 	resize(fontMetrics().width(prompt.text())+50,fontMetrics().height()*6);
1393     } else {
1394 	resize(fontMetrics().width(prompt.text())*2+50,fontMetrics().height()*4);
1395     }
1396 
1397     centerOnMain(this);
1398     show();
1399     input.setFocus();
1400     setResult(-1);
1401     while (result()==-1) {
1402 	// Put keys in buffer (eg. from macros, from out-of-focus input)
1403 	if (!keysource.Empty()) {
1404 	    while (!keysource.Empty()) {
1405 		int key=keysource.TopKey();
1406 		int ascii=keysource.TopAscii();
1407 		int state=keysource.GetState();
1408 		if (ascii=='\r' || ascii=='\n') {
1409 		    // CR or LF in buffer causes confirmation
1410 		    strcpy(buffer,input.text());
1411 		    return TRUE;
1412 		} else if (ascii=='\033') {
1413 		    return FALSE;
1414 		} else {
1415 		    input.fakeEvent(key,ascii,state);
1416 		}
1417 	    }
1418 	}
1419 	qApp->enter_loop();
1420     }
1421     // XXX Get rid of extra keys, since we couldn't get focus!
1422     while (!keysource.Empty()) keysource.GetKey();
1423 
1424     if (result()) {
1425 	strcpy(buffer,input.text());
1426 	return TRUE;
1427     } else {
1428 	return FALSE;
1429     }
1430 }
done(int i)1431 void NetHackQtStringRequestor::done(int i)
1432 {
1433     setResult(i);
1434     qApp->exit_loop();
1435 }
1436 
1437 
NetHackQtWindow()1438 NetHackQtWindow::NetHackQtWindow()
1439 {
1440 }
1441 
~NetHackQtWindow()1442 NetHackQtWindow::~NetHackQtWindow()
1443 {
1444 }
1445 
1446 // XXX Use "expected ..." for now, abort or default later.
1447 //
Clear()1448 void NetHackQtWindow::Clear() { puts("unexpected Clear"); }
Display(bool block)1449 void NetHackQtWindow::Display(bool block) { puts("unexpected Display"); }
Destroy()1450 bool NetHackQtWindow::Destroy() { return TRUE; }
CursorTo(int x,int y)1451 void NetHackQtWindow::CursorTo(int x,int y) { puts("unexpected CursorTo"); }
PutStr(int attr,const char * text)1452 void NetHackQtWindow::PutStr(int attr, const char* text) { puts("unexpected PutStr"); }
StartMenu()1453 void NetHackQtWindow::StartMenu() { puts("unexpected StartMenu"); }
AddMenu(int glyph,const ANY_P * identifier,char ch,char gch,int attr,const char * str,bool presel)1454 void NetHackQtWindow::AddMenu(int glyph, const ANY_P* identifier, char ch, char gch, int attr,
1455     const char* str, bool presel) { puts("unexpected AddMenu"); }
EndMenu(const char * prompt)1456 void NetHackQtWindow::EndMenu(const char* prompt) { puts("unexpected EndMenu"); }
SelectMenu(int how,MENU_ITEM_P ** menu_list)1457 int NetHackQtWindow::SelectMenu(int how, MENU_ITEM_P **menu_list) { puts("unexpected SelectMenu"); return 0; }
ClipAround(int x,int y)1458 void NetHackQtWindow::ClipAround(int x,int y) { puts("unexpected ClipAround"); }
PrintGlyph(int x,int y,int glyph)1459 void NetHackQtWindow::PrintGlyph(int x,int y,int glyph) { puts("unexpected PrintGlyph"); }
1460 //void NetHackQtWindow::PrintGlyphCompose(int x,int y,int,int) { puts("unexpected PrintGlyphCompose"); }
UseRIP(int how,time_t when)1461 void NetHackQtWindow::UseRIP(int how, time_t when) { puts("unexpected UseRIP"); }
1462 
1463 
1464 
1465 // XXX Hmmm... crash after saving bones file if Map window is
1466 // XXX deleted.  Strange bug somewhere.
Destroy()1467 bool NetHackQtMapWindow::Destroy() { return FALSE; }
1468 
NetHackQtMapWindow(NetHackQtClickBuffer & click_sink)1469 NetHackQtMapWindow::NetHackQtMapWindow(NetHackQtClickBuffer& click_sink) :
1470     clicksink(click_sink),
1471     change(10),
1472     rogue_font(0)
1473 {
1474     viewport.addChild(this);
1475 
1476     setBackgroundColor(black);
1477     viewport.setBackgroundColor(black);
1478 
1479     pet_annotation = QPixmap(qt_compact_mode ? pet_mark_small_xpm : pet_mark_xpm);
1480 
1481     cursor.setX(0);
1482     cursor.setY(0);
1483     Clear();
1484 
1485     connect(qt_settings,SIGNAL(tilesChanged()),this,SLOT(updateTiles()));
1486     connect(&viewport, SIGNAL(contentsMoving(int,int)), this,
1487 		SLOT(moveMessages(int,int)));
1488 
1489     updateTiles();
1490     //setFocusPolicy(StrongFocus);
1491 #ifdef SAFERHANGUP
1492     QTimer* deadman = new QTimer(this);
1493     connect(deadman, SIGNAL(timeout()), SLOT(timeout()));
1494     deadman->start(2000);		// deadman timer every 2 seconds
1495 #endif
1496 }
1497 
1498 #ifdef SAFERHANGUP
1499 // The "deadman" timer is received by this slot
timeout()1500 void NetHackQtMapWindow::timeout() {}
1501 #endif
1502 
moveMessages(int x,int y)1503 void NetHackQtMapWindow::moveMessages(int x, int y)
1504 {
1505     QRect u = messages_rect;
1506     messages_rect.moveTopLeft(QPoint(x,y));
1507     u |= messages_rect;
1508     update(u);
1509 }
1510 
clearMessages()1511 void NetHackQtMapWindow::clearMessages()
1512 {
1513     messages = "";
1514     update(messages_rect);
1515     messages_rect = QRect();
1516 }
1517 
putMessage(int attr,const char * text)1518 void NetHackQtMapWindow::putMessage(int attr, const char* text)
1519 {
1520     if ( !messages.isEmpty() )
1521 	messages += "\n";
1522     messages += text;
1523     QFontMetrics fm = fontMetrics();
1524     messages_rect = fm.boundingRect(viewport.contentsX(), viewport.contentsY(),
1525                                     viewport.width(), 0,
1526                                     WordBreak|AlignTop|AlignLeft|DontClip,
1527                                     messages);
1528     update(messages_rect);
1529 }
1530 
updateTiles()1531 void NetHackQtMapWindow::updateTiles()
1532 {
1533     NetHackQtGlyphs& glyphs = qt_settings->glyphs();
1534     int gw = glyphs.width();
1535     int gh = glyphs.height();
1536     // Be exactly the size we want to be - full map...
1537     resize(COLNO*gw,ROWNO*gh);
1538 
1539     viewport.verticalScrollBar()->setSteps(gh,gh);
1540     viewport.horizontalScrollBar()->setSteps(gw,gw);
1541     /*
1542     viewport.setMaximumSize(
1543 	gw*COLNO + viewport.verticalScrollBar()->width(),
1544 	gh*ROWNO + viewport.horizontalScrollBar()->height()
1545     );
1546     */
1547     viewport.updateScrollBars();
1548 
1549     change.clear();
1550     change.add(0,0,COLNO,ROWNO);
1551     delete rogue_font; rogue_font = 0;
1552     Display(FALSE);
1553 
1554     emit resized();
1555 }
1556 
~NetHackQtMapWindow()1557 NetHackQtMapWindow::~NetHackQtMapWindow()
1558 {
1559     // Remove from viewport porthole, since that is a destructible member.
1560     viewport.removeChild(this);
1561     recreate(0,0,QPoint(0,0));
1562 }
1563 
Widget()1564 QWidget* NetHackQtMapWindow::Widget()
1565 {
1566     return &viewport;
1567 }
1568 
Scroll(int dx,int dy)1569 void NetHackQtMapWindow::Scroll(int dx, int dy)
1570 {
1571     if (viewport.horizontalScrollBar()->isVisible()) {
1572 	while (dx<0) { viewport.horizontalScrollBar()->subtractPage(); dx++; }
1573 	while (dx>0) { viewport.horizontalScrollBar()->addPage(); dx--; }
1574     }
1575     if (viewport.verticalScrollBar()->isVisible()) {
1576 	while (dy<0) { viewport.verticalScrollBar()->subtractPage(); dy++; }
1577 	while (dy>0) { viewport.verticalScrollBar()->addPage(); dy--; }
1578     }
1579 }
1580 
Clear()1581 void NetHackQtMapWindow::Clear()
1582 {
1583     unsigned short stone=cmap_to_glyph(S_stone);
1584 
1585     for (int j=0; j<ROWNO; j++) {
1586 	for (int i=0; i<COLNO; i++) {
1587 	    Glyph(i,j)=stone;
1588 	}
1589     }
1590 
1591     change.clear();
1592     change.add(0,0,COLNO,ROWNO);
1593 }
1594 
clickCursor()1595 void NetHackQtMapWindow::clickCursor()
1596 {
1597     clicksink.Put(cursor.x(),cursor.y(),CLICK_1);
1598     qApp->exit_loop();
1599 }
1600 
mousePressEvent(QMouseEvent * event)1601 void NetHackQtMapWindow::mousePressEvent(QMouseEvent* event)
1602 {
1603     clicksink.Put(
1604 	event->pos().x()/qt_settings->glyphs().width(),
1605 	event->pos().y()/qt_settings->glyphs().height(),
1606 	event->button()==LeftButton ? CLICK_1 : CLICK_2
1607     );
1608     qApp->exit_loop();
1609 }
1610 
1611 #ifdef TEXTCOLOR
1612 static
nhcolor_to_pen(int c)1613 const QPen& nhcolor_to_pen(int c)
1614 {
1615     static QPen* pen=0;
1616     if ( !pen ) {
1617 	pen = new QPen[17];
1618 	pen[0] = QColor(24,24,24); // "black" on black
1619 	pen[1] = Qt::red;
1620 	pen[2] = QColor(0,191,0);
1621 	pen[3] = QColor(127,127,0);
1622 	pen[4] = Qt::blue;
1623 	pen[5] = Qt::magenta;
1624 	pen[6] = Qt::cyan;
1625 	pen[7] = Qt::gray;
1626 	pen[8] = Qt::white; // no color
1627 	pen[9] = QColor(255,127,0);
1628 	pen[10] = QColor(127,255,127);
1629 	pen[11] = Qt::yellow;
1630 	pen[12] = QColor(127,127,255);
1631 	pen[13] = QColor(255,127,255);
1632 	pen[14] = QColor(127,255,255);
1633 	pen[15] = Qt::white;
1634 	pen[16] = QColor(24,24,24); // "black" on black
1635     }
1636 
1637     return pen[c];
1638 }
1639 #endif
1640 
paintEvent(QPaintEvent * event)1641 void NetHackQtMapWindow::paintEvent(QPaintEvent* event)
1642 {
1643     QRect area=event->rect();
1644     QRect garea;
1645     garea.setCoords(
1646 	QMAX(0,area.left()/qt_settings->glyphs().width()),
1647 	QMAX(0,area.top()/qt_settings->glyphs().height()),
1648 	QMIN(COLNO-1,area.right()/qt_settings->glyphs().width()),
1649 	QMIN(ROWNO-1,area.bottom()/qt_settings->glyphs().height())
1650     );
1651 
1652     QPainter painter;
1653 
1654     painter.begin(this);
1655 
1656     if (Is_rogue_level(&u.uz) || iflags.wc_ascii_map)
1657     {
1658 	// You enter a VERY primitive world!
1659 
1660 	painter.setClipRect( event->rect() ); // (normally we don't clip)
1661 	painter.fillRect( event->rect(), black );
1662 
1663 	if ( !rogue_font ) {
1664 	    // Find font...
1665 	    int pts = 5;
1666 	    QString fontfamily = iflags.wc_font_map
1667 		? iflags.wc_font_map : "Courier";
1668 	    bool bold = FALSE;
1669 	    if ( fontfamily.right(5).lower() == "-bold" ) {
1670 		fontfamily.truncate(fontfamily.length()-5);
1671 		bold = TRUE;
1672 	    }
1673 	    while ( pts < 32 ) {
1674 		QFont f(fontfamily, pts, bold ? QFont::Bold : QFont::Normal);
1675 		painter.setFont(QFont(fontfamily, pts));
1676 		QFontMetrics fm = painter.fontMetrics();
1677 		if ( fm.width("M") > qt_settings->glyphs().width() )
1678 		    break;
1679 		if ( fm.height() > qt_settings->glyphs().height() )
1680 		    break;
1681 		pts++;
1682 	    }
1683 	    rogue_font = new QFont(fontfamily,pts-1);
1684 	}
1685 	painter.setFont(*rogue_font);
1686 
1687 	for (int j=garea.top(); j<=garea.bottom(); j++) {
1688 	    for (int i=garea.left(); i<=garea.right(); i++) {
1689 		unsigned short g=Glyph(i,j);
1690 		uchar ch;
1691 		int color, och;
1692 		unsigned special;
1693 
1694 		painter.setPen( green );
1695 		/* map glyph to character and color */
1696     		(void)mapglyph(g, &och, &color, &special, i, j, 0);
1697 		ch = (uchar)och;
1698 #ifdef TEXTCOLOR
1699 		painter.setPen( nhcolor_to_pen(color) );
1700 #endif
1701 		painter.drawText(
1702 		    i*qt_settings->glyphs().width(),
1703 		    j*qt_settings->glyphs().height(),
1704 		    qt_settings->glyphs().width(),
1705 		    qt_settings->glyphs().height(),
1706 		    AlignCenter,
1707 		    (const char*)&ch, 1
1708 		);
1709 		if (glyph_is_pet(g)
1710 #ifdef TEXTCOLOR
1711 		    && ::iflags.hilite_pet
1712 #endif
1713 		) {
1714 		    painter.drawPixmap(QPoint(i*qt_settings->glyphs().width(),
1715                                               j*qt_settings->glyphs().height()),
1716                                        pet_annotation);
1717 		}
1718 	    }
1719 	}
1720 
1721 	painter.setFont(font());
1722     } else {
1723 	for (int j=garea.top(); j<=garea.bottom(); j++) {
1724 	    for (int i=garea.left(); i<=garea.right(); i++) {
1725 		unsigned short g=Glyph(i,j);
1726 		qt_settings->glyphs().drawCell(painter, g, i, j);
1727 		if (glyph_is_pet(g)
1728 #ifdef TEXTCOLOR
1729 		    && ::iflags.hilite_pet
1730 #endif
1731 		) {
1732 		    painter.drawPixmap(QPoint(i*qt_settings->glyphs().width(),
1733                                               j*qt_settings->glyphs().height()),
1734                                        pet_annotation);
1735 		}
1736 	    }
1737 	}
1738     }
1739 
1740     if (garea.contains(cursor)) {
1741 	if (Is_rogue_level(&u.uz)) {
1742 #ifdef TEXTCOLOR
1743 	    painter.setPen( white );
1744 #else
1745 	    painter.setPen( green ); // REALLY primitive
1746 #endif
1747 	} else
1748 	{
1749 	    int hp100;
1750 	    if (u.mtimedone) {
1751 		hp100=u.mhmax ? u.mh*100/u.mhmax : 100;
1752 	    } else {
1753 		hp100=u.uhpmax ? u.uhp*100/u.uhpmax : 100;
1754 	    }
1755 
1756 	    if (hp100 > 75) painter.setPen(white);
1757 	    else if (hp100 > 50) painter.setPen(yellow);
1758 	    else if (hp100 > 25) painter.setPen(QColor(0xff,0xbf,0x00)); // orange
1759 	    else if (hp100 > 10) painter.setPen(red);
1760 	    else painter.setPen(magenta);
1761 	}
1762 
1763 	painter.drawRect(
1764 	    cursor.x()*qt_settings->glyphs().width(),cursor.y()*qt_settings->glyphs().height(),
1765 	    qt_settings->glyphs().width(),qt_settings->glyphs().height());
1766     }
1767 
1768     if (area.intersects(messages_rect)) {
1769 	painter.setPen(black);
1770 	painter.drawText(viewport.contentsX()+1,viewport.contentsY()+1,
1771 	    viewport.width(),0, WordBreak|AlignTop|AlignLeft|DontClip, messages);
1772 	painter.setPen(white);
1773 	painter.drawText(viewport.contentsX(),viewport.contentsY(),
1774 	    viewport.width(),0, WordBreak|AlignTop|AlignLeft|DontClip, messages);
1775     }
1776 
1777     painter.end();
1778 }
1779 
Display(bool block)1780 void NetHackQtMapWindow::Display(bool block)
1781 {
1782     for (int i=0; i<change.clusters(); i++) {
1783 	const QRect& ch=change[i];
1784 	repaint(
1785 	    ch.x()*qt_settings->glyphs().width(),
1786 	    ch.y()*qt_settings->glyphs().height(),
1787 	    ch.width()*qt_settings->glyphs().width(),
1788 	    ch.height()*qt_settings->glyphs().height(),
1789 	    FALSE
1790 	);
1791     }
1792 
1793     change.clear();
1794 
1795     if (block) {
1796 	yn_function("Press a key when done viewing",0,'\0');
1797     }
1798 }
1799 
CursorTo(int x,int y)1800 void NetHackQtMapWindow::CursorTo(int x,int y)
1801 {
1802     Changed(cursor.x(),cursor.y());
1803     cursor.setX(x);
1804     cursor.setY(y);
1805     Changed(cursor.x(),cursor.y());
1806 }
1807 
PutStr(int attr,const char * text)1808 void NetHackQtMapWindow::PutStr(int attr, const char* text)
1809 {
1810     puts("unexpected PutStr in MapWindow");
1811 }
1812 
ClipAround(int x,int y)1813 void NetHackQtMapWindow::ClipAround(int x,int y)
1814 {
1815     // Convert to pixel of center of tile
1816     x=x*qt_settings->glyphs().width()+qt_settings->glyphs().width()/2;
1817     y=y*qt_settings->glyphs().height()+qt_settings->glyphs().height()/2;
1818 
1819     // Then ensure that pixel is visible
1820     viewport.center(x,y,0.45,0.45);
1821 }
1822 
PrintGlyph(int x,int y,int glyph)1823 void NetHackQtMapWindow::PrintGlyph(int x,int y,int glyph)
1824 {
1825     Glyph(x,y)=glyph;
1826     Changed(x,y);
1827 }
1828 
1829 //void NetHackQtMapWindow::PrintGlyphCompose(int x,int y,int glyph1, int glyph2)
1830 //{
1831     // TODO: composed graphics
1832 //}
1833 
Changed(int x,int y)1834 void NetHackQtMapWindow::Changed(int x, int y)
1835 {
1836     change.add(x,y);
1837 }
1838 
1839 
1840 class NetHackQtScrollText : public QTableView {
1841     struct UData {
UDataNetHackQtScrollText::UData1842 	UData() : text(0), attr(0) { }
~UDataNetHackQtScrollText::UData1843 	~UData() { if (text) free(text); }
1844 
1845 	char* text;
1846 	int attr;
1847     };
1848 public:
1849     int uncleared;
1850 
NetHackQtScrollText(int maxlength)1851     NetHackQtScrollText(int maxlength) :
1852 	uncleared(0),
1853 	maxitems(maxlength),
1854 	first(0),
1855 	count(0),
1856 	item_cycle(maxlength)
1857     {
1858 	setNumCols(1);
1859 	setCellWidth(200);
1860 	setCellHeight(fontMetrics().height());
1861 	setBackgroundColor(white);
1862 	setTableFlags(Tbl_vScrollBar
1863 	    |Tbl_autoHScrollBar
1864 	    |Tbl_clipCellPainting
1865 	    |Tbl_smoothScrolling);
1866     }
1867 
~NetHackQtScrollText()1868     ~NetHackQtScrollText()
1869     {
1870     }
1871 
Scroll(int dx,int dy)1872     void Scroll(int dx, int dy)
1873     {
1874 	setXOffset(xOffset()+dx*viewWidth());
1875 	setYOffset(yOffset()+dy*viewHeight());
1876     }
1877 
insertItem(int attr,const char * text)1878     void insertItem(int attr, const char* text)
1879     {
1880 	setTopCell(count);
1881 
1882 	setAutoUpdate(FALSE);
1883 
1884 	int i;
1885 	if (count<maxitems) {
1886 	    i=count++;
1887 	    setNumRows(count);
1888 	} else {
1889 	    i=count-1;
1890 	    first=(first+1)%maxitems;
1891 	}
1892 	item(i).attr=attr;
1893 	item(i).text=strdup(text);
1894 	int w=datumWidth(item(i));
1895 
1896 	if (w > cellWidth()) {
1897 	    // Get wider.
1898 	    setCellWidth(w);
1899 	}
1900 	setTopCell(count);
1901 
1902 	setAutoUpdate(TRUE);
1903 
1904 	if (viewHeight() >= totalHeight()-cellHeight()) {
1905 	    repaint();
1906 	} else {
1907 	    scroll(0,cellHeight());
1908 	}
1909     }
1910 
setFont(const QFont & font)1911     virtual void setFont(const QFont& font)
1912     {
1913 	QTableView::setFont(font);
1914 	setCellHeight(fontMetrics().height());
1915     }
1916 
1917 protected:
1918 
item(int i)1919     UData& item(int i)
1920     {
1921 	return item_cycle[(first+i)%maxitems];
1922     }
1923 
1924     const int maxitems;
1925     int first, count;
1926     QArray<UData> item_cycle;
1927 
datumWidth(const UData & uitem)1928     int datumWidth(const UData& uitem)
1929     {
1930 	if (uitem.text) {
1931 	    int width=fontMetrics().width(uitem.text)+3;
1932 	    if (uitem.attr) {
1933 		// XXX Too expensive to do properly, because
1934 		// XXX we have to set the font of the widget
1935 		// XXX just to get the font metrics information!
1936 		// XXX Could hold a fake widget for that
1937 		// XXX purpose, but this hack is less ugly.
1938 		width+=width/10;
1939 	    }
1940 	    return width;
1941 	} else {
1942 	    return 0;
1943 	}
1944     }
1945 
setupPainter(QPainter * p)1946     virtual void setupPainter(QPainter *p)
1947     {
1948 	// XXX This shouldn't be needed - we set the bg in the constructor.
1949 	p->setBackgroundColor(white);
1950     }
1951 
paintCell(QPainter * p,int row,int col)1952     virtual void paintCell(QPainter *p, int row, int col)
1953     {
1954 	bool sel=FALSE;
1955 	UData& uitem=item(row);
1956 
1957 	if (!sel && row < count-uncleared) {
1958 	    p->setPen(darkGray);
1959 	} else {
1960 	    p->setPen(black);
1961 	}
1962 
1963 	if (uitem.attr) {
1964 	    // XXX only bold
1965 	    QFont bold(font().family(),font().pointSize(),QFont::Bold);
1966 	    p->setFont(bold);
1967 	}
1968 
1969 	p->drawText(3, 0, cellWidth(), cellHeight(),
1970 		AlignLeft|AlignVCenter, uitem.text);
1971 
1972 	if (uitem.attr) {
1973 	    p->setFont(font());
1974 	}
1975     }
1976 };
1977 
NetHackQtMessageWindow()1978 NetHackQtMessageWindow::NetHackQtMessageWindow() :
1979     list(new NetHackQtScrollText(::iflags.msg_history))
1980 {
1981     ::iflags.window_inited = 1;
1982     map = 0;
1983     connect(qt_settings,SIGNAL(fontChanged()),this,SLOT(updateFont()));
1984     updateFont();
1985 }
1986 
~NetHackQtMessageWindow()1987 NetHackQtMessageWindow::~NetHackQtMessageWindow()
1988 {
1989     ::iflags.window_inited = 0;
1990     delete list;
1991 }
1992 
Widget()1993 QWidget* NetHackQtMessageWindow::Widget() { return list; }
1994 
setMap(NetHackQtMapWindow * m)1995 void NetHackQtMessageWindow::setMap(NetHackQtMapWindow* m)
1996 {
1997     map = m;
1998     updateFont();
1999 }
2000 
updateFont()2001 void NetHackQtMessageWindow::updateFont()
2002 {
2003     list->setFont(qt_settings->normalFont());
2004     if ( map )
2005 	map->setFont(qt_settings->normalFont());
2006 }
2007 
Scroll(int dx,int dy)2008 void NetHackQtMessageWindow::Scroll(int dx, int dy)
2009 {
2010     list->Scroll(dx,dy);
2011 }
2012 
Clear()2013 void NetHackQtMessageWindow::Clear()
2014 {
2015     if ( map )
2016 	map->clearMessages();
2017     if (list->uncleared) {
2018 	list->uncleared=0;
2019 	changed=TRUE;
2020 	Display(FALSE);
2021     }
2022 }
2023 
Display(bool block)2024 void NetHackQtMessageWindow::Display(bool block)
2025 {
2026     if (changed) {
2027 	list->repaint();
2028 	changed=FALSE;
2029     }
2030 }
2031 
PutStr(int attr,const char * text)2032 void NetHackQtMessageWindow::PutStr(int attr, const char* text)
2033 {
2034 #ifdef USER_SOUNDS
2035     play_sound_for_message(text);
2036 #endif
2037 
2038     changed=TRUE;
2039     list->uncleared++;
2040     list->insertItem(attr,text);
2041 
2042     // Force scrollbar to bottom
2043     // XXX list->setTopItem(list->count());
2044 
2045     if ( map )
2046 	map->putMessage(attr, text);
2047 }
2048 
2049 
2050 
NetHackQtLabelledIcon(QWidget * parent,const char * l)2051 NetHackQtLabelledIcon::NetHackQtLabelledIcon(QWidget* parent, const char* l) :
2052     QWidget(parent),
2053     low_is_good(FALSE),
2054     prev_value(-123),
2055     turn_count(-1),
2056     label(new QLabel(l,this)),
2057     icon(0)
2058 {
2059     initHighlight();
2060 }
NetHackQtLabelledIcon(QWidget * parent,const char * l,const QPixmap & i)2061 NetHackQtLabelledIcon::NetHackQtLabelledIcon(QWidget* parent, const char* l, const QPixmap& i) :
2062     QWidget(parent),
2063     low_is_good(FALSE),
2064     prev_value(-123),
2065     turn_count(-1),
2066     label(new QLabel(l,this)),
2067     icon(new QLabel(this))
2068 {
2069     setIcon(i);
2070     initHighlight();
2071 }
initHighlight()2072 void NetHackQtLabelledIcon::initHighlight()
2073 {
2074     const QPalette& pal=palette();
2075     const QColorGroup& pa=pal.normal();
2076     //QColorGroup good(white,darkGreen,pa.light(),pa.dark(),pa.mid(),white,pa.base());
2077     QColorGroup good(black,green,pa.light(),pa.dark(),pa.mid(),black,pa.base());
2078     QColorGroup bad(white,red,pa.light(),pa.dark(),pa.mid(),white,pa.base());
2079     hl_good=pal.copy();
2080     hl_good.setNormal(good);
2081     hl_good.setActive(good);
2082     hl_bad=pal.copy();
2083     hl_bad.setNormal(bad);
2084     hl_bad.setActive(bad);
2085 }
2086 
setLabel(const char * t,bool lower)2087 void NetHackQtLabelledIcon::setLabel(const char* t, bool lower)
2088 {
2089     if (!label) {
2090 	label=new QLabel(this);
2091 	label->setFont(font());
2092 	resizeEvent(0);
2093     }
2094     if (0!=strcmp(label->text(),t)) {
2095 	label->setText(t);
2096 	highlight(lower==low_is_good ? hl_good : hl_bad);
2097     }
2098 }
setLabel(const char * t,long v,long cv,const char * tail)2099 void NetHackQtLabelledIcon::setLabel(const char* t, long v, long cv, const char* tail)
2100 {
2101     char buf[BUFSZ];
2102     if (v==NoNum) {
2103 	Sprintf(buf,"%s%s",t,tail);
2104     } else {
2105 	Sprintf(buf,"%s%ld%s",t,v,tail);
2106     }
2107     setLabel(buf,cv<prev_value);
2108     prev_value=cv;
2109 }
setLabel(const char * t,long v,const char * tail)2110 void NetHackQtLabelledIcon::setLabel(const char* t, long v, const char* tail)
2111 {
2112     setLabel(t,v,v,tail);
2113 }
setIcon(const QPixmap & i)2114 void NetHackQtLabelledIcon::setIcon(const QPixmap& i)
2115 {
2116     if (icon) icon->setPixmap(i);
2117     else { icon=new QLabel(this); icon->setPixmap(i); resizeEvent(0); }
2118     icon->resize(i.width(),i.height());
2119 }
setFont(const QFont & f)2120 void NetHackQtLabelledIcon::setFont(const QFont& f)
2121 {
2122     QWidget::setFont(f);
2123     if (label) label->setFont(f);
2124 }
show()2125 void NetHackQtLabelledIcon::show()
2126 {
2127 #if QT_VERSION >= 300
2128     if (isHidden())
2129 #else
2130     if (!isVisible())
2131 #endif
2132 	highlight(hl_bad);
2133     QWidget::show();
2134 }
highlightWhenChanging()2135 void NetHackQtLabelledIcon::highlightWhenChanging()
2136 {
2137     turn_count=0;
2138 }
lowIsGood()2139 void NetHackQtLabelledIcon::lowIsGood()
2140 {
2141     low_is_good=TRUE;
2142 }
dissipateHighlight()2143 void NetHackQtLabelledIcon::dissipateHighlight()
2144 {
2145     if (turn_count>0) {
2146 	turn_count--;
2147 	if (!turn_count)
2148 	    unhighlight();
2149     }
2150 }
highlight(const QPalette & hl)2151 void NetHackQtLabelledIcon::highlight(const QPalette& hl)
2152 {
2153     if (label) { // Surely it is?!
2154 	if (turn_count>=0) {
2155 	    label->setPalette(hl);
2156 	    turn_count=4;
2157 	    // `4' includes this turn, so dissipates after
2158 	    // 3 more keypresses.
2159 	} else {
2160 	    label->setPalette(palette());
2161 	}
2162     }
2163 }
unhighlight()2164 void NetHackQtLabelledIcon::unhighlight()
2165 {
2166     if (label) { // Surely it is?!
2167 	label->setPalette(palette());
2168     }
2169 }
resizeEvent(QResizeEvent *)2170 void NetHackQtLabelledIcon::resizeEvent(QResizeEvent*)
2171 {
2172     setAlignments();
2173 
2174     //int labw=label ? label->fontMetrics().width(label->text()) : 0;
2175     int labh=label ? label->fontMetrics().height() : 0;
2176     int icoh=icon ? icon->height() : 0;
2177     int h=icoh+labh;
2178     int icoy=(h>height() ? height()-labh-icoh : height()/2-h/2);
2179     int laby=icoy+icoh;
2180     if (icon) {
2181 	icon->setGeometry(0,icoy,width(),icoh);
2182     }
2183     if (label) {
2184 	label->setGeometry(0,laby,width(),labh);
2185     }
2186 }
2187 
setAlignments()2188 void NetHackQtLabelledIcon::setAlignments()
2189 {
2190     if (label) label->setAlignment(AlignHCenter|AlignVCenter);
2191     if (icon) icon->setAlignment(AlignHCenter|AlignVCenter);
2192 }
2193 
2194 static void
tryload(QPixmap & pm,const char * fn)2195 tryload(QPixmap& pm, const char* fn)
2196 {
2197     if (!pm.load(fn)) {
2198 	QString msg;
2199 	msg.sprintf("Cannot load \"%s\"", fn);
2200 	QMessageBox::warning(0, "IO Error", msg);
2201     }
2202 }
2203 
NetHackQtStatusWindow()2204 NetHackQtStatusWindow::NetHackQtStatusWindow() :
2205     // Notes:
2206     //  Alignment needs -2 init value, because -1 is an alignment.
2207     //  Armor Class is an schar, so 256 is out of range.
2208     //  Blank value is 0 and should never change.
2209     name(this,"(name)"),
2210     dlevel(this,"(dlevel)"),
2211     str(this,"STR"),
2212     dex(this,"DEX"),
2213     con(this,"CON"),
2214     intel(this,"INT"),
2215     wis(this,"WIS"),
2216     cha(this,"CHA"),
2217     gold(this,"Gold"),
2218     hp(this,"Hit Points"),
2219     power(this,"Power"),
2220     ac(this,"Armour Class"),
2221     level(this,"Level"),
2222     exp(this,"Experience"),
2223     align(this,"Alignment"),
2224     time(this,"Time"),
2225     score(this,"Score"),
2226     hunger(this,""),
2227     confused(this,"Confused"),
2228     sick_fp(this,"Sick"),
2229     sick_il(this,"Ill"),
2230     blind(this,"Blind"),
2231     stunned(this,"Stunned"),
2232     hallu(this,"Hallu"),
2233     encumber(this,""),
2234     hline1(this),
2235     hline2(this),
2236     hline3(this),
2237     first_set(TRUE)
2238 {
2239     p_str = QPixmap(str_xpm);
2240     p_str = QPixmap(str_xpm);
2241     p_dex = QPixmap(dex_xpm);
2242     p_con = QPixmap(cns_xpm);
2243     p_int = QPixmap(int_xpm);
2244     p_wis = QPixmap(wis_xpm);
2245     p_cha = QPixmap(cha_xpm);
2246 
2247     p_chaotic = QPixmap(chaotic_xpm);
2248     p_neutral = QPixmap(neutral_xpm);
2249     p_lawful = QPixmap(lawful_xpm);
2250 
2251     p_satiated = QPixmap(satiated_xpm);
2252     p_hungry = QPixmap(hungry_xpm);
2253 
2254     p_confused = QPixmap(confused_xpm);
2255     p_sick_fp = QPixmap(sick_fp_xpm);
2256     p_sick_il = QPixmap(sick_il_xpm);
2257     p_blind = QPixmap(blind_xpm);
2258     p_stunned = QPixmap(stunned_xpm);
2259     p_hallu = QPixmap(hallu_xpm);
2260 
2261     p_encumber[0] = QPixmap(slt_enc_xpm);
2262     p_encumber[1] = QPixmap(mod_enc_xpm);
2263     p_encumber[2] = QPixmap(hvy_enc_xpm);
2264     p_encumber[3] = QPixmap(ext_enc_xpm);
2265     p_encumber[4] = QPixmap(ovr_enc_xpm);
2266 
2267     str.setIcon(p_str);
2268     dex.setIcon(p_dex);
2269     con.setIcon(p_con);
2270     intel.setIcon(p_int);
2271     wis.setIcon(p_wis);
2272     cha.setIcon(p_cha);
2273 
2274     align.setIcon(p_neutral);
2275     hunger.setIcon(p_hungry);
2276 
2277     confused.setIcon(p_confused);
2278     sick_fp.setIcon(p_sick_fp);
2279     sick_il.setIcon(p_sick_il);
2280     blind.setIcon(p_blind);
2281     stunned.setIcon(p_stunned);
2282     hallu.setIcon(p_hallu);
2283 
2284     encumber.setIcon(p_encumber[0]);
2285 
2286     hline1.setFrameStyle(QFrame::HLine|QFrame::Sunken);
2287     hline2.setFrameStyle(QFrame::HLine|QFrame::Sunken);
2288     hline3.setFrameStyle(QFrame::HLine|QFrame::Sunken);
2289     hline1.setLineWidth(1);
2290     hline2.setLineWidth(1);
2291     hline3.setLineWidth(1);
2292 
2293     connect(qt_settings,SIGNAL(fontChanged()),this,SLOT(doUpdate()));
2294     doUpdate();
2295 }
2296 
doUpdate()2297 void NetHackQtStatusWindow::doUpdate()
2298 {
2299     const QFont& large=qt_settings->largeFont();
2300     name.setFont(large);
2301     dlevel.setFont(large);
2302 
2303     const QFont& normal=qt_settings->normalFont();
2304     str.setFont(normal);
2305     dex.setFont(normal);
2306     con.setFont(normal);
2307     intel.setFont(normal);
2308     wis.setFont(normal);
2309     cha.setFont(normal);
2310     gold.setFont(normal);
2311     hp.setFont(normal);
2312     power.setFont(normal);
2313     ac.setFont(normal);
2314     level.setFont(normal);
2315     exp.setFont(normal);
2316     align.setFont(normal);
2317     time.setFont(normal);
2318     score.setFont(normal);
2319     hunger.setFont(normal);
2320     confused.setFont(normal);
2321     sick_fp.setFont(normal);
2322     sick_il.setFont(normal);
2323     blind.setFont(normal);
2324     stunned.setFont(normal);
2325     hallu.setFont(normal);
2326     encumber.setFont(normal);
2327 
2328     updateStats();
2329 }
2330 
Widget()2331 QWidget* NetHackQtStatusWindow::Widget() { return this; }
2332 
Clear()2333 void NetHackQtStatusWindow::Clear()
2334 {
2335 }
Display(bool block)2336 void NetHackQtStatusWindow::Display(bool block)
2337 {
2338 }
CursorTo(int,int y)2339 void NetHackQtStatusWindow::CursorTo(int,int y)
2340 {
2341     cursy=y;
2342 }
PutStr(int attr,const char * text)2343 void NetHackQtStatusWindow::PutStr(int attr, const char* text)
2344 {
2345     // do a complete update when line 0 is done (as per X11 fancy status)
2346     if (cursy==0) updateStats();
2347 }
2348 
resizeEvent(QResizeEvent *)2349 void NetHackQtStatusWindow::resizeEvent(QResizeEvent*)
2350 {
2351     const float SP_name=0.13; //     <Name> the <Class> (large)
2352     const float SP_dlev=0.13; //   Level 3 in The Dungeons of Doom (large)
2353     const float SP_atr1=0.25; //  STR   DEX   CON   INT   WIS   CHA
2354     const float SP_hln1=0.02; // ---
2355     const float SP_atr2=0.09; //  Au    HP    PW    AC    LVL   EXP
2356     const float SP_hln2=0.02; // ---
2357     const float SP_time=0.09; //      time    score
2358     const float SP_hln3=0.02; // ---
2359     const float SP_stat=0.25; // Alignment, Poisoned, Hungry, Sick, etc.
2360 
2361     int h=height();
2362     int x=0,y=0;
2363 
2364     int iw; // Width of an item across line
2365     int lh; // Height of a line of values
2366 
2367     lh=int(h*SP_name);
2368     name.setGeometry(0,0,width(),lh); y+=lh;
2369     lh=int(h*SP_dlev);
2370     dlevel.setGeometry(0,y,width(),lh); y+=lh;
2371 
2372     lh=int(h*SP_hln1);
2373     hline1.setGeometry(0,y,width(),lh); y+=lh;
2374 
2375     lh=int(h*SP_atr1);
2376     iw=width()/6;
2377     str.setGeometry(x,y,iw,lh); x+=iw;
2378     dex.setGeometry(x,y,iw,lh); x+=iw;
2379     con.setGeometry(x,y,iw,lh); x+=iw;
2380     intel.setGeometry(x,y,iw,lh); x+=iw;
2381     wis.setGeometry(x,y,iw,lh); x+=iw;
2382     cha.setGeometry(x,y,iw,lh); x+=iw;
2383     x=0; y+=lh;
2384 
2385     lh=int(h*SP_hln2);
2386     hline2.setGeometry(0,y,width(),lh); y+=lh;
2387 
2388     lh=int(h*SP_atr2);
2389     iw=width()/6;
2390     gold.setGeometry(x,y,iw,lh); x+=iw;
2391     hp.setGeometry(x,y,iw,lh); x+=iw;
2392     power.setGeometry(x,y,iw,lh); x+=iw;
2393     ac.setGeometry(x,y,iw,lh); x+=iw;
2394     level.setGeometry(x,y,iw,lh); x+=iw;
2395     exp.setGeometry(x,y,iw,lh); x+=iw;
2396     x=0; y+=lh;
2397 
2398     lh=int(h*SP_hln3);
2399     hline3.setGeometry(0,y,width(),lh); y+=lh;
2400 
2401     lh=int(h*SP_time);
2402     iw=width()/3; x+=iw/2;
2403     time.setGeometry(x,y,iw,lh); x+=iw;
2404     score.setGeometry(x,y,iw,lh); x+=iw;
2405     x=0; y+=lh;
2406 
2407     lh=int(h*SP_stat);
2408     iw=width()/9;
2409     align.setGeometry(x,y,iw,lh); x+=iw;
2410     hunger.setGeometry(x,y,iw,lh); x+=iw;
2411     confused.setGeometry(x,y,iw,lh); x+=iw;
2412     sick_fp.setGeometry(x,y,iw,lh); x+=iw;
2413     sick_il.setGeometry(x,y,iw,lh); x+=iw;
2414     blind.setGeometry(x,y,iw,lh); x+=iw;
2415     stunned.setGeometry(x,y,iw,lh); x+=iw;
2416     hallu.setGeometry(x,y,iw,lh); x+=iw;
2417     encumber.setGeometry(x,y,iw,lh); x+=iw;
2418     x=0; y+=lh;
2419 }
2420 
2421 
2422 /*
2423  * Set all widget values to a null string.  This is used after all spacings
2424  * have been calculated so that when the window is popped up we don't get all
2425  * kinds of funny values being displayed.
2426  */
nullOut()2427 void NetHackQtStatusWindow::nullOut()
2428 {
2429 }
2430 
fadeHighlighting()2431 void NetHackQtStatusWindow::fadeHighlighting()
2432 {
2433     name.dissipateHighlight();
2434     dlevel.dissipateHighlight();
2435 
2436     str.dissipateHighlight();
2437     dex.dissipateHighlight();
2438     con.dissipateHighlight();
2439     intel.dissipateHighlight();
2440     wis.dissipateHighlight();
2441     cha.dissipateHighlight();
2442 
2443     gold.dissipateHighlight();
2444     hp.dissipateHighlight();
2445     power.dissipateHighlight();
2446     ac.dissipateHighlight();
2447     level.dissipateHighlight();
2448     exp.dissipateHighlight();
2449     align.dissipateHighlight();
2450 
2451     time.dissipateHighlight();
2452     score.dissipateHighlight();
2453 
2454     hunger.dissipateHighlight();
2455     confused.dissipateHighlight();
2456     sick_fp.dissipateHighlight();
2457     sick_il.dissipateHighlight();
2458     blind.dissipateHighlight();
2459     stunned.dissipateHighlight();
2460     hallu.dissipateHighlight();
2461     encumber.dissipateHighlight();
2462 }
2463 
2464 /*
2465  * Update the displayed status.  The current code in botl.c updates
2466  * two lines of information.  Both lines are always updated one after
2467  * the other.  So only do our update when we update the second line.
2468  *
2469  * Information on the first line:
2470  *    name, attributes, alignment, score
2471  *
2472  * Information on the second line:
2473  *    dlvl, gold, hp, power, ac, {level & exp or HD **}
2474  *    status (hunger, conf, halu, stun, sick, blind), time, encumbrance
2475  *
2476  * [**] HD is shown instead of level and exp if mtimedone is non-zero.
2477  */
updateStats()2478 void NetHackQtStatusWindow::updateStats()
2479 {
2480     if (!parentWidget()) return;
2481 
2482     char buf[BUFSZ];
2483 
2484     if (cursy != 0) return;    /* do a complete update when line 0 is done */
2485 
2486     if (ACURR(A_STR) > 118) {
2487 	Sprintf(buf,"STR:%d",ACURR(A_STR)-100);
2488     } else if (ACURR(A_STR)==118) {
2489 	Sprintf(buf,"STR:18/**");
2490     } else if(ACURR(A_STR) > 18) {
2491 	Sprintf(buf,"STR:18/%02d",ACURR(A_STR)-18);
2492     } else {
2493 	Sprintf(buf,"STR:%d",ACURR(A_STR));
2494     }
2495     str.setLabel(buf,NetHackQtLabelledIcon::NoNum,ACURR(A_STR));
2496 
2497     dex.setLabel("DEX:",(long)ACURR(A_DEX));
2498     con.setLabel("CON:",(long)ACURR(A_CON));
2499     intel.setLabel("INT:",(long)ACURR(A_INT));
2500     wis.setLabel("WIS:",(long)ACURR(A_WIS));
2501     cha.setLabel("CHA:",(long)ACURR(A_CHA));
2502     const char* hung=hu_stat[u.uhs];
2503     if (hung[0]==' ') {
2504 	hunger.hide();
2505     } else {
2506 	hunger.setIcon(u.uhs ? p_hungry : p_satiated);
2507 	hunger.setLabel(hung);
2508 	hunger.show();
2509     }
2510     if (Confusion) confused.show(); else confused.hide();
2511     if (Sick) {
2512 	if (u.usick_type & SICK_VOMITABLE) {
2513 	    sick_fp.show();
2514 	} else {
2515 	    sick_fp.hide();
2516 	}
2517 	if (u.usick_type & SICK_NONVOMITABLE) {
2518 	    sick_il.show();
2519 	} else {
2520 	    sick_il.hide();
2521 	}
2522     } else {
2523 	sick_fp.hide();
2524 	sick_il.hide();
2525     }
2526     if (Blind) blind.show(); else blind.hide();
2527     if (Stunned) stunned.show(); else stunned.hide();
2528     if (Hallucination) hallu.show(); else hallu.hide();
2529     const char* enc=enc_stat[near_capacity()];
2530     if (enc[0]==' ' || !enc[0]) {
2531 	encumber.hide();
2532     } else {
2533 	encumber.setIcon(p_encumber[near_capacity()-1]);
2534 	encumber.setLabel(enc);
2535 	encumber.show();
2536     }
2537     Strcpy(buf, plname);
2538     if ('a' <= buf[0] && buf[0] <= 'z') buf[0] += 'A'-'a';
2539     Strcat(buf, " the ");
2540     if (u.mtimedone) {
2541 	char mname[BUFSZ];
2542 	int k = 0;
2543 
2544 	Strcpy(mname, mons[u.umonnum].mname);
2545 	while(mname[k] != 0) {
2546 	    if ((k == 0 || (k > 0 && mname[k-1] == ' '))
2547 	     && 'a' <= mname[k] && mname[k] <= 'z')
2548 	    {
2549 		mname[k] += 'A' - 'a';
2550 	    }
2551 	    k++;
2552 	}
2553 	Strcat(buf, mname);
2554     } else {
2555 	Strcat(buf, rank_of(u.ulevel, pl_character[0], ::flags.female));
2556     }
2557     name.setLabel(buf,NetHackQtLabelledIcon::NoNum,u.ulevel);
2558 
2559     if (describe_level(buf)) {
2560 	dlevel.setLabel(buf,(bool)TRUE);
2561     } else {
2562 	Sprintf(buf, "%s, level ", dungeons[u.uz.dnum].dname);
2563 	dlevel.setLabel(buf,(long)depth(&u.uz));
2564     }
2565 
2566     gold.setLabel("Au:", money_cnt(invent));
2567     if (u.mtimedone) {
2568 	// You're a monster!
2569 
2570 	Sprintf(buf, "/%d", u.mhmax);
2571 	hp.setLabel("HP:",u.mh  > 0 ? u.mh  : 0,buf);
2572 	level.setLabel("HD:",(long)mons[u.umonnum].mlevel);
2573     } else {
2574 	// You're normal.
2575 
2576 	Sprintf(buf, "/%d", u.uhpmax);
2577 	hp.setLabel("HP:",u.uhp > 0 ? u.uhp : 0,buf);
2578 	level.setLabel("Level:",(long)u.ulevel);
2579     }
2580     Sprintf(buf, "/%d", u.uenmax);
2581     power.setLabel("Pow:",u.uen,buf);
2582     ac.setLabel("AC:",(long)u.uac);
2583     if (::flags.showexp) {
2584 	exp.setLabel("Exp:",(long)u.uexp);
2585     } else
2586     {
2587 	exp.setLabel("");
2588     }
2589     if (u.ualign.type==A_CHAOTIC) {
2590 	align.setIcon(p_chaotic);
2591 	align.setLabel("Chaotic");
2592     } else if (u.ualign.type==A_NEUTRAL) {
2593 	align.setIcon(p_neutral);
2594 	align.setLabel("Neutral");
2595     } else {
2596 	align.setIcon(p_lawful);
2597 	align.setLabel("Lawful");
2598     }
2599 
2600     if (::flags.time) time.setLabel("Time:",(long)moves);
2601     else time.setLabel("");
2602 #ifdef SCORE_ON_BOTL
2603     if (::flags.showscore) {
2604 	score.setLabel("Score:",(long)botl_score());
2605     } else
2606 #endif
2607     {
2608 	score.setLabel("");
2609     }
2610 
2611     if (first_set)
2612     {
2613 	first_set=FALSE;
2614 
2615 	name.highlightWhenChanging();
2616 	dlevel.highlightWhenChanging();
2617 
2618 	str.highlightWhenChanging();
2619 	dex.highlightWhenChanging();
2620 	con.highlightWhenChanging();
2621 	intel.highlightWhenChanging();
2622 	wis.highlightWhenChanging();
2623 	cha.highlightWhenChanging();
2624 
2625 	gold.highlightWhenChanging();
2626 	hp.highlightWhenChanging();
2627 	power.highlightWhenChanging();
2628 	ac.highlightWhenChanging(); ac.lowIsGood();
2629 	level.highlightWhenChanging();
2630 	exp.highlightWhenChanging();
2631 	align.highlightWhenChanging();
2632 
2633 	//time.highlightWhenChanging();
2634 	score.highlightWhenChanging();
2635 
2636 	hunger.highlightWhenChanging();
2637 	confused.highlightWhenChanging();
2638 	sick_fp.highlightWhenChanging();
2639 	sick_il.highlightWhenChanging();
2640 	blind.highlightWhenChanging();
2641 	stunned.highlightWhenChanging();
2642 	hallu.highlightWhenChanging();
2643 	encumber.highlightWhenChanging();
2644     }
2645 }
2646 
2647 /*
2648  * Turn off hilighted status values after a certain amount of turns.
2649  */
checkTurnEvents()2650 void NetHackQtStatusWindow::checkTurnEvents()
2651 {
2652 }
2653 
2654 
2655 
NetHackQtMenuDialog()2656 NetHackQtMenuDialog::NetHackQtMenuDialog() :
2657     QDialog(qApp->mainWidget(),0,FALSE)
2658 {
2659 }
2660 
resizeEvent(QResizeEvent *)2661 void NetHackQtMenuDialog::resizeEvent(QResizeEvent*)
2662 {
2663     emit Resized();
2664 }
2665 
Accept()2666 void NetHackQtMenuDialog::Accept()
2667 {
2668     accept();
2669 }
2670 
Reject()2671 void NetHackQtMenuDialog::Reject()
2672 {
2673     reject();
2674 }
2675 
SetResult(int r)2676 void NetHackQtMenuDialog::SetResult(int r)
2677 {
2678     setResult(r);
2679 }
2680 
done(int i)2681 void NetHackQtMenuDialog::done(int i)
2682 {
2683     setResult(i);
2684     qApp->exit_loop();
2685 }
2686 
2687 // Table view columns:
2688 //
2689 // [pick-count] [accel] [glyph] [string]
2690 //
2691 // Maybe accel should be near string.  We'll see.
2692 // pick-count normally blank.
2693 //   double-clicking or click-on-count gives pop-up entry
2694 // string is green when selected
2695 //
NetHackQtMenuWindow(NetHackQtKeyBuffer & ks)2696 NetHackQtMenuWindow::NetHackQtMenuWindow(NetHackQtKeyBuffer& ks) :
2697     QTableView(),
2698     keysource(ks),
2699     dialog(new NetHackQtMenuDialog()),
2700     prompt(0),
2701     pressed(-1)
2702 {
2703     setNumCols(4);
2704     setCellHeight(QMAX(qt_settings->glyphs().height()+1,fontMetrics().height()));
2705     setBackgroundColor(lightGray);
2706     setFrameStyle(Panel|Sunken);
2707     setLineWidth(2);
2708 
2709     ok=new QPushButton("Ok",dialog);
2710     connect(ok,SIGNAL(clicked()),dialog,SLOT(accept()));
2711 
2712     cancel=new QPushButton("Cancel",dialog);
2713     connect(cancel,SIGNAL(clicked()),dialog,SLOT(reject()));
2714 
2715     all=new QPushButton("All",dialog);
2716     connect(all,SIGNAL(clicked()),this,SLOT(All()));
2717 
2718     none=new QPushButton("None",dialog);
2719     connect(none,SIGNAL(clicked()),this,SLOT(ChooseNone()));
2720 
2721     invert=new QPushButton("Invert",dialog);
2722     connect(invert,SIGNAL(clicked()),this,SLOT(Invert()));
2723 
2724     search=new QPushButton("Search",dialog);
2725     connect(search,SIGNAL(clicked()),this,SLOT(Search()));
2726 
2727     QPoint pos(0,ok->height());
2728     recreate(dialog,0,pos);
2729     prompt.recreate(dialog,0,pos);
2730 
2731     setBackgroundColor(lightGray);
2732 
2733     connect(dialog,SIGNAL(Resized()),this,SLOT(Layout()));
2734 
2735     setTableFlags(Tbl_autoHScrollBar|Tbl_autoVScrollBar
2736 	    |Tbl_smoothScrolling|Tbl_clipCellPainting);
2737     setFocusPolicy(StrongFocus);
2738 }
2739 
~NetHackQtMenuWindow()2740 NetHackQtMenuWindow::~NetHackQtMenuWindow()
2741 {
2742     // Remove from dialog before we destruct it
2743     recreate(0,0,QPoint(0,0));
2744     delete dialog;
2745 }
2746 
focusInEvent(QFocusEvent *)2747 void NetHackQtMenuWindow::focusInEvent(QFocusEvent *)
2748 {
2749     // Don't repaint at all, since nothing is using the focus colour
2750 }
focusOutEvent(QFocusEvent *)2751 void NetHackQtMenuWindow::focusOutEvent(QFocusEvent *)
2752 {
2753     // Don't repaint at all, since nothing is using the focus colour
2754 }
2755 
cellWidth(int col)2756 int NetHackQtMenuWindow::cellWidth(int col)
2757 {
2758     switch (col) {
2759      case 0:
2760 	return fontMetrics().width("All ");
2761     break; case 1:
2762 	return fontMetrics().width(" m ");
2763     break; case 2:
2764 	return qt_settings->glyphs().width();
2765     break; case 3:
2766 	return str_width;
2767     }
2768     impossible("Extra column (#%d) in MenuWindow",col);
2769     return 0;
2770 }
2771 
Widget()2772 QWidget* NetHackQtMenuWindow::Widget() { return dialog; }
2773 
StartMenu()2774 void NetHackQtMenuWindow::StartMenu()
2775 {
2776     setNumRows((itemcount=0));
2777     str_width=200;
2778     str_fixed=FALSE;
2779     next_accel=0;
2780     has_glyphs=FALSE;
2781 }
2782 
MenuItem()2783 NetHackQtMenuWindow::MenuItem::MenuItem() :
2784     str(0)
2785 {
2786 }
2787 
~MenuItem()2788 NetHackQtMenuWindow::MenuItem::~MenuItem()
2789 {
2790     if (str) free((void*)str);
2791 }
2792 
2793 #define STR_MARGIN 4
2794 
AddMenu(int glyph,const ANY_P * identifier,char ch,char gch,int attr,const char * str,bool presel)2795 void NetHackQtMenuWindow::AddMenu(int glyph, const ANY_P* identifier,
2796 	char ch, char gch, int attr, const char* str, bool presel)
2797 {
2798     if (!ch && identifier->a_void!=0) {
2799 	// Supply a keyboard accelerator.  Limited supply.
2800 	static char accel[]="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
2801 	if (accel[next_accel]) {
2802 	    ch=accel[next_accel++];
2803 	}
2804     }
2805 
2806     if ((int)item.size() < itemcount+1) {
2807 	item.resize(itemcount*4+10);
2808     }
2809     item[itemcount].glyph=glyph;
2810     item[itemcount].identifier=*identifier;
2811     item[itemcount].ch=ch;
2812     item[itemcount].attr=attr;
2813     item[itemcount].str=strdup(str);
2814     item[itemcount].selected=presel;
2815     item[itemcount].count=-1;
2816     ++itemcount;
2817 
2818     str_fixed=str_fixed || strstr(str,"     ");
2819     if (glyph!=NO_GLYPH) has_glyphs=TRUE;
2820 }
EndMenu(const char * p)2821 void NetHackQtMenuWindow::EndMenu(const char* p)
2822 {
2823     prompt.setText(p ? p : "");
2824 }
Layout()2825 void NetHackQtMenuWindow::Layout()
2826 {
2827     int butw=totalWidth()/6; // 6 buttons
2828     int buth=fontMetrics().height()+8; // 8 for spacing & mitres
2829     int prompth=(prompt.text().isNull() ? 0 : buth);
2830 
2831     prompt.setGeometry(6,buth,dialog->width()-6,prompth);
2832     int h=dialog->height()-buth-prompth;
2833     setGeometry(0,buth+prompth, dialog->width(), h);
2834 
2835     // Below, we take care to use up full width
2836     int x=0;
2837     ok->setGeometry(x,0,butw,buth); x+=butw; butw=(dialog->width()-x)/5;
2838     cancel->setGeometry(x,0,butw,buth); x+=butw; butw=(dialog->width()-x)/4;
2839     all->setGeometry(x,0,butw,buth); x+=butw; butw=(dialog->width()-x)/3;
2840     none->setGeometry(x,0,butw,buth); x+=butw; butw=(dialog->width()-x)/2;
2841     invert->setGeometry(x,0,butw,buth); x+=butw; butw=(dialog->width()-x)/1;
2842     search->setGeometry(x,0,butw,buth);
2843 }
2844 
SelectMenu(int h,MENU_ITEM_P ** menu_list)2845 int NetHackQtMenuWindow::SelectMenu(int h, MENU_ITEM_P **menu_list)
2846 {
2847     setFont(str_fixed ?
2848 	qt_settings->normalFixedFont() : qt_settings->normalFont());
2849 
2850     for (int i=0; i<itemcount; i++) {
2851 	str_width=QMAX(str_width,
2852 	    STR_MARGIN+fontMetrics().width(item[i].str));
2853     }
2854 
2855     setCellHeight(QMAX(qt_settings->glyphs().height()+1,fontMetrics().height()));
2856     setNumRows(itemcount);
2857 
2858     int buth=fontMetrics().height()+8; // 8 for spacing & mitres
2859 
2860     how=h;
2861 
2862     ok->setEnabled(how!=PICK_ONE);ok->setDefault(how!=PICK_ONE);
2863     cancel->setEnabled(TRUE);
2864     all->setEnabled(how==PICK_ANY);
2865     none->setEnabled(how==PICK_ANY);
2866     invert->setEnabled(how==PICK_ANY);
2867     search->setEnabled(how!=PICK_NONE);
2868 
2869     dialog->SetResult(-1);
2870 
2871     // 20 allows for scrollbar or spacing
2872     // 4 for frame borders
2873     int mh = QApplication::desktop()->height()*3/5;
2874     if ( qt_compact_mode && totalHeight() > mh ) {
2875 	// big, so make it fill
2876 	dialog->showMaximized();
2877     } else {
2878 	dialog->resize(totalWidth()+20,
2879 	    QMIN(totalHeight(), mh)+buth+4+(prompt.text().isNull() ? 0 : buth));
2880 	if ( dialog->width() > QApplication::desktop()->width() )
2881 	    dialog->resize(QApplication::desktop()->width(),dialog->height()+16);
2882 	centerOnMain(dialog);
2883 	dialog->show();
2884     }
2885 
2886     setFocus();
2887     while (dialog->result()<0) {
2888 	// changed the defaults below to the values in wintype.h 000119 - azy
2889 	if (!keysource.Empty()) {
2890 	    char k=keysource.GetAscii();
2891 	    k=map_menu_cmd(k); /* added 000119 - azy */
2892 	    if (k=='\033')
2893 		dialog->Reject();
2894 	    else if (k=='\r' || k=='\n' || k==' ')
2895 		dialog->Accept();
2896 	    else if (k==MENU_SEARCH)
2897 		Search();
2898 	    else if (k==MENU_SELECT_ALL)
2899 		All();
2900 	    else if (k==MENU_INVERT_ALL)
2901 		Invert();
2902 	    else if (k==MENU_UNSELECT_ALL)
2903 		ChooseNone();
2904 	    else {
2905 		for (int i=0; i<itemcount; i++) {
2906 		    if (item[i].ch==k)
2907 			ToggleSelect(i);
2908 		}
2909 	    }
2910 	}
2911 	if (dialog->result()<0)
2912 	    qApp->enter_loop();
2913     }
2914     //if ( (nhid != WIN_INVEN || !iflags.perm_invent) ) // doesn't work yet
2915     {
2916 	dialog->hide();
2917     }
2918     int result=dialog->result();
2919 
2920     // Consume ^M (which QDialog steals for default button)
2921     while (!keysource.Empty() &&
2922 	(keysource.TopAscii()=='\n' || keysource.TopAscii()=='\r'))
2923 	keysource.GetAscii();
2924 
2925     *menu_list=0;
2926     if (how==PICK_NONE)
2927 	return result==0 ? -1 : 0;
2928 
2929     if (result>0) {
2930 	if (how==PICK_ONE) {
2931 	    int i;
2932 	    for (i=0; i<itemcount && !item[i].selected; i++)
2933 		;
2934 	    if (i<itemcount) {
2935 		*menu_list=(MENU_ITEM_P*)malloc(sizeof(MENU_ITEM_P));
2936 		(*menu_list)[0].item=item[i].identifier;
2937 		(*menu_list)[0].count=item[i].count;
2938 		return 1;
2939 	    } else {
2940 		return 0;
2941 	    }
2942 	} else {
2943 	    int count=0;
2944 	    for (int i=0; i<itemcount; i++)
2945 		if (item[i].selected) count++;
2946 	    if (count) {
2947 		*menu_list=(MENU_ITEM_P*)malloc(count*sizeof(MENU_ITEM_P));
2948 		int j=0;
2949 		for (int i=0; i<itemcount; i++) {
2950 		    if (item[i].selected) {
2951 			(*menu_list)[j].item=item[i].identifier;
2952 			(*menu_list)[j].count=item[i].count;
2953 			j++;
2954 		    }
2955 		}
2956 		return count;
2957 	    } else {
2958 		return 0;
2959 	    }
2960 	}
2961     } else {
2962 	return -1;
2963     }
2964 }
2965 
keyPressEvent(QKeyEvent * event)2966 void NetHackQtMenuWindow::keyPressEvent(QKeyEvent* event)
2967 {
2968     if (viewHeight() < totalHeight() && !(event->state()&ShiftButton)) {
2969 	if (event->key()==Key_Prior) {
2970 	    setYOffset(yOffset()-viewHeight());
2971 	} else if (event->key()==Key_Next) {
2972 	    setYOffset(yOffset()+viewHeight());
2973 	} else {
2974 	    event->ignore();
2975 	}
2976     } else {
2977 	event->ignore();
2978     }
2979 }
2980 
All()2981 void NetHackQtMenuWindow::All()
2982 {
2983     for (int i=0; i<itemcount; i++) {
2984 	if (!item[i].selected) ToggleSelect(i);
2985     }
2986 }
ChooseNone()2987 void NetHackQtMenuWindow::ChooseNone()
2988 {
2989     for (int i=0; i<itemcount; i++) {
2990 	if (item[i].selected) ToggleSelect(i);
2991     }
2992 }
2993 
Invert()2994 void NetHackQtMenuWindow::Invert()
2995 {
2996     for (int i=0; i<itemcount; i++) {
2997 	ToggleSelect(i);
2998     }
2999 }
3000 
Search()3001 void NetHackQtMenuWindow::Search()
3002 {
3003     NetHackQtStringRequestor requestor(keysource,"Search for:");
3004     char line[256];
3005     if (requestor.Get(line)) {
3006 	for (int i=0; i<itemcount; i++) {
3007 	    if (strstr(item[i].str,line))
3008 		ToggleSelect(i);
3009 	}
3010     }
3011 }
3012 
ToggleSelect(int i)3013 void NetHackQtMenuWindow::ToggleSelect(int i)
3014 {
3015     if (item[i].Selectable()) {
3016 	item[i].selected = !item[i].selected;
3017 	if ( !item[i].selected )
3018 	    item[i].count=-1;
3019 	updateCell(i,3);
3020 	if (how==PICK_ONE) {
3021 	    dialog->Accept();
3022 	}
3023     }
3024 }
3025 
3026 
paintCell(QPainter * painter,int row,int col)3027 void NetHackQtMenuWindow::paintCell(QPainter* painter, int row, int col)
3028 {
3029     // [pick-count] [accel] [glyph] [string]
3030 
3031     MenuItem& i = item[row];
3032 
3033     painter->setPen(black);
3034     painter->setFont(font());
3035 
3036     if (i.selected) {
3037 	painter->setPen(darkGreen);
3038     }
3039 
3040     switch (col) {
3041      case 0:
3042 	if ( i.ch || i.attr!=ATR_INVERSE ) {
3043 	    QString text;
3044 	    if ( i.selected && i.count == -1 ) {
3045 		if ( i.str[0]>='0' && i.str[0]<='9' )
3046 		    text = "All";
3047 		else
3048 		    text = "*";
3049 	    } else if ( i.count<0 ) {
3050 		text = "-";
3051 	    } else {
3052 		text.sprintf("%d",i.count);
3053 	    }
3054 	    painter->drawText(0,0,cellWidth(col),cellHeight(),
3055 	    AlignHCenter|AlignVCenter,text);
3056 	}
3057     break; case 1:
3058 	if ((signed char)i.ch >= 0) {
3059 	    char text[2]={i.ch,0};
3060 	    painter->drawText(0,0,cellWidth(col),cellHeight(),
3061 		AlignHCenter|AlignVCenter,text);
3062 	}
3063     break; case 2:
3064 	if (i.glyph!=NO_GLYPH) {
3065 	    // Centered in height
3066 	    int y=(cellHeight()-qt_settings->glyphs().height())/2;
3067 	    if (y<0) y=0;
3068 	    qt_settings->glyphs().drawGlyph(*painter, i.glyph, 0, y);
3069 	}
3070     break; case 3:
3071 	// XXX should qt_settings have ALL the various fonts
3072 	QFont newfont=font();
3073 
3074 	if (i.attr) {
3075 	    switch(i.attr) {
3076 	     case ATR_ULINE:
3077 		newfont.setUnderline(TRUE);
3078 	    break; case ATR_BOLD:
3079 		painter->setPen(red);
3080 	    break; case ATR_BLINK:
3081 		newfont.setItalic(TRUE);
3082 	    break; case ATR_INVERSE:
3083 		newfont=qt_settings->largeFont();
3084 		newfont.setWeight(QFont::Bold);
3085 
3086 		if (i.selected) {
3087 		    painter->setPen(blue);
3088 		} else {
3089 		    painter->setPen(darkBlue);
3090 		}
3091 	    }
3092 	}
3093 	painter->setFont(newfont);
3094 
3095 	painter->drawText(STR_MARGIN,0,cellWidth(col),cellHeight(),
3096 	    AlignLeft|AlignVCenter,i.str);
3097     }
3098 }
3099 
mousePressEvent(QMouseEvent * event)3100 void NetHackQtMenuWindow::mousePressEvent(QMouseEvent* event)
3101 {
3102     int col=findCol(event->pos().x());
3103     int row=findRow(event->pos().y());
3104 
3105     if (col<0 || row<0 || !item[row].Selectable()) return;
3106 
3107     if (how!=PICK_NONE) {
3108 	if (col==0) {
3109 	    // Changing count.
3110 	    NetHackQtStringRequestor requestor(keysource,"Count:");
3111 	    char buf[BUFSZ];
3112 
3113 	    if (item[row].count>0)
3114 		Sprintf(buf,"%d", item[row].count);
3115 	    else
3116 		Strcpy(buf, "");
3117 
3118 	    requestor.SetDefault(buf);
3119 	    if (requestor.Get(buf)) {
3120 		item[row].count=atoi(buf);
3121 		if (item[row].count==0) {
3122 		    item[row].count=-1;
3123 		    if (item[row].selected) ToggleSelect(row);
3124 		} else {
3125 		    if (!item[row].selected) ToggleSelect(row);
3126 		}
3127 		updateCell(row,0);
3128 	    }
3129 	} else {
3130 	    pressed=row;
3131 	    was_sel=item[row].selected;
3132 	    ToggleSelect(row);
3133 	    updateCell(row,0);
3134 	}
3135     }
3136 }
3137 
mouseReleaseEvent(QMouseEvent * event)3138 void NetHackQtMenuWindow::mouseReleaseEvent(QMouseEvent* event)
3139 {
3140     if (pressed>=0) {
3141 	int p=pressed;
3142 	pressed=-1;
3143 	updateCell(p,3);
3144     }
3145 }
3146 
mouseMoveEvent(QMouseEvent * event)3147 void NetHackQtMenuWindow::mouseMoveEvent(QMouseEvent* event)
3148 {
3149     if (pressed>=0) {
3150 	int col=findCol(event->pos().x());
3151 	int row=findRow(event->pos().y());
3152 
3153 	if (row>=0 && col>=0) {
3154 	    if (pressed!=row) {
3155 		// reset to initial state
3156 		if (item[pressed].selected!=was_sel)
3157 		    ToggleSelect(pressed);
3158 	    } else {
3159 		// reset to new state
3160 		if (item[pressed].selected==was_sel)
3161 		    ToggleSelect(pressed);
3162 	    }
3163 	}
3164     }
3165 }
3166 
3167 
3168 class NetHackQtTextListBox : public QListBox {
3169 public:
NetHackQtTextListBox(QWidget * parent)3170     NetHackQtTextListBox(QWidget* parent) : QListBox(parent) { }
3171 
TotalWidth()3172     int TotalWidth()
3173     {
3174 	doLayout();
3175 	return contentsWidth();
3176     }
TotalHeight()3177     int TotalHeight()
3178     {
3179 	doLayout();
3180 	return contentsHeight();
3181     }
3182 
setFont(const QFont & font)3183     virtual void setFont(const QFont &font)
3184     {
3185 	QListBox::setFont(font);
3186     }
keyPressEvent(QKeyEvent * e)3187     void keyPressEvent(QKeyEvent* e)
3188     {
3189 	QListBox::keyPressEvent(e);
3190     }
3191 };
3192 
3193 
3194 QPixmap* NetHackQtRIP::pixmap=0;
3195 
NetHackQtRIP(QWidget * parent)3196 NetHackQtRIP::NetHackQtRIP(QWidget* parent) :
3197     QWidget(parent)
3198 {
3199     if (!pixmap) {
3200 	pixmap=new QPixmap;
3201 	tryload(*pixmap, "rip.xpm");
3202     }
3203     riplines=0;
3204     resize(pixmap->width(),pixmap->height());
3205     setFont(QFont("times",12)); // XXX may need to be configurable
3206 }
3207 
setLines(char ** l,int n)3208 void NetHackQtRIP::setLines(char** l, int n)
3209 {
3210     line=l;
3211     riplines=n;
3212 }
3213 
sizeHint() const3214 QSize NetHackQtRIP::sizeHint() const
3215 {
3216     return pixmap->size();
3217 }
3218 
paintEvent(QPaintEvent * event)3219 void NetHackQtRIP::paintEvent(QPaintEvent* event)
3220 {
3221     if ( riplines ) {
3222 	int pix_x=(width()-pixmap->width())/2;
3223 	int pix_y=(height()-pixmap->height())/2;
3224 
3225 	// XXX positions based on RIP image
3226 	int rip_text_x=pix_x+156;
3227 	int rip_text_y=pix_y+67;
3228 	int rip_text_h=94/riplines;
3229 
3230 	QPainter painter;
3231 	painter.begin(this);
3232 	painter.drawPixmap(pix_x,pix_y,*pixmap);
3233 	for (int i=0; i<riplines; i++) {
3234 	    painter.drawText(rip_text_x-i/2,rip_text_y+i*rip_text_h,
3235 		1,1,DontClip|AlignHCenter,line[i]);
3236 	}
3237 	painter.end();
3238     }
3239 }
3240 
NetHackQtTextWindow(NetHackQtKeyBuffer & ks)3241 NetHackQtTextWindow::NetHackQtTextWindow(NetHackQtKeyBuffer& ks) :
3242     QDialog(qApp->mainWidget(),0,FALSE),
3243     keysource(ks),
3244     use_rip(FALSE),
3245     str_fixed(FALSE),
3246     ok("Dismiss",this),
3247     search("Search",this),
3248     lines(new NetHackQtTextListBox(this)),
3249     rip(this)
3250 {
3251     ok.setDefault(TRUE);
3252     connect(&ok,SIGNAL(clicked()),this,SLOT(accept()));
3253     connect(&search,SIGNAL(clicked()),this,SLOT(Search()));
3254     connect(qt_settings,SIGNAL(fontChanged()),this,SLOT(doUpdate()));
3255 
3256     QVBoxLayout* vb = new QVBoxLayout(this);
3257     vb->addWidget(&rip);
3258     QHBoxLayout* hb = new QHBoxLayout(vb);
3259     hb->addWidget(&ok);
3260     hb->addWidget(&search);
3261     vb->addWidget(lines);
3262 }
3263 
doUpdate()3264 void NetHackQtTextWindow::doUpdate()
3265 {
3266     update();
3267 }
3268 
3269 
~NetHackQtTextWindow()3270 NetHackQtTextWindow::~NetHackQtTextWindow()
3271 {
3272 
3273 }
3274 
Widget()3275 QWidget* NetHackQtTextWindow::Widget()
3276 {
3277     return this;
3278 }
3279 
Destroy()3280 bool NetHackQtTextWindow::Destroy()
3281 {
3282     return !isVisible();
3283 }
3284 
UseRIP(int how,time_t when)3285 void NetHackQtTextWindow::UseRIP(int how, time_t when)
3286 {
3287 // Code from X11 windowport
3288 #define STONE_LINE_LEN 16    /* # chars that fit on one line */
3289 #define NAME_LINE 0	/* line # for player name */
3290 #define GOLD_LINE 1	/* line # for amount of gold */
3291 #define DEATH_LINE 2	/* line # for death description */
3292 #define YEAR_LINE 6	/* line # for year */
3293 
3294 static char** rip_line=0;
3295     if (!rip_line) {
3296 	rip_line=new char*[YEAR_LINE+1];
3297 	for (int i=0; i<YEAR_LINE+1; i++) {
3298 	    rip_line[i]=new char[STONE_LINE_LEN+1];
3299 	}
3300     }
3301 
3302     /* Follows same algorithm as genl_outrip() */
3303 
3304     char buf[BUFSZ];
3305     char *dpx;
3306     int line;
3307     long year;
3308 
3309     /* Put name on stone */
3310     Sprintf(rip_line[NAME_LINE], "%s", plname);
3311 
3312     /* Put $ on stone */
3313     Sprintf(rip_line[GOLD_LINE], "%ld Au", done_money);
3314 
3315     /* Put together death description */
3316     formatkiller(buf, sizeof buf, how, FALSE);
3317 
3318     /* Put death type on stone */
3319     for (line=DEATH_LINE, dpx = buf; line<YEAR_LINE; line++) {
3320 	register int i,i0;
3321 	char tmpchar;
3322 
3323 	if ( (i0=strlen(dpx)) > STONE_LINE_LEN) {
3324 	    for(i = STONE_LINE_LEN;
3325 		((i0 > STONE_LINE_LEN) && i); i--)
3326 		if(dpx[i] == ' ') i0 = i;
3327 	    if(!i) i0 = STONE_LINE_LEN;
3328 	}
3329 	tmpchar = dpx[i0];
3330 	dpx[i0] = 0;
3331 	strcpy(rip_line[line], dpx);
3332 	if (tmpchar != ' ') {
3333 	    dpx[i0] = tmpchar;
3334 	    dpx= &dpx[i0];
3335 	} else  dpx= &dpx[i0+1];
3336     }
3337 
3338     /* Put year on stone */
3339     year = yyyymmdd(when) / 10000L;
3340     Sprintf(rip_line[YEAR_LINE], "%4ld", year);
3341 
3342     rip.setLines(rip_line,YEAR_LINE+1);
3343 
3344     use_rip=TRUE;
3345 }
3346 
Clear()3347 void NetHackQtTextWindow::Clear()
3348 {
3349     lines->clear();
3350     use_rip=FALSE;
3351     str_fixed=FALSE;
3352 }
3353 
Display(bool block)3354 void NetHackQtTextWindow::Display(bool block)
3355 {
3356     if (str_fixed) {
3357 	lines->setFont(qt_settings->normalFixedFont());
3358     } else {
3359 	lines->setFont(qt_settings->normalFont());
3360     }
3361 
3362     int h=0;
3363     if (use_rip) {
3364 	h+=rip.height();
3365 	ok.hide();
3366 	search.hide();
3367 	rip.show();
3368     } else {
3369 	h+=ok.height()*2;
3370 	ok.show();
3371 	search.show();
3372 	rip.hide();
3373     }
3374     int mh = QApplication::desktop()->height()*3/5;
3375     if ( qt_compact_mode && (lines->TotalHeight() > mh || use_rip) ) {
3376 	// big, so make it fill
3377 	showMaximized();
3378     } else {
3379 	resize(QMAX(use_rip ? rip.width() : 200,
3380 		lines->TotalWidth()+24),
3381 	    QMIN(mh, lines->TotalHeight()+h));
3382 	centerOnMain(this);
3383 	show();
3384     }
3385     if (block) {
3386 	setResult(-1);
3387 	while (result()==-1) {
3388 	    qApp->enter_loop();
3389 	    if (result()==-1 && !keysource.Empty()) {
3390 		char k=keysource.GetAscii();
3391 		if (k=='\033' || k==' ' || k=='\r' || k=='\n') {
3392 		    accept();
3393 		} else if (k=='/') {
3394 		    Search();
3395 		}
3396 	    }
3397 	}
3398     }
3399 }
3400 
PutStr(int attr,const char * text)3401 void NetHackQtTextWindow::PutStr(int attr, const char* text)
3402 {
3403     str_fixed=str_fixed || strstr(text,"    ");
3404     lines->insertItem(text);
3405 }
3406 
done(int i)3407 void NetHackQtTextWindow::done(int i)
3408 {
3409     setResult(i+1000);
3410     hide();
3411     qApp->exit_loop();
3412 }
3413 
keyPressEvent(QKeyEvent * e)3414 void NetHackQtTextWindow::keyPressEvent(QKeyEvent* e)
3415 {
3416     if ( e->ascii() != '\r' && e->ascii() != '\n' && e->ascii() != '\033' )
3417 	lines->keyPressEvent(e);
3418     else
3419 	QDialog::keyPressEvent(e);
3420 }
3421 
Search()3422 void NetHackQtTextWindow::Search()
3423 {
3424     NetHackQtStringRequestor requestor(keysource,"Search for:");
3425     static char line[256]="";
3426     requestor.SetDefault(line);
3427     if (requestor.Get(line)) {
3428 	int current=lines->currentItem();
3429 	for (uint i=1; i<lines->count(); i++) {
3430 	    int lnum=(i+current)%lines->count();
3431 	    QString str=lines->text(lnum);
3432 	    if (str.contains(line)) {
3433 		lines->setCurrentItem(lnum);
3434 		lines->centerCurrentItem();
3435 		return;
3436 	    }
3437 	}
3438 	lines->setCurrentItem(-1);
3439     }
3440 }
3441 
3442 
NetHackQtDelay(int ms)3443 NetHackQtDelay::NetHackQtDelay(int ms) :
3444     msec(ms)
3445 {
3446 }
3447 
wait()3448 void NetHackQtDelay::wait()
3449 {
3450     startTimer(msec);
3451     qApp->enter_loop();
3452 }
3453 
timerEvent(QTimerEvent * timer)3454 void NetHackQtDelay::timerEvent(QTimerEvent* timer)
3455 {
3456     qApp->exit_loop();
3457     killTimers();
3458 }
3459 
NetHackQtInvUsageWindow(QWidget * parent)3460 NetHackQtInvUsageWindow::NetHackQtInvUsageWindow(QWidget* parent) :
3461     QWidget(parent)
3462 {
3463 }
3464 
drawWorn(QPainter & painter,obj * nhobj,int x,int y,bool canbe)3465 void NetHackQtInvUsageWindow::drawWorn(QPainter& painter, obj* nhobj, int x, int y, bool canbe)
3466 {
3467     short int glyph;
3468     if (nhobj)
3469 	glyph=obj_to_glyph(nhobj, rn2_on_display_rng);
3470     else if (canbe)
3471 	glyph=cmap_to_glyph(S_room);
3472     else
3473 	glyph=cmap_to_glyph(S_stone);
3474 
3475     qt_settings->glyphs().drawCell(painter,glyph,x,y);
3476 }
3477 
paintEvent(QPaintEvent *)3478 void NetHackQtInvUsageWindow::paintEvent(QPaintEvent*)
3479 {
3480     //  012
3481     //
3482     //0 WhB
3483     //1 s"w
3484     //2 gCg
3485     //3 =A=
3486     //4  T
3487     //5  S
3488 
3489     QPainter painter;
3490     painter.begin(this);
3491 
3492     // Blanks
3493     drawWorn(painter,0,0,4,FALSE);
3494     drawWorn(painter,0,0,5,FALSE);
3495     drawWorn(painter,0,2,4,FALSE);
3496     drawWorn(painter,0,2,5,FALSE);
3497 
3498     drawWorn(painter,uarm,1,3); // Armour
3499     drawWorn(painter,uarmc,1,2); // Cloak
3500     drawWorn(painter,uarmh,1,0); // Helmet
3501     drawWorn(painter,uarms,0,1); // Shield
3502     drawWorn(painter,uarmg,0,2); // Gloves - repeated
3503     drawWorn(painter,uarmg,2,2); // Gloves - repeated
3504     drawWorn(painter,uarmf,1,5); // Shoes (feet)
3505     drawWorn(painter,uarmu,1,4); // Undershirt
3506     drawWorn(painter,uleft,0,3); // RingL
3507     drawWorn(painter,uright,2,3); // RingR
3508 
3509     drawWorn(painter,uwep,2,1); // Weapon
3510     drawWorn(painter,uswapwep,0,0); // Secondary weapon
3511     drawWorn(painter,uamul,1,1); // Amulet
3512     drawWorn(painter,ublindf,2,0); // Blindfold
3513 
3514     painter.end();
3515 }
3516 
3517 class SmallToolButton : public QToolButton {
3518 public:
SmallToolButton(const QPixmap & pm,const QString & textLabel,const QString & grouptext,QObject * receiver,const char * slot,QToolBar * parent)3519     SmallToolButton(const QPixmap & pm, const QString &textLabel,
3520                  const QString& grouptext,
3521                  QObject * receiver, const char* slot,
3522                  QToolBar * parent) :
3523 	QToolButton(pm, textLabel,
3524 #if QT_VERSION < 210
3525 		QString::null,
3526 #else
3527 		grouptext,
3528 #endif
3529 		    receiver, slot, parent)
3530     {
3531     }
3532 
sizeHint() const3533     QSize sizeHint() const
3534     {
3535 	// get just a couple more pixels for the map
3536 	return QToolButton::sizeHint()-QSize(0,2);
3537     }
3538 };
3539 
3540 
NetHackQtMainWindow(NetHackQtKeyBuffer & ks)3541 NetHackQtMainWindow::NetHackQtMainWindow(NetHackQtKeyBuffer& ks) :
3542     message(0), map(0), status(0), invusage(0),
3543     keysink(ks), dirkey(0)
3544 {
3545     QToolBar* toolbar = new QToolBar(this);
3546 #if QT_VERSION >= 210
3547     setToolBarsMovable(FALSE);
3548     toolbar->setHorizontalStretchable(TRUE);
3549     toolbar->setVerticalStretchable(TRUE);
3550 #endif
3551     addToolBar(toolbar);
3552     menubar = menuBar();
3553 
3554     setCaption("Qt NetHack");
3555     if ( qt_compact_mode )
3556 	setIcon(QPixmap(nh_icon_small));
3557     else
3558 	setIcon(QPixmap(nh_icon));
3559 
3560     QPopupMenu* game=new QPopupMenu;
3561     QPopupMenu* apparel=new QPopupMenu;
3562     QPopupMenu* act1=new QPopupMenu;
3563     QPopupMenu* act2 = qt_compact_mode ? new QPopupMenu : act1;
3564     QPopupMenu* magic=new QPopupMenu;
3565     QPopupMenu* info=new QPopupMenu;
3566 
3567     QPopupMenu *help;
3568 
3569 #ifdef KDE
3570     help = kapp->getHelpMenu( TRUE, "" );
3571     help->insertSeparator();
3572 #else
3573     help = qt_compact_mode ? info : new QPopupMenu;
3574 #endif
3575 
3576     enum { OnDesktop=1, OnHandhelds=2 };
3577     struct Macro {
3578 	QPopupMenu* menu;
3579 	const char* name;
3580 	const char* action;
3581 	int flags;
3582     } item[] = {
3583 	{ game,		0, 0, 3},
3584 	{ game,		"Version\tv",           "v", 3},
3585 	{ game,		"Compilation\tAlt+V",     "\366", 3},
3586 	{ game,		"History\tShift+V",           "V", 3},
3587 	{ game,		"Redraw\tCtrl+R",          "\022", 0}, // useless
3588 	{ game,		"Options\tShift+O",           "O", 3},
3589 	{ game,		"Explore mode\tShift+X",      "X", 3},
3590 	{ game,		0, 0, 3},
3591 	{ game,		"Save\tSy",              "Sy", 3},
3592 	{ game,		"Quit\tAlt+Q",                "\361", 3},
3593 
3594 	{ apparel,	"Apparel off\tShift+A",       "A", 2},
3595 	{ apparel,	"Remove many\tShift+A",       "A", 1},
3596 	{ apparel,	0, 0, 3},
3597 	{ apparel,	"Wield weapon\tw",      "w", 3},
3598 	{ apparel,	"Exchange weapons\tx",      "x", 3},
3599 	{ apparel,	"Two weapon combat\t#two",      "#tw", 3},
3600 	{ apparel,	"Load quiver\tShift+Q",       "Q", 3},
3601 	{ apparel,	0, 0, 3},
3602 	{ apparel,	"Wear armour\tShift+W",       "W", 3},
3603 	{ apparel,	"Take off armour\tShift+T",   "T", 3},
3604 	{ apparel,	0, 0, 3},
3605 	{ apparel,	"Put on non-armour\tShift+P", "P", 3},
3606 	{ apparel,	"Remove non-armour\tShift+R", "R", 3},
3607 
3608 	{ act1,	"Again\tCtrl+A",        "\001", 2},
3609 	{ act1,	0, 0, 3},
3610 	{ act1,	"Apply\ta?",            "a?", 3},
3611 	{ act1,	"Chat\tAlt+C",          "\343", 3},
3612 	{ act1,	"Close door\tc",        "c", 3},
3613 	{ act1,	"Down\t>",              ">", 3},
3614 	{ act1,	"Drop many\tShift+D",   "D", 2},
3615 	{ act1,	"Drop\td?",             "d?", 2},
3616 	{ act1,	"Eat\te?",              "e?", 2},
3617 	{ act1,	"Engrave\tShift+E",     "E", 3},
3618 	{ act1,	"Fight\tShift+F",       "F", 3},
3619 	{ act1,	"Fire from quiver\tf",  "f", 2},
3620 	{ act1,	"Force\tAlt+F",         "\346", 3},
3621 	{ act1,	"Get\t,",               ",", 2},
3622 	{ act1,	"Jump\tAlt+J",          "\352", 3},
3623 	{ act2,	"Kick\tCtrl+D",         "\004", 2},
3624 	{ act2,	"Loot\tAlt+L",          "\354", 3},
3625 	{ act2,	"Open door\to",         "o", 3},
3626 	{ act2,	"Pay\tp",               "p", 3},
3627 	{ act2,	"Rest\t.",              ".", 2},
3628 	{ act2,	"Ride\t#ri",            "#ri", 3},
3629 	{ act2,	"Search\ts",            "s", 3},
3630 	{ act2,	"Sit\tAlt+S",           "\363", 3},
3631 	{ act2,	"Throw\tt",             "t", 2},
3632 	{ act2,	"Untrap\t#u",           "#u", 3},
3633 	{ act2,	"Up\t<",                "<", 3},
3634 	{ act2,	"Wipe face\tAlt+W",     "\367", 3},
3635 
3636 	{ magic,	"Quaff potion\tq?",      "q?", 3},
3637 	{ magic,	"Read scroll/book\tr?", "r?", 3},
3638 	{ magic,	"Zap wand\tz?",         "z?", 3},
3639 	{ magic,	"Zap spell\tShift+Z",        "Z", 3},
3640 	{ magic,	"Dip\tAlt+D",             "\344", 3},
3641 	{ magic,	"Rub\tAlt+R",             "\362", 3},
3642 	{ magic,	"Invoke\tAlt+I",          "\351", 3},
3643 	{ magic,	0, 0, 3},
3644 	{ magic,	"Offer\tAlt+O",           "\357", 3},
3645 	{ magic,	"Pray\tAlt+P",            "\360", 3},
3646 	{ magic,	0, 0, 3},
3647 	{ magic,	"Teleport\tCtrl+T",        "\024", 3},
3648 	{ magic,	"Monster action\tAlt+M",  "\355", 3},
3649 	{ magic,	"Turn undead\tAlt+T",     "\364", 3},
3650 
3651 	{ help,		"Help\t?",              "?", 3},
3652 	{ help,		0, 0, 3},
3653 	{ help,		"What is here\t:",      ":", 3},
3654 	{ help,		"What is there\t;",      ";", 3},
3655 	{ help,		"What is...\t/y",        "/y", 2},
3656 	{ help,		0, 0, 1},
3657 
3658 	{ info,		"Inventory\ti",         "i", 3},
3659 #ifdef SLASHEM
3660 	{ info,		"Angbandish inventory\t*",    "*", 3},
3661 #endif
3662 	{ info,		"Conduct\t#co",         "#co", 3},
3663 	{ info,		"Discoveries\t\\",      "\\", 3},
3664 	{ info,		"List/reorder spells\t+",     "+", 3},
3665 	{ info,		"Adjust letters\tAlt+A",  "\341", 2},
3666 	{ info,		0, 0, 3},
3667 	{ info,		"Name object\tAlt+N",    "\356y?", 3},
3668 	{ info,		"Name object type\tAlt+N",    "\356n?", 3},
3669 	{ info,		"Name creature\tShift+C",      "C", 3},
3670 	{ info,		0, 0, 3},
3671 	{ info,		"Qualifications\tAlt+E",  "\345", 3},
3672 
3673 	{ 0, 0, 0, 0 }
3674     };
3675 
3676     int i;
3677     int count=0;
3678     for (i=0; item[i].menu; i++)
3679 	if (item[i].name) count++;
3680 
3681     macro=new const char* [count];
3682 
3683     game->insertItem("Qt settings...",1000);
3684     help->insertItem("About Qt NetHack...",2000);
3685     //help->insertItem("NetHack Guidebook...",3000);
3686     help->insertSeparator();
3687 
3688     count=0;
3689     for (i=0; item[i].menu; i++) {
3690 	if ( item[i].flags & (qt_compact_mode ? 1 : 2) ) {
3691 	    if (item[i].name) {
3692 		QString name = item[i].name;
3693 		if ( qt_compact_mode ) // accelerators aren't
3694 		    name.replace(QRegExp("\t.*"),"");
3695 		item[i].menu->insertItem(name,count);
3696 		macro[count++]=item[i].action;
3697 	    } else {
3698 		item[i].menu->insertSeparator();
3699 	    }
3700 	}
3701     }
3702 
3703     menubar->insertItem("Game",game);
3704     menubar->insertItem("Gear",apparel);
3705 
3706     if ( qt_compact_mode ) {
3707 	menubar->insertItem("A-J",act1);
3708 	menubar->insertItem("K-Z",act2);
3709 	menubar->insertItem("Magic",magic);
3710 	menubar->insertItem(QPixmap(info_xpm),info);
3711 	menubar->insertItem(QPixmap(map_xpm), this, SLOT(raiseMap()));
3712 	menubar->insertItem(QPixmap(msg_xpm), this, SLOT(raiseMessages()));
3713 	menubar->insertItem(QPixmap(stat_xpm), this, SLOT(raiseStatus()));
3714     } else {
3715 	menubar->insertItem("Action",act1);
3716 	menubar->insertItem("Magic",magic);
3717 	menubar->insertItem("Info",info);
3718 	menubar->insertSeparator();
3719 	menubar->insertItem("Help",help);
3720     }
3721 
3722     QSignalMapper* sm = new QSignalMapper(this);
3723     connect(sm, SIGNAL(mapped(const QString&)), this, SLOT(doKeys(const QString&)));
3724     QToolButton* tb;
3725     tb = new SmallToolButton( QPixmap(again_xpm),"Again","Action", sm, SLOT(map()), toolbar );
3726     sm->setMapping(tb, "\001" );
3727     tb = new SmallToolButton( QPixmap(get_xpm),"Get","Action", sm, SLOT(map()), toolbar );
3728     sm->setMapping(tb, "," );
3729     tb = new SmallToolButton( QPixmap(kick_xpm),"Kick","Action", sm, SLOT(map()), toolbar );
3730     sm->setMapping(tb, "\004" );
3731     tb = new SmallToolButton( QPixmap(throw_xpm),"Throw","Action", sm, SLOT(map()), toolbar );
3732     sm->setMapping(tb, "t" );
3733     tb = new SmallToolButton( QPixmap(fire_xpm),"Fire","Action", sm, SLOT(map()), toolbar );
3734     sm->setMapping(tb, "f" );
3735     tb = new SmallToolButton( QPixmap(drop_xpm),"Drop","Action", sm, SLOT(map()), toolbar );
3736     sm->setMapping(tb, "D" );
3737     tb = new SmallToolButton( QPixmap(eat_xpm),"Eat","Action", sm, SLOT(map()), toolbar );
3738     sm->setMapping(tb, "e" );
3739     tb = new SmallToolButton( QPixmap(rest_xpm),"Rest","Action", sm, SLOT(map()), toolbar );
3740     sm->setMapping(tb, "." );
3741     tb = new SmallToolButton( QPixmap(cast_a_xpm),"Cast A","Magic", sm, SLOT(map()), toolbar );
3742     sm->setMapping(tb, "Za" );
3743     tb = new SmallToolButton( QPixmap(cast_b_xpm),"Cast B","Magic", sm, SLOT(map()), toolbar );
3744     sm->setMapping(tb, "Zb" );
3745     tb = new SmallToolButton( QPixmap(cast_c_xpm),"Cast C","Magic", sm, SLOT(map()), toolbar );
3746     sm->setMapping(tb, "Zc" );
3747     if ( !qt_compact_mode ) {
3748 	QWidget* filler = new QWidget(toolbar);
3749 	filler->setBackgroundMode(PaletteButton);
3750 	toolbar->setStretchableWidget(filler);
3751     }
3752 
3753     connect(menubar,SIGNAL(activated(int)),this,SLOT(doMenuItem(int)));
3754 
3755 #ifdef KDE
3756     setMenu (menubar);
3757 #endif
3758 
3759     int x=0,y=0;
3760     int w=QApplication::desktop()->width()-10; // XXX arbitrary extra space for frame
3761     int h=QApplication::desktop()->height()-50;
3762 
3763     int maxwn;
3764     int maxhn;
3765     if (qt_tilewidth != NULL) {
3766 	maxwn = atoi(qt_tilewidth) * COLNO + 10;
3767     } else {
3768 	maxwn = 1400;
3769     }
3770     if (qt_tileheight != NULL) {
3771 	maxhn = atoi(qt_tileheight) * ROWNO * 6/4;
3772     } else {
3773 	maxhn = 1024;
3774     }
3775 
3776     // Be exactly the size we want to be - full map...
3777     if (w>maxwn) {
3778 	x+=(w-maxwn)/2;
3779 	w=maxwn; // Doesn't need to be any wider
3780     }
3781     if (h>maxhn) {
3782 	y+=(h-maxhn)/2;
3783 	h=maxhn; // Doesn't need to be any taller
3784     }
3785 
3786     setGeometry(x,y,w,h);
3787 
3788     if ( qt_compact_mode ) {
3789 	stack = new QWidgetStack(this);
3790 	setCentralWidget(stack);
3791     } else {
3792 	setCentralWidget(new QWidget(this));
3793 	invusage = new NetHackQtInvUsageWindow(centralWidget());
3794     }
3795 }
3796 
zoomMap()3797 void NetHackQtMainWindow::zoomMap()
3798 {
3799     qt_settings->toggleGlyphSize();
3800 }
3801 
raiseMap()3802 void NetHackQtMainWindow::raiseMap()
3803 {
3804     if ( stack->id(stack->visibleWidget()) == 0 ) {
3805 	zoomMap();
3806     } else {
3807 	stack->raiseWidget(0);
3808     }
3809 }
3810 
raiseMessages()3811 void NetHackQtMainWindow::raiseMessages()
3812 {
3813     stack->raiseWidget(1);
3814 }
3815 
raiseStatus()3816 void NetHackQtMainWindow::raiseStatus()
3817 {
3818     stack->raiseWidget(2);
3819 }
3820 
3821 class NetHackMimeSourceFactory : public QMimeSourceFactory {
3822 public:
data(const QString & abs_name) const3823     const QMimeSource* data(const QString& abs_name) const
3824     {
3825 	const QMimeSource* r = 0;
3826 	if ( (NetHackMimeSourceFactory*)this == QMimeSourceFactory::defaultFactory() )
3827 	    r = QMimeSourceFactory::data(abs_name);
3828 	else
3829 	    r = QMimeSourceFactory::defaultFactory()->data(abs_name);
3830 	if ( !r ) {
3831 	    int sl = abs_name.length();
3832 	    do {
3833 		sl = abs_name.findRev('/',sl-1);
3834 		QString name = sl>=0 ? abs_name.mid(sl+1) : abs_name;
3835 		int dot = name.findRev('.');
3836 		if ( dot >= 0 )
3837 		    name = name.left(dot);
3838 		if ( name == "map" )
3839 		    r = new QImageDrag(QImage(map_xpm));
3840 		else if ( name == "msg" )
3841 		    r = new QImageDrag(QImage(msg_xpm));
3842 		else if ( name == "stat" )
3843 		    r = new QImageDrag(QImage(stat_xpm));
3844 	    } while (!r && sl>0);
3845 	}
3846 	return r;
3847     }
3848 };
3849 
doMenuItem(int id)3850 void NetHackQtMainWindow::doMenuItem(int id)
3851 {
3852     switch (id) {
3853       case 1000:
3854 	centerOnMain(qt_settings);
3855 	qt_settings->show();
3856 	break;
3857       case 2000:
3858 	QMessageBox::about(this,  "About Qt NetHack", aboutMsg());
3859 	break;
3860       case 3000: {
3861 	    QDialog dlg(this,0,TRUE);
3862 	    (new QVBoxLayout(&dlg))->setAutoAdd(TRUE);
3863 	    QTextBrowser browser(&dlg);
3864 	    NetHackMimeSourceFactory ms;
3865 	    browser.setMimeSourceFactory(&ms);
3866 	    browser.setSource(QDir::currentDirPath()+"/Guidebook.html");
3867 	    if ( qt_compact_mode )
3868 		dlg.showMaximized();
3869 	    dlg.exec();
3870 	}
3871 	break;
3872       default:
3873 	if ( id >= 0 )
3874 	    doKeys(macro[id]);
3875     }
3876 }
3877 
doKeys(const QString & k)3878 void NetHackQtMainWindow::doKeys(const QString& k)
3879 {
3880     keysink.Put(k);
3881     qApp->exit_loop();
3882 }
3883 
AddMessageWindow(NetHackQtMessageWindow * window)3884 void NetHackQtMainWindow::AddMessageWindow(NetHackQtMessageWindow* window)
3885 {
3886     message=window;
3887     ShowIfReady();
3888 }
3889 
AddMapWindow(NetHackQtMapWindow * window)3890 void NetHackQtMainWindow::AddMapWindow(NetHackQtMapWindow* window)
3891 {
3892     map=window;
3893     ShowIfReady();
3894     connect(map,SIGNAL(resized()),this,SLOT(layout()));
3895 }
3896 
AddStatusWindow(NetHackQtStatusWindow * window)3897 void NetHackQtMainWindow::AddStatusWindow(NetHackQtStatusWindow* window)
3898 {
3899     status=window;
3900     ShowIfReady();
3901 }
3902 
RemoveWindow(NetHackQtWindow * window)3903 void NetHackQtMainWindow::RemoveWindow(NetHackQtWindow* window)
3904 {
3905     if (window==status) {
3906 	status=0;
3907 	ShowIfReady();
3908     } else if (window==map) {
3909 	map=0;
3910 	ShowIfReady();
3911     } else if (window==message) {
3912 	message=0;
3913 	ShowIfReady();
3914     }
3915 }
3916 
updateInventory()3917 void NetHackQtMainWindow::updateInventory()
3918 {
3919     if ( invusage )
3920 	invusage->repaint(FALSE);
3921 }
3922 
fadeHighlighting()3923 void NetHackQtMainWindow::fadeHighlighting()
3924 {
3925     if (status) {
3926 	status->fadeHighlighting();
3927     }
3928 }
3929 
layout()3930 void NetHackQtMainWindow::layout()
3931 {
3932     if ( qt_compact_mode )
3933 	return;
3934     if (message && map && status) {
3935 	QSize maxs=map->Widget()->maximumSize();
3936 	int maph=QMIN(height()*2/3,maxs.height());
3937 
3938 	QWidget* c = centralWidget();
3939 	int h=c->height();
3940 	int toph=h-maph;
3941 	int iuw=3*qt_settings->glyphs().width();
3942 	int topw=(c->width()-iuw)/2;
3943 
3944 	message->Widget()->setGeometry(0,0,topw,toph);
3945 	invusage->setGeometry(topw,0,iuw,toph);
3946 	status->Widget()->setGeometry(topw+iuw,0,topw,toph);
3947 	map->Widget()->setGeometry(QMAX(0,(c->width()-maxs.width())/2),
3948 				   toph,c->width(),maph);
3949     }
3950 }
3951 
resizeEvent(QResizeEvent *)3952 void NetHackQtMainWindow::resizeEvent(QResizeEvent*)
3953 {
3954     layout();
3955 #ifdef KDE
3956     updateRects();
3957 #endif
3958 }
3959 
keyReleaseEvent(QKeyEvent * event)3960 void NetHackQtMainWindow::keyReleaseEvent(QKeyEvent* event)
3961 {
3962     if ( dirkey ) {
3963 	doKeys(QString(QChar(dirkey)));
3964 	if ( !event->isAutoRepeat() )
3965 	    dirkey = 0;
3966     }
3967 }
3968 
keyPressEvent(QKeyEvent * event)3969 void NetHackQtMainWindow::keyPressEvent(QKeyEvent* event)
3970 {
3971     // Global key controls
3972 
3973     // For desktop, arrow keys scroll map, since we don't want players
3974     // to think that's the way to move. For handhelds, the normal way is to
3975     // click-to-travel, so we allow the cursor keys for fine movements.
3976 
3977     //  321
3978     //  4 0
3979     //  567
3980 
3981     if ( event->isAutoRepeat() &&
3982 	event->key() >= Key_Left && event->key() <= Key_Down )
3983 	return;
3984 
3985     const char* d = Cmd.dirchars;
3986     switch (event->key()) {
3987      case Key_Up:
3988 	if ( dirkey == d[0] )
3989 	    dirkey = d[1];
3990 	else if ( dirkey == d[4] )
3991 	    dirkey = d[3];
3992 	else
3993 	    dirkey = d[2];
3994     break; case Key_Down:
3995 	if ( dirkey == d[0] )
3996 	    dirkey = d[7];
3997 	else if ( dirkey == d[4] )
3998 	    dirkey = d[5];
3999 	else
4000 	    dirkey = d[6];
4001     break; case Key_Left:
4002 	if ( dirkey == d[2] )
4003 	    dirkey = d[1];
4004 	else if ( dirkey == d[6] )
4005 	    dirkey = d[7];
4006 	else
4007 	    dirkey = d[0];
4008     break; case Key_Right:
4009 	if ( dirkey == d[2] )
4010 	    dirkey = d[3];
4011 	else if ( dirkey == d[6] )
4012 	    dirkey = d[5];
4013 	else
4014 	    dirkey = d[4];
4015     break; case Key_Prior:
4016 	dirkey = 0;
4017 	if (message) message->Scroll(0,-1);
4018     break; case Key_Next:
4019 	dirkey = 0;
4020 	if (message) message->Scroll(0,+1);
4021     break; case Key_Space:
4022 	if ( flags.rest_on_space ) {
4023 	    event->ignore();
4024 	    return;
4025 	}
4026 	case Key_Enter:
4027 	if ( map )
4028 	    map->clickCursor();
4029     break; default:
4030 	dirkey = 0;
4031 	event->ignore();
4032     }
4033 }
4034 
closeEvent(QCloseEvent * e)4035 void NetHackQtMainWindow::closeEvent(QCloseEvent* e)
4036 {
4037     if ( program_state.something_worth_saving ) {
4038 	switch ( QMessageBox::information( this, "NetHack",
4039 	    "This will end your NetHack session",
4040 	    "&Save", "&Cancel", 0, 1 ) )
4041 	{
4042 	    case 0:
4043 		// See dosave() function
4044 		if (dosave0()) {
4045 		    u.uhp = -1;
4046 		    NetHackQtBind::qt_exit_nhwindows(0);
4047 		    nh_terminate(EXIT_SUCCESS);
4048 		}
4049 		break;
4050 	    case 1:
4051 		break; // ignore the event
4052 	}
4053     } else {
4054 	e->accept();
4055     }
4056 }
4057 
ShowIfReady()4058 void NetHackQtMainWindow::ShowIfReady()
4059 {
4060     if (message && map && status) {
4061 	QPoint pos(0,0);
4062 	QWidget* p = qt_compact_mode ? stack : centralWidget();
4063 	message->Widget()->recreate(p,0,pos);
4064 	map->Widget()->recreate(p,0,pos);
4065 	status->Widget()->recreate(p,0,pos);
4066 	if ( qt_compact_mode ) {
4067 	    message->setMap(map);
4068 	    stack->addWidget(map->Widget(), 0);
4069 	    stack->addWidget(message->Widget(), 1);
4070 	    stack->addWidget(status->Widget(), 2);
4071 	    raiseMap();
4072 	} else {
4073 	    layout();
4074 	}
4075 	showMaximized();
4076     } else if (isVisible()) {
4077 	hide();
4078     }
4079 }
4080 
4081 
NetHackQtYnDialog(NetHackQtKeyBuffer & keysrc,const char * q,const char * ch,char df)4082 NetHackQtYnDialog::NetHackQtYnDialog(NetHackQtKeyBuffer& keysrc,const char* q,const char* ch,char df) :
4083     QDialog(qApp->mainWidget(),0,FALSE),
4084     question(q), choices(ch), def(df),
4085     keysource(keysrc)
4086 {
4087     setCaption("NetHack: Question");
4088 }
4089 
Exec()4090 char NetHackQtYnDialog::Exec()
4091 {
4092     QString ch(choices);
4093     int ch_per_line=6;
4094     QString qlabel;
4095     QString enable;
4096     if ( qt_compact_mode && !choices ) {
4097 	// expand choices from prompt
4098 	// ##### why isn't choices set properly???
4099 	const char* c=question;
4100 	while ( *c && *c != '[' )
4101 	    c++;
4102 	qlabel = QString(question).left(c-question);
4103 	if ( *c ) {
4104 	    c++;
4105 	    if ( *c == '-' )
4106 		ch.append(*c++);
4107 	    char from=0;
4108 	    while ( *c && *c != ']' && *c != ' ' ) {
4109 		if ( *c == '-' ) {
4110 		    from = c[-1];
4111 		} else if ( from ) {
4112 		    for (char f=from+1; f<=*c; f++)
4113 			ch.append(f);
4114 		    from = 0;
4115 		} else {
4116 		    ch.append(*c);
4117 		    from = 0;
4118 		}
4119 		c++;
4120 	    }
4121 	    if ( *c == ' ' ) {
4122 		while ( *c && *c != ']' ) {
4123 		    if ( *c == '*' || *c == '?' )
4124 			ch.append(*c);
4125 		    c++;
4126 		}
4127 	    }
4128 	}
4129 	if ( strstr(question, "what direction") ) {
4130 	    // We replace this regardless, since sometimes you get choices.
4131 	    const char* d = Cmd.dirchars;
4132 	    enable=ch;
4133 	    ch="";
4134 	    ch.append(d[1]);
4135 	    ch.append(d[2]);
4136 	    ch.append(d[3]);
4137 	    ch.append(d[0]);
4138 	    ch.append('.');
4139 	    ch.append(d[4]);
4140 	    ch.append(d[7]);
4141 	    ch.append(d[6]);
4142 	    ch.append(d[5]);
4143 	    ch.append(d[8]);
4144 	    ch.append(d[9]);
4145 	    ch_per_line = 3;
4146 	    def = ' ';
4147 	} else {
4148 	    // Hmm... they'll have to use a virtual keyboard
4149 	}
4150     } else {
4151 	qlabel = question;
4152     }
4153     if (!ch.isNull()) {
4154 	QVBoxLayout vb(this);
4155 	vb.setAutoAdd(TRUE);
4156 	bool bigq = qlabel.length()>40;
4157 	if ( bigq ) {
4158 	    QLabel* q = new QLabel(qlabel,this);
4159 	    q->setAlignment(AlignLeft|WordBreak);
4160 	    q->setMargin(4);
4161 	}
4162 	QButtonGroup group(ch_per_line, Horizontal,
4163 	    bigq ? QString::null : qlabel, this);
4164 
4165 	int nchoices=ch.length();
4166 
4167 	bool allow_count=ch.contains('#');
4168 
4169 	const int margin=8;
4170 	const int gutter=8;
4171 	const int extra=fontMetrics().height(); // Extra for group
4172 	int x=margin, y=extra+margin;
4173 	int butsize=fontMetrics().height()*2+5;
4174 
4175 	QPushButton* button;
4176 	for (int i=0; i<nchoices && ch[i]!='\033'; i++) {
4177 	    button=new QPushButton(QString(ch[i]),&group);
4178 	    if ( !enable.isNull() ) {
4179 		if ( !enable.contains(ch[i]) )
4180 		    button->setEnabled(FALSE);
4181 	    }
4182 	    button->setFixedSize(butsize,butsize); // Square
4183 	    if (ch[i]==def) button->setDefault(TRUE);
4184 	    if (i%10==9) {
4185 		// last in row
4186 		x=margin;
4187 		y+=butsize+gutter;
4188 	    } else {
4189 		x+=butsize+gutter;
4190 	    }
4191 	}
4192 
4193 	connect(&group,SIGNAL(clicked(int)),this,SLOT(doneItem(int)));
4194 
4195 	QLabel* lb=0;
4196 	QLineEdit* le=0;
4197 
4198 	if (allow_count) {
4199 	    QHBox *hb = new QHBox(this);
4200 	    lb=new QLabel("Count: ",hb);
4201 	    le=new QLineEdit(hb);
4202 	}
4203 
4204 	adjustSize();
4205 	centerOnMain(this);
4206 	show();
4207 	char choice=0;
4208 	char ch_esc=0;
4209 	for (uint i=0; i<ch.length(); i++) {
4210 	    if (ch[i].latin1()=='q') ch_esc='q';
4211 	    else if (!ch_esc && ch[i].latin1()=='n') ch_esc='n';
4212 	}
4213 	setResult(-1);
4214 	while (!choice) {
4215 	    if (!keysource.Empty()) {
4216 		char k=keysource.GetAscii();
4217 		char ch_esc=0;
4218 		for (uint i=0; i<ch.length(); i++)
4219 		    if (ch[i].latin1()==k)
4220 			choice=k;
4221 		if (!choice) {
4222 		    if (k=='\033' && ch_esc)
4223 			choice=ch_esc;
4224 		    else if (k==' ' || k=='\r' || k=='\n')
4225 			choice=def;
4226 		    // else choice remains 0
4227 		}
4228 	    } else if ( result() == 0 ) {
4229 		choice = ch_esc ? ch_esc : def ? def : ' ';
4230 	    } else if ( result() == 1 ) {
4231 		choice = def ? def : ch_esc ? ch_esc : ' ';
4232 	    } else if ( result() >= 1000 ) {
4233 		choice = ch[result() - 1000].latin1();
4234 	    }
4235 	    if ( !choice )
4236 		qApp->enter_loop();
4237 	}
4238 	hide();
4239 	if (allow_count && !le->text().isEmpty()) {
4240 	    yn_number=atoi(le->text());
4241 	    choice='#';
4242 	}
4243 	return choice;
4244     } else {
4245 	QLabel label(qlabel,this);
4246 	QPushButton cancel("Dismiss",this);
4247 	label.setFrameStyle(QFrame::Box|QFrame::Sunken);
4248 	label.setAlignment(AlignCenter);
4249 	label.resize(fontMetrics().width(qlabel)+60,30+fontMetrics().height());
4250 	cancel.move(width()/2-cancel.width()/2,label.geometry().bottom()+8);
4251 	connect(&cancel,SIGNAL(clicked()),this,SLOT(reject()));
4252 	centerOnMain(this);
4253 	setResult(-1);
4254 	show();
4255 	while (result()<0 && keysource.Empty()) {
4256 	    qApp->enter_loop();
4257 	}
4258 	hide();
4259 	if (keysource.Empty()) {
4260 	    return '\033';
4261 	} else {
4262 	    return keysource.GetAscii();
4263 	}
4264     }
4265 }
keyPressEvent(QKeyEvent * event)4266 void NetHackQtYnDialog::keyPressEvent(QKeyEvent* event)
4267 {
4268     // Don't want QDialog's Return/Esc behaviour
4269     event->ignore();
4270 }
4271 
doneItem(int i)4272 void NetHackQtYnDialog::doneItem(int i)
4273 {
4274     done(i+1000);
4275 }
4276 
done(int i)4277 void NetHackQtYnDialog::done(int i)
4278 {
4279     setResult(i);
4280     qApp->exit_loop();
4281 }
4282 
NetHackQtGlyphs()4283 NetHackQtGlyphs::NetHackQtGlyphs()
4284 {
4285     const char* tile_file = "nhtiles.bmp";
4286     if ( iflags.wc_tile_file )
4287 	tile_file = iflags.wc_tile_file;
4288 
4289     if (!img.load(tile_file)) {
4290 	tile_file = "x11tiles";
4291 	if (!img.load(tile_file)) {
4292 	    QString msg;
4293 	    msg.sprintf("Cannot load x11tiles or nhtiles.bmp");
4294 	    QMessageBox::warning(0, "IO Error", msg);
4295 	} else {
4296 	    tiles_per_row = TILES_PER_ROW;
4297 	    if (img.width()%tiles_per_row) {
4298 		impossible(
4299             "Tile file \"%s\" has %d columns, not multiple of row count (%d)",
4300                            tile_file, img.width(), tiles_per_row);
4301 	    }
4302 	}
4303     } else {
4304 	tiles_per_row = 40;
4305     }
4306 
4307     if ( iflags.wc_tile_width )
4308 	tilefile_tile_W = iflags.wc_tile_width;
4309     else
4310 	tilefile_tile_W = img.width() / tiles_per_row;
4311     if ( iflags.wc_tile_height )
4312 	tilefile_tile_H = iflags.wc_tile_height;
4313     else
4314 	tilefile_tile_H = tilefile_tile_W;
4315 
4316     setSize(tilefile_tile_W, tilefile_tile_H);
4317 }
4318 
drawGlyph(QPainter & painter,int glyph,int x,int y)4319 void NetHackQtGlyphs::drawGlyph(QPainter& painter, int glyph, int x, int y)
4320 {
4321     int tile = glyph2tile[glyph];
4322     int px = (tile%tiles_per_row)*width();
4323     int py = tile/tiles_per_row*height();
4324 
4325     painter.drawPixmap(
4326 	x,
4327 	y,
4328 	pm,
4329 	px,py,
4330 	width(),height()
4331     );
4332 }
drawCell(QPainter & painter,int glyph,int cellx,int celly)4333 void NetHackQtGlyphs::drawCell(QPainter& painter, int glyph, int cellx, int celly)
4334 {
4335     drawGlyph(painter,glyph,cellx*width(),celly*height());
4336 }
setSize(int w,int h)4337 void NetHackQtGlyphs::setSize(int w, int h)
4338 {
4339     if ( size == QSize(w,h) )
4340 	return;
4341 
4342     bool was1 = size == pm1.size();
4343     size = QSize(w,h);
4344     if (!w || !h)
4345 	return; // Still not decided
4346 
4347     if ( size == pm1.size() ) {
4348 	pm = pm1;
4349 	return;
4350     }
4351     if ( size == pm2.size() ) {
4352 	pm = pm2;
4353 	return;
4354     }
4355 
4356     if (w==tilefile_tile_W && h==tilefile_tile_H) {
4357 	pm.convertFromImage(img);
4358     } else {
4359 	QApplication::setOverrideCursor( Qt::waitCursor );
4360 	QImage scaled = img.smoothScale(
4361 	    w*img.width()/tilefile_tile_W,
4362 	    h*img.height()/tilefile_tile_H
4363 	);
4364 	pm.convertFromImage(scaled,Qt::ThresholdDither|Qt::PreferDither);
4365 	QApplication::restoreOverrideCursor();
4366     }
4367     (was1 ? pm2 : pm1) = pm;
4368 }
4369 
4370 
4371 //////////////////////////////////////////////////////////////
4372 //
4373 //  The ugly C binding classes...
4374 //
4375 //////////////////////////////////////////////////////////////
4376 
4377 
NetHackQtMenuOrTextWindow(NetHackQtKeyBuffer & ks)4378 NetHackQtMenuOrTextWindow::NetHackQtMenuOrTextWindow(NetHackQtKeyBuffer& ks) :
4379     actual(0),
4380     keysource(ks)
4381 {
4382 }
4383 
Widget()4384 QWidget* NetHackQtMenuOrTextWindow::Widget()
4385 {
4386     if (!actual) impossible("Widget called before we know if Menu or Text");
4387     return actual->Widget();
4388 }
4389 
4390 // Text
Clear()4391 void NetHackQtMenuOrTextWindow::Clear()
4392 {
4393     if (!actual)
4394         impossible("Clear called before we know if Menu or Text");
4395     else
4396         actual->Clear();
4397 }
4398 
Display(bool block)4399 void NetHackQtMenuOrTextWindow::Display(bool block)
4400 {
4401     if (!actual)
4402         impossible("Display called before we know if Menu or Text");
4403     else
4404         actual->Display(block);
4405 }
4406 
Destroy()4407 bool NetHackQtMenuOrTextWindow::Destroy()
4408 {
4409     bool res = FALSE;
4410 
4411     if (!actual)
4412         impossible("Destroy called before we know if Menu or Text");
4413     else
4414         res = actual->Destroy();
4415 
4416     return res;
4417 }
4418 
PutStr(int attr,const char * text)4419 void NetHackQtMenuOrTextWindow::PutStr(int attr, const char* text)
4420 {
4421     if (!actual) actual=new NetHackQtTextWindow(keysource);
4422     actual->PutStr(attr,text);
4423 }
4424 
4425 // Menu
StartMenu()4426 void NetHackQtMenuOrTextWindow::StartMenu()
4427 {
4428     if (!actual) actual=new NetHackQtMenuWindow(keysource);
4429     actual->StartMenu();
4430 }
4431 
AddMenu(int glyph,const ANY_P * identifier,char ch,char gch,int attr,const char * str,bool presel)4432 void NetHackQtMenuOrTextWindow::AddMenu(int glyph, const ANY_P* identifier, char ch, char gch, int attr,
4433 	const char* str, bool presel)
4434 {
4435     if (!actual) impossible("AddMenu called before we know if Menu or Text");
4436     actual->AddMenu(glyph,identifier,ch,gch,attr,str,presel);
4437 }
4438 
EndMenu(const char * prompt)4439 void NetHackQtMenuOrTextWindow::EndMenu(const char* prompt)
4440 {
4441     if (!actual) impossible("EndMenu called before we know if Menu or Text");
4442     actual->EndMenu(prompt);
4443 }
4444 
SelectMenu(int how,MENU_ITEM_P ** menu_list)4445 int NetHackQtMenuOrTextWindow::SelectMenu(int how, MENU_ITEM_P **menu_list)
4446 {
4447     if (!actual) impossible("SelectMenu called before we know if Menu or Text");
4448     return actual->SelectMenu(how,menu_list);
4449 }
4450 
4451 
4452 // XXX Should be from Options
4453 //
4454 // XXX Hmm.  Tricky part is that perhaps some macros should only be active
4455 // XXX       when a key is about to be gotten.  For example, the user could
4456 // XXX       define "-" to do "E-yyyyyyyy\r", but would still need "-" for
4457 // XXX       other purposes.  Maybe just too bad.
4458 //
4459 struct {
4460     int key;
4461     int state;
4462     const char* macro;
4463 } key_macro[]={
4464     { Qt::Key_F1, 0, "n100." }, // Rest (x100)
4465     { Qt::Key_F2, 0, "n20s" },  // Search (x20)
4466     { Qt::Key_F3, 0, "o8o4o6o2o8o4o6o2o8o4o6o2" }, // Open all doors (x3)
4467     { Qt::Key_Tab, 0, "\001" },
4468     { 0, 0, 0 }
4469 };
4470 
4471 
NetHackQtBind(int & argc,char ** argv)4472 NetHackQtBind::NetHackQtBind(int& argc, char** argv) :
4473 #ifdef KDE
4474     KApplication(argc,argv)
4475 #elif defined(QWS) // not quite the right condition
4476     QPEApplication(argc,argv)
4477 #else
4478     QApplication(argc,argv)
4479 #endif
4480 {
4481     QPixmap pm("nhsplash.xpm");
4482     if ( iflags.wc_splash_screen && !pm.isNull() ) {
4483 	QVBox *vb = new QVBox(0,0,
4484 	    WStyle_Customize | WStyle_NoBorder | nh_WX11BypassWM | WStyle_StaysOnTop );
4485 	splash = vb;
4486 	QLabel *lsplash = new QLabel(vb);
4487 	lsplash->setAlignment(AlignCenter);
4488 	lsplash->setPixmap(pm);
4489 	QLabel* capt = new QLabel("Loading...",vb);
4490 	capt->setAlignment(AlignCenter);
4491 	if ( pm.mask() ) {
4492 	    lsplash->setFixedSize(pm.size());
4493 	    lsplash->setMask(*pm.mask());
4494 	}
4495 	splash->move((QApplication::desktop()->width()-pm.width())/2,
4496 		      (QApplication::desktop()->height()-pm.height())/2);
4497 	//splash->setGeometry(0,0,100,100);
4498 	if ( qt_compact_mode ) {
4499 	    splash->showMaximized();
4500 	} else {
4501 	    vb->setFrameStyle(QFrame::WinPanel|QFrame::Raised);
4502 	    vb->setMargin(10);
4503 	    splash->adjustSize();
4504 	    splash->show();
4505 	}
4506 
4507 	// force content refresh outside event loop
4508 	splash->repaint(FALSE);
4509 	lsplash->repaint(FALSE);
4510 	capt->repaint(FALSE);
4511 	qApp->flushX();
4512 
4513     } else {
4514 	splash = 0;
4515     }
4516     main = new NetHackQtMainWindow(keybuffer);
4517 #if defined(QWS) // not quite the right condition
4518     showMainWidget(main);
4519 #else
4520     setMainWidget(main);
4521 #endif
4522     qt_settings=new NetHackQtSettings(main->width(),main->height());
4523 }
4524 
qt_init_nhwindows(int * argc,char ** argv)4525 void NetHackQtBind::qt_init_nhwindows(int* argc, char** argv)
4526 {
4527 #ifdef UNIX
4528 // Userid control
4529 //
4530 // Michael Hohmuth <hohmuth@inf.tu-dresden.de>...
4531 //
4532 // As the game runs setuid games, it must seteuid(getuid()) before
4533 // calling XOpenDisplay(), and reset the euid afterwards.
4534 // Otherwise, it can't read the $HOME/.Xauthority file and whines about
4535 // not being able to open the X display (if a magic-cookie
4536 // authorization mechanism is being used).
4537 
4538     uid_t gamesuid=geteuid();
4539     seteuid(getuid());
4540 #endif
4541 
4542     QApplication::setColorSpec(ManyColor);
4543     instance=new NetHackQtBind(*argc,argv);
4544 
4545 #ifdef UNIX
4546     seteuid(gamesuid);
4547 #endif
4548 
4549 #ifdef _WS_WIN_
4550     // This nethack engine feature should be moved into windowport API
4551     nt_kbhit = NetHackQtBind::qt_kbhit;
4552 #endif
4553 }
4554 
qt_kbhit()4555 int NetHackQtBind::qt_kbhit()
4556 {
4557     return !keybuffer.Empty();
4558 }
4559 
4560 static bool have_asked = FALSE;
4561 
qt_player_selection()4562 void NetHackQtBind::qt_player_selection()
4563 {
4564     if ( !have_asked )
4565 	qt_askname();
4566 }
4567 
NetHackQtSavedGameSelector(const char ** saved)4568 NetHackQtSavedGameSelector::NetHackQtSavedGameSelector(const char** saved) :
4569     QDialog(qApp->mainWidget(),"sgsel",TRUE)
4570 {
4571     QVBoxLayout *vbl = new QVBoxLayout(this,6);
4572     QHBox* hb;
4573 
4574     QLabel* logo = new QLabel(this); vbl->addWidget(logo);
4575     logo->setAlignment(AlignCenter);
4576     logo->setPixmap(QPixmap("nhsplash.xpm"));
4577     QLabel* attr = new QLabel("by the NetHack DevTeam",this);
4578     attr->setAlignment(AlignCenter);
4579     vbl->addWidget(attr);
4580     vbl->addStretch(2);
4581     /*
4582     QLabel* logo = new QLabel(hb);
4583     hb = new QHBox(this);
4584     vbl->addWidget(hb, AlignCenter);
4585     logo->setPixmap(QPixmap(nh_icon));
4586     logo->setAlignment(AlignRight|AlignVCenter);
4587     new QLabel(nh_attribution,hb);
4588     */
4589 
4590     hb = new QHBox(this);
4591     vbl->addWidget(hb, AlignCenter);
4592     QPushButton* q = new QPushButton("Quit",hb);
4593     connect(q, SIGNAL(clicked()), this, SLOT(reject()));
4594     QPushButton* c = new QPushButton("New Game",hb);
4595     connect(c, SIGNAL(clicked()), this, SLOT(accept()));
4596     c->setDefault(TRUE);
4597 
4598     QButtonGroup* bg = new QButtonGroup(3, Horizontal, "Saved Characters",this);
4599     vbl->addWidget(bg);
4600     connect(bg, SIGNAL(clicked(int)), this, SLOT(done(int)));
4601     for (int i=0; saved[i]; i++) {
4602 	QPushButton* b = new QPushButton(saved[i],bg);
4603 	bg->insert(b, i+2);
4604     }
4605 }
4606 
choose()4607 int NetHackQtSavedGameSelector::choose()
4608 {
4609 #if defined(QWS) // probably safe with Qt 3, too (where show!=exec in QDialog).
4610     if ( qt_compact_mode )
4611 	showMaximized();
4612 #endif
4613     return exec()-2;
4614 }
4615 
qt_askname()4616 void NetHackQtBind::qt_askname()
4617 {
4618     have_asked = TRUE;
4619 
4620     // We do it all here, and nothing in askname
4621 
4622     char** saved = get_saved_games();
4623     int ch = -1;
4624     if ( saved && *saved ) {
4625 	if ( splash ) splash->hide();
4626 	NetHackQtSavedGameSelector sgsel((const char**)saved);
4627 	ch = sgsel.choose();
4628 	if ( ch >= 0 )
4629 	    strcpy(plname,saved[ch]);
4630     }
4631     free_saved_games(saved);
4632 
4633     switch (ch) {
4634       case -1:
4635 	if ( splash ) splash->hide();
4636 	if (NetHackQtPlayerSelector(keybuffer).Choose())
4637 	    return;
4638       case -2:
4639 	break;
4640       default:
4641 	return;
4642     }
4643 
4644     // Quit
4645     clearlocks();
4646     qt_exit_nhwindows(0);
4647     nh_terminate(0);
4648 }
4649 
qt_get_nh_event()4650 void NetHackQtBind::qt_get_nh_event()
4651 {
4652 }
4653 
4654 #if defined(QWS)
4655 // Kludge to access lastWindowClosed() signal.
4656 class TApp : public QApplication {
4657 public:
TApp(int & c,char ** v)4658     TApp(int& c, char**v) : QApplication(c,v) {}
lwc()4659     void lwc() { emit lastWindowClosed(); }
4660 };
4661 #endif
4662 
qt_exit_nhwindows(const char *)4663 void NetHackQtBind::qt_exit_nhwindows(const char *)
4664 {
4665 #if defined(QWS)
4666     // Avoids bug in SHARP SL5500
4667     ((TApp*)qApp)->lwc();
4668     qApp->quit();
4669 #endif
4670 
4671     delete instance; // ie. qApp
4672 }
4673 
qt_suspend_nhwindows(const char *)4674 void NetHackQtBind::qt_suspend_nhwindows(const char *)
4675 {
4676 }
4677 
qt_resume_nhwindows()4678 void NetHackQtBind::qt_resume_nhwindows()
4679 {
4680 }
4681 
4682 static QArray<NetHackQtWindow*> id_to_window;
4683 
qt_create_nhwindow(int type)4684 winid NetHackQtBind::qt_create_nhwindow(int type)
4685 {
4686     winid id;
4687     for (id = 0; id < (winid) id_to_window.size(); id++) {
4688 	if ( !id_to_window[id] )
4689 	    break;
4690     }
4691     if ( id == (winid) id_to_window.size() )
4692 	id_to_window.resize(id+1);
4693 
4694     NetHackQtWindow* window=0;
4695 
4696     switch (type) {
4697      case NHW_MAP: {
4698 	NetHackQtMapWindow* w=new NetHackQtMapWindow(clickbuffer);
4699 	main->AddMapWindow(w);
4700 	window=w;
4701     } break; case NHW_MESSAGE: {
4702 	NetHackQtMessageWindow* w=new NetHackQtMessageWindow;
4703 	main->AddMessageWindow(w);
4704 	window=w;
4705     } break; case NHW_STATUS: {
4706 	NetHackQtStatusWindow* w=new NetHackQtStatusWindow;
4707 	main->AddStatusWindow(w);
4708 	window=w;
4709     } break; case NHW_MENU:
4710 	window=new NetHackQtMenuOrTextWindow(keybuffer);
4711     break; case NHW_TEXT:
4712 	window=new NetHackQtTextWindow(keybuffer);
4713     }
4714 
4715     window->nhid = id;
4716 
4717     // Note: use of isHidden does not work with Qt 2.1
4718     if ( splash
4719 #if QT_VERSION >= 300
4720         && !main->isHidden()
4721 #else
4722 	&& main->isVisible()
4723 #endif
4724 	)
4725     {
4726 	delete splash;
4727 	splash = 0;
4728     }
4729 
4730     id_to_window[id] = window;
4731     return id;
4732 }
4733 
qt_clear_nhwindow(winid wid)4734 void NetHackQtBind::qt_clear_nhwindow(winid wid)
4735 {
4736     NetHackQtWindow* window=id_to_window[wid];
4737     window->Clear();
4738 }
4739 
qt_display_nhwindow(winid wid,BOOLEAN_P block)4740 void NetHackQtBind::qt_display_nhwindow(winid wid, BOOLEAN_P block)
4741 {
4742     NetHackQtWindow* window=id_to_window[wid];
4743     window->Display(block);
4744 }
4745 
qt_destroy_nhwindow(winid wid)4746 void NetHackQtBind::qt_destroy_nhwindow(winid wid)
4747 {
4748     NetHackQtWindow* window=id_to_window[wid];
4749     main->RemoveWindow(window);
4750     if (window->Destroy())
4751 	delete window;
4752     id_to_window[wid] = 0;
4753 }
4754 
qt_curs(winid wid,int x,int y)4755 void NetHackQtBind::qt_curs(winid wid, int x, int y)
4756 {
4757     NetHackQtWindow* window=id_to_window[wid];
4758     window->CursorTo(x,y);
4759 }
4760 
qt_putstr(winid wid,int attr,const char * text)4761 void NetHackQtBind::qt_putstr(winid wid, int attr, const char *text)
4762 {
4763     NetHackQtWindow* window=id_to_window[wid];
4764     window->PutStr(attr,text);
4765 }
4766 
qt_display_file(const char * filename,BOOLEAN_P must_exist)4767 void NetHackQtBind::qt_display_file(const char *filename, BOOLEAN_P must_exist)
4768 {
4769     NetHackQtTextWindow* window=new NetHackQtTextWindow(keybuffer);
4770     bool complain = FALSE;
4771 
4772 #ifdef DLB
4773     {
4774 	dlb *f;
4775 	char buf[BUFSZ];
4776 	char *cr;
4777 
4778 	window->Clear();
4779 	f = dlb_fopen(filename, "r");
4780 	if (!f) {
4781 	    complain = must_exist;
4782 	} else {
4783 	    while (dlb_fgets(buf, BUFSZ, f)) {
4784 		if ((cr = index(buf, '\n')) != 0) *cr = 0;
4785 #ifdef MSDOS
4786 		if ((cr = index(buf, '\r')) != 0) *cr = 0;
4787 #endif
4788 		if (index(buf, '\t') != 0) (void) tabexpand(buf);
4789 		window->PutStr(ATR_NONE, buf);
4790 	    }
4791 	    window->Display(FALSE);
4792 	    (void) dlb_fclose(f);
4793 	}
4794     }
4795 #else
4796     QFile file(filename);
4797 
4798     if (file.open(IO_ReadOnly)) {
4799 	char line[128];
4800 	while (file.readLine(line,127) >= 0) {
4801 	    line[strlen(line)-1]=0;// remove newline
4802 	    window->PutStr(ATR_NONE,line);
4803 	}
4804 	window->Display(FALSE);
4805     } else {
4806 	complain = must_exist;
4807     }
4808 #endif
4809 
4810     if (complain) {
4811 	QString message;
4812 	message.sprintf("File not found: %s\n",filename);
4813 	QMessageBox::message("File Error", (const char*)message, "Ignore");
4814     }
4815 }
4816 
qt_start_menu(winid wid)4817 void NetHackQtBind::qt_start_menu(winid wid)
4818 {
4819     NetHackQtWindow* window=id_to_window[wid];
4820     window->StartMenu();
4821 }
4822 
qt_add_menu(winid wid,int glyph,const ANY_P * identifier,CHAR_P ch,CHAR_P gch,int attr,const char * str,BOOLEAN_P presel)4823 void NetHackQtBind::qt_add_menu(winid wid, int glyph,
4824     const ANY_P * identifier, CHAR_P ch, CHAR_P gch, int attr,
4825     const char *str, BOOLEAN_P presel)
4826 {
4827     NetHackQtWindow* window=id_to_window[wid];
4828     window->AddMenu(glyph, identifier, ch, gch, attr, str, presel);
4829 }
4830 
qt_end_menu(winid wid,const char * prompt)4831 void NetHackQtBind::qt_end_menu(winid wid, const char *prompt)
4832 {
4833     NetHackQtWindow* window=id_to_window[wid];
4834     window->EndMenu(prompt);
4835 }
4836 
qt_select_menu(winid wid,int how,MENU_ITEM_P ** menu_list)4837 int NetHackQtBind::qt_select_menu(winid wid, int how, MENU_ITEM_P **menu_list)
4838 {
4839     NetHackQtWindow* window=id_to_window[wid];
4840     return window->SelectMenu(how,menu_list);
4841 }
4842 
qt_update_inventory()4843 void NetHackQtBind::qt_update_inventory()
4844 {
4845     if (main)
4846 	main->updateInventory();
4847     /* doesn't work yet
4848     if (program_state.something_worth_saving && iflags.perm_invent)
4849         display_inventory(NULL, FALSE);
4850     */
4851 }
4852 
qt_mark_synch()4853 void NetHackQtBind::qt_mark_synch()
4854 {
4855 }
4856 
qt_wait_synch()4857 void NetHackQtBind::qt_wait_synch()
4858 {
4859 }
4860 
qt_cliparound(int x,int y)4861 void NetHackQtBind::qt_cliparound(int x, int y)
4862 {
4863     // XXXNH - winid should be a parameter!
4864     qt_cliparound_window(WIN_MAP,x,y);
4865 }
4866 
qt_cliparound_window(winid wid,int x,int y)4867 void NetHackQtBind::qt_cliparound_window(winid wid, int x, int y)
4868 {
4869     NetHackQtWindow* window=id_to_window[wid];
4870     window->ClipAround(x,y);
4871 }
qt_print_glyph(winid wid,XCHAR_P x,XCHAR_P y,int glyph,int bkglyph)4872 void NetHackQtBind::qt_print_glyph(winid wid,XCHAR_P x,XCHAR_P y,int glyph, int bkglyph)
4873 {
4874     NetHackQtWindow* window=id_to_window[wid];
4875     window->PrintGlyph(x,y,glyph);
4876 }
4877 //void NetHackQtBind::qt_print_glyph_compose(winid wid,XCHAR_P x,XCHAR_P y,int glyph1, int glyph2)
4878 //{
4879     //NetHackQtWindow* window=id_to_window[wid];
4880     //window->PrintGlyphCompose(x,y,glyph1,glyph2);
4881 //}
4882 
qt_raw_print(const char * str)4883 void NetHackQtBind::qt_raw_print(const char *str)
4884 {
4885     puts(str);
4886 }
4887 
qt_raw_print_bold(const char * str)4888 void NetHackQtBind::qt_raw_print_bold(const char *str)
4889 {
4890     puts(str);
4891 }
4892 
qt_nhgetch()4893 int NetHackQtBind::qt_nhgetch()
4894 {
4895     if (main)
4896 	main->fadeHighlighting();
4897 
4898     // Process events until a key arrives.
4899     //
4900     while (keybuffer.Empty()
4901 #ifdef SAFERHANGUP
4902 	   && !program_state.done_hup
4903 #endif
4904 	   ) {
4905 	qApp->enter_loop();
4906     }
4907 
4908 #ifdef SAFERHANGUP
4909     if (program_state.done_hup && keybuffer.Empty()) return '\033';
4910 #endif
4911     return keybuffer.GetAscii();
4912 }
4913 
qt_nh_poskey(int * x,int * y,int * mod)4914 int NetHackQtBind::qt_nh_poskey(int *x, int *y, int *mod)
4915 {
4916     if (main)
4917 	main->fadeHighlighting();
4918 
4919     // Process events until a key or map-click arrives.
4920     //
4921     while (keybuffer.Empty() && clickbuffer.Empty()
4922 #ifdef SAFERHANGUP
4923 	   && !program_state.done_hup
4924 #endif
4925 	   ) {
4926 	qApp->enter_loop();
4927     }
4928 #ifdef SAFERHANGUP
4929     if (program_state.done_hup && keybuffer.Empty()) return '\033';
4930 #endif
4931     if (!keybuffer.Empty()) {
4932 	return keybuffer.GetAscii();
4933     } else {
4934 	*x=clickbuffer.NextX();
4935 	*y=clickbuffer.NextY();
4936 	*mod=clickbuffer.NextMod();
4937 	clickbuffer.Get();
4938 	return 0;
4939     }
4940 }
4941 
qt_nhbell()4942 void NetHackQtBind::qt_nhbell()
4943 {
4944     QApplication::beep();
4945 }
4946 
qt_doprev_message()4947 int NetHackQtBind::qt_doprev_message()
4948 {
4949     // Don't need it - uses scrollbar
4950     // XXX but could make this a shortcut
4951     return 0;
4952 }
4953 
qt_yn_function(const char * question,const char * choices,CHAR_P def)4954 char NetHackQtBind::qt_yn_function(const char *question, const char *choices, CHAR_P def)
4955 {
4956     if (qt_settings->ynInMessages() && WIN_MESSAGE!=WIN_ERR) {
4957 	// Similar to X11 windowport `slow' feature.
4958 
4959 	char message[BUFSZ];
4960 	char yn_esc_map='\033';
4961 
4962 	if (choices) {
4963 	    char *cb, choicebuf[QBUFSZ];
4964 	    Strcpy(choicebuf, choices);
4965 	    if ((cb = index(choicebuf, '\033')) != 0) {
4966 		// anything beyond <esc> is hidden
4967 		*cb = '\0';
4968 	    }
4969 	    (void)strncpy(message, question, QBUFSZ-1);
4970 	    message[QBUFSZ-1] = '\0';
4971 	    Sprintf(eos(message), " [%s]", choicebuf);
4972 	    if (def) Sprintf(eos(message), " (%c)", def);
4973 	    Strcat(message, " ");
4974 	    // escape maps to 'q' or 'n' or default, in that order
4975 	    yn_esc_map = (index(choices, 'q') ? 'q' :
4976 		     (index(choices, 'n') ? 'n' : def));
4977 	} else {
4978 	    Strcpy(message, question);
4979 	}
4980 
4981 #ifdef USE_POPUPS
4982 	// Improve some special-cases (DIRKS 08/02/23)
4983 	if (strcmp (choices,"ynq") == 0) {
4984 	    switch (QMessageBox::information (qApp->mainWidget(),"NetHack",question,"&Yes","&No","&Quit",0,2))
4985 	    {
4986 	      case 0: return 'y';
4987 	      case 1: return 'n';
4988 	      case 2: return 'q';
4989 	    }
4990 	}
4991 
4992 	if (strcmp (choices,"yn") == 0) {
4993 	    switch (QMessageBox::information(qApp->mainWidget(),"NetHack",question,"&Yes", "&No",0,1))
4994 	    {
4995 	      case 0: return 'y';
4996 	      case 1: return 'n';
4997 	    }
4998 	}
4999 #endif
5000 
5001 	NetHackQtBind::qt_putstr(WIN_MESSAGE, ATR_BOLD, message);
5002 
5003 	int result=-1;
5004 	while (result<0) {
5005 	    char ch=NetHackQtBind::qt_nhgetch();
5006 	    if (ch=='\033') {
5007 		result=yn_esc_map;
5008 	    } else if (choices && !index(choices,ch)) {
5009 		if (def && (ch==' ' || ch=='\r' || ch=='\n')) {
5010 		    result=def;
5011 		} else {
5012 		    NetHackQtBind::qt_nhbell();
5013 		    // and try again...
5014 		}
5015 	    } else {
5016 		result=ch;
5017 	    }
5018 	}
5019 
5020 	NetHackQtBind::qt_clear_nhwindow(WIN_MESSAGE);
5021 
5022 	return result;
5023     } else {
5024 	NetHackQtYnDialog dialog(keybuffer,question,choices,def);
5025 	return dialog.Exec();
5026     }
5027 }
5028 
qt_getlin(const char * prompt,char * line)5029 void NetHackQtBind::qt_getlin(const char *prompt, char *line)
5030 {
5031     NetHackQtStringRequestor requestor(keybuffer,prompt);
5032     if (!requestor.Get(line)) {
5033 	line[0]=0;
5034     }
5035 }
5036 
NetHackQtExtCmdRequestor(NetHackQtKeyBuffer & ks)5037 NetHackQtExtCmdRequestor::NetHackQtExtCmdRequestor(NetHackQtKeyBuffer& ks) :
5038     QDialog(qApp->mainWidget(), "ext-cmd", FALSE),
5039     keysource(ks)
5040 {
5041     int marg=4;
5042     QVBoxLayout *l = new QVBoxLayout(this,marg,marg);
5043 
5044     QPushButton* can = new QPushButton("Cancel", this);
5045     can->setDefault(TRUE);
5046     can->setMinimumSize(can->sizeHint());
5047     l->addWidget(can);
5048 
5049     QButtonGroup *group=new QButtonGroup("",0);
5050     QGroupBox *grid=new QGroupBox("Extended commands",this);
5051     l->addWidget(grid);
5052 
5053     int i;
5054     int butw=50;
5055     QFontMetrics fm = fontMetrics();
5056     for (i=0; extcmdlist[i].ef_txt; i++) {
5057 	butw = QMAX(butw,30+fm.width(extcmdlist[i].ef_txt));
5058     }
5059     int ncols=4;
5060     int nrows=(i+ncols-1)/ncols;
5061 
5062     QVBoxLayout* bl = new QVBoxLayout(grid,marg);
5063     bl->addSpacing(fm.height());
5064     QGridLayout* gl = new QGridLayout(nrows,ncols,marg);
5065     bl->addLayout(gl);
5066     for (i=0; extcmdlist[i].ef_txt; i++) {
5067 	QPushButton* pb=new QPushButton(extcmdlist[i].ef_txt, grid);
5068 	pb->setMinimumSize(butw,pb->sizeHint().height());
5069 	group->insert(pb);
5070 	gl->addWidget(pb,i/ncols,i%ncols);
5071     }
5072     connect(group,SIGNAL(clicked(int)),this,SLOT(done(int)));
5073 
5074     bl->activate();
5075     l->activate();
5076     resize(1,1);
5077 
5078     connect(can,SIGNAL(clicked()),this,SLOT(cancel()));
5079 }
5080 
cancel()5081 void NetHackQtExtCmdRequestor::cancel()
5082 {
5083     setResult(-1);
5084     qApp->exit_loop();
5085 }
5086 
done(int i)5087 void NetHackQtExtCmdRequestor::done(int i)
5088 {
5089     setResult(i);
5090     qApp->exit_loop();
5091 }
5092 
get()5093 int NetHackQtExtCmdRequestor::get()
5094 {
5095     const int none = -10;
5096     char str[32];
5097     int cursor=0;
5098     resize(1,1); // pack
5099     centerOnMain(this);
5100     show();
5101     setResult(none);
5102     while (result()==none) {
5103 	while (result()==none && !keysource.Empty()) {
5104 	    char k=keysource.GetAscii();
5105 	    if (k=='\r' || k=='\n' || k==' ' || k=='\033') {
5106 		setResult(-1);
5107 	    } else {
5108 		str[cursor++] = k;
5109 		int r=-1;
5110 		for (int i=0; extcmdlist[i].ef_txt; i++) {
5111 		    if (qstrnicmp(str, extcmdlist[i].ef_txt, cursor)==0) {
5112 			if ( r == -1 )
5113 			    r = i;
5114 			else
5115 			    r = -2;
5116 		    }
5117 		}
5118 		if ( r == -1 ) { // no match!
5119 		    QApplication::beep();
5120 		    cursor=0;
5121 		} else if ( r != -2 ) { // only one match
5122 		    setResult(r);
5123 		}
5124 	    }
5125 	}
5126 	if (result()==none)
5127 	    qApp->enter_loop();
5128     }
5129     hide();
5130     return result();
5131 }
5132 
5133 
qt_get_ext_cmd()5134 int NetHackQtBind::qt_get_ext_cmd()
5135 {
5136     NetHackQtExtCmdRequestor requestor(keybuffer);
5137     return requestor.get();
5138 }
5139 
qt_number_pad(int)5140 void NetHackQtBind::qt_number_pad(int)
5141 {
5142     // Ignore.
5143 }
5144 
qt_delay_output()5145 void NetHackQtBind::qt_delay_output()
5146 {
5147     NetHackQtDelay delay(15);
5148     delay.wait();
5149 }
5150 
qt_start_screen()5151 void NetHackQtBind::qt_start_screen()
5152 {
5153     // Ignore.
5154 }
5155 
qt_end_screen()5156 void NetHackQtBind::qt_end_screen()
5157 {
5158     // Ignore.
5159 }
5160 
qt_outrip(winid wid,int how,time_t when)5161 void NetHackQtBind::qt_outrip(winid wid, int how, time_t when)
5162 {
5163     NetHackQtWindow* window=id_to_window[wid];
5164 
5165     window->UseRIP(how, when);
5166 }
5167 
notify(QObject * receiver,QEvent * event)5168 bool NetHackQtBind::notify(QObject *receiver, QEvent *event)
5169 {
5170     // Ignore Alt-key navigation to menubar, it's annoying when you
5171     // use Alt-Direction to move around.
5172     if ( main && event->type()==QEvent::KeyRelease && main==receiver
5173 	    && ((QKeyEvent*)event)->key() == Key_Alt )
5174 	return TRUE;
5175 
5176     bool result=QApplication::notify(receiver,event);
5177 #ifdef SAFERHANGUP
5178     if (program_state.done_hup) {
5179 	keybuffer.Put('\033');
5180 	qApp->exit_loop();
5181 	return TRUE;
5182     }
5183 #endif
5184     if (event->type()==QEvent::KeyPress) {
5185 	QKeyEvent* key_event=(QKeyEvent*)event;
5186 
5187 	if (!key_event->isAccepted()) {
5188 	    const int k=key_event->key();
5189 	    bool macro=FALSE;
5190 	    for (int i=0; !macro && key_macro[i].key; i++) {
5191 		if (key_macro[i].key==k
5192 		 && ((key_macro[i].state&key_event->state())==key_macro[i].state))
5193 		{
5194 		    keybuffer.Put(key_macro[i].macro);
5195 		    macro=TRUE;
5196 		}
5197 	    }
5198 	    char ch=key_event->ascii();
5199 	    if ( !ch && (key_event->state() & Qt::ControlButton) ) {
5200 		// On Mac, it aint-ncessarily-control
5201 		if ( k>=Qt::Key_A && k<=Qt::Key_Z )
5202 		    ch = k - Qt::Key_A + 1;
5203 	    }
5204 	    if (!macro && ch) {
5205 		bool alt = (key_event->state()&AltButton) ||
5206 		   (k >= Key_0 && k <= Key_9 && (key_event->state()&ControlButton));
5207 		keybuffer.Put(key_event->key(),ch + (alt ? 128 : 0),
5208 		    key_event->state());
5209 		key_event->accept();
5210 		result=TRUE;
5211 	    }
5212 
5213 	    if (ch || macro) {
5214 		qApp->exit_loop();
5215 	    }
5216 	}
5217     }
5218     return result;
5219 }
5220 
5221 NetHackQtBind* NetHackQtBind::instance=0;
5222 NetHackQtKeyBuffer NetHackQtBind::keybuffer;
5223 NetHackQtClickBuffer NetHackQtBind::clickbuffer;
5224 NetHackQtMainWindow* NetHackQtBind::main=0;
5225 QWidget* NetHackQtBind::splash=0;
5226 
5227 
5228 extern "C" struct window_procs Qt_procs;
5229 
5230 struct window_procs Qt_procs = {
5231     "Qt",
5232     WC_COLOR|WC_HILITE_PET|
5233 	WC_ASCII_MAP|WC_TILED_MAP|
5234 	WC_FONT_MAP|WC_TILE_FILE|WC_TILE_WIDTH|WC_TILE_HEIGHT|
5235 	WC_PLAYER_SELECTION|WC_SPLASH_SCREEN,
5236     0L,
5237     {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},   /* color availability */
5238     NetHackQtBind::qt_init_nhwindows,
5239     NetHackQtBind::qt_player_selection,
5240     NetHackQtBind::qt_askname,
5241     NetHackQtBind::qt_get_nh_event,
5242     NetHackQtBind::qt_exit_nhwindows,
5243     NetHackQtBind::qt_suspend_nhwindows,
5244     NetHackQtBind::qt_resume_nhwindows,
5245     NetHackQtBind::qt_create_nhwindow,
5246     NetHackQtBind::qt_clear_nhwindow,
5247     NetHackQtBind::qt_display_nhwindow,
5248     NetHackQtBind::qt_destroy_nhwindow,
5249     NetHackQtBind::qt_curs,
5250     NetHackQtBind::qt_putstr,
5251     genl_putmixed,
5252     NetHackQtBind::qt_display_file,
5253     NetHackQtBind::qt_start_menu,
5254     NetHackQtBind::qt_add_menu,
5255     NetHackQtBind::qt_end_menu,
5256     NetHackQtBind::qt_select_menu,
5257     genl_message_menu,      /* no need for X-specific handling */
5258     NetHackQtBind::qt_update_inventory,
5259     NetHackQtBind::qt_mark_synch,
5260     NetHackQtBind::qt_wait_synch,
5261 #ifdef CLIPPING
5262     NetHackQtBind::qt_cliparound,
5263 #endif
5264 #ifdef POSITIONBAR
5265     donull,
5266 #endif
5267     NetHackQtBind::qt_print_glyph,
5268     //NetHackQtBind::qt_print_glyph_compose,
5269     NetHackQtBind::qt_raw_print,
5270     NetHackQtBind::qt_raw_print_bold,
5271     NetHackQtBind::qt_nhgetch,
5272     NetHackQtBind::qt_nh_poskey,
5273     NetHackQtBind::qt_nhbell,
5274     NetHackQtBind::qt_doprev_message,
5275     NetHackQtBind::qt_yn_function,
5276     NetHackQtBind::qt_getlin,
5277     NetHackQtBind::qt_get_ext_cmd,
5278     NetHackQtBind::qt_number_pad,
5279     NetHackQtBind::qt_delay_output,
5280 #ifdef CHANGE_COLOR     /* only a Mac option currently */
5281     donull,
5282     donull,
5283 #endif
5284     /* other defs that really should go away (they're tty specific) */
5285     NetHackQtBind::qt_start_screen,
5286     NetHackQtBind::qt_end_screen,
5287 #ifdef GRAPHIC_TOMBSTONE
5288     NetHackQtBind::qt_outrip,
5289 #else
5290     genl_outrip,
5291 #endif
5292     genl_preference_update,
5293     genl_getmsghistory,
5294     genl_putmsghistory,
5295     genl_status_init,
5296     genl_status_finish,
5297     genl_status_enablefield,
5298     genl_status_update,
5299     genl_can_suspend_yes,
5300 };
5301 
play_usersound(const char * filename,int volume)5302 extern "C" void play_usersound(const char* filename, int volume)
5303 {
5304 #ifdef USER_SOUNDS
5305 #ifndef QT_NO_SOUND
5306     QSound::play(filename);
5307 #endif
5308 #endif
5309 }
5310 
5311 #include "qt_win.moc"
5312 #ifndef KDE
5313 #include "qt_kde0.moc"
5314 #endif
5315 #if QT_VERSION >= 300
5316 #include "qttableview.moc"
5317 #endif
5318