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