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