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