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