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