Occasionally remove maze dead ends, creating loops
[aNetHack.git] / win / Qt / qt_win.cpp
blobdb995f7ea36b352888aa005158190fcf15a58f22
1 // NetHack 3.6 qt_win.cpp $NHDT-Date$ $NHDT-Branch$:$NHDT-Revision$
2 // Copyright (c) Warwick Allison, 1999.
3 // NetHack may be freely redistributed. See license for details.
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.
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.
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.
43 extern "C" {
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.
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
77 #include "qt_win.h"
78 #include <qregexp.h>
79 #include <qpainter.h>
80 #include <qdir.h>
81 #include <qbitmap.h>
82 #include <qkeycode.h>
83 #include <qmenubar.h>
84 #include <qpopupmenu.h>
85 #include <qlayout.h>
86 #include <qheader.h>
87 #include <qradiobutton.h>
88 #include <qtoolbar.h>
89 #include <qtoolbutton.h>
90 #include <qcombobox.h>
91 #include <qvbox.h>
92 #include <qdragobject.h>
93 #include <qtextbrowser.h>
94 #include <qhbox.h>
95 #include <qsignalmapper.h>
96 //#include <qgrid.h>
97 //#include <qlabelled.h>
99 #include <ctype.h>
101 #include "qt_clust.h"
102 #include "qt_xpms.h"
104 #include <dirent.h>
105 #ifdef Q_WS_MACX
106 # include <sys/malloc.h>
107 #else
108 # include <malloc.h>
109 #endif
111 #ifdef _WS_X11_
112 // For userid control
113 #include <unistd.h>
114 #endif
116 // Some distributors released Qt 2.1.0beta4
117 #if QT_VERSION < 220
118 # define nh_WX11BypassWM 0x01000000
119 #else
120 # define nh_WX11BypassWM WX11BypassWM
121 #endif
123 #ifdef USER_SOUNDS
124 # if QT_VERSION < 220
125 # undef USER_SOUNDS
126 # else
127 # include <qsound.h>
128 # endif
129 #endif
132 #ifdef USER_SOUNDS
133 extern "C" void play_sound_for_message(const char* str);
134 #endif
136 #ifdef SAFERHANGUP
137 #include <qtimer.h>
138 #endif
140 // Warwick prefers it this way...
141 #define QT_CHOOSE_RACE_FIRST
143 static const char nh_attribution[] = "<center><big>NetHack</big>"
144 "<br><small>by the NetHack DevTeam</small></center>";
146 static QString
147 aboutMsg()
149 QString msg;
150 msg.sprintf(
151 "Qt NetHack is a version of NetHack built\n"
152 #ifdef KDE
153 "using KDE and the Qt GUI toolkit.\n"
154 #else
155 "using the Qt GUI toolkit.\n"
156 #endif
157 "This is version %d.%d.%d\n\n"
158 "Homepage:\n http://trolls.troll.no/warwick/nethack/\n\n"
159 #ifdef KDE
160 "KDE:\n http://www.kde.org\n"
161 #endif
162 "Qt:\n http://www.troll.no",
163 VERSION_MAJOR,
164 VERSION_MINOR,
165 PATCHLEVEL);
166 return msg;
169 static void
170 centerOnMain( QWidget* w )
172 QWidget* m = qApp->mainWidget();
173 if (!m) m = qApp->desktop();
174 QPoint p = m->mapToGlobal(QPoint(0,0));
175 w->move( p.x() + m->width()/2 - w->width()/2,
176 p.y() + m->height()/2 - w->height()/2 );
179 NetHackQtLineEdit::NetHackQtLineEdit() :
180 QLineEdit(0)
184 NetHackQtLineEdit::NetHackQtLineEdit(QWidget* parent, const char* name) :
185 QLineEdit(parent,name)
189 void NetHackQtLineEdit::fakeEvent(int key, int ascii, int state)
191 QKeyEvent fake(QEvent::KeyPress,key,ascii,state);
192 keyPressEvent(&fake);
195 extern "C" {
196 /* Used by tile/font-size patch below and in ../../src/files.c */
197 char *qt_tilewidth=NULL;
198 char *qt_tileheight=NULL;
199 char *qt_fontsize=NULL;
200 #if defined(QWS)
201 int qt_compact_mode = 1;
202 #else
203 int qt_compact_mode = 0;
204 #endif
205 extern const char *enc_stat[]; /* from botl.c */
206 extern const char *hu_stat[]; /* from eat.c */
207 extern int total_tiles_used; // from tile.c
208 extern short glyph2tile[]; // from tile.c
211 static int tilefile_tile_W=16;
212 static int tilefile_tile_H=16;
214 #define TILEWMIN 1
215 #define TILEHMIN 1
218 /* XPM */
219 static const char * nh_icon[] = {
220 "40 40 6 1",
221 " s None c none",
222 ". c #ffffff",
223 "X c #dadab6",
224 "o c #6c91b6",
225 "O c #476c6c",
226 "+ c #000000",
227 " ",
228 " ",
229 " ",
230 " . .X..XX.XX X ",
231 " .. .....X.XXXXXX XX ",
232 " ... ....X..XX.XXXXX XXX ",
233 " .. ..........X.XXXXXXXXXXX XX ",
234 " .... ........X..XX.XXXXXXXXX XXXX ",
235 " .... ..........X.XXXXXXXXXXX XXXX ",
236 " ooOOO..ooooooOooOOoOOOOOOOXX+++OO++ ",
237 " ooOOO..ooooooooOoOOOOOOOOOXX+++OO++ ",
238 " ....O..ooooooOooOOoOOOOOOOXX+XXXX++ ",
239 " ....O..ooooooooOoOOOOOOOOOXX+XXXX++ ",
240 " ..OOO..ooooooOooOOoOOOOOOOXX+++XX++ ",
241 " ++++..ooooooooOoOOOOOOOOOXX+++ +++ ",
242 " +++..ooooooOooOOoOOOOOOOXX+++ + ",
243 " ++..ooooooooOoOOOOOOOOOXX+++ ",
244 " ..ooooooOooOOoOOOOOOOXX+++ ",
245 " ..ooooooooOoOOOOOOOOOXX+++ ",
246 " ..ooooooOooOOoOOOOOOOXX+++ ",
247 " ..ooooooooOoOOOOOOOOOXX+++ ",
248 " ..oooooOooOOoOOOOOOXX+++ ",
249 " ..oooooooOoOOOOOOOOXX+++ ",
250 " ..ooooOooOOoOOOOOXX+++ ",
251 " ..ooooooOoOOOOOOOXX++++ ",
252 " ..o..oooOooOOoOOOOXX+XX+++ ",
253 " ...o..oooooOoOOOOOXX++XXX++ ",
254 " ....OO..ooOooOOoOOXX+++XXXX++ ",
255 " ...oo..+..oooOoOOOXX++XXooXXX++ ",
256 " ...ooo..++..OooOOoXX+++XXooOXXX+ ",
257 " ..oooOOXX+++....XXXX++++XXOOoOOXX+ ",
258 " ..oooOOXX+++ ...XXX+++++XXOOooOXX++ ",
259 " ..oooOXXX+++ ..XX+++ +XXOOooOXX++ ",
260 " .....XXX++++ XXXXXXX++ ",
261 " ....XX++++ XXXXXXX+ ",
262 " ...XX+++ XXXXX++ ",
263 " ",
264 " ",
265 " ",
266 " "};
267 /* XPM */
268 static const char * nh_icon_small[] = {
269 /* width height ncolors chars_per_pixel */
270 "16 16 16 1",
271 /* colors */
272 " c #587070",
273 ". c #D1D5C9",
274 "X c #8B8C84",
275 "o c #2A2A28",
276 "O c #9AABA9",
277 "+ c #6A8FB2",
278 "@ c #C4CAC4",
279 "# c #B6BEB6",
280 "$ c None",
281 "% c #54564E",
282 "& c #476C6C",
283 "* c #ADB2AB",
284 "= c #ABABA2",
285 "- c #5E8295",
286 "; c #8B988F",
287 ": c #E8EAE7",
288 /* pixels */
289 "$$$$$$$$$$$$$$$$",
290 "$$$.$#::.#==*$$$",
291 "$.*:::::....#*=$",
292 "$@#:..@#*==#;XX;",
293 "$@O:+++- &&; X%X",
294 "$#%.+++- &&;% oX",
295 "$$o.++-- &&;%%X$",
296 "$$$:++-- &&;%%$$",
297 "$$$.O++- &&=o $$",
298 "$$$=:++- & XoX$$",
299 "$$*:@O-- ;%Xo$$",
300 "$*:O#$+--;oOOX $",
301 "$:+ =o::=oo=-;%X",
302 "$::.%o$*;X;##@%$",
303 "$$@# ;$$$$$=*;X$",
304 "$$$$$$$$$$$$$$$$"
307 /* XPM */
308 static const char * map_xpm[] = {
309 "12 13 4 1",
310 ". c None",
311 " c #000000000000",
312 "X c #0000B6DAFFFF",
313 "o c #69A69248B6DA",
314 " .",
315 " XXXXX ooo ",
316 " XoooX o ",
317 " XoooX o o ",
318 " XoooX ooo ",
319 " XXoXX o ",
320 " oooooXXX ",
321 " oo o oooX ",
322 " o XooX ",
323 " oooo XooX ",
324 " o o XXXX ",
325 " ",
326 ". "};
327 /* XPM */
328 static const char * msg_xpm[] = {
329 "12 13 4 1",
330 ". c None",
331 " c #FFFFFFFFFFFF",
332 "X c #69A69248B6DA",
333 "o c #000000000000",
334 " .",
335 " XXX XXX X o",
336 " o",
337 " XXXXX XX o",
338 " o",
339 " XX XXXXX o",
340 " o",
341 " XXXXXX o",
342 " o",
343 " XX XXX XX o",
344 " o",
345 " o",
346 ".ooooooooooo"};
347 /* XPM */
348 static const char * stat_xpm[] = {
349 "12 13 5 1",
350 " c None",
351 ". c #FFFF00000000",
352 "X c #000000000000",
353 "o c #FFFFFFFF0000",
354 "O c #69A6FFFF0000",
355 " ",
356 " ",
357 "... ",
358 "...X ",
359 "...X ... ",
360 "oooX oooX",
361 "oooXooo oooX",
362 "OOOXOOOXOOOX",
363 "OOOXOOOXOOOX",
364 "OOOXOOOXOOOX",
365 "OOOXOOOXOOOX",
366 "OOOXOOOXOOOX",
367 " XXXXXXXXXXX"};
368 /* XPM */
369 static const char * info_xpm[] = {
370 "12 13 4 1",
371 " c None",
372 ". c #00000000FFFF",
373 "X c #FFFFFFFFFFFF",
374 "o c #000000000000",
375 " ... ",
376 " ....... ",
377 " ...XXX... ",
378 " .........o ",
379 "...XXXX.... ",
380 "....XXX....o",
381 "....XXX....o",
382 "....XXX....o",
383 " ...XXX...oo",
384 " ..XXXXX..o ",
385 " .......oo ",
386 " o...ooo ",
387 " ooo "};
390 /* XPM */
391 static const char * again_xpm[] = {
392 "12 13 2 1",
393 " c None",
394 ". c #000000000000",
395 " .. ",
396 " .. ",
397 " ..... ",
398 " ....... ",
399 "... .. .. ",
400 ".. .. .. ",
401 ".. ..",
402 ".. ..",
403 ".. ..",
404 " .. .. ",
405 " .......... ",
406 " ...... ",
407 " "};
408 /* XPM */
409 static const char * kick_xpm[] = {
410 "12 13 3 1",
411 " c None",
412 ". c #000000000000",
413 "X c #FFFF6DB60000",
414 " ",
415 " ",
416 " . . . ",
417 " ... . . ",
418 " ... . ",
419 " ... . ",
420 " ... ",
421 "XXX ... ",
422 "XXX. ... ",
423 "XXX. ... ",
424 "XXX. .. ",
425 " ... ",
426 " "};
427 /* XPM */
428 static const char * throw_xpm[] = {
429 "12 13 3 1",
430 " c None",
431 ". c #FFFF6DB60000",
432 "X c #000000000000",
433 " ",
434 " ",
435 " ",
436 " ",
437 ".... X ",
438 "....X X ",
439 "....X XXXXXX",
440 "....X X ",
441 " XXXX X ",
442 " ",
443 " ",
444 " ",
445 " "};
446 /* XPM */
447 static const char * fire_xpm[] = {
448 "12 13 5 1",
449 " c None",
450 ". c #B6DA45140000",
451 "X c #FFFFB6DA9658",
452 "o c #000000000000",
453 "O c #FFFF6DB60000",
454 " . ",
455 " X. ",
456 " X . ",
457 " X .o ",
458 " X . o ",
459 " X .o o ",
460 "OOOOOOOOoooo",
461 " X .o o ",
462 " X . o o ",
463 " X .o ",
464 " X. o ",
465 " . o ",
466 " o "};
467 /* XPM */
468 static const char * get_xpm[] = {
469 "12 13 3 1",
470 " c None",
471 ". c #000000000000",
472 "X c #FFFF6DB60000",
473 " ",
474 " . ",
475 " ... ",
476 " . . . ",
477 " . ",
478 " . ",
479 " ",
480 " XXXXX ",
481 " XXXXX. ",
482 " XXXXX. ",
483 " XXXXX. ",
484 " ..... ",
485 " "};
486 /* XPM */
487 static const char * drop_xpm[] = {
488 "12 13 3 1",
489 " c None",
490 ". c #FFFF6DB60000",
491 "X c #000000000000",
492 " ",
493 " ..... ",
494 " .....X ",
495 " .....X ",
496 " .....X ",
497 " XXXXX ",
498 " ",
499 " X ",
500 " X ",
501 " X X X ",
502 " XXX ",
503 " X ",
504 " "};
505 /* XPM */
506 static const char * eat_xpm[] = {
507 "12 13 4 1",
508 " c None",
509 ". c #000000000000",
510 "X c #FFFFB6DA9658",
511 "o c #FFFF6DB60000",
512 " .X. .. ",
513 " .X. .. ",
514 " .X. .. ",
515 " .X. .. ",
516 " ... .. ",
517 " .. .. ",
518 " .. .. ",
519 " oo oo ",
520 " oo oo ",
521 " oo oo ",
522 " oo oo ",
523 " oo oo ",
524 " oo oo "};
525 /* XPM */
526 static const char * rest_xpm[] = {
527 "12 13 2 1",
528 " c None",
529 ". c #000000000000",
530 " ..... ",
531 " . ",
532 " . ",
533 " . ....",
534 " ..... . ",
535 " . ",
536 " ....",
537 " ",
538 " .... ",
539 " . ",
540 " . ",
541 " .... ",
542 " "};
543 /* XPM */
544 static const char * cast_a_xpm[] = {
545 "12 13 3 1",
546 " c None",
547 ". c #FFFF6DB60000",
548 "X c #000000000000",
549 " . ",
550 " . ",
551 " .. ",
552 " .. ",
553 " .. . ",
554 " .. . ",
555 " ...... ",
556 " .. .. XX ",
557 " .. X X ",
558 " .. X X ",
559 " .. XXXX ",
560 " . X X ",
561 " . X X "};
562 /* XPM */
563 static const char * cast_b_xpm[] = {
564 "12 13 3 1",
565 " c None",
566 ". c #FFFF6DB60000",
567 "X c #000000000000",
568 " . ",
569 " . ",
570 " .. ",
571 " .. ",
572 " .. . ",
573 " .. . ",
574 " ...... ",
575 " .. .. XXX ",
576 " .. X X ",
577 " .. XXX ",
578 " .. X X ",
579 " . X X ",
580 " . XXX "};
581 /* XPM */
582 static const char * cast_c_xpm[] = {
583 "12 13 3 1",
584 " c None",
585 ". c #FFFF6DB60000",
586 "X c #000000000000",
587 " . ",
588 " . ",
589 " .. ",
590 " .. ",
591 " .. . ",
592 " .. . ",
593 " ...... ",
594 " .. .. XX ",
595 " .. X X ",
596 " .. X ",
597 " .. X ",
598 " . X X ",
599 " . XX "};
601 NetHackQtSettings::NetHackQtSettings(int w, int h) :
602 tilewidth(TILEWMIN,64,1,this),
603 tileheight(TILEHMIN,64,1,this),
604 widthlbl(&tilewidth,"&Width:",this),
605 heightlbl(&tileheight,"&Height:",this),
606 whichsize("&Zoomed",this),
607 fontsize(this),
608 normal("times"),
609 #ifdef WS_WIN
610 normalfixed("courier new"),
611 #else
612 normalfixed("fixed"),
613 #endif
614 large("times"),
615 theglyphs(0)
618 int default_fontsize;
620 if (w<=300) {
621 // ~240x320
622 default_fontsize=4;
623 tilewidth.setValue(8);
624 tileheight.setValue(12);
625 } else if (w<=700) {
626 // ~640x480
627 default_fontsize=3;
628 tilewidth.setValue(8);
629 tileheight.setValue(14);
630 } else if (w<=900) {
631 // ~800x600
632 default_fontsize=3;
633 tilewidth.setValue(10);
634 tileheight.setValue(17);
635 } else if (w<=1100) {
636 // ~1024x768
637 default_fontsize=2;
638 tilewidth.setValue(12);
639 tileheight.setValue(22);
640 } else if (w<=1200) {
641 // ~1152x900
642 default_fontsize=1;
643 tilewidth.setValue(14);
644 tileheight.setValue(26);
645 } else {
646 // ~1280x1024 and larger
647 default_fontsize=0;
648 tilewidth.setValue(16);
649 tileheight.setValue(30);
652 // Tile/font sizes read from .nethackrc
653 if (qt_tilewidth != NULL) {
654 tilewidth.setValue(atoi(qt_tilewidth));
655 free(qt_tilewidth);
657 if (qt_tileheight != NULL) {
658 tileheight.setValue(atoi(qt_tileheight));
659 free(qt_tileheight);
661 if (qt_fontsize != NULL) {
662 switch (tolower(qt_fontsize[0])) {
663 case 'h': default_fontsize = 0; break;
664 case 'l': default_fontsize = 1; break;
665 case 'm': default_fontsize = 2; break;
666 case 's': default_fontsize = 3; break;
667 case 't': default_fontsize = 4; break;
669 free(qt_fontsize);
672 theglyphs=new NetHackQtGlyphs();
673 resizeTiles();
675 connect(&tilewidth,SIGNAL(valueChanged(int)),this,SLOT(resizeTiles()));
676 connect(&tileheight,SIGNAL(valueChanged(int)),this,SLOT(resizeTiles()));
677 connect(&whichsize,SIGNAL(toggled(bool)),this,SLOT(setGlyphSize(bool)));
679 fontsize.insertItem("Huge");
680 fontsize.insertItem("Large");
681 fontsize.insertItem("Medium");
682 fontsize.insertItem("Small");
683 fontsize.insertItem("Tiny");
684 fontsize.setCurrentItem(default_fontsize);
685 connect(&fontsize,SIGNAL(activated(int)),this,SIGNAL(fontChanged()));
687 QGridLayout* grid = new QGridLayout(this, 5, 2, 8);
688 grid->addMultiCellWidget(&whichsize, 0, 0, 0, 1);
689 grid->addWidget(&tilewidth, 1, 1); grid->addWidget(&widthlbl, 1, 0);
690 grid->addWidget(&tileheight, 2, 1); grid->addWidget(&heightlbl, 2, 0);
691 QLabel* flabel=new QLabel(&fontsize, "&Font:",this);
692 grid->addWidget(flabel, 3, 0); grid->addWidget(&fontsize, 3, 1);
693 QPushButton* dismiss=new QPushButton("Dismiss",this);
694 dismiss->setDefault(TRUE);
695 grid->addMultiCellWidget(dismiss, 4, 4, 0, 1);
696 grid->setRowStretch(4,0);
697 grid->setColStretch(1,1);
698 grid->setColStretch(2,2);
699 grid->activate();
701 connect(dismiss,SIGNAL(clicked()),this,SLOT(accept()));
702 resize(150,140);
705 NetHackQtGlyphs& NetHackQtSettings::glyphs()
707 return *theglyphs;
710 void NetHackQtSettings::resizeTiles()
712 int w = tilewidth.value();
713 int h = tileheight.value();
715 theglyphs->setSize(w,h);
716 emit tilesChanged();
719 void NetHackQtSettings::toggleGlyphSize()
721 whichsize.toggle();
724 void NetHackQtSettings::setGlyphSize(bool which)
726 QSize n = QSize(tilewidth.value(),tileheight.value());
727 if ( othersize.isValid() ) {
728 tilewidth.blockSignals(TRUE);
729 tileheight.blockSignals(TRUE);
730 tilewidth.setValue(othersize.width());
731 tileheight.setValue(othersize.height());
732 tileheight.blockSignals(FALSE);
733 tilewidth.blockSignals(FALSE);
734 resizeTiles();
736 othersize = n;
739 const QFont& NetHackQtSettings::normalFont()
741 static int size[]={ 18, 14, 12, 10, 8 };
742 normal.setPointSize(size[fontsize.currentItem()]);
743 return normal;
746 const QFont& NetHackQtSettings::normalFixedFont()
748 static int size[]={ 18, 14, 13, 10, 8 };
749 normalfixed.setPointSize(size[fontsize.currentItem()]);
750 return normalfixed;
753 const QFont& NetHackQtSettings::largeFont()
755 static int size[]={ 24, 18, 14, 12, 10 };
756 large.setPointSize(size[fontsize.currentItem()]);
757 return large;
760 bool NetHackQtSettings::ynInMessages()
762 return !qt_compact_mode;
766 NetHackQtSettings* qt_settings;
770 NetHackQtKeyBuffer::NetHackQtKeyBuffer() :
771 in(0), out(0)
775 bool NetHackQtKeyBuffer::Empty() const { return in==out; }
776 bool NetHackQtKeyBuffer::Full() const { return (in+1)%maxkey==out; }
778 void NetHackQtKeyBuffer::Put(int k, int a, int state)
780 if ( Full() ) return; // Safety
781 key[in]=k;
782 ascii[in]=a;
783 in=(in+1)%maxkey;
786 void NetHackQtKeyBuffer::Put(char a)
788 Put(0,a,0);
791 void NetHackQtKeyBuffer::Put(const char* str)
793 while (*str) Put(*str++);
796 int NetHackQtKeyBuffer::GetKey()
798 if ( Empty() ) return 0;
799 int r=TopKey();
800 out=(out+1)%maxkey;
801 return r;
804 int NetHackQtKeyBuffer::GetAscii()
806 if ( Empty() ) return 0; // Safety
807 int r=TopAscii();
808 out=(out+1)%maxkey;
809 return r;
812 int NetHackQtKeyBuffer::GetState()
814 if ( Empty() ) return 0;
815 int r=TopState();
816 out=(out+1)%maxkey;
817 return r;
820 int NetHackQtKeyBuffer::TopKey() const
822 if ( Empty() ) return 0;
823 return key[out];
826 int NetHackQtKeyBuffer::TopAscii() const
828 if ( Empty() ) return 0;
829 return ascii[out];
832 int NetHackQtKeyBuffer::TopState() const
834 if ( Empty() ) return 0;
835 return state[out];
839 NetHackQtClickBuffer::NetHackQtClickBuffer() :
840 in(0), out(0)
844 bool NetHackQtClickBuffer::Empty() const { return in==out; }
845 bool NetHackQtClickBuffer::Full() const { return (in+1)%maxclick==out; }
847 void NetHackQtClickBuffer::Put(int x, int y, int mod)
849 click[in].x=x;
850 click[in].y=y;
851 click[in].mod=mod;
852 in=(in+1)%maxclick;
855 int NetHackQtClickBuffer::NextX() const { return click[out].x; }
856 int NetHackQtClickBuffer::NextY() const { return click[out].y; }
857 int NetHackQtClickBuffer::NextMod() const { return click[out].mod; }
859 void NetHackQtClickBuffer::Get()
861 out=(out+1)%maxclick;
864 class NhPSListViewItem : public QListViewItem {
865 public:
866 NhPSListViewItem( QListView* parent, const QString& name ) :
867 QListViewItem(parent, name)
871 void setGlyph(int g)
873 NetHackQtGlyphs& glyphs = qt_settings->glyphs();
874 int gw = glyphs.width();
875 int gh = glyphs.height();
876 QPixmap pm(gw,gh);
877 QPainter p(&pm);
878 glyphs.drawGlyph(p, g, 0, 0);
879 p.end();
880 setPixmap(0,pm);
881 setHeight(QMAX(pm.height()+1,height()));
884 void paintCell( QPainter *p, const QColorGroup &cg,
885 int column, int width, int alignment )
887 if ( isSelectable() ) {
888 QListViewItem::paintCell( p, cg, column, width, alignment );
889 } else {
890 QColorGroup disabled(
891 cg.foreground().light(),
892 cg.button().light(),
893 cg.light(), cg.dark(), cg.mid(),
894 gray, cg.base() );
895 QListViewItem::paintCell( p, disabled, column, width, alignment );
900 class NhPSListViewRole : public NhPSListViewItem {
901 public:
902 NhPSListViewRole( QListView* parent, int id ) :
903 NhPSListViewItem(parent,
904 #ifdef QT_CHOOSE_RACE_FIRST // Lowerize - looks better
905 QString(QChar(roles[id].name.m[0])).lower()+QString(roles[id].name.m+1)
906 #else
907 roles[id].name.m
908 #endif
911 setGlyph(monnum_to_glyph(roles[id].malenum));
915 class NhPSListViewRace : public NhPSListViewItem {
916 public:
917 NhPSListViewRace( QListView* parent, int id ) :
918 NhPSListViewItem(parent,
919 #ifdef QT_CHOOSE_RACE_FIRST // Capitalize - looks better
920 QString(QChar(races[id].noun[0])).upper()+QString(races[id].noun+1)
921 #else
922 QString(QChar(races[id].noun[0])+QString(races[id].noun+1))
923 #endif
926 setGlyph(monnum_to_glyph(races[id].malenum));
930 class NhPSListView : public QListView {
931 public:
932 NhPSListView( QWidget* parent ) :
933 QListView(parent)
935 setSorting(-1); // order is identity
936 header()->setClickEnabled(FALSE);
939 QSizePolicy sizePolicy() const
941 return QSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
944 QSize minimumSizeHint() const
946 return sizeHint();
949 QSize sizeHint() const
951 QListView::sizeHint();
952 QSize sz = header()->sizeHint();
953 int h=0;
954 QListViewItem* c=firstChild();
955 while (c) h+=c->height(),c = c->nextSibling();
956 sz += QSize(frameWidth()*2, h+frameWidth()*2);
957 return sz;
960 int selectedItemNumber() const
962 int i=0;
963 QListViewItem* c = firstChild();
964 while (c) {
965 if (c == selectedItem()) {
966 return i;
968 i++;
969 c = c->nextSibling();
971 return -1;
974 void setSelectedItemNumber(int i)
976 QListViewItem* c=firstChild();
977 while (i--)
978 c = c->nextSibling();
979 c->setSelected(TRUE);
983 NetHackQtPlayerSelector::NetHackQtPlayerSelector(NetHackQtKeyBuffer& ks) :
984 QDialog(qApp->mainWidget(),"plsel",TRUE),
985 keysource(ks),
986 fully_specified_role(TRUE)
989 0 1 2
990 + Name ------------------------------------+
991 0 | |
992 + ---- ------------------------------------+
993 + Role ---+ + Race ---+ + Gender ------+
994 | | | | | * Male |
995 1 | | | | | * Female |
996 | | | | +--------------+
997 | | | |
998 | | | | + Alignment ---+
999 2 | | | | | * Male |
1000 | | | | | * Female |
1001 | | | | +--------------+
1002 3 | | | | ...stretch...
1003 | | | |
1004 4 | | | | [ Play ]
1005 5 | | | | [ Quit ]
1006 +---------+ +---------+
1009 int marg=4;
1010 QGridLayout *l = new QGridLayout(this,6,3,marg,marg);
1012 QButtonGroup* namebox = new QButtonGroup(1,Horizontal,"Name",this);
1013 QLineEdit* name = new QLineEdit(namebox);
1014 name->setMaxLength(sizeof(plname)-1);
1015 if ( strncmp(plname,"player",6) && strncmp(plname,"games",5) )
1016 name->setText(plname);
1017 connect(name, SIGNAL(textChanged(const QString&)),
1018 this, SLOT(selectName(const QString&)) );
1019 name->setFocus();
1020 QButtonGroup* genderbox = new QButtonGroup("Sex",this);
1021 QButtonGroup* alignbox = new QButtonGroup("Alignment",this);
1022 QVBoxLayout* vbgb = new QVBoxLayout(genderbox,3,1);
1023 vbgb->setAutoAdd(TRUE);
1024 vbgb->addSpacing(fontMetrics().height()*3/4);
1025 QVBoxLayout* vbab = new QVBoxLayout(alignbox,3,1);
1026 vbab->setAutoAdd(TRUE);
1027 vbab->addSpacing(fontMetrics().height());
1028 QLabel* logo = new QLabel(nh_attribution, this);
1030 l->addMultiCellWidget( namebox, 0,0,0,2 );
1031 #ifdef QT_CHOOSE_RACE_FIRST
1032 race = new NhPSListView(this);
1033 role = new NhPSListView(this);
1034 l->addMultiCellWidget( race, 1,5,0,0 );
1035 l->addMultiCellWidget( role, 1,5,1,1 );
1036 #else
1037 role = new NhPSListView(this);
1038 race = new NhPSListView(this);
1039 l->addMultiCellWidget( role, 1,5,0,0 );
1040 l->addMultiCellWidget( race, 1,5,1,1 );
1041 #endif
1042 role->addColumn("Role");
1043 race->addColumn("Race");
1045 l->addWidget( genderbox, 1, 2 );
1046 l->addWidget( alignbox, 2, 2 );
1047 l->addWidget( logo, 3, 2, AlignCenter );
1048 l->setRowStretch( 3, 5 );
1050 int i;
1051 int nrole;
1053 for (nrole=0; roles[nrole].name.m; nrole++)
1055 for (i=nrole-1; i>=0; i--) { // XXX QListView unsorted goes in rev.
1056 new NhPSListViewRole( role, i );
1058 connect( role, SIGNAL(selectionChanged()), this, SLOT(selectRole()) );
1060 int nrace;
1061 for (nrace=0; races[nrace].noun; nrace++)
1063 for (i=nrace-1; i>=0; i--) {
1064 new NhPSListViewRace( race, i );
1066 connect( race, SIGNAL(selectionChanged()), this, SLOT(selectRace()) );
1068 gender = new QRadioButton*[ROLE_GENDERS];
1069 for (i=0; i<ROLE_GENDERS; i++) {
1070 gender[i] = new QRadioButton( genders[i].adj, genderbox );
1072 connect( genderbox, SIGNAL(clicked(int)), this, SLOT(selectGender(int)) );
1074 alignment = new QRadioButton*[ROLE_ALIGNS];
1075 for (i=0; i<ROLE_ALIGNS; i++) {
1076 alignment[i] = new QRadioButton( aligns[i].adj, alignbox );
1078 connect( alignbox, SIGNAL(clicked(int)), this, SLOT(selectAlignment(int)) );
1080 QPushButton* ok = new QPushButton("Play",this);
1081 l->addWidget( ok, 4, 2 );
1082 ok->setDefault(TRUE);
1083 connect( ok, SIGNAL(clicked()), this, SLOT(accept()) );
1085 QPushButton* cancel = new QPushButton("Quit",this);
1086 l->addWidget( cancel, 5, 2 );
1087 connect( cancel, SIGNAL(clicked()), this, SLOT(reject()) );
1089 // Randomize race and role, unless specified in config
1090 int ro = flags.initrole;
1091 if (ro == ROLE_NONE || ro == ROLE_RANDOM) {
1092 ro = rn2(nrole);
1093 if (flags.initrole != ROLE_RANDOM) {
1094 fully_specified_role = FALSE;
1097 int ra = flags.initrace;
1098 if (ra == ROLE_NONE || ra == ROLE_RANDOM) {
1099 ra = rn2(nrace);
1100 if (flags.initrace != ROLE_RANDOM) {
1101 fully_specified_role = FALSE;
1105 // make sure we have a valid combination, honoring
1106 // the users request if possible.
1107 bool choose_race_first;
1108 #ifdef QT_CHOOSE_RACE_FIRST
1109 choose_race_first = TRUE;
1110 if (flags.initrole >= 0 && flags.initrace < 0) {
1111 choose_race_first = FALSE;
1113 #else
1114 choose_race_first = FALSE;
1115 if (flags.initrace >= 0 && flags.initrole < 0) {
1116 choose_race_first = TRUE;
1118 #endif
1119 while (!validrace(ro,ra)) {
1120 if (choose_race_first) {
1121 ro = rn2(nrole);
1122 if (flags.initrole != ROLE_RANDOM) {
1123 fully_specified_role = FALSE;
1125 } else {
1126 ra = rn2(nrace);
1127 if (flags.initrace != ROLE_RANDOM) {
1128 fully_specified_role = FALSE;
1133 int g = flags.initgend;
1134 if (g == -1) {
1135 g = rn2(ROLE_GENDERS);
1136 fully_specified_role = FALSE;
1138 while (!validgend(ro,ra,g)) {
1139 g = rn2(ROLE_GENDERS);
1141 gender[g]->setChecked(TRUE);
1142 selectGender(g);
1144 int a = flags.initalign;
1145 if (a == -1) {
1146 a = rn2(ROLE_ALIGNS);
1147 fully_specified_role = FALSE;
1149 while (!validalign(ro,ra,a)) {
1150 a = rn2(ROLE_ALIGNS);
1152 alignment[a]->setChecked(TRUE);
1153 selectAlignment(a);
1155 QListViewItem* li;
1157 li = role->firstChild();
1158 while (ro--) li=li->nextSibling();
1159 role->setSelected(li,TRUE);
1161 li = race->firstChild();
1162 while (ra--) li=li->nextSibling();
1163 race->setSelected(li,TRUE);
1165 flags.initrace = race->selectedItemNumber();
1166 flags.initrole = role->selectedItemNumber();
1170 void NetHackQtPlayerSelector::selectName(const QString& n)
1172 strncpy(plname,n.latin1(),sizeof(plname)-1);
1175 void NetHackQtPlayerSelector::selectRole()
1177 int ra = race->selectedItemNumber();
1178 int ro = role->selectedItemNumber();
1179 if (ra == -1 || ro == -1) return;
1181 #ifdef QT_CHOOSE_RACE_FIRST
1182 selectRace();
1183 #else
1184 QListViewItem* i=role->currentItem();
1185 QListViewItem* valid=0;
1186 int j;
1187 NhPSListViewItem* item;
1188 item = (NhPSListViewItem*)role->firstChild();
1189 for (j=0; roles[j].name.m; j++) {
1190 bool v = validrace(j,ra);
1191 item->setSelectable(TRUE);
1192 if ( !valid && v ) valid = item;
1193 item=(NhPSListViewItem*)item->nextSibling();
1195 if ( !validrace(role->selectedItemNumber(),ra) )
1196 i = valid;
1197 role->setSelected(i,TRUE);
1198 item = (NhPSListViewItem*)role->firstChild();
1199 for (j=0; roles[j].name.m; j++) {
1200 bool v = validrace(j,ra);
1201 item->setSelectable(v);
1202 item->repaint();
1203 item=(NhPSListViewItem*)item->nextSibling();
1205 #endif
1207 flags.initrole = role->selectedItemNumber();
1208 setupOthers();
1211 void NetHackQtPlayerSelector::selectRace()
1213 int ra = race->selectedItemNumber();
1214 int ro = role->selectedItemNumber();
1215 if (ra == -1 || ro == -1) return;
1217 #ifndef QT_CHOOSE_RACE_FIRST
1218 selectRole();
1219 #else
1220 QListViewItem* i=race->currentItem();
1221 QListViewItem* valid=0;
1222 int j;
1223 NhPSListViewItem* item;
1224 item = (NhPSListViewItem*)race->firstChild();
1225 for (j=0; races[j].noun; j++) {
1226 bool v = validrace(ro,j);
1227 item->setSelectable(TRUE);
1228 if ( !valid && v ) valid = item;
1229 item=(NhPSListViewItem*)item->nextSibling();
1231 if ( !validrace(ro,race->selectedItemNumber()) )
1232 i = valid;
1233 race->setSelected(i,TRUE);
1234 item = (NhPSListViewItem*)race->firstChild();
1235 for (j=0; races[j].noun; j++) {
1236 bool v = validrace(ro,j);
1237 item->setSelectable(v);
1238 item->repaint();
1239 item=(NhPSListViewItem*)item->nextSibling();
1241 #endif
1243 flags.initrace = race->selectedItemNumber();
1244 setupOthers();
1247 void NetHackQtPlayerSelector::setupOthers()
1249 int ro = role->selectedItemNumber();
1250 int ra = race->selectedItemNumber();
1251 int valid=-1;
1252 int c=0;
1253 int j;
1254 for (j=0; j<ROLE_GENDERS; j++) {
1255 bool v = validgend(ro,ra,j);
1256 if ( gender[j]->isChecked() )
1257 c = j;
1258 gender[j]->setEnabled(v);
1259 if ( valid<0 && v ) valid = j;
1261 if ( !validgend(ro,ra,c) )
1262 c = valid;
1263 int k;
1264 for (k=0; k<ROLE_GENDERS; k++) {
1265 gender[k]->setChecked(c==k);
1267 selectGender(c);
1269 valid=-1;
1270 for (j=0; j<ROLE_ALIGNS; j++) {
1271 bool v = validalign(ro,ra,j);
1272 if ( alignment[j]->isChecked() )
1273 c = j;
1274 alignment[j]->setEnabled(v);
1275 if ( valid<0 && v ) valid = j;
1277 if ( !validalign(ro,ra,c) )
1278 c = valid;
1279 for (k=0; k<ROLE_ALIGNS; k++) {
1280 alignment[k]->setChecked(c==k);
1282 selectAlignment(c);
1285 void NetHackQtPlayerSelector::selectGender(int i)
1287 flags.initgend = i;
1290 void NetHackQtPlayerSelector::selectAlignment(int i)
1292 flags.initalign = i;
1296 void NetHackQtPlayerSelector::done(int i)
1298 setResult(i);
1299 qApp->exit_loop();
1302 void NetHackQtPlayerSelector::Quit()
1304 done(R_Quit);
1305 qApp->exit_loop();
1308 void NetHackQtPlayerSelector::Random()
1310 done(R_Rand);
1311 qApp->exit_loop();
1314 bool NetHackQtPlayerSelector::Choose()
1316 if (fully_specified_role) return TRUE;
1318 #if defined(QWS) // probably safe with Qt 3, too (where show!=exec in QDialog).
1319 if ( qt_compact_mode ) {
1320 showMaximized();
1321 } else
1322 #endif
1324 adjustSize();
1325 centerOnMain(this);
1328 if ( exec() ) {
1329 return TRUE;
1330 } else {
1331 return FALSE;
1336 NetHackQtStringRequestor::NetHackQtStringRequestor(NetHackQtKeyBuffer& ks, const char* p, const char* cancelstr) :
1337 QDialog(qApp->mainWidget(),"string",FALSE),
1338 prompt(p,this,"prompt"),
1339 input(this,"input"),
1340 keysource(ks)
1342 cancel=new QPushButton(cancelstr,this);
1343 connect(cancel,SIGNAL(clicked()),this,SLOT(reject()));
1345 okay=new QPushButton("Okay",this);
1346 connect(okay,SIGNAL(clicked()),this,SLOT(accept()));
1347 connect(&input,SIGNAL(returnPressed()),this,SLOT(accept()));
1348 okay->setDefault(TRUE);
1350 setFocusPolicy(StrongFocus);
1353 void NetHackQtStringRequestor::resizeEvent(QResizeEvent*)
1355 const int margin=5;
1356 const int gutter=5;
1358 int h=(height()-margin*2-gutter);
1360 if (strlen(prompt.text()) > 16) {
1361 h/=3;
1362 prompt.setGeometry(margin,margin,width()-margin*2,h);
1363 input.setGeometry(width()*1/5,margin+h+gutter,
1364 (width()-margin-2-gutter)*4/5,h);
1365 } else {
1366 h/=2;
1367 prompt.setGeometry(margin,margin,(width()-margin*2-gutter)*2/5,h);
1368 input.setGeometry(prompt.geometry().right()+gutter,margin,
1369 (width()-margin-2-gutter)*3/5,h);
1372 cancel->setGeometry(margin,input.geometry().bottom()+gutter,
1373 (width()-margin*2-gutter)/2,h);
1374 okay->setGeometry(cancel->geometry().right()+gutter,cancel->geometry().y(),
1375 cancel->width(),h);
1378 void NetHackQtStringRequestor::SetDefault(const char* d)
1380 input.setText(d);
1383 bool NetHackQtStringRequestor::Get(char* buffer, int maxchar)
1385 input.setMaxLength(maxchar);
1386 if (strlen(prompt.text()) > 16) {
1387 resize(fontMetrics().width(prompt.text())+50,fontMetrics().height()*6);
1388 } else {
1389 resize(fontMetrics().width(prompt.text())*2+50,fontMetrics().height()*4);
1392 centerOnMain(this);
1393 show();
1394 input.setFocus();
1395 setResult(-1);
1396 while (result()==-1) {
1397 // Put keys in buffer (eg. from macros, from out-of-focus input)
1398 if (!keysource.Empty()) {
1399 while (!keysource.Empty()) {
1400 int key=keysource.TopKey();
1401 int ascii=keysource.TopAscii();
1402 int state=keysource.GetState();
1403 if (ascii=='\r' || ascii=='\n') {
1404 // CR or LF in buffer causes confirmation
1405 strcpy(buffer,input.text());
1406 return TRUE;
1407 } else if (ascii=='\033') {
1408 return FALSE;
1409 } else {
1410 input.fakeEvent(key,ascii,state);
1414 qApp->enter_loop();
1416 // XXX Get rid of extra keys, since we couldn't get focus!
1417 while (!keysource.Empty()) keysource.GetKey();
1419 if (result()) {
1420 strcpy(buffer,input.text());
1421 return TRUE;
1422 } else {
1423 return FALSE;
1426 void NetHackQtStringRequestor::done(int i)
1428 setResult(i);
1429 qApp->exit_loop();
1433 NetHackQtWindow::NetHackQtWindow()
1437 NetHackQtWindow::~NetHackQtWindow()
1441 // XXX Use "expected ..." for now, abort or default later.
1443 void NetHackQtWindow::Clear() { puts("unexpected Clear"); }
1444 void NetHackQtWindow::Display(bool block) { puts("unexpected Display"); }
1445 bool NetHackQtWindow::Destroy() { return TRUE; }
1446 void NetHackQtWindow::CursorTo(int x,int y) { puts("unexpected CursorTo"); }
1447 void NetHackQtWindow::PutStr(int attr, const char* text) { puts("unexpected PutStr"); }
1448 void NetHackQtWindow::StartMenu() { puts("unexpected StartMenu"); }
1449 void NetHackQtWindow::AddMenu(int glyph, const ANY_P* identifier, char ch, char gch, int attr,
1450 const char* str, bool presel) { puts("unexpected AddMenu"); }
1451 void NetHackQtWindow::EndMenu(const char* prompt) { puts("unexpected EndMenu"); }
1452 int NetHackQtWindow::SelectMenu(int how, MENU_ITEM_P **menu_list) { puts("unexpected SelectMenu"); return 0; }
1453 void NetHackQtWindow::ClipAround(int x,int y) { puts("unexpected ClipAround"); }
1454 void NetHackQtWindow::PrintGlyph(int x,int y,int glyph) { puts("unexpected PrintGlyph"); }
1455 //void NetHackQtWindow::PrintGlyphCompose(int x,int y,int,int) { puts("unexpected PrintGlyphCompose"); }
1456 void NetHackQtWindow::UseRIP(int how, time_t when) { puts("unexpected UseRIP"); }
1460 // XXX Hmmm... crash after saving bones file if Map window is
1461 // XXX deleted. Strange bug somewhere.
1462 bool NetHackQtMapWindow::Destroy() { return FALSE; }
1464 NetHackQtMapWindow::NetHackQtMapWindow(NetHackQtClickBuffer& click_sink) :
1465 clicksink(click_sink),
1466 change(10),
1467 rogue_font(0)
1469 viewport.addChild(this);
1471 setBackgroundColor(black);
1472 viewport.setBackgroundColor(black);
1474 pet_annotation = QPixmap(qt_compact_mode ? pet_mark_small_xpm : pet_mark_xpm);
1476 cursor.setX(0);
1477 cursor.setY(0);
1478 Clear();
1480 connect(qt_settings,SIGNAL(tilesChanged()),this,SLOT(updateTiles()));
1481 connect(&viewport, SIGNAL(contentsMoving(int,int)), this,
1482 SLOT(moveMessages(int,int)));
1484 updateTiles();
1485 //setFocusPolicy(StrongFocus);
1486 #ifdef SAFERHANGUP
1487 QTimer* deadman = new QTimer(this);
1488 connect(deadman, SIGNAL(timeout()), SLOT(timeout()));
1489 deadman->start(2000); // deadman timer every 2 seconds
1490 #endif
1493 #ifdef SAFERHANGUP
1494 // The "deadman" timer is received by this slot
1495 void NetHackQtMapWindow::timeout() {}
1496 #endif
1498 void NetHackQtMapWindow::moveMessages(int x, int y)
1500 QRect u = messages_rect;
1501 messages_rect.moveTopLeft(QPoint(x,y));
1502 u |= messages_rect;
1503 update(u);
1506 void NetHackQtMapWindow::clearMessages()
1508 messages = "";
1509 update(messages_rect);
1510 messages_rect = QRect();
1513 void NetHackQtMapWindow::putMessage(int attr, const char* text)
1515 if ( !messages.isEmpty() )
1516 messages += "\n";
1517 messages += text;
1518 QFontMetrics fm = fontMetrics();
1519 messages_rect = fm.boundingRect(viewport.contentsX(), viewport.contentsY(),
1520 viewport.width(), 0,
1521 WordBreak|AlignTop|AlignLeft|DontClip,
1522 messages);
1523 update(messages_rect);
1526 void NetHackQtMapWindow::updateTiles()
1528 NetHackQtGlyphs& glyphs = qt_settings->glyphs();
1529 int gw = glyphs.width();
1530 int gh = glyphs.height();
1531 // Be exactly the size we want to be - full map...
1532 resize(COLNO*gw,ROWNO*gh);
1534 viewport.verticalScrollBar()->setSteps(gh,gh);
1535 viewport.horizontalScrollBar()->setSteps(gw,gw);
1537 viewport.setMaximumSize(
1538 gw*COLNO + viewport.verticalScrollBar()->width(),
1539 gh*ROWNO + viewport.horizontalScrollBar()->height()
1542 viewport.updateScrollBars();
1544 change.clear();
1545 change.add(0,0,COLNO,ROWNO);
1546 delete rogue_font; rogue_font = 0;
1547 Display(FALSE);
1549 emit resized();
1552 NetHackQtMapWindow::~NetHackQtMapWindow()
1554 // Remove from viewport porthole, since that is a destructible member.
1555 viewport.removeChild(this);
1556 recreate(0,0,QPoint(0,0));
1559 QWidget* NetHackQtMapWindow::Widget()
1561 return &viewport;
1564 void NetHackQtMapWindow::Scroll(int dx, int dy)
1566 if (viewport.horizontalScrollBar()->isVisible()) {
1567 while (dx<0) { viewport.horizontalScrollBar()->subtractPage(); dx++; }
1568 while (dx>0) { viewport.horizontalScrollBar()->addPage(); dx--; }
1570 if (viewport.verticalScrollBar()->isVisible()) {
1571 while (dy<0) { viewport.verticalScrollBar()->subtractPage(); dy++; }
1572 while (dy>0) { viewport.verticalScrollBar()->addPage(); dy--; }
1576 void NetHackQtMapWindow::Clear()
1578 unsigned short stone=cmap_to_glyph(S_stone);
1580 for (int j=0; j<ROWNO; j++) {
1581 for (int i=0; i<COLNO; i++) {
1582 Glyph(i,j)=stone;
1586 change.clear();
1587 change.add(0,0,COLNO,ROWNO);
1590 void NetHackQtMapWindow::clickCursor()
1592 clicksink.Put(cursor.x(),cursor.y(),CLICK_1);
1593 qApp->exit_loop();
1596 void NetHackQtMapWindow::mousePressEvent(QMouseEvent* event)
1598 clicksink.Put(
1599 event->pos().x()/qt_settings->glyphs().width(),
1600 event->pos().y()/qt_settings->glyphs().height(),
1601 event->button()==LeftButton ? CLICK_1 : CLICK_2
1603 qApp->exit_loop();
1606 #ifdef TEXTCOLOR
1607 static
1608 const QPen& nhcolor_to_pen(int c)
1610 static QPen* pen=0;
1611 if ( !pen ) {
1612 pen = new QPen[17];
1613 pen[0] = QColor(24,24,24); // "black" on black
1614 pen[1] = Qt::red;
1615 pen[2] = QColor(0,191,0);
1616 pen[3] = QColor(127,127,0);
1617 pen[4] = Qt::blue;
1618 pen[5] = Qt::magenta;
1619 pen[6] = Qt::cyan;
1620 pen[7] = Qt::gray;
1621 pen[8] = Qt::white; // no color
1622 pen[9] = QColor(255,127,0);
1623 pen[10] = QColor(127,255,127);
1624 pen[11] = Qt::yellow;
1625 pen[12] = QColor(127,127,255);
1626 pen[13] = QColor(255,127,255);
1627 pen[14] = QColor(127,255,255);
1628 pen[15] = Qt::white;
1629 pen[16] = QColor(24,24,24); // "black" on black
1632 return pen[c];
1634 #endif
1636 void NetHackQtMapWindow::paintEvent(QPaintEvent* event)
1638 QRect area=event->rect();
1639 QRect garea;
1640 garea.setCoords(
1641 QMAX(0,area.left()/qt_settings->glyphs().width()),
1642 QMAX(0,area.top()/qt_settings->glyphs().height()),
1643 QMIN(COLNO-1,area.right()/qt_settings->glyphs().width()),
1644 QMIN(ROWNO-1,area.bottom()/qt_settings->glyphs().height())
1647 QPainter painter;
1649 painter.begin(this);
1651 if (Is_rogue_level(&u.uz) || iflags.wc_ascii_map)
1653 // You enter a VERY primitive world!
1655 painter.setClipRect( event->rect() ); // (normally we don't clip)
1656 painter.fillRect( event->rect(), black );
1658 if ( !rogue_font ) {
1659 // Find font...
1660 int pts = 5;
1661 QString fontfamily = iflags.wc_font_map
1662 ? iflags.wc_font_map : "Courier";
1663 bool bold = FALSE;
1664 if ( fontfamily.right(5).lower() == "-bold" ) {
1665 fontfamily.truncate(fontfamily.length()-5);
1666 bold = TRUE;
1668 while ( pts < 32 ) {
1669 QFont f(fontfamily, pts, bold ? QFont::Bold : QFont::Normal);
1670 painter.setFont(QFont(fontfamily, pts));
1671 QFontMetrics fm = painter.fontMetrics();
1672 if ( fm.width("M") > qt_settings->glyphs().width() )
1673 break;
1674 if ( fm.height() > qt_settings->glyphs().height() )
1675 break;
1676 pts++;
1678 rogue_font = new QFont(fontfamily,pts-1);
1680 painter.setFont(*rogue_font);
1682 for (int j=garea.top(); j<=garea.bottom(); j++) {
1683 for (int i=garea.left(); i<=garea.right(); i++) {
1684 unsigned short g=Glyph(i,j);
1685 uchar ch;
1686 int color, och;
1687 unsigned special;
1689 painter.setPen( green );
1690 /* map glyph to character and color */
1691 (void)mapglyph(g, &och, &color, &special, i, j);
1692 ch = (uchar)och;
1693 #ifdef TEXTCOLOR
1694 painter.setPen( nhcolor_to_pen(color) );
1695 #endif
1696 painter.drawText(
1697 i*qt_settings->glyphs().width(),
1698 j*qt_settings->glyphs().height(),
1699 qt_settings->glyphs().width(),
1700 qt_settings->glyphs().height(),
1701 AlignCenter,
1702 (const char*)&ch, 1
1704 if (glyph_is_pet(g)
1705 #ifdef TEXTCOLOR
1706 && ::iflags.hilite_pet
1707 #endif
1709 painter.drawPixmap(QPoint(i*qt_settings->glyphs().width(),
1710 j*qt_settings->glyphs().height()),
1711 pet_annotation);
1716 painter.setFont(font());
1717 } else {
1718 for (int j=garea.top(); j<=garea.bottom(); j++) {
1719 for (int i=garea.left(); i<=garea.right(); i++) {
1720 unsigned short g=Glyph(i,j);
1721 qt_settings->glyphs().drawCell(painter, g, i, j);
1722 if (glyph_is_pet(g)
1723 #ifdef TEXTCOLOR
1724 && ::iflags.hilite_pet
1725 #endif
1727 painter.drawPixmap(QPoint(i*qt_settings->glyphs().width(),
1728 j*qt_settings->glyphs().height()),
1729 pet_annotation);
1735 if (garea.contains(cursor)) {
1736 if (Is_rogue_level(&u.uz)) {
1737 #ifdef TEXTCOLOR
1738 painter.setPen( white );
1739 #else
1740 painter.setPen( green ); // REALLY primitive
1741 #endif
1742 } else
1744 int hp100;
1745 if (u.mtimedone) {
1746 hp100=u.mhmax ? u.mh*100/u.mhmax : 100;
1747 } else {
1748 hp100=u.uhpmax ? u.uhp*100/u.uhpmax : 100;
1751 if (hp100 > 75) painter.setPen(white);
1752 else if (hp100 > 50) painter.setPen(yellow);
1753 else if (hp100 > 25) painter.setPen(QColor(0xff,0xbf,0x00)); // orange
1754 else if (hp100 > 10) painter.setPen(red);
1755 else painter.setPen(magenta);
1758 painter.drawRect(
1759 cursor.x()*qt_settings->glyphs().width(),cursor.y()*qt_settings->glyphs().height(),
1760 qt_settings->glyphs().width(),qt_settings->glyphs().height());
1763 if (area.intersects(messages_rect)) {
1764 painter.setPen(black);
1765 painter.drawText(viewport.contentsX()+1,viewport.contentsY()+1,
1766 viewport.width(),0, WordBreak|AlignTop|AlignLeft|DontClip, messages);
1767 painter.setPen(white);
1768 painter.drawText(viewport.contentsX(),viewport.contentsY(),
1769 viewport.width(),0, WordBreak|AlignTop|AlignLeft|DontClip, messages);
1772 painter.end();
1775 void NetHackQtMapWindow::Display(bool block)
1777 for (int i=0; i<change.clusters(); i++) {
1778 const QRect& ch=change[i];
1779 repaint(
1780 ch.x()*qt_settings->glyphs().width(),
1781 ch.y()*qt_settings->glyphs().height(),
1782 ch.width()*qt_settings->glyphs().width(),
1783 ch.height()*qt_settings->glyphs().height(),
1784 FALSE
1788 change.clear();
1790 if (block) {
1791 yn_function("Press a key when done viewing",0,'\0');
1795 void NetHackQtMapWindow::CursorTo(int x,int y)
1797 Changed(cursor.x(),cursor.y());
1798 cursor.setX(x);
1799 cursor.setY(y);
1800 Changed(cursor.x(),cursor.y());
1803 void NetHackQtMapWindow::PutStr(int attr, const char* text)
1805 puts("unexpected PutStr in MapWindow");
1808 void NetHackQtMapWindow::ClipAround(int x,int y)
1810 // Convert to pixel of center of tile
1811 x=x*qt_settings->glyphs().width()+qt_settings->glyphs().width()/2;
1812 y=y*qt_settings->glyphs().height()+qt_settings->glyphs().height()/2;
1814 // Then ensure that pixel is visible
1815 viewport.center(x,y,0.45,0.45);
1818 void NetHackQtMapWindow::PrintGlyph(int x,int y,int glyph)
1820 Glyph(x,y)=glyph;
1821 Changed(x,y);
1824 //void NetHackQtMapWindow::PrintGlyphCompose(int x,int y,int glyph1, int glyph2)
1826 // TODO: composed graphics
1829 void NetHackQtMapWindow::Changed(int x, int y)
1831 change.add(x,y);
1835 class NetHackQtScrollText : public QTableView {
1836 struct UData {
1837 UData() : text(0), attr(0) { }
1838 ~UData() { if (text) free(text); }
1840 char* text;
1841 int attr;
1843 public:
1844 int uncleared;
1846 NetHackQtScrollText(int maxlength) :
1847 uncleared(0),
1848 maxitems(maxlength),
1849 first(0),
1850 count(0),
1851 item_cycle(maxlength)
1853 setNumCols(1);
1854 setCellWidth(200);
1855 setCellHeight(fontMetrics().height());
1856 setBackgroundColor(white);
1857 setTableFlags(Tbl_vScrollBar
1858 |Tbl_autoHScrollBar
1859 |Tbl_clipCellPainting
1860 |Tbl_smoothScrolling);
1863 ~NetHackQtScrollText()
1867 void Scroll(int dx, int dy)
1869 setXOffset(xOffset()+dx*viewWidth());
1870 setYOffset(yOffset()+dy*viewHeight());
1873 void insertItem(int attr, const char* text)
1875 setTopCell(count);
1877 setAutoUpdate(FALSE);
1879 int i;
1880 if (count<maxitems) {
1881 i=count++;
1882 setNumRows(count);
1883 } else {
1884 i=count-1;
1885 first=(first+1)%maxitems;
1887 item(i).attr=attr;
1888 item(i).text=strdup(text);
1889 int w=datumWidth(item(i));
1891 if (w > cellWidth()) {
1892 // Get wider.
1893 setCellWidth(w);
1895 setTopCell(count);
1897 setAutoUpdate(TRUE);
1899 if (viewHeight() >= totalHeight()-cellHeight()) {
1900 repaint();
1901 } else {
1902 scroll(0,cellHeight());
1906 virtual void setFont(const QFont& font)
1908 QTableView::setFont(font);
1909 setCellHeight(fontMetrics().height());
1912 protected:
1914 UData& item(int i)
1916 return item_cycle[(first+i)%maxitems];
1919 const int maxitems;
1920 int first, count;
1921 QArray<UData> item_cycle;
1923 int datumWidth(const UData& uitem)
1925 if (uitem.text) {
1926 int width=fontMetrics().width(uitem.text)+3;
1927 if (uitem.attr) {
1928 // XXX Too expensive to do properly, because
1929 // XXX we have to set the font of the widget
1930 // XXX just to get the font metrics information!
1931 // XXX Could hold a fake widget for that
1932 // XXX purpose, but this hack is less ugly.
1933 width+=width/10;
1935 return width;
1936 } else {
1937 return 0;
1941 virtual void setupPainter(QPainter *p)
1943 // XXX This shouldn't be needed - we set the bg in the constructor.
1944 p->setBackgroundColor(white);
1947 virtual void paintCell(QPainter *p, int row, int col)
1949 bool sel=FALSE;
1950 UData& uitem=item(row);
1952 if (!sel && row < count-uncleared) {
1953 p->setPen(darkGray);
1954 } else {
1955 p->setPen(black);
1958 if (uitem.attr) {
1959 // XXX only bold
1960 QFont bold(font().family(),font().pointSize(),QFont::Bold);
1961 p->setFont(bold);
1964 p->drawText(3, 0, cellWidth(), cellHeight(),
1965 AlignLeft|AlignVCenter, uitem.text);
1967 if (uitem.attr) {
1968 p->setFont(font());
1973 NetHackQtMessageWindow::NetHackQtMessageWindow() :
1974 list(new NetHackQtScrollText(::iflags.msg_history))
1976 ::iflags.window_inited = 1;
1977 map = 0;
1978 connect(qt_settings,SIGNAL(fontChanged()),this,SLOT(updateFont()));
1979 updateFont();
1982 NetHackQtMessageWindow::~NetHackQtMessageWindow()
1984 ::iflags.window_inited = 0;
1985 delete list;
1988 QWidget* NetHackQtMessageWindow::Widget() { return list; }
1990 void NetHackQtMessageWindow::setMap(NetHackQtMapWindow* m)
1992 map = m;
1993 updateFont();
1996 void NetHackQtMessageWindow::updateFont()
1998 list->setFont(qt_settings->normalFont());
1999 if ( map )
2000 map->setFont(qt_settings->normalFont());
2003 void NetHackQtMessageWindow::Scroll(int dx, int dy)
2005 list->Scroll(dx,dy);
2008 void NetHackQtMessageWindow::Clear()
2010 if ( map )
2011 map->clearMessages();
2012 if (list->uncleared) {
2013 list->uncleared=0;
2014 changed=TRUE;
2015 Display(FALSE);
2019 void NetHackQtMessageWindow::Display(bool block)
2021 if (changed) {
2022 list->repaint();
2023 changed=FALSE;
2027 void NetHackQtMessageWindow::PutStr(int attr, const char* text)
2029 #ifdef USER_SOUNDS
2030 play_sound_for_message(text);
2031 #endif
2033 changed=TRUE;
2034 list->uncleared++;
2035 list->insertItem(attr,text);
2037 // Force scrollbar to bottom
2038 // XXX list->setTopItem(list->count());
2040 if ( map )
2041 map->putMessage(attr, text);
2046 NetHackQtLabelledIcon::NetHackQtLabelledIcon(QWidget* parent, const char* l) :
2047 QWidget(parent),
2048 low_is_good(FALSE),
2049 prev_value(-123),
2050 turn_count(-1),
2051 label(new QLabel(l,this)),
2052 icon(0)
2054 initHighlight();
2056 NetHackQtLabelledIcon::NetHackQtLabelledIcon(QWidget* parent, const char* l, const QPixmap& i) :
2057 QWidget(parent),
2058 low_is_good(FALSE),
2059 prev_value(-123),
2060 turn_count(-1),
2061 label(new QLabel(l,this)),
2062 icon(new QLabel(this))
2064 setIcon(i);
2065 initHighlight();
2067 void NetHackQtLabelledIcon::initHighlight()
2069 const QPalette& pal=palette();
2070 const QColorGroup& pa=pal.normal();
2071 //QColorGroup good(white,darkGreen,pa.light(),pa.dark(),pa.mid(),white,pa.base());
2072 QColorGroup good(black,green,pa.light(),pa.dark(),pa.mid(),black,pa.base());
2073 QColorGroup bad(white,red,pa.light(),pa.dark(),pa.mid(),white,pa.base());
2074 hl_good=pal.copy();
2075 hl_good.setNormal(good);
2076 hl_good.setActive(good);
2077 hl_bad=pal.copy();
2078 hl_bad.setNormal(bad);
2079 hl_bad.setActive(bad);
2082 void NetHackQtLabelledIcon::setLabel(const char* t, bool lower)
2084 if (!label) {
2085 label=new QLabel(this);
2086 label->setFont(font());
2087 resizeEvent(0);
2089 if (0!=strcmp(label->text(),t)) {
2090 label->setText(t);
2091 highlight(lower==low_is_good ? hl_good : hl_bad);
2094 void NetHackQtLabelledIcon::setLabel(const char* t, long v, long cv, const char* tail)
2096 char buf[BUFSZ];
2097 if (v==NoNum) {
2098 Sprintf(buf,"%s%s",t,tail);
2099 } else {
2100 Sprintf(buf,"%s%ld%s",t,v,tail);
2102 setLabel(buf,cv<prev_value);
2103 prev_value=cv;
2105 void NetHackQtLabelledIcon::setLabel(const char* t, long v, const char* tail)
2107 setLabel(t,v,v,tail);
2109 void NetHackQtLabelledIcon::setIcon(const QPixmap& i)
2111 if (icon) icon->setPixmap(i);
2112 else { icon=new QLabel(this); icon->setPixmap(i); resizeEvent(0); }
2113 icon->resize(i.width(),i.height());
2115 void NetHackQtLabelledIcon::setFont(const QFont& f)
2117 QWidget::setFont(f);
2118 if (label) label->setFont(f);
2120 void NetHackQtLabelledIcon::show()
2122 #if QT_VERSION >= 300
2123 if (isHidden())
2124 #else
2125 if (!isVisible())
2126 #endif
2127 highlight(hl_bad);
2128 QWidget::show();
2130 void NetHackQtLabelledIcon::highlightWhenChanging()
2132 turn_count=0;
2134 void NetHackQtLabelledIcon::lowIsGood()
2136 low_is_good=TRUE;
2138 void NetHackQtLabelledIcon::dissipateHighlight()
2140 if (turn_count>0) {
2141 turn_count--;
2142 if (!turn_count)
2143 unhighlight();
2146 void NetHackQtLabelledIcon::highlight(const QPalette& hl)
2148 if (label) { // Surely it is?!
2149 if (turn_count>=0) {
2150 label->setPalette(hl);
2151 turn_count=4;
2152 // `4' includes this turn, so dissipates after
2153 // 3 more keypresses.
2154 } else {
2155 label->setPalette(palette());
2159 void NetHackQtLabelledIcon::unhighlight()
2161 if (label) { // Surely it is?!
2162 label->setPalette(palette());
2165 void NetHackQtLabelledIcon::resizeEvent(QResizeEvent*)
2167 setAlignments();
2169 //int labw=label ? label->fontMetrics().width(label->text()) : 0;
2170 int labh=label ? label->fontMetrics().height() : 0;
2171 int icoh=icon ? icon->height() : 0;
2172 int h=icoh+labh;
2173 int icoy=(h>height() ? height()-labh-icoh : height()/2-h/2);
2174 int laby=icoy+icoh;
2175 if (icon) {
2176 icon->setGeometry(0,icoy,width(),icoh);
2178 if (label) {
2179 label->setGeometry(0,laby,width(),labh);
2183 void NetHackQtLabelledIcon::setAlignments()
2185 if (label) label->setAlignment(AlignHCenter|AlignVCenter);
2186 if (icon) icon->setAlignment(AlignHCenter|AlignVCenter);
2189 static void
2190 tryload(QPixmap& pm, const char* fn)
2192 if (!pm.load(fn)) {
2193 QString msg;
2194 msg.sprintf("Cannot load \"%s\"", fn);
2195 QMessageBox::warning(0, "IO Error", msg);
2199 NetHackQtStatusWindow::NetHackQtStatusWindow() :
2200 // Notes:
2201 // Alignment needs -2 init value, because -1 is an alignment.
2202 // Armor Class is an schar, so 256 is out of range.
2203 // Blank value is 0 and should never change.
2204 name(this,"(name)"),
2205 dlevel(this,"(dlevel)"),
2206 str(this,"STR"),
2207 dex(this,"DEX"),
2208 con(this,"CON"),
2209 intel(this,"INT"),
2210 wis(this,"WIS"),
2211 cha(this,"CHA"),
2212 gold(this,"Gold"),
2213 hp(this,"Hit Points"),
2214 power(this,"Power"),
2215 ac(this,"Armour Class"),
2216 level(this,"Level"),
2217 exp(this,"Experience"),
2218 align(this,"Alignment"),
2219 time(this,"Time"),
2220 score(this,"Score"),
2221 hunger(this,""),
2222 confused(this,"Confused"),
2223 sick_fp(this,"Sick"),
2224 sick_il(this,"Ill"),
2225 blind(this,"Blind"),
2226 stunned(this,"Stunned"),
2227 hallu(this,"Hallu"),
2228 encumber(this,""),
2229 hline1(this),
2230 hline2(this),
2231 hline3(this),
2232 first_set(TRUE)
2234 p_str = QPixmap(str_xpm);
2235 p_str = QPixmap(str_xpm);
2236 p_dex = QPixmap(dex_xpm);
2237 p_con = QPixmap(cns_xpm);
2238 p_int = QPixmap(int_xpm);
2239 p_wis = QPixmap(wis_xpm);
2240 p_cha = QPixmap(cha_xpm);
2242 p_chaotic = QPixmap(chaotic_xpm);
2243 p_neutral = QPixmap(neutral_xpm);
2244 p_lawful = QPixmap(lawful_xpm);
2246 p_satiated = QPixmap(satiated_xpm);
2247 p_hungry = QPixmap(hungry_xpm);
2249 p_confused = QPixmap(confused_xpm);
2250 p_sick_fp = QPixmap(sick_fp_xpm);
2251 p_sick_il = QPixmap(sick_il_xpm);
2252 p_blind = QPixmap(blind_xpm);
2253 p_stunned = QPixmap(stunned_xpm);
2254 p_hallu = QPixmap(hallu_xpm);
2256 p_encumber[0] = QPixmap(slt_enc_xpm);
2257 p_encumber[1] = QPixmap(mod_enc_xpm);
2258 p_encumber[2] = QPixmap(hvy_enc_xpm);
2259 p_encumber[3] = QPixmap(ext_enc_xpm);
2260 p_encumber[4] = QPixmap(ovr_enc_xpm);
2262 str.setIcon(p_str);
2263 dex.setIcon(p_dex);
2264 con.setIcon(p_con);
2265 intel.setIcon(p_int);
2266 wis.setIcon(p_wis);
2267 cha.setIcon(p_cha);
2269 align.setIcon(p_neutral);
2270 hunger.setIcon(p_hungry);
2272 confused.setIcon(p_confused);
2273 sick_fp.setIcon(p_sick_fp);
2274 sick_il.setIcon(p_sick_il);
2275 blind.setIcon(p_blind);
2276 stunned.setIcon(p_stunned);
2277 hallu.setIcon(p_hallu);
2279 encumber.setIcon(p_encumber[0]);
2281 hline1.setFrameStyle(QFrame::HLine|QFrame::Sunken);
2282 hline2.setFrameStyle(QFrame::HLine|QFrame::Sunken);
2283 hline3.setFrameStyle(QFrame::HLine|QFrame::Sunken);
2284 hline1.setLineWidth(1);
2285 hline2.setLineWidth(1);
2286 hline3.setLineWidth(1);
2288 connect(qt_settings,SIGNAL(fontChanged()),this,SLOT(doUpdate()));
2289 doUpdate();
2292 void NetHackQtStatusWindow::doUpdate()
2294 const QFont& large=qt_settings->largeFont();
2295 name.setFont(large);
2296 dlevel.setFont(large);
2298 const QFont& normal=qt_settings->normalFont();
2299 str.setFont(normal);
2300 dex.setFont(normal);
2301 con.setFont(normal);
2302 intel.setFont(normal);
2303 wis.setFont(normal);
2304 cha.setFont(normal);
2305 gold.setFont(normal);
2306 hp.setFont(normal);
2307 power.setFont(normal);
2308 ac.setFont(normal);
2309 level.setFont(normal);
2310 exp.setFont(normal);
2311 align.setFont(normal);
2312 time.setFont(normal);
2313 score.setFont(normal);
2314 hunger.setFont(normal);
2315 confused.setFont(normal);
2316 sick_fp.setFont(normal);
2317 sick_il.setFont(normal);
2318 blind.setFont(normal);
2319 stunned.setFont(normal);
2320 hallu.setFont(normal);
2321 encumber.setFont(normal);
2323 updateStats();
2326 QWidget* NetHackQtStatusWindow::Widget() { return this; }
2328 void NetHackQtStatusWindow::Clear()
2331 void NetHackQtStatusWindow::Display(bool block)
2334 void NetHackQtStatusWindow::CursorTo(int,int y)
2336 cursy=y;
2338 void NetHackQtStatusWindow::PutStr(int attr, const char* text)
2340 // do a complete update when line 0 is done (as per X11 fancy status)
2341 if (cursy==0) updateStats();
2344 void NetHackQtStatusWindow::resizeEvent(QResizeEvent*)
2346 const float SP_name=0.13; // <Name> the <Class> (large)
2347 const float SP_dlev=0.13; // Level 3 in The Dungeons of Doom (large)
2348 const float SP_atr1=0.25; // STR DEX CON INT WIS CHA
2349 const float SP_hln1=0.02; // ---
2350 const float SP_atr2=0.09; // Au HP PW AC LVL EXP
2351 const float SP_hln2=0.02; // ---
2352 const float SP_time=0.09; // time score
2353 const float SP_hln3=0.02; // ---
2354 const float SP_stat=0.25; // Alignment, Poisoned, Hungry, Sick, etc.
2356 int h=height();
2357 int x=0,y=0;
2359 int iw; // Width of an item across line
2360 int lh; // Height of a line of values
2362 lh=int(h*SP_name);
2363 name.setGeometry(0,0,width(),lh); y+=lh;
2364 lh=int(h*SP_dlev);
2365 dlevel.setGeometry(0,y,width(),lh); y+=lh;
2367 lh=int(h*SP_hln1);
2368 hline1.setGeometry(0,y,width(),lh); y+=lh;
2370 lh=int(h*SP_atr1);
2371 iw=width()/6;
2372 str.setGeometry(x,y,iw,lh); x+=iw;
2373 dex.setGeometry(x,y,iw,lh); x+=iw;
2374 con.setGeometry(x,y,iw,lh); x+=iw;
2375 intel.setGeometry(x,y,iw,lh); x+=iw;
2376 wis.setGeometry(x,y,iw,lh); x+=iw;
2377 cha.setGeometry(x,y,iw,lh); x+=iw;
2378 x=0; y+=lh;
2380 lh=int(h*SP_hln2);
2381 hline2.setGeometry(0,y,width(),lh); y+=lh;
2383 lh=int(h*SP_atr2);
2384 iw=width()/6;
2385 gold.setGeometry(x,y,iw,lh); x+=iw;
2386 hp.setGeometry(x,y,iw,lh); x+=iw;
2387 power.setGeometry(x,y,iw,lh); x+=iw;
2388 ac.setGeometry(x,y,iw,lh); x+=iw;
2389 level.setGeometry(x,y,iw,lh); x+=iw;
2390 exp.setGeometry(x,y,iw,lh); x+=iw;
2391 x=0; y+=lh;
2393 lh=int(h*SP_hln3);
2394 hline3.setGeometry(0,y,width(),lh); y+=lh;
2396 lh=int(h*SP_time);
2397 iw=width()/3; x+=iw/2;
2398 time.setGeometry(x,y,iw,lh); x+=iw;
2399 score.setGeometry(x,y,iw,lh); x+=iw;
2400 x=0; y+=lh;
2402 lh=int(h*SP_stat);
2403 iw=width()/9;
2404 align.setGeometry(x,y,iw,lh); x+=iw;
2405 hunger.setGeometry(x,y,iw,lh); x+=iw;
2406 confused.setGeometry(x,y,iw,lh); x+=iw;
2407 sick_fp.setGeometry(x,y,iw,lh); x+=iw;
2408 sick_il.setGeometry(x,y,iw,lh); x+=iw;
2409 blind.setGeometry(x,y,iw,lh); x+=iw;
2410 stunned.setGeometry(x,y,iw,lh); x+=iw;
2411 hallu.setGeometry(x,y,iw,lh); x+=iw;
2412 encumber.setGeometry(x,y,iw,lh); x+=iw;
2413 x=0; y+=lh;
2418 * Set all widget values to a null string. This is used after all spacings
2419 * have been calculated so that when the window is popped up we don't get all
2420 * kinds of funny values being displayed.
2422 void NetHackQtStatusWindow::nullOut()
2426 void NetHackQtStatusWindow::fadeHighlighting()
2428 name.dissipateHighlight();
2429 dlevel.dissipateHighlight();
2431 str.dissipateHighlight();
2432 dex.dissipateHighlight();
2433 con.dissipateHighlight();
2434 intel.dissipateHighlight();
2435 wis.dissipateHighlight();
2436 cha.dissipateHighlight();
2438 gold.dissipateHighlight();
2439 hp.dissipateHighlight();
2440 power.dissipateHighlight();
2441 ac.dissipateHighlight();
2442 level.dissipateHighlight();
2443 exp.dissipateHighlight();
2444 align.dissipateHighlight();
2446 time.dissipateHighlight();
2447 score.dissipateHighlight();
2449 hunger.dissipateHighlight();
2450 confused.dissipateHighlight();
2451 sick_fp.dissipateHighlight();
2452 sick_il.dissipateHighlight();
2453 blind.dissipateHighlight();
2454 stunned.dissipateHighlight();
2455 hallu.dissipateHighlight();
2456 encumber.dissipateHighlight();
2460 * Update the displayed status. The current code in botl.c updates
2461 * two lines of information. Both lines are always updated one after
2462 * the other. So only do our update when we update the second line.
2464 * Information on the first line:
2465 * name, attributes, alignment, score
2467 * Information on the second line:
2468 * dlvl, gold, hp, power, ac, {level & exp or HD **}
2469 * status (hunger, conf, halu, stun, sick, blind), time, encumbrance
2471 * [**] HD is shown instead of level and exp if mtimedone is non-zero.
2473 void NetHackQtStatusWindow::updateStats()
2475 if (!parentWidget()) return;
2477 char buf[BUFSZ];
2479 if (cursy != 0) return; /* do a complete update when line 0 is done */
2481 if (ACURR(A_STR) > 118) {
2482 Sprintf(buf,"STR:%d",ACURR(A_STR)-100);
2483 } else if (ACURR(A_STR)==118) {
2484 Sprintf(buf,"STR:18/**");
2485 } else if(ACURR(A_STR) > 18) {
2486 Sprintf(buf,"STR:18/%02d",ACURR(A_STR)-18);
2487 } else {
2488 Sprintf(buf,"STR:%d",ACURR(A_STR));
2490 str.setLabel(buf,NetHackQtLabelledIcon::NoNum,ACURR(A_STR));
2492 dex.setLabel("DEX:",(long)ACURR(A_DEX));
2493 con.setLabel("CON:",(long)ACURR(A_CON));
2494 intel.setLabel("INT:",(long)ACURR(A_INT));
2495 wis.setLabel("WIS:",(long)ACURR(A_WIS));
2496 cha.setLabel("CHA:",(long)ACURR(A_CHA));
2497 const char* hung=hu_stat[u.uhs];
2498 if (hung[0]==' ') {
2499 hunger.hide();
2500 } else {
2501 hunger.setIcon(u.uhs ? p_hungry : p_satiated);
2502 hunger.setLabel(hung);
2503 hunger.show();
2505 if (Confusion) confused.show(); else confused.hide();
2506 if (Sick) {
2507 if (u.usick_type & SICK_VOMITABLE) {
2508 sick_fp.show();
2509 } else {
2510 sick_fp.hide();
2512 if (u.usick_type & SICK_NONVOMITABLE) {
2513 sick_il.show();
2514 } else {
2515 sick_il.hide();
2517 } else {
2518 sick_fp.hide();
2519 sick_il.hide();
2521 if (Blind) blind.show(); else blind.hide();
2522 if (Stunned) stunned.show(); else stunned.hide();
2523 if (Hallucination) hallu.show(); else hallu.hide();
2524 const char* enc=enc_stat[near_capacity()];
2525 if (enc[0]==' ' || !enc[0]) {
2526 encumber.hide();
2527 } else {
2528 encumber.setIcon(p_encumber[near_capacity()-1]);
2529 encumber.setLabel(enc);
2530 encumber.show();
2532 Strcpy(buf, plname);
2533 if ('a' <= buf[0] && buf[0] <= 'z') buf[0] += 'A'-'a';
2534 Strcat(buf, " the ");
2535 if (u.mtimedone) {
2536 char mname[BUFSZ];
2537 int k = 0;
2539 Strcpy(mname, mons[u.umonnum].mname);
2540 while(mname[k] != 0) {
2541 if ((k == 0 || (k > 0 && mname[k-1] == ' '))
2542 && 'a' <= mname[k] && mname[k] <= 'z')
2544 mname[k] += 'A' - 'a';
2546 k++;
2548 Strcat(buf, mname);
2549 } else {
2550 Strcat(buf, rank_of(u.ulevel, pl_character[0], ::flags.female));
2552 name.setLabel(buf,NetHackQtLabelledIcon::NoNum,u.ulevel);
2554 if (describe_level(buf)) {
2555 dlevel.setLabel(buf,(bool)TRUE);
2556 } else {
2557 Sprintf(buf, "%s, level ", dungeons[u.uz.dnum].dname);
2558 dlevel.setLabel(buf,(long)depth(&u.uz));
2561 gold.setLabel("Au:", money_cnt(invent));
2562 if (u.mtimedone) {
2563 // You're a monster!
2565 Sprintf(buf, "/%d", u.mhmax);
2566 hp.setLabel("HP:",u.mh > 0 ? u.mh : 0,buf);
2567 level.setLabel("HD:",(long)mons[u.umonnum].mlevel);
2568 } else {
2569 // You're normal.
2571 Sprintf(buf, "/%d", u.uhpmax);
2572 hp.setLabel("HP:",u.uhp > 0 ? u.uhp : 0,buf);
2573 level.setLabel("Level:",(long)u.ulevel);
2575 Sprintf(buf, "/%d", u.uenmax);
2576 power.setLabel("Pow:",u.uen,buf);
2577 ac.setLabel("AC:",(long)u.uac);
2578 if (::flags.showexp) {
2579 exp.setLabel("Exp:",(long)u.uexp);
2580 } else
2582 exp.setLabel("");
2584 if (u.ualign.type==A_CHAOTIC) {
2585 align.setIcon(p_chaotic);
2586 align.setLabel("Chaotic");
2587 } else if (u.ualign.type==A_NEUTRAL) {
2588 align.setIcon(p_neutral);
2589 align.setLabel("Neutral");
2590 } else {
2591 align.setIcon(p_lawful);
2592 align.setLabel("Lawful");
2595 if (::flags.time) time.setLabel("Time:",(long)moves);
2596 else time.setLabel("");
2597 #ifdef SCORE_ON_BOTL
2598 if (::flags.showscore) {
2599 score.setLabel("Score:",(long)botl_score());
2600 } else
2601 #endif
2603 score.setLabel("");
2606 if (first_set)
2608 first_set=FALSE;
2610 name.highlightWhenChanging();
2611 dlevel.highlightWhenChanging();
2613 str.highlightWhenChanging();
2614 dex.highlightWhenChanging();
2615 con.highlightWhenChanging();
2616 intel.highlightWhenChanging();
2617 wis.highlightWhenChanging();
2618 cha.highlightWhenChanging();
2620 gold.highlightWhenChanging();
2621 hp.highlightWhenChanging();
2622 power.highlightWhenChanging();
2623 ac.highlightWhenChanging(); ac.lowIsGood();
2624 level.highlightWhenChanging();
2625 exp.highlightWhenChanging();
2626 align.highlightWhenChanging();
2628 //time.highlightWhenChanging();
2629 score.highlightWhenChanging();
2631 hunger.highlightWhenChanging();
2632 confused.highlightWhenChanging();
2633 sick_fp.highlightWhenChanging();
2634 sick_il.highlightWhenChanging();
2635 blind.highlightWhenChanging();
2636 stunned.highlightWhenChanging();
2637 hallu.highlightWhenChanging();
2638 encumber.highlightWhenChanging();
2643 * Turn off hilighted status values after a certain amount of turns.
2645 void NetHackQtStatusWindow::checkTurnEvents()
2651 NetHackQtMenuDialog::NetHackQtMenuDialog() :
2652 QDialog(qApp->mainWidget(),0,FALSE)
2656 void NetHackQtMenuDialog::resizeEvent(QResizeEvent*)
2658 emit Resized();
2661 void NetHackQtMenuDialog::Accept()
2663 accept();
2666 void NetHackQtMenuDialog::Reject()
2668 reject();
2671 void NetHackQtMenuDialog::SetResult(int r)
2673 setResult(r);
2676 void NetHackQtMenuDialog::done(int i)
2678 setResult(i);
2679 qApp->exit_loop();
2682 // Table view columns:
2684 // [pick-count] [accel] [glyph] [string]
2686 // Maybe accel should be near string. We'll see.
2687 // pick-count normally blank.
2688 // double-clicking or click-on-count gives pop-up entry
2689 // string is green when selected
2691 NetHackQtMenuWindow::NetHackQtMenuWindow(NetHackQtKeyBuffer& ks) :
2692 QTableView(),
2693 keysource(ks),
2694 dialog(new NetHackQtMenuDialog()),
2695 prompt(0),
2696 pressed(-1)
2698 setNumCols(4);
2699 setCellHeight(QMAX(qt_settings->glyphs().height()+1,fontMetrics().height()));
2700 setBackgroundColor(lightGray);
2701 setFrameStyle(Panel|Sunken);
2702 setLineWidth(2);
2704 ok=new QPushButton("Ok",dialog);
2705 connect(ok,SIGNAL(clicked()),dialog,SLOT(accept()));
2707 cancel=new QPushButton("Cancel",dialog);
2708 connect(cancel,SIGNAL(clicked()),dialog,SLOT(reject()));
2710 all=new QPushButton("All",dialog);
2711 connect(all,SIGNAL(clicked()),this,SLOT(All()));
2713 none=new QPushButton("None",dialog);
2714 connect(none,SIGNAL(clicked()),this,SLOT(ChooseNone()));
2716 invert=new QPushButton("Invert",dialog);
2717 connect(invert,SIGNAL(clicked()),this,SLOT(Invert()));
2719 search=new QPushButton("Search",dialog);
2720 connect(search,SIGNAL(clicked()),this,SLOT(Search()));
2722 QPoint pos(0,ok->height());
2723 recreate(dialog,0,pos);
2724 prompt.recreate(dialog,0,pos);
2726 setBackgroundColor(lightGray);
2728 connect(dialog,SIGNAL(Resized()),this,SLOT(Layout()));
2730 setTableFlags(Tbl_autoHScrollBar|Tbl_autoVScrollBar
2731 |Tbl_smoothScrolling|Tbl_clipCellPainting);
2732 setFocusPolicy(StrongFocus);
2735 NetHackQtMenuWindow::~NetHackQtMenuWindow()
2737 // Remove from dialog before we destruct it
2738 recreate(0,0,QPoint(0,0));
2739 delete dialog;
2742 void NetHackQtMenuWindow::focusInEvent(QFocusEvent *)
2744 // Don't repaint at all, since nothing is using the focus colour
2746 void NetHackQtMenuWindow::focusOutEvent(QFocusEvent *)
2748 // Don't repaint at all, since nothing is using the focus colour
2751 int NetHackQtMenuWindow::cellWidth(int col)
2753 switch (col) {
2754 case 0:
2755 return fontMetrics().width("All ");
2756 break; case 1:
2757 return fontMetrics().width(" m ");
2758 break; case 2:
2759 return qt_settings->glyphs().width();
2760 break; case 3:
2761 return str_width;
2763 impossible("Extra column (#%d) in MenuWindow",col);
2764 return 0;
2767 QWidget* NetHackQtMenuWindow::Widget() { return dialog; }
2769 void NetHackQtMenuWindow::StartMenu()
2771 setNumRows((itemcount=0));
2772 str_width=200;
2773 str_fixed=FALSE;
2774 next_accel=0;
2775 has_glyphs=FALSE;
2778 NetHackQtMenuWindow::MenuItem::MenuItem() :
2779 str(0)
2783 NetHackQtMenuWindow::MenuItem::~MenuItem()
2785 if (str) free((void*)str);
2788 #define STR_MARGIN 4
2790 void NetHackQtMenuWindow::AddMenu(int glyph, const ANY_P* identifier,
2791 char ch, char gch, int attr, const char* str, bool presel)
2793 if (!ch && identifier->a_void!=0) {
2794 // Supply a keyboard accelerator. Limited supply.
2795 static char accel[]="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
2796 if (accel[next_accel]) {
2797 ch=accel[next_accel++];
2801 if ((int)item.size() < itemcount+1) {
2802 item.resize(itemcount*4+10);
2804 item[itemcount].glyph=glyph;
2805 item[itemcount].identifier=*identifier;
2806 item[itemcount].ch=ch;
2807 item[itemcount].attr=attr;
2808 item[itemcount].str=strdup(str);
2809 item[itemcount].selected=presel;
2810 item[itemcount].count=-1;
2811 ++itemcount;
2813 str_fixed=str_fixed || strstr(str," ");
2814 if (glyph!=NO_GLYPH) has_glyphs=TRUE;
2816 void NetHackQtMenuWindow::EndMenu(const char* p)
2818 prompt.setText(p ? p : "");
2820 void NetHackQtMenuWindow::Layout()
2822 int butw=totalWidth()/6; // 6 buttons
2823 int buth=fontMetrics().height()+8; // 8 for spacing & mitres
2824 int prompth=(prompt.text().isNull() ? 0 : buth);
2826 prompt.setGeometry(6,buth,dialog->width()-6,prompth);
2827 int h=dialog->height()-buth-prompth;
2828 setGeometry(0,buth+prompth, dialog->width(), h);
2830 // Below, we take care to use up full width
2831 int x=0;
2832 ok->setGeometry(x,0,butw,buth); x+=butw; butw=(dialog->width()-x)/5;
2833 cancel->setGeometry(x,0,butw,buth); x+=butw; butw=(dialog->width()-x)/4;
2834 all->setGeometry(x,0,butw,buth); x+=butw; butw=(dialog->width()-x)/3;
2835 none->setGeometry(x,0,butw,buth); x+=butw; butw=(dialog->width()-x)/2;
2836 invert->setGeometry(x,0,butw,buth); x+=butw; butw=(dialog->width()-x)/1;
2837 search->setGeometry(x,0,butw,buth);
2840 int NetHackQtMenuWindow::SelectMenu(int h, MENU_ITEM_P **menu_list)
2842 setFont(str_fixed ?
2843 qt_settings->normalFixedFont() : qt_settings->normalFont());
2845 for (int i=0; i<itemcount; i++) {
2846 str_width=QMAX(str_width,
2847 STR_MARGIN+fontMetrics().width(item[i].str));
2850 setCellHeight(QMAX(qt_settings->glyphs().height()+1,fontMetrics().height()));
2851 setNumRows(itemcount);
2853 int buth=fontMetrics().height()+8; // 8 for spacing & mitres
2855 how=h;
2857 ok->setEnabled(how!=PICK_ONE);ok->setDefault(how!=PICK_ONE);
2858 cancel->setEnabled(TRUE);
2859 all->setEnabled(how==PICK_ANY);
2860 none->setEnabled(how==PICK_ANY);
2861 invert->setEnabled(how==PICK_ANY);
2862 search->setEnabled(how!=PICK_NONE);
2864 dialog->SetResult(-1);
2866 // 20 allows for scrollbar or spacing
2867 // 4 for frame borders
2868 int mh = QApplication::desktop()->height()*3/5;
2869 if ( qt_compact_mode && totalHeight() > mh ) {
2870 // big, so make it fill
2871 dialog->showMaximized();
2872 } else {
2873 dialog->resize(totalWidth()+20,
2874 QMIN(totalHeight(), mh)+buth+4+(prompt.text().isNull() ? 0 : buth));
2875 if ( dialog->width() > QApplication::desktop()->width() )
2876 dialog->resize(QApplication::desktop()->width(),dialog->height()+16);
2877 centerOnMain(dialog);
2878 dialog->show();
2881 setFocus();
2882 while (dialog->result()<0) {
2883 // changed the defaults below to the values in wintype.h 000119 - azy
2884 if (!keysource.Empty()) {
2885 char k=keysource.GetAscii();
2886 k=map_menu_cmd(k); /* added 000119 - azy */
2887 if (k=='\033')
2888 dialog->Reject();
2889 else if (k=='\r' || k=='\n' || k==' ')
2890 dialog->Accept();
2891 else if (k==MENU_SEARCH)
2892 Search();
2893 else if (k==MENU_SELECT_ALL)
2894 All();
2895 else if (k==MENU_INVERT_ALL)
2896 Invert();
2897 else if (k==MENU_UNSELECT_ALL)
2898 ChooseNone();
2899 else {
2900 for (int i=0; i<itemcount; i++) {
2901 if (item[i].ch==k)
2902 ToggleSelect(i);
2906 if (dialog->result()<0)
2907 qApp->enter_loop();
2909 //if ( (nhid != WIN_INVEN || !flags.perm_invent) ) // doesn't work yet
2911 dialog->hide();
2913 int result=dialog->result();
2915 // Consume ^M (which QDialog steals for default button)
2916 while (!keysource.Empty() &&
2917 (keysource.TopAscii()=='\n' || keysource.TopAscii()=='\r'))
2918 keysource.GetAscii();
2920 *menu_list=0;
2921 if (how==PICK_NONE)
2922 return result==0 ? -1 : 0;
2924 if (result>0) {
2925 if (how==PICK_ONE) {
2926 int i;
2927 for (i=0; i<itemcount && !item[i].selected; i++)
2929 if (i<itemcount) {
2930 *menu_list=(MENU_ITEM_P*)malloc(sizeof(MENU_ITEM_P));
2931 (*menu_list)[0].item=item[i].identifier;
2932 (*menu_list)[0].count=item[i].count;
2933 return 1;
2934 } else {
2935 return 0;
2937 } else {
2938 int count=0;
2939 for (int i=0; i<itemcount; i++)
2940 if (item[i].selected) count++;
2941 if (count) {
2942 *menu_list=(MENU_ITEM_P*)malloc(count*sizeof(MENU_ITEM_P));
2943 int j=0;
2944 for (int i=0; i<itemcount; i++) {
2945 if (item[i].selected) {
2946 (*menu_list)[j].item=item[i].identifier;
2947 (*menu_list)[j].count=item[i].count;
2948 j++;
2951 return count;
2952 } else {
2953 return 0;
2956 } else {
2957 return -1;
2961 void NetHackQtMenuWindow::keyPressEvent(QKeyEvent* event)
2963 if (viewHeight() < totalHeight() && !(event->state()&ShiftButton)) {
2964 if (event->key()==Key_Prior) {
2965 setYOffset(yOffset()-viewHeight());
2966 } else if (event->key()==Key_Next) {
2967 setYOffset(yOffset()+viewHeight());
2968 } else {
2969 event->ignore();
2971 } else {
2972 event->ignore();
2976 void NetHackQtMenuWindow::All()
2978 for (int i=0; i<itemcount; i++) {
2979 if (!item[i].selected) ToggleSelect(i);
2982 void NetHackQtMenuWindow::ChooseNone()
2984 for (int i=0; i<itemcount; i++) {
2985 if (item[i].selected) ToggleSelect(i);
2989 void NetHackQtMenuWindow::Invert()
2991 for (int i=0; i<itemcount; i++) {
2992 ToggleSelect(i);
2996 void NetHackQtMenuWindow::Search()
2998 NetHackQtStringRequestor requestor(keysource,"Search for:");
2999 char line[256];
3000 if (requestor.Get(line)) {
3001 for (int i=0; i<itemcount; i++) {
3002 if (strstr(item[i].str,line))
3003 ToggleSelect(i);
3008 void NetHackQtMenuWindow::ToggleSelect(int i)
3010 if (item[i].Selectable()) {
3011 item[i].selected = !item[i].selected;
3012 if ( !item[i].selected )
3013 item[i].count=-1;
3014 updateCell(i,3);
3015 if (how==PICK_ONE) {
3016 dialog->Accept();
3022 void NetHackQtMenuWindow::paintCell(QPainter* painter, int row, int col)
3024 // [pick-count] [accel] [glyph] [string]
3026 MenuItem& i = item[row];
3028 painter->setPen(black);
3029 painter->setFont(font());
3031 if (i.selected) {
3032 painter->setPen(darkGreen);
3035 switch (col) {
3036 case 0:
3037 if ( i.ch || i.attr!=ATR_INVERSE ) {
3038 QString text;
3039 if ( i.selected && i.count == -1 ) {
3040 if ( i.str[0]>='0' && i.str[0]<='9' )
3041 text = "All";
3042 else
3043 text = "*";
3044 } else if ( i.count<0 ) {
3045 text = "-";
3046 } else {
3047 text.sprintf("%d",i.count);
3049 painter->drawText(0,0,cellWidth(col),cellHeight(),
3050 AlignHCenter|AlignVCenter,text);
3052 break; case 1:
3053 if ((signed char)i.ch >= 0) {
3054 char text[2]={i.ch,0};
3055 painter->drawText(0,0,cellWidth(col),cellHeight(),
3056 AlignHCenter|AlignVCenter,text);
3058 break; case 2:
3059 if (i.glyph!=NO_GLYPH) {
3060 // Centered in height
3061 int y=(cellHeight()-qt_settings->glyphs().height())/2;
3062 if (y<0) y=0;
3063 qt_settings->glyphs().drawGlyph(*painter, i.glyph, 0, y);
3065 break; case 3:
3066 // XXX should qt_settings have ALL the various fonts
3067 QFont newfont=font();
3069 if (i.attr) {
3070 switch(i.attr) {
3071 case ATR_ULINE:
3072 newfont.setUnderline(TRUE);
3073 break; case ATR_BOLD:
3074 painter->setPen(red);
3075 break; case ATR_BLINK:
3076 newfont.setItalic(TRUE);
3077 break; case ATR_INVERSE:
3078 newfont=qt_settings->largeFont();
3079 newfont.setWeight(QFont::Bold);
3081 if (i.selected) {
3082 painter->setPen(blue);
3083 } else {
3084 painter->setPen(darkBlue);
3088 painter->setFont(newfont);
3090 painter->drawText(STR_MARGIN,0,cellWidth(col),cellHeight(),
3091 AlignLeft|AlignVCenter,i.str);
3095 void NetHackQtMenuWindow::mousePressEvent(QMouseEvent* event)
3097 int col=findCol(event->pos().x());
3098 int row=findRow(event->pos().y());
3100 if (col<0 || row<0 || !item[row].Selectable()) return;
3102 if (how!=PICK_NONE) {
3103 if (col==0) {
3104 // Changing count.
3105 NetHackQtStringRequestor requestor(keysource,"Count:");
3106 char buf[BUFSZ];
3108 if (item[row].count>0)
3109 Sprintf(buf,"%d", item[row].count);
3110 else
3111 Strcpy(buf, "");
3113 requestor.SetDefault(buf);
3114 if (requestor.Get(buf)) {
3115 item[row].count=atoi(buf);
3116 if (item[row].count==0) {
3117 item[row].count=-1;
3118 if (item[row].selected) ToggleSelect(row);
3119 } else {
3120 if (!item[row].selected) ToggleSelect(row);
3122 updateCell(row,0);
3124 } else {
3125 pressed=row;
3126 was_sel=item[row].selected;
3127 ToggleSelect(row);
3128 updateCell(row,0);
3133 void NetHackQtMenuWindow::mouseReleaseEvent(QMouseEvent* event)
3135 if (pressed>=0) {
3136 int p=pressed;
3137 pressed=-1;
3138 updateCell(p,3);
3142 void NetHackQtMenuWindow::mouseMoveEvent(QMouseEvent* event)
3144 if (pressed>=0) {
3145 int col=findCol(event->pos().x());
3146 int row=findRow(event->pos().y());
3148 if (row>=0 && col>=0) {
3149 if (pressed!=row) {
3150 // reset to initial state
3151 if (item[pressed].selected!=was_sel)
3152 ToggleSelect(pressed);
3153 } else {
3154 // reset to new state
3155 if (item[pressed].selected==was_sel)
3156 ToggleSelect(pressed);
3163 class NetHackQtTextListBox : public QListBox {
3164 public:
3165 NetHackQtTextListBox(QWidget* parent) : QListBox(parent) { }
3167 int TotalWidth()
3169 doLayout();
3170 return contentsWidth();
3172 int TotalHeight()
3174 doLayout();
3175 return contentsHeight();
3178 virtual void setFont(const QFont &font)
3180 QListBox::setFont(font);
3182 void keyPressEvent(QKeyEvent* e)
3184 QListBox::keyPressEvent(e);
3189 QPixmap* NetHackQtRIP::pixmap=0;
3191 NetHackQtRIP::NetHackQtRIP(QWidget* parent) :
3192 QWidget(parent)
3194 if (!pixmap) {
3195 pixmap=new QPixmap;
3196 tryload(*pixmap, "rip.xpm");
3198 riplines=0;
3199 resize(pixmap->width(),pixmap->height());
3200 setFont(QFont("times",12)); // XXX may need to be configurable
3203 void NetHackQtRIP::setLines(char** l, int n)
3205 line=l;
3206 riplines=n;
3209 QSize NetHackQtRIP::sizeHint() const
3211 return pixmap->size();
3214 void NetHackQtRIP::paintEvent(QPaintEvent* event)
3216 if ( riplines ) {
3217 int pix_x=(width()-pixmap->width())/2;
3218 int pix_y=(height()-pixmap->height())/2;
3220 // XXX positions based on RIP image
3221 int rip_text_x=pix_x+156;
3222 int rip_text_y=pix_y+67;
3223 int rip_text_h=94/riplines;
3225 QPainter painter;
3226 painter.begin(this);
3227 painter.drawPixmap(pix_x,pix_y,*pixmap);
3228 for (int i=0; i<riplines; i++) {
3229 painter.drawText(rip_text_x-i/2,rip_text_y+i*rip_text_h,
3230 1,1,DontClip|AlignHCenter,line[i]);
3232 painter.end();
3236 NetHackQtTextWindow::NetHackQtTextWindow(NetHackQtKeyBuffer& ks) :
3237 QDialog(qApp->mainWidget(),0,FALSE),
3238 keysource(ks),
3239 use_rip(FALSE),
3240 str_fixed(FALSE),
3241 ok("Dismiss",this),
3242 search("Search",this),
3243 lines(new NetHackQtTextListBox(this)),
3244 rip(this)
3246 ok.setDefault(TRUE);
3247 connect(&ok,SIGNAL(clicked()),this,SLOT(accept()));
3248 connect(&search,SIGNAL(clicked()),this,SLOT(Search()));
3249 connect(qt_settings,SIGNAL(fontChanged()),this,SLOT(doUpdate()));
3251 QVBoxLayout* vb = new QVBoxLayout(this);
3252 vb->addWidget(&rip);
3253 QHBoxLayout* hb = new QHBoxLayout(vb);
3254 hb->addWidget(&ok);
3255 hb->addWidget(&search);
3256 vb->addWidget(lines);
3259 void NetHackQtTextWindow::doUpdate()
3261 update();
3265 NetHackQtTextWindow::~NetHackQtTextWindow()
3270 QWidget* NetHackQtTextWindow::Widget()
3272 return this;
3275 bool NetHackQtTextWindow::Destroy()
3277 return !isVisible();
3280 void NetHackQtTextWindow::UseRIP(int how, time_t when)
3282 // Code from X11 windowport
3283 #define STONE_LINE_LEN 16 /* # chars that fit on one line */
3284 #define NAME_LINE 0 /* line # for player name */
3285 #define GOLD_LINE 1 /* line # for amount of gold */
3286 #define DEATH_LINE 2 /* line # for death description */
3287 #define YEAR_LINE 6 /* line # for year */
3289 static char** rip_line=0;
3290 if (!rip_line) {
3291 rip_line=new char*[YEAR_LINE+1];
3292 for (int i=0; i<YEAR_LINE+1; i++) {
3293 rip_line[i]=new char[STONE_LINE_LEN+1];
3297 /* Follows same algorithm as genl_outrip() */
3299 char buf[BUFSZ];
3300 char *dpx;
3301 int line;
3302 long year;
3304 /* Put name on stone */
3305 Sprintf(rip_line[NAME_LINE], "%s", plname);
3307 /* Put $ on stone */
3308 Sprintf(rip_line[GOLD_LINE], "%ld Au", done_money);
3310 /* Put together death description */
3311 formatkiller(buf, sizeof buf, how, FALSE);
3313 /* Put death type on stone */
3314 for (line=DEATH_LINE, dpx = buf; line<YEAR_LINE; line++) {
3315 register int i,i0;
3316 char tmpchar;
3318 if ( (i0=strlen(dpx)) > STONE_LINE_LEN) {
3319 for(i = STONE_LINE_LEN;
3320 ((i0 > STONE_LINE_LEN) && i); i--)
3321 if(dpx[i] == ' ') i0 = i;
3322 if(!i) i0 = STONE_LINE_LEN;
3324 tmpchar = dpx[i0];
3325 dpx[i0] = 0;
3326 strcpy(rip_line[line], dpx);
3327 if (tmpchar != ' ') {
3328 dpx[i0] = tmpchar;
3329 dpx= &dpx[i0];
3330 } else dpx= &dpx[i0+1];
3333 /* Put year on stone */
3334 year = yyyymmdd(when) / 10000L;
3335 Sprintf(rip_line[YEAR_LINE], "%4ld", year);
3337 rip.setLines(rip_line,YEAR_LINE+1);
3339 use_rip=TRUE;
3342 void NetHackQtTextWindow::Clear()
3344 lines->clear();
3345 use_rip=FALSE;
3346 str_fixed=FALSE;
3349 void NetHackQtTextWindow::Display(bool block)
3351 if (str_fixed) {
3352 lines->setFont(qt_settings->normalFixedFont());
3353 } else {
3354 lines->setFont(qt_settings->normalFont());
3357 int h=0;
3358 if (use_rip) {
3359 h+=rip.height();
3360 ok.hide();
3361 search.hide();
3362 rip.show();
3363 } else {
3364 h+=ok.height()*2;
3365 ok.show();
3366 search.show();
3367 rip.hide();
3369 int mh = QApplication::desktop()->height()*3/5;
3370 if ( qt_compact_mode && (lines->TotalHeight() > mh || use_rip) ) {
3371 // big, so make it fill
3372 showMaximized();
3373 } else {
3374 resize(QMAX(use_rip ? rip.width() : 200,
3375 lines->TotalWidth()+24),
3376 QMIN(mh, lines->TotalHeight()+h));
3377 centerOnMain(this);
3378 show();
3380 if (block) {
3381 setResult(-1);
3382 while (result()==-1) {
3383 qApp->enter_loop();
3384 if (result()==-1 && !keysource.Empty()) {
3385 char k=keysource.GetAscii();
3386 if (k=='\033' || k==' ' || k=='\r' || k=='\n') {
3387 accept();
3388 } else if (k=='/') {
3389 Search();
3396 void NetHackQtTextWindow::PutStr(int attr, const char* text)
3398 str_fixed=str_fixed || strstr(text," ");
3399 lines->insertItem(text);
3402 void NetHackQtTextWindow::done(int i)
3404 setResult(i+1000);
3405 hide();
3406 qApp->exit_loop();
3409 void NetHackQtTextWindow::keyPressEvent(QKeyEvent* e)
3411 if ( e->ascii() != '\r' && e->ascii() != '\n' && e->ascii() != '\033' )
3412 lines->keyPressEvent(e);
3413 else
3414 QDialog::keyPressEvent(e);
3417 void NetHackQtTextWindow::Search()
3419 NetHackQtStringRequestor requestor(keysource,"Search for:");
3420 static char line[256]="";
3421 requestor.SetDefault(line);
3422 if (requestor.Get(line)) {
3423 int current=lines->currentItem();
3424 for (uint i=1; i<lines->count(); i++) {
3425 int lnum=(i+current)%lines->count();
3426 QString str=lines->text(lnum);
3427 if (str.contains(line)) {
3428 lines->setCurrentItem(lnum);
3429 lines->centerCurrentItem();
3430 return;
3433 lines->setCurrentItem(-1);
3438 NetHackQtDelay::NetHackQtDelay(int ms) :
3439 msec(ms)
3443 void NetHackQtDelay::wait()
3445 startTimer(msec);
3446 qApp->enter_loop();
3449 void NetHackQtDelay::timerEvent(QTimerEvent* timer)
3451 qApp->exit_loop();
3452 killTimers();
3455 NetHackQtInvUsageWindow::NetHackQtInvUsageWindow(QWidget* parent) :
3456 QWidget(parent)
3460 void NetHackQtInvUsageWindow::drawWorn(QPainter& painter, obj* nhobj, int x, int y, bool canbe)
3462 short int glyph;
3463 if (nhobj)
3464 glyph=obj_to_glyph(nhobj);
3465 else if (canbe)
3466 glyph=cmap_to_glyph(S_room);
3467 else
3468 glyph=cmap_to_glyph(S_stone);
3470 qt_settings->glyphs().drawCell(painter,glyph,x,y);
3473 void NetHackQtInvUsageWindow::paintEvent(QPaintEvent*)
3475 // 012
3477 //0 WhB
3478 //1 s"w
3479 //2 gCg
3480 //3 =A=
3481 //4 T
3482 //5 S
3484 QPainter painter;
3485 painter.begin(this);
3487 // Blanks
3488 drawWorn(painter,0,0,4,FALSE);
3489 drawWorn(painter,0,0,5,FALSE);
3490 drawWorn(painter,0,2,4,FALSE);
3491 drawWorn(painter,0,2,5,FALSE);
3493 drawWorn(painter,uarm,1,3); // Armour
3494 drawWorn(painter,uarmc,1,2); // Cloak
3495 drawWorn(painter,uarmh,1,0); // Helmet
3496 drawWorn(painter,uarms,0,1); // Shield
3497 drawWorn(painter,uarmg,0,2); // Gloves - repeated
3498 drawWorn(painter,uarmg,2,2); // Gloves - repeated
3499 drawWorn(painter,uarmf,1,5); // Shoes (feet)
3500 drawWorn(painter,uarmu,1,4); // Undershirt
3501 drawWorn(painter,uleft,0,3); // RingL
3502 drawWorn(painter,uright,2,3); // RingR
3504 drawWorn(painter,uwep,2,1); // Weapon
3505 drawWorn(painter,uswapwep,0,0); // Secondary weapon
3506 drawWorn(painter,uamul,1,1); // Amulet
3507 drawWorn(painter,ublindf,2,0); // Blindfold
3509 painter.end();
3512 class SmallToolButton : public QToolButton {
3513 public:
3514 SmallToolButton(const QPixmap & pm, const QString &textLabel,
3515 const QString& grouptext,
3516 QObject * receiver, const char* slot,
3517 QToolBar * parent) :
3518 QToolButton(pm, textLabel,
3519 #if QT_VERSION < 210
3520 QString::null,
3521 #else
3522 grouptext,
3523 #endif
3524 receiver, slot, parent)
3528 QSize sizeHint() const
3530 // get just a couple more pixels for the map
3531 return QToolButton::sizeHint()-QSize(0,2);
3536 NetHackQtMainWindow::NetHackQtMainWindow(NetHackQtKeyBuffer& ks) :
3537 message(0), map(0), status(0), invusage(0),
3538 keysink(ks), dirkey(0)
3540 QToolBar* toolbar = new QToolBar(this);
3541 #if QT_VERSION >= 210
3542 setToolBarsMovable(FALSE);
3543 toolbar->setHorizontalStretchable(TRUE);
3544 toolbar->setVerticalStretchable(TRUE);
3545 #endif
3546 addToolBar(toolbar);
3547 menubar = menuBar();
3549 setCaption("Qt NetHack");
3550 if ( qt_compact_mode )
3551 setIcon(QPixmap(nh_icon_small));
3552 else
3553 setIcon(QPixmap(nh_icon));
3555 QPopupMenu* game=new QPopupMenu;
3556 QPopupMenu* apparel=new QPopupMenu;
3557 QPopupMenu* act1=new QPopupMenu;
3558 QPopupMenu* act2 = qt_compact_mode ? new QPopupMenu : act1;
3559 QPopupMenu* magic=new QPopupMenu;
3560 QPopupMenu* info=new QPopupMenu;
3562 QPopupMenu *help;
3564 #ifdef KDE
3565 help = kapp->getHelpMenu( TRUE, "" );
3566 help->insertSeparator();
3567 #else
3568 help = qt_compact_mode ? info : new QPopupMenu;
3569 #endif
3571 enum { OnDesktop=1, OnHandhelds=2 };
3572 struct Macro {
3573 QPopupMenu* menu;
3574 const char* name;
3575 const char* action;
3576 int flags;
3577 } item[] = {
3578 { game, 0, 0, 3},
3579 { game, "Version\tv", "v", 3},
3580 { game, "Compilation\tAlt+V", "\366", 3},
3581 { game, "History\tShift+V", "V", 3},
3582 { game, "Redraw\tCtrl+R", "\022", 0}, // useless
3583 { game, "Options\tShift+O", "O", 3},
3584 { game, "Explore mode\tShift+X", "X", 3},
3585 { game, 0, 0, 3},
3586 { game, "Save\tSy", "Sy", 3},
3587 { game, "Quit\tAlt+Q", "\361", 3},
3589 { apparel, "Apparel off\tShift+A", "A", 2},
3590 { apparel, "Remove many\tShift+A", "A", 1},
3591 { apparel, 0, 0, 3},
3592 { apparel, "Wield weapon\tw", "w", 3},
3593 { apparel, "Exchange weapons\tx", "x", 3},
3594 { apparel, "Two weapon combat\t#two", "#tw", 3},
3595 { apparel, "Load quiver\tShift+Q", "Q", 3},
3596 { apparel, 0, 0, 3},
3597 { apparel, "Wear armour\tShift+W", "W", 3},
3598 { apparel, "Take off armour\tShift+T", "T", 3},
3599 { apparel, 0, 0, 3},
3600 { apparel, "Put on non-armour\tShift+P", "P", 3},
3601 { apparel, "Remove non-armour\tShift+R", "R", 3},
3603 { act1, "Again\tCtrl+A", "\001", 2},
3604 { act1, 0, 0, 3},
3605 { act1, "Apply\ta?", "a?", 3},
3606 { act1, "Chat\tAlt+C", "\343", 3},
3607 { act1, "Close door\tc", "c", 3},
3608 { act1, "Down\t>", ">", 3},
3609 { act1, "Drop many\tShift+D", "D", 2},
3610 { act1, "Drop\td?", "d?", 2},
3611 { act1, "Eat\te?", "e?", 2},
3612 { act1, "Engrave\tShift+E", "E", 3},
3613 { act1, "Fight\tShift+F", "F", 3},
3614 { act1, "Fire from quiver\tf", "f", 2},
3615 { act1, "Force\tAlt+F", "\346", 3},
3616 { act1, "Get\t,", ",", 2},
3617 { act1, "Jump\tAlt+J", "\352", 3},
3618 { act2, "Kick\tCtrl+D", "\004", 2},
3619 { act2, "Loot\tAlt+L", "\354", 3},
3620 { act2, "Open door\to", "o", 3},
3621 { act2, "Pay\tp", "p", 3},
3622 { act2, "Rest\t.", ".", 2},
3623 { act2, "Ride\t#ri", "#ri", 3},
3624 { act2, "Search\ts", "s", 3},
3625 { act2, "Sit\tAlt+S", "\363", 3},
3626 { act2, "Throw\tt", "t", 2},
3627 { act2, "Untrap\t#u", "#u", 3},
3628 { act2, "Up\t<", "<", 3},
3629 { act2, "Wipe face\tAlt+W", "\367", 3},
3631 { magic, "Quaff potion\tq?", "q?", 3},
3632 { magic, "Read scroll/book\tr?", "r?", 3},
3633 { magic, "Zap wand\tz?", "z?", 3},
3634 { magic, "Zap spell\tShift+Z", "Z", 3},
3635 { magic, "Dip\tAlt+D", "\344", 3},
3636 { magic, "Rub\tAlt+R", "\362", 3},
3637 { magic, "Invoke\tAlt+I", "\351", 3},
3638 { magic, 0, 0, 3},
3639 { magic, "Offer\tAlt+O", "\357", 3},
3640 { magic, "Pray\tAlt+P", "\360", 3},
3641 { magic, 0, 0, 3},
3642 { magic, "Teleport\tCtrl+T", "\024", 3},
3643 { magic, "Monster action\tAlt+M", "\355", 3},
3644 { magic, "Turn undead\tAlt+T", "\364", 3},
3646 { help, "Help\t?", "?", 3},
3647 { help, 0, 0, 3},
3648 { help, "What is here\t:", ":", 3},
3649 { help, "What is there\t;", ";", 3},
3650 { help, "What is...\t/y", "/y", 2},
3651 { help, 0, 0, 1},
3653 { info, "Inventory\ti", "i", 3},
3654 #ifdef SLASHEM
3655 { info, "Angbandish inventory\t*", "*", 3},
3656 #endif
3657 { info, "Conduct\t#co", "#co", 3},
3658 { info, "Discoveries\t\\", "\\", 3},
3659 { info, "List/reorder spells\t+", "+", 3},
3660 { info, "Adjust letters\tAlt+A", "\341", 2},
3661 { info, 0, 0, 3},
3662 { info, "Name object\tAlt+N", "\356y?", 3},
3663 { info, "Name object type\tAlt+N", "\356n?", 3},
3664 { info, "Name creature\tShift+C", "C", 3},
3665 { info, 0, 0, 3},
3666 { info, "Qualifications\tAlt+E", "\345", 3},
3668 { 0, 0, 0, 0 }
3671 int i;
3672 int count=0;
3673 for (i=0; item[i].menu; i++)
3674 if (item[i].name) count++;
3676 macro=new const char* [count];
3678 game->insertItem("Qt settings...",1000);
3679 help->insertItem("About Qt NetHack...",2000);
3680 //help->insertItem("NetHack Guidebook...",3000);
3681 help->insertSeparator();
3683 count=0;
3684 for (i=0; item[i].menu; i++) {
3685 if ( item[i].flags & (qt_compact_mode ? 1 : 2) ) {
3686 if (item[i].name) {
3687 QString name = item[i].name;
3688 if ( qt_compact_mode ) // accelerators aren't
3689 name.replace(QRegExp("\t.*"),"");
3690 item[i].menu->insertItem(name,count);
3691 macro[count++]=item[i].action;
3692 } else {
3693 item[i].menu->insertSeparator();
3698 menubar->insertItem("Game",game);
3699 menubar->insertItem("Gear",apparel);
3701 if ( qt_compact_mode ) {
3702 menubar->insertItem("A-J",act1);
3703 menubar->insertItem("K-Z",act2);
3704 menubar->insertItem("Magic",magic);
3705 menubar->insertItem(QPixmap(info_xpm),info);
3706 menubar->insertItem(QPixmap(map_xpm), this, SLOT(raiseMap()));
3707 menubar->insertItem(QPixmap(msg_xpm), this, SLOT(raiseMessages()));
3708 menubar->insertItem(QPixmap(stat_xpm), this, SLOT(raiseStatus()));
3709 } else {
3710 menubar->insertItem("Action",act1);
3711 menubar->insertItem("Magic",magic);
3712 menubar->insertItem("Info",info);
3713 menubar->insertSeparator();
3714 menubar->insertItem("Help",help);
3717 QSignalMapper* sm = new QSignalMapper(this);
3718 connect(sm, SIGNAL(mapped(const QString&)), this, SLOT(doKeys(const QString&)));
3719 QToolButton* tb;
3720 tb = new SmallToolButton( QPixmap(again_xpm),"Again","Action", sm, SLOT(map()), toolbar );
3721 sm->setMapping(tb, "\001" );
3722 tb = new SmallToolButton( QPixmap(get_xpm),"Get","Action", sm, SLOT(map()), toolbar );
3723 sm->setMapping(tb, "," );
3724 tb = new SmallToolButton( QPixmap(kick_xpm),"Kick","Action", sm, SLOT(map()), toolbar );
3725 sm->setMapping(tb, "\004" );
3726 tb = new SmallToolButton( QPixmap(throw_xpm),"Throw","Action", sm, SLOT(map()), toolbar );
3727 sm->setMapping(tb, "t" );
3728 tb = new SmallToolButton( QPixmap(fire_xpm),"Fire","Action", sm, SLOT(map()), toolbar );
3729 sm->setMapping(tb, "f" );
3730 tb = new SmallToolButton( QPixmap(drop_xpm),"Drop","Action", sm, SLOT(map()), toolbar );
3731 sm->setMapping(tb, "D" );
3732 tb = new SmallToolButton( QPixmap(eat_xpm),"Eat","Action", sm, SLOT(map()), toolbar );
3733 sm->setMapping(tb, "e" );
3734 tb = new SmallToolButton( QPixmap(rest_xpm),"Rest","Action", sm, SLOT(map()), toolbar );
3735 sm->setMapping(tb, "." );
3736 tb = new SmallToolButton( QPixmap(cast_a_xpm),"Cast A","Magic", sm, SLOT(map()), toolbar );
3737 sm->setMapping(tb, "Za" );
3738 tb = new SmallToolButton( QPixmap(cast_b_xpm),"Cast B","Magic", sm, SLOT(map()), toolbar );
3739 sm->setMapping(tb, "Zb" );
3740 tb = new SmallToolButton( QPixmap(cast_c_xpm),"Cast C","Magic", sm, SLOT(map()), toolbar );
3741 sm->setMapping(tb, "Zc" );
3742 if ( !qt_compact_mode ) {
3743 QWidget* filler = new QWidget(toolbar);
3744 filler->setBackgroundMode(PaletteButton);
3745 toolbar->setStretchableWidget(filler);
3748 connect(menubar,SIGNAL(activated(int)),this,SLOT(doMenuItem(int)));
3750 #ifdef KDE
3751 setMenu (menubar);
3752 #endif
3754 int x=0,y=0;
3755 int w=QApplication::desktop()->width()-10; // XXX arbitrary extra space for frame
3756 int h=QApplication::desktop()->height()-50;
3758 int maxwn;
3759 int maxhn;
3760 if (qt_tilewidth != NULL) {
3761 maxwn = atoi(qt_tilewidth) * COLNO + 10;
3762 } else {
3763 maxwn = 1400;
3765 if (qt_tileheight != NULL) {
3766 maxhn = atoi(qt_tileheight) * ROWNO * 6/4;
3767 } else {
3768 maxhn = 1024;
3771 // Be exactly the size we want to be - full map...
3772 if (w>maxwn) {
3773 x+=(w-maxwn)/2;
3774 w=maxwn; // Doesn't need to be any wider
3776 if (h>maxhn) {
3777 y+=(h-maxhn)/2;
3778 h=maxhn; // Doesn't need to be any taller
3781 setGeometry(x,y,w,h);
3783 if ( qt_compact_mode ) {
3784 stack = new QWidgetStack(this);
3785 setCentralWidget(stack);
3786 } else {
3787 setCentralWidget(new QWidget(this));
3788 invusage = new NetHackQtInvUsageWindow(centralWidget());
3792 void NetHackQtMainWindow::zoomMap()
3794 qt_settings->toggleGlyphSize();
3797 void NetHackQtMainWindow::raiseMap()
3799 if ( stack->id(stack->visibleWidget()) == 0 ) {
3800 zoomMap();
3801 } else {
3802 stack->raiseWidget(0);
3806 void NetHackQtMainWindow::raiseMessages()
3808 stack->raiseWidget(1);
3811 void NetHackQtMainWindow::raiseStatus()
3813 stack->raiseWidget(2);
3816 class NetHackMimeSourceFactory : public QMimeSourceFactory {
3817 public:
3818 const QMimeSource* data(const QString& abs_name) const
3820 const QMimeSource* r = 0;
3821 if ( (NetHackMimeSourceFactory*)this == QMimeSourceFactory::defaultFactory() )
3822 r = QMimeSourceFactory::data(abs_name);
3823 else
3824 r = QMimeSourceFactory::defaultFactory()->data(abs_name);
3825 if ( !r ) {
3826 int sl = abs_name.length();
3827 do {
3828 sl = abs_name.findRev('/',sl-1);
3829 QString name = sl>=0 ? abs_name.mid(sl+1) : abs_name;
3830 int dot = name.findRev('.');
3831 if ( dot >= 0 )
3832 name = name.left(dot);
3833 if ( name == "map" )
3834 r = new QImageDrag(QImage(map_xpm));
3835 else if ( name == "msg" )
3836 r = new QImageDrag(QImage(msg_xpm));
3837 else if ( name == "stat" )
3838 r = new QImageDrag(QImage(stat_xpm));
3839 } while (!r && sl>0);
3841 return r;
3845 void NetHackQtMainWindow::doMenuItem(int id)
3847 switch (id) {
3848 case 1000:
3849 centerOnMain(qt_settings);
3850 qt_settings->show();
3851 break;
3852 case 2000:
3853 QMessageBox::about(this, "About Qt NetHack", aboutMsg());
3854 break;
3855 case 3000: {
3856 QDialog dlg(this,0,TRUE);
3857 (new QVBoxLayout(&dlg))->setAutoAdd(TRUE);
3858 QTextBrowser browser(&dlg);
3859 NetHackMimeSourceFactory ms;
3860 browser.setMimeSourceFactory(&ms);
3861 browser.setSource(QDir::currentDirPath()+"/Guidebook.html");
3862 if ( qt_compact_mode )
3863 dlg.showMaximized();
3864 dlg.exec();
3866 break;
3867 default:
3868 if ( id >= 0 )
3869 doKeys(macro[id]);
3873 void NetHackQtMainWindow::doKeys(const QString& k)
3875 keysink.Put(k);
3876 qApp->exit_loop();
3879 void NetHackQtMainWindow::AddMessageWindow(NetHackQtMessageWindow* window)
3881 message=window;
3882 ShowIfReady();
3885 void NetHackQtMainWindow::AddMapWindow(NetHackQtMapWindow* window)
3887 map=window;
3888 ShowIfReady();
3889 connect(map,SIGNAL(resized()),this,SLOT(layout()));
3892 void NetHackQtMainWindow::AddStatusWindow(NetHackQtStatusWindow* window)
3894 status=window;
3895 ShowIfReady();
3898 void NetHackQtMainWindow::RemoveWindow(NetHackQtWindow* window)
3900 if (window==status) {
3901 status=0;
3902 ShowIfReady();
3903 } else if (window==map) {
3904 map=0;
3905 ShowIfReady();
3906 } else if (window==message) {
3907 message=0;
3908 ShowIfReady();
3912 void NetHackQtMainWindow::updateInventory()
3914 if ( invusage )
3915 invusage->repaint(FALSE);
3918 void NetHackQtMainWindow::fadeHighlighting()
3920 if (status) {
3921 status->fadeHighlighting();
3925 void NetHackQtMainWindow::layout()
3927 if ( qt_compact_mode )
3928 return;
3929 if (message && map && status) {
3930 QSize maxs=map->Widget()->maximumSize();
3931 int maph=QMIN(height()*2/3,maxs.height());
3933 QWidget* c = centralWidget();
3934 int h=c->height();
3935 int toph=h-maph;
3936 int iuw=3*qt_settings->glyphs().width();
3937 int topw=(c->width()-iuw)/2;
3939 message->Widget()->setGeometry(0,0,topw,toph);
3940 invusage->setGeometry(topw,0,iuw,toph);
3941 status->Widget()->setGeometry(topw+iuw,0,topw,toph);
3942 map->Widget()->setGeometry(QMAX(0,(c->width()-maxs.width())/2),
3943 toph,c->width(),maph);
3947 void NetHackQtMainWindow::resizeEvent(QResizeEvent*)
3949 layout();
3950 #ifdef KDE
3951 updateRects();
3952 #endif
3955 void NetHackQtMainWindow::keyReleaseEvent(QKeyEvent* event)
3957 if ( dirkey ) {
3958 doKeys(QString(QChar(dirkey)));
3959 if ( !event->isAutoRepeat() )
3960 dirkey = 0;
3964 void NetHackQtMainWindow::keyPressEvent(QKeyEvent* event)
3966 // Global key controls
3968 // For desktop, arrow keys scroll map, since we don't want players
3969 // to think that's the way to move. For handhelds, the normal way is to
3970 // click-to-travel, so we allow the cursor keys for fine movements.
3972 // 321
3973 // 4 0
3974 // 567
3976 if ( event->isAutoRepeat() &&
3977 event->key() >= Key_Left && event->key() <= Key_Down )
3978 return;
3980 const char* d = Cmd.dirchars;
3981 switch (event->key()) {
3982 case Key_Up:
3983 if ( dirkey == d[0] )
3984 dirkey = d[1];
3985 else if ( dirkey == d[4] )
3986 dirkey = d[3];
3987 else
3988 dirkey = d[2];
3989 break; case Key_Down:
3990 if ( dirkey == d[0] )
3991 dirkey = d[7];
3992 else if ( dirkey == d[4] )
3993 dirkey = d[5];
3994 else
3995 dirkey = d[6];
3996 break; case Key_Left:
3997 if ( dirkey == d[2] )
3998 dirkey = d[1];
3999 else if ( dirkey == d[6] )
4000 dirkey = d[7];
4001 else
4002 dirkey = d[0];
4003 break; case Key_Right:
4004 if ( dirkey == d[2] )
4005 dirkey = d[3];
4006 else if ( dirkey == d[6] )
4007 dirkey = d[5];
4008 else
4009 dirkey = d[4];
4010 break; case Key_Prior:
4011 dirkey = 0;
4012 if (message) message->Scroll(0,-1);
4013 break; case Key_Next:
4014 dirkey = 0;
4015 if (message) message->Scroll(0,+1);
4016 break; case Key_Space:
4017 if ( flags.rest_on_space ) {
4018 event->ignore();
4019 return;
4021 case Key_Enter:
4022 if ( map )
4023 map->clickCursor();
4024 break; default:
4025 dirkey = 0;
4026 event->ignore();
4030 void NetHackQtMainWindow::closeEvent(QCloseEvent* e)
4032 if ( program_state.something_worth_saving ) {
4033 switch ( QMessageBox::information( this, "NetHack",
4034 "This will end your NetHack session",
4035 "&Save", "&Cancel", 0, 1 ) )
4037 case 0:
4038 // See dosave() function
4039 if (dosave0()) {
4040 u.uhp = -1;
4041 NetHackQtBind::qt_exit_nhwindows(0);
4042 terminate(EXIT_SUCCESS);
4044 break;
4045 case 1:
4046 break; // ignore the event
4048 } else {
4049 e->accept();
4053 void NetHackQtMainWindow::ShowIfReady()
4055 if (message && map && status) {
4056 QPoint pos(0,0);
4057 QWidget* p = qt_compact_mode ? stack : centralWidget();
4058 message->Widget()->recreate(p,0,pos);
4059 map->Widget()->recreate(p,0,pos);
4060 status->Widget()->recreate(p,0,pos);
4061 if ( qt_compact_mode ) {
4062 message->setMap(map);
4063 stack->addWidget(map->Widget(), 0);
4064 stack->addWidget(message->Widget(), 1);
4065 stack->addWidget(status->Widget(), 2);
4066 raiseMap();
4067 } else {
4068 layout();
4070 showMaximized();
4071 } else if (isVisible()) {
4072 hide();
4077 NetHackQtYnDialog::NetHackQtYnDialog(NetHackQtKeyBuffer& keysrc,const char* q,const char* ch,char df) :
4078 QDialog(qApp->mainWidget(),0,FALSE),
4079 question(q), choices(ch), def(df),
4080 keysource(keysrc)
4082 setCaption("NetHack: Question");
4085 char NetHackQtYnDialog::Exec()
4087 QString ch(choices);
4088 int ch_per_line=6;
4089 QString qlabel;
4090 QString enable;
4091 if ( qt_compact_mode && !choices ) {
4092 // expand choices from prompt
4093 // ##### why isn't choices set properly???
4094 const char* c=question;
4095 while ( *c && *c != '[' )
4096 c++;
4097 qlabel = QString(question).left(c-question);
4098 if ( *c ) {
4099 c++;
4100 if ( *c == '-' )
4101 ch.append(*c++);
4102 char from=0;
4103 while ( *c && *c != ']' && *c != ' ' ) {
4104 if ( *c == '-' ) {
4105 from = c[-1];
4106 } else if ( from ) {
4107 for (char f=from+1; f<=*c; f++)
4108 ch.append(f);
4109 from = 0;
4110 } else {
4111 ch.append(*c);
4112 from = 0;
4114 c++;
4116 if ( *c == ' ' ) {
4117 while ( *c && *c != ']' ) {
4118 if ( *c == '*' || *c == '?' )
4119 ch.append(*c);
4120 c++;
4124 if ( strstr(question, "what direction") ) {
4125 // We replace this regardless, since sometimes you get choices.
4126 const char* d = Cmd.dirchars;
4127 enable=ch;
4128 ch="";
4129 ch.append(d[1]);
4130 ch.append(d[2]);
4131 ch.append(d[3]);
4132 ch.append(d[0]);
4133 ch.append('.');
4134 ch.append(d[4]);
4135 ch.append(d[7]);
4136 ch.append(d[6]);
4137 ch.append(d[5]);
4138 ch.append(d[8]);
4139 ch.append(d[9]);
4140 ch_per_line = 3;
4141 def = ' ';
4142 } else {
4143 // Hmm... they'll have to use a virtual keyboard
4145 } else {
4146 qlabel = question;
4148 if (!ch.isNull()) {
4149 QVBoxLayout vb(this);
4150 vb.setAutoAdd(TRUE);
4151 bool bigq = qlabel.length()>40;
4152 if ( bigq ) {
4153 QLabel* q = new QLabel(qlabel,this);
4154 q->setAlignment(AlignLeft|WordBreak);
4155 q->setMargin(4);
4157 QButtonGroup group(ch_per_line, Horizontal,
4158 bigq ? QString::null : qlabel, this);
4160 int nchoices=ch.length();
4162 bool allow_count=ch.contains('#');
4164 const int margin=8;
4165 const int gutter=8;
4166 const int extra=fontMetrics().height(); // Extra for group
4167 int x=margin, y=extra+margin;
4168 int butsize=fontMetrics().height()*2+5;
4170 QPushButton* button;
4171 for (int i=0; i<nchoices && ch[i]!='\033'; i++) {
4172 button=new QPushButton(QString(ch[i]),&group);
4173 if ( !enable.isNull() ) {
4174 if ( !enable.contains(ch[i]) )
4175 button->setEnabled(FALSE);
4177 button->setFixedSize(butsize,butsize); // Square
4178 if (ch[i]==def) button->setDefault(TRUE);
4179 if (i%10==9) {
4180 // last in row
4181 x=margin;
4182 y+=butsize+gutter;
4183 } else {
4184 x+=butsize+gutter;
4188 connect(&group,SIGNAL(clicked(int)),this,SLOT(doneItem(int)));
4190 QLabel* lb=0;
4191 QLineEdit* le=0;
4193 if (allow_count) {
4194 QHBox *hb = new QHBox(this);
4195 lb=new QLabel("Count: ",hb);
4196 le=new QLineEdit(hb);
4199 adjustSize();
4200 centerOnMain(this);
4201 show();
4202 char choice=0;
4203 char ch_esc=0;
4204 for (uint i=0; i<ch.length(); i++) {
4205 if (ch[i].latin1()=='q') ch_esc='q';
4206 else if (!ch_esc && ch[i].latin1()=='n') ch_esc='n';
4208 setResult(-1);
4209 while (!choice) {
4210 if (!keysource.Empty()) {
4211 char k=keysource.GetAscii();
4212 char ch_esc=0;
4213 for (uint i=0; i<ch.length(); i++)
4214 if (ch[i].latin1()==k)
4215 choice=k;
4216 if (!choice) {
4217 if (k=='\033' && ch_esc)
4218 choice=ch_esc;
4219 else if (k==' ' || k=='\r' || k=='\n')
4220 choice=def;
4221 // else choice remains 0
4223 } else if ( result() == 0 ) {
4224 choice = ch_esc ? ch_esc : def ? def : ' ';
4225 } else if ( result() == 1 ) {
4226 choice = def ? def : ch_esc ? ch_esc : ' ';
4227 } else if ( result() >= 1000 ) {
4228 choice = ch[result() - 1000].latin1();
4230 if ( !choice )
4231 qApp->enter_loop();
4233 hide();
4234 if (allow_count && !le->text().isEmpty()) {
4235 yn_number=atoi(le->text());
4236 choice='#';
4238 return choice;
4239 } else {
4240 QLabel label(qlabel,this);
4241 QPushButton cancel("Dismiss",this);
4242 label.setFrameStyle(QFrame::Box|QFrame::Sunken);
4243 label.setAlignment(AlignCenter);
4244 label.resize(fontMetrics().width(qlabel)+60,30+fontMetrics().height());
4245 cancel.move(width()/2-cancel.width()/2,label.geometry().bottom()+8);
4246 connect(&cancel,SIGNAL(clicked()),this,SLOT(reject()));
4247 centerOnMain(this);
4248 setResult(-1);
4249 show();
4250 while (result()<0 && keysource.Empty()) {
4251 qApp->enter_loop();
4253 hide();
4254 if (keysource.Empty()) {
4255 return '\033';
4256 } else {
4257 return keysource.GetAscii();
4261 void NetHackQtYnDialog::keyPressEvent(QKeyEvent* event)
4263 // Don't want QDialog's Return/Esc behaviour
4264 event->ignore();
4267 void NetHackQtYnDialog::doneItem(int i)
4269 done(i+1000);
4272 void NetHackQtYnDialog::done(int i)
4274 setResult(i);
4275 qApp->exit_loop();
4278 NetHackQtGlyphs::NetHackQtGlyphs()
4280 const char* tile_file = "nhtiles.bmp";
4281 if ( iflags.wc_tile_file )
4282 tile_file = iflags.wc_tile_file;
4284 if (!img.load(tile_file)) {
4285 tile_file = "x11tiles";
4286 if (!img.load(tile_file)) {
4287 QString msg;
4288 msg.sprintf("Cannot load x11tiles or nhtiles.bmp");
4289 QMessageBox::warning(0, "IO Error", msg);
4290 } else {
4291 tiles_per_row = TILES_PER_ROW;
4292 if (img.width()%tiles_per_row) {
4293 impossible(
4294 "Tile file \"%s\" has %d columns, not multiple of row count (%d)",
4295 tile_file, img.width(), tiles_per_row);
4298 } else {
4299 tiles_per_row = 40;
4302 if ( iflags.wc_tile_width )
4303 tilefile_tile_W = iflags.wc_tile_width;
4304 else
4305 tilefile_tile_W = img.width() / tiles_per_row;
4306 if ( iflags.wc_tile_height )
4307 tilefile_tile_H = iflags.wc_tile_height;
4308 else
4309 tilefile_tile_H = tilefile_tile_W;
4311 setSize(tilefile_tile_W, tilefile_tile_H);
4314 void NetHackQtGlyphs::drawGlyph(QPainter& painter, int glyph, int x, int y)
4316 int tile = glyph2tile[glyph];
4317 int px = (tile%tiles_per_row)*width();
4318 int py = tile/tiles_per_row*height();
4320 painter.drawPixmap(
4324 px,py,
4325 width(),height()
4328 void NetHackQtGlyphs::drawCell(QPainter& painter, int glyph, int cellx, int celly)
4330 drawGlyph(painter,glyph,cellx*width(),celly*height());
4332 void NetHackQtGlyphs::setSize(int w, int h)
4334 if ( size == QSize(w,h) )
4335 return;
4337 bool was1 = size == pm1.size();
4338 size = QSize(w,h);
4339 if (!w || !h)
4340 return; // Still not decided
4342 if ( size == pm1.size() ) {
4343 pm = pm1;
4344 return;
4346 if ( size == pm2.size() ) {
4347 pm = pm2;
4348 return;
4351 if (w==tilefile_tile_W && h==tilefile_tile_H) {
4352 pm.convertFromImage(img);
4353 } else {
4354 QApplication::setOverrideCursor( Qt::waitCursor );
4355 QImage scaled = img.smoothScale(
4356 w*img.width()/tilefile_tile_W,
4357 h*img.height()/tilefile_tile_H
4359 pm.convertFromImage(scaled,Qt::ThresholdDither|Qt::PreferDither);
4360 QApplication::restoreOverrideCursor();
4362 (was1 ? pm2 : pm1) = pm;
4366 //////////////////////////////////////////////////////////////
4368 // The ugly C binding classes...
4370 //////////////////////////////////////////////////////////////
4373 NetHackQtMenuOrTextWindow::NetHackQtMenuOrTextWindow(NetHackQtKeyBuffer& ks) :
4374 actual(0),
4375 keysource(ks)
4379 QWidget* NetHackQtMenuOrTextWindow::Widget()
4381 if (!actual) impossible("Widget called before we know if Menu or Text");
4382 return actual->Widget();
4385 // Text
4386 void NetHackQtMenuOrTextWindow::Clear()
4388 if (!actual)
4389 impossible("Clear called before we know if Menu or Text");
4390 else
4391 actual->Clear();
4394 void NetHackQtMenuOrTextWindow::Display(bool block)
4396 if (!actual)
4397 impossible("Display called before we know if Menu or Text");
4398 else
4399 actual->Display(block);
4402 bool NetHackQtMenuOrTextWindow::Destroy()
4404 bool res = FALSE;
4406 if (!actual)
4407 impossible("Destroy called before we know if Menu or Text");
4408 else
4409 res = actual->Destroy();
4411 return res;
4414 void NetHackQtMenuOrTextWindow::PutStr(int attr, const char* text)
4416 if (!actual) actual=new NetHackQtTextWindow(keysource);
4417 actual->PutStr(attr,text);
4420 // Menu
4421 void NetHackQtMenuOrTextWindow::StartMenu()
4423 if (!actual) actual=new NetHackQtMenuWindow(keysource);
4424 actual->StartMenu();
4427 void NetHackQtMenuOrTextWindow::AddMenu(int glyph, const ANY_P* identifier, char ch, char gch, int attr,
4428 const char* str, bool presel)
4430 if (!actual) impossible("AddMenu called before we know if Menu or Text");
4431 actual->AddMenu(glyph,identifier,ch,gch,attr,str,presel);
4434 void NetHackQtMenuOrTextWindow::EndMenu(const char* prompt)
4436 if (!actual) impossible("EndMenu called before we know if Menu or Text");
4437 actual->EndMenu(prompt);
4440 int NetHackQtMenuOrTextWindow::SelectMenu(int how, MENU_ITEM_P **menu_list)
4442 if (!actual) impossible("SelectMenu called before we know if Menu or Text");
4443 return actual->SelectMenu(how,menu_list);
4447 // XXX Should be from Options
4449 // XXX Hmm. Tricky part is that perhaps some macros should only be active
4450 // XXX when a key is about to be gotten. For example, the user could
4451 // XXX define "-" to do "E-yyyyyyyy\r", but would still need "-" for
4452 // XXX other purposes. Maybe just too bad.
4454 struct {
4455 int key;
4456 int state;
4457 const char* macro;
4458 } key_macro[]={
4459 { Qt::Key_F1, 0, "n100." }, // Rest (x100)
4460 { Qt::Key_F2, 0, "n20s" }, // Search (x20)
4461 { Qt::Key_F3, 0, "o8o4o6o2o8o4o6o2o8o4o6o2" }, // Open all doors (x3)
4462 { Qt::Key_Tab, 0, "\001" },
4463 { 0, 0, 0 }
4467 NetHackQtBind::NetHackQtBind(int& argc, char** argv) :
4468 #ifdef KDE
4469 KApplication(argc,argv)
4470 #elif defined(QWS) // not quite the right condition
4471 QPEApplication(argc,argv)
4472 #else
4473 QApplication(argc,argv)
4474 #endif
4476 QPixmap pm("nhsplash.xpm");
4477 if ( iflags.wc_splash_screen && !pm.isNull() ) {
4478 QVBox *vb = new QVBox(0,0,
4479 WStyle_Customize | WStyle_NoBorder | nh_WX11BypassWM | WStyle_StaysOnTop );
4480 splash = vb;
4481 QLabel *lsplash = new QLabel(vb);
4482 lsplash->setAlignment(AlignCenter);
4483 lsplash->setPixmap(pm);
4484 QLabel* capt = new QLabel("Loading...",vb);
4485 capt->setAlignment(AlignCenter);
4486 if ( pm.mask() ) {
4487 lsplash->setFixedSize(pm.size());
4488 lsplash->setMask(*pm.mask());
4490 splash->move((QApplication::desktop()->width()-pm.width())/2,
4491 (QApplication::desktop()->height()-pm.height())/2);
4492 //splash->setGeometry(0,0,100,100);
4493 if ( qt_compact_mode ) {
4494 splash->showMaximized();
4495 } else {
4496 vb->setFrameStyle(QFrame::WinPanel|QFrame::Raised);
4497 vb->setMargin(10);
4498 splash->adjustSize();
4499 splash->show();
4502 // force content refresh outside event loop
4503 splash->repaint(FALSE);
4504 lsplash->repaint(FALSE);
4505 capt->repaint(FALSE);
4506 qApp->flushX();
4508 } else {
4509 splash = 0;
4511 main = new NetHackQtMainWindow(keybuffer);
4512 #if defined(QWS) // not quite the right condition
4513 showMainWidget(main);
4514 #else
4515 setMainWidget(main);
4516 #endif
4517 qt_settings=new NetHackQtSettings(main->width(),main->height());
4520 void NetHackQtBind::qt_init_nhwindows(int* argc, char** argv)
4522 #ifdef UNIX
4523 // Userid control
4525 // Michael Hohmuth <hohmuth@inf.tu-dresden.de>...
4527 // As the game runs setuid games, it must seteuid(getuid()) before
4528 // calling XOpenDisplay(), and reset the euid afterwards.
4529 // Otherwise, it can't read the $HOME/.Xauthority file and whines about
4530 // not being able to open the X display (if a magic-cookie
4531 // authorization mechanism is being used).
4533 uid_t gamesuid=geteuid();
4534 seteuid(getuid());
4535 #endif
4537 QApplication::setColorSpec(ManyColor);
4538 instance=new NetHackQtBind(*argc,argv);
4540 #ifdef UNIX
4541 seteuid(gamesuid);
4542 #endif
4544 #ifdef _WS_WIN_
4545 // This nethack engine feature should be moved into windowport API
4546 nt_kbhit = NetHackQtBind::qt_kbhit;
4547 #endif
4550 int NetHackQtBind::qt_kbhit()
4552 return !keybuffer.Empty();
4555 static bool have_asked = FALSE;
4557 void NetHackQtBind::qt_player_selection()
4559 if ( !have_asked )
4560 qt_askname();
4563 NetHackQtSavedGameSelector::NetHackQtSavedGameSelector(const char** saved) :
4564 QDialog(qApp->mainWidget(),"sgsel",TRUE)
4566 QVBoxLayout *vbl = new QVBoxLayout(this,6);
4567 QHBox* hb;
4569 QLabel* logo = new QLabel(this); vbl->addWidget(logo);
4570 logo->setAlignment(AlignCenter);
4571 logo->setPixmap(QPixmap("nhsplash.xpm"));
4572 QLabel* attr = new QLabel("by the NetHack DevTeam",this);
4573 attr->setAlignment(AlignCenter);
4574 vbl->addWidget(attr);
4575 vbl->addStretch(2);
4577 QLabel* logo = new QLabel(hb);
4578 hb = new QHBox(this);
4579 vbl->addWidget(hb, AlignCenter);
4580 logo->setPixmap(QPixmap(nh_icon));
4581 logo->setAlignment(AlignRight|AlignVCenter);
4582 new QLabel(nh_attribution,hb);
4585 hb = new QHBox(this);
4586 vbl->addWidget(hb, AlignCenter);
4587 QPushButton* q = new QPushButton("Quit",hb);
4588 connect(q, SIGNAL(clicked()), this, SLOT(reject()));
4589 QPushButton* c = new QPushButton("New Game",hb);
4590 connect(c, SIGNAL(clicked()), this, SLOT(accept()));
4591 c->setDefault(TRUE);
4593 QButtonGroup* bg = new QButtonGroup(3, Horizontal, "Saved Characters",this);
4594 vbl->addWidget(bg);
4595 connect(bg, SIGNAL(clicked(int)), this, SLOT(done(int)));
4596 for (int i=0; saved[i]; i++) {
4597 QPushButton* b = new QPushButton(saved[i],bg);
4598 bg->insert(b, i+2);
4602 int NetHackQtSavedGameSelector::choose()
4604 #if defined(QWS) // probably safe with Qt 3, too (where show!=exec in QDialog).
4605 if ( qt_compact_mode )
4606 showMaximized();
4607 #endif
4608 return exec()-2;
4611 void NetHackQtBind::qt_askname()
4613 have_asked = TRUE;
4615 // We do it all here, and nothing in askname
4617 char** saved = get_saved_games();
4618 int ch = -1;
4619 if ( saved && *saved ) {
4620 if ( splash ) splash->hide();
4621 NetHackQtSavedGameSelector sgsel((const char**)saved);
4622 ch = sgsel.choose();
4623 if ( ch >= 0 )
4624 strcpy(plname,saved[ch]);
4626 free_saved_games(saved);
4628 switch (ch) {
4629 case -1:
4630 if ( splash ) splash->hide();
4631 if (NetHackQtPlayerSelector(keybuffer).Choose())
4632 return;
4633 case -2:
4634 break;
4635 default:
4636 return;
4639 // Quit
4640 clearlocks();
4641 qt_exit_nhwindows(0);
4642 terminate(0);
4645 void NetHackQtBind::qt_get_nh_event()
4649 #if defined(QWS)
4650 // Kludge to access lastWindowClosed() signal.
4651 class TApp : public QApplication {
4652 public:
4653 TApp(int& c, char**v) : QApplication(c,v) {}
4654 void lwc() { emit lastWindowClosed(); }
4656 #endif
4658 void NetHackQtBind::qt_exit_nhwindows(const char *)
4660 #if defined(QWS)
4661 // Avoids bug in SHARP SL5500
4662 ((TApp*)qApp)->lwc();
4663 qApp->quit();
4664 #endif
4666 delete instance; // ie. qApp
4669 void NetHackQtBind::qt_suspend_nhwindows(const char *)
4673 void NetHackQtBind::qt_resume_nhwindows()
4677 static QArray<NetHackQtWindow*> id_to_window;
4679 winid NetHackQtBind::qt_create_nhwindow(int type)
4681 winid id;
4682 for (id = 0; id < (winid) id_to_window.size(); id++) {
4683 if ( !id_to_window[id] )
4684 break;
4686 if ( id == (winid) id_to_window.size() )
4687 id_to_window.resize(id+1);
4689 NetHackQtWindow* window=0;
4691 switch (type) {
4692 case NHW_MAP: {
4693 NetHackQtMapWindow* w=new NetHackQtMapWindow(clickbuffer);
4694 main->AddMapWindow(w);
4695 window=w;
4696 } break; case NHW_MESSAGE: {
4697 NetHackQtMessageWindow* w=new NetHackQtMessageWindow;
4698 main->AddMessageWindow(w);
4699 window=w;
4700 } break; case NHW_STATUS: {
4701 NetHackQtStatusWindow* w=new NetHackQtStatusWindow;
4702 main->AddStatusWindow(w);
4703 window=w;
4704 } break; case NHW_MENU:
4705 window=new NetHackQtMenuOrTextWindow(keybuffer);
4706 break; case NHW_TEXT:
4707 window=new NetHackQtTextWindow(keybuffer);
4710 window->nhid = id;
4712 // Note: use of isHidden does not work with Qt 2.1
4713 if ( splash
4714 #if QT_VERSION >= 300
4715 && !main->isHidden()
4716 #else
4717 && main->isVisible()
4718 #endif
4721 delete splash;
4722 splash = 0;
4725 id_to_window[id] = window;
4726 return id;
4729 void NetHackQtBind::qt_clear_nhwindow(winid wid)
4731 NetHackQtWindow* window=id_to_window[wid];
4732 window->Clear();
4735 void NetHackQtBind::qt_display_nhwindow(winid wid, BOOLEAN_P block)
4737 NetHackQtWindow* window=id_to_window[wid];
4738 window->Display(block);
4741 void NetHackQtBind::qt_destroy_nhwindow(winid wid)
4743 NetHackQtWindow* window=id_to_window[wid];
4744 main->RemoveWindow(window);
4745 if (window->Destroy())
4746 delete window;
4747 id_to_window[wid] = 0;
4750 void NetHackQtBind::qt_curs(winid wid, int x, int y)
4752 NetHackQtWindow* window=id_to_window[wid];
4753 window->CursorTo(x,y);
4756 void NetHackQtBind::qt_putstr(winid wid, int attr, const char *text)
4758 NetHackQtWindow* window=id_to_window[wid];
4759 window->PutStr(attr,text);
4762 void NetHackQtBind::qt_display_file(const char *filename, BOOLEAN_P must_exist)
4764 NetHackQtTextWindow* window=new NetHackQtTextWindow(keybuffer);
4765 bool complain = FALSE;
4767 #ifdef DLB
4769 dlb *f;
4770 char buf[BUFSZ];
4771 char *cr;
4773 window->Clear();
4774 f = dlb_fopen(filename, "r");
4775 if (!f) {
4776 complain = must_exist;
4777 } else {
4778 while (dlb_fgets(buf, BUFSZ, f)) {
4779 if ((cr = index(buf, '\n')) != 0) *cr = 0;
4780 #ifdef MSDOS
4781 if ((cr = index(buf, '\r')) != 0) *cr = 0;
4782 #endif
4783 if (index(buf, '\t') != 0) (void) tabexpand(buf);
4784 window->PutStr(ATR_NONE, buf);
4786 window->Display(FALSE);
4787 (void) dlb_fclose(f);
4790 #else
4791 QFile file(filename);
4793 if (file.open(IO_ReadOnly)) {
4794 char line[128];
4795 while (file.readLine(line,127) >= 0) {
4796 line[strlen(line)-1]=0;// remove newline
4797 window->PutStr(ATR_NONE,line);
4799 window->Display(FALSE);
4800 } else {
4801 complain = must_exist;
4803 #endif
4805 if (complain) {
4806 QString message;
4807 message.sprintf("File not found: %s\n",filename);
4808 QMessageBox::message("File Error", (const char*)message, "Ignore");
4812 void NetHackQtBind::qt_start_menu(winid wid)
4814 NetHackQtWindow* window=id_to_window[wid];
4815 window->StartMenu();
4818 void NetHackQtBind::qt_add_menu(winid wid, int glyph,
4819 const ANY_P * identifier, CHAR_P ch, CHAR_P gch, int attr,
4820 const char *str, BOOLEAN_P presel)
4822 NetHackQtWindow* window=id_to_window[wid];
4823 window->AddMenu(glyph, identifier, ch, gch, attr, str, presel);
4826 void NetHackQtBind::qt_end_menu(winid wid, const char *prompt)
4828 NetHackQtWindow* window=id_to_window[wid];
4829 window->EndMenu(prompt);
4832 int NetHackQtBind::qt_select_menu(winid wid, int how, MENU_ITEM_P **menu_list)
4834 NetHackQtWindow* window=id_to_window[wid];
4835 return window->SelectMenu(how,menu_list);
4838 void NetHackQtBind::qt_update_inventory()
4840 if (main)
4841 main->updateInventory();
4842 /* doesn't work yet
4843 if (program_state.something_worth_saving && flags.perm_invent)
4844 display_inventory(NULL, FALSE);
4848 void NetHackQtBind::qt_mark_synch()
4852 void NetHackQtBind::qt_wait_synch()
4856 void NetHackQtBind::qt_cliparound(int x, int y)
4858 // XXXNH - winid should be a parameter!
4859 qt_cliparound_window(WIN_MAP,x,y);
4862 void NetHackQtBind::qt_cliparound_window(winid wid, int x, int y)
4864 NetHackQtWindow* window=id_to_window[wid];
4865 window->ClipAround(x,y);
4867 void NetHackQtBind::qt_print_glyph(winid wid,XCHAR_P x,XCHAR_P y,int glyph, int bkglyph)
4869 NetHackQtWindow* window=id_to_window[wid];
4870 window->PrintGlyph(x,y,glyph);
4872 //void NetHackQtBind::qt_print_glyph_compose(winid wid,XCHAR_P x,XCHAR_P y,int glyph1, int glyph2)
4874 //NetHackQtWindow* window=id_to_window[wid];
4875 //window->PrintGlyphCompose(x,y,glyph1,glyph2);
4878 void NetHackQtBind::qt_raw_print(const char *str)
4880 puts(str);
4883 void NetHackQtBind::qt_raw_print_bold(const char *str)
4885 puts(str);
4888 int NetHackQtBind::qt_nhgetch()
4890 if (main)
4891 main->fadeHighlighting();
4893 // Process events until a key arrives.
4895 while (keybuffer.Empty()
4896 #ifdef SAFERHANGUP
4897 && !program_state.done_hup
4898 #endif
4900 qApp->enter_loop();
4903 #ifdef SAFERHANGUP
4904 if (program_state.done_hup && keybuffer.Empty()) return '\033';
4905 #endif
4906 return keybuffer.GetAscii();
4909 int NetHackQtBind::qt_nh_poskey(int *x, int *y, int *mod)
4911 if (main)
4912 main->fadeHighlighting();
4914 // Process events until a key or map-click arrives.
4916 while (keybuffer.Empty() && clickbuffer.Empty()
4917 #ifdef SAFERHANGUP
4918 && !program_state.done_hup
4919 #endif
4921 qApp->enter_loop();
4923 #ifdef SAFERHANGUP
4924 if (program_state.done_hup && keybuffer.Empty()) return '\033';
4925 #endif
4926 if (!keybuffer.Empty()) {
4927 return keybuffer.GetAscii();
4928 } else {
4929 *x=clickbuffer.NextX();
4930 *y=clickbuffer.NextY();
4931 *mod=clickbuffer.NextMod();
4932 clickbuffer.Get();
4933 return 0;
4937 void NetHackQtBind::qt_nhbell()
4939 QApplication::beep();
4942 int NetHackQtBind::qt_doprev_message()
4944 // Don't need it - uses scrollbar
4945 // XXX but could make this a shortcut
4946 return 0;
4949 char NetHackQtBind::qt_yn_function(const char *question, const char *choices, CHAR_P def)
4951 if (qt_settings->ynInMessages() && WIN_MESSAGE!=WIN_ERR) {
4952 // Similar to X11 windowport `slow' feature.
4954 char message[BUFSZ];
4955 char yn_esc_map='\033';
4957 if (choices) {
4958 char *cb, choicebuf[QBUFSZ];
4959 Strcpy(choicebuf, choices);
4960 if ((cb = index(choicebuf, '\033')) != 0) {
4961 // anything beyond <esc> is hidden
4962 *cb = '\0';
4964 (void)strncpy(message, question, QBUFSZ-1);
4965 message[QBUFSZ-1] = '\0';
4966 Sprintf(eos(message), " [%s]", choicebuf);
4967 if (def) Sprintf(eos(message), " (%c)", def);
4968 Strcat(message, " ");
4969 // escape maps to 'q' or 'n' or default, in that order
4970 yn_esc_map = (index(choices, 'q') ? 'q' :
4971 (index(choices, 'n') ? 'n' : def));
4972 } else {
4973 Strcpy(message, question);
4976 #ifdef USE_POPUPS
4977 // Improve some special-cases (DIRKS 08/02/23)
4978 if (strcmp (choices,"ynq") == 0) {
4979 switch (QMessageBox::information (qApp->mainWidget(),"NetHack",question,"&Yes","&No","&Quit",0,2))
4981 case 0: return 'y';
4982 case 1: return 'n';
4983 case 2: return 'q';
4987 if (strcmp (choices,"yn") == 0) {
4988 switch (QMessageBox::information(qApp->mainWidget(),"NetHack",question,"&Yes", "&No",0,1))
4990 case 0: return 'y';
4991 case 1: return 'n';
4994 #endif
4996 NetHackQtBind::qt_putstr(WIN_MESSAGE, ATR_BOLD, message);
4998 int result=-1;
4999 while (result<0) {
5000 char ch=NetHackQtBind::qt_nhgetch();
5001 if (ch=='\033') {
5002 result=yn_esc_map;
5003 } else if (choices && !index(choices,ch)) {
5004 if (def && (ch==' ' || ch=='\r' || ch=='\n')) {
5005 result=def;
5006 } else {
5007 NetHackQtBind::qt_nhbell();
5008 // and try again...
5010 } else {
5011 result=ch;
5015 NetHackQtBind::qt_clear_nhwindow(WIN_MESSAGE);
5017 return result;
5018 } else {
5019 NetHackQtYnDialog dialog(keybuffer,question,choices,def);
5020 return dialog.Exec();
5024 void NetHackQtBind::qt_getlin(const char *prompt, char *line)
5026 NetHackQtStringRequestor requestor(keybuffer,prompt);
5027 if (!requestor.Get(line)) {
5028 line[0]=0;
5032 NetHackQtExtCmdRequestor::NetHackQtExtCmdRequestor(NetHackQtKeyBuffer& ks) :
5033 QDialog(qApp->mainWidget(), "ext-cmd", FALSE),
5034 keysource(ks)
5036 int marg=4;
5037 QVBoxLayout *l = new QVBoxLayout(this,marg,marg);
5039 QPushButton* can = new QPushButton("Cancel", this);
5040 can->setDefault(TRUE);
5041 can->setMinimumSize(can->sizeHint());
5042 l->addWidget(can);
5044 QButtonGroup *group=new QButtonGroup("",0);
5045 QGroupBox *grid=new QGroupBox("Extended commands",this);
5046 l->addWidget(grid);
5048 int i;
5049 int butw=50;
5050 QFontMetrics fm = fontMetrics();
5051 for (i=0; extcmdlist[i].ef_txt; i++) {
5052 butw = QMAX(butw,30+fm.width(extcmdlist[i].ef_txt));
5054 int ncols=4;
5055 int nrows=(i+ncols-1)/ncols;
5057 QVBoxLayout* bl = new QVBoxLayout(grid,marg);
5058 bl->addSpacing(fm.height());
5059 QGridLayout* gl = new QGridLayout(nrows,ncols,marg);
5060 bl->addLayout(gl);
5061 for (i=0; extcmdlist[i].ef_txt; i++) {
5062 QPushButton* pb=new QPushButton(extcmdlist[i].ef_txt, grid);
5063 pb->setMinimumSize(butw,pb->sizeHint().height());
5064 group->insert(pb);
5065 gl->addWidget(pb,i/ncols,i%ncols);
5067 connect(group,SIGNAL(clicked(int)),this,SLOT(done(int)));
5069 bl->activate();
5070 l->activate();
5071 resize(1,1);
5073 connect(can,SIGNAL(clicked()),this,SLOT(cancel()));
5076 void NetHackQtExtCmdRequestor::cancel()
5078 setResult(-1);
5079 qApp->exit_loop();
5082 void NetHackQtExtCmdRequestor::done(int i)
5084 setResult(i);
5085 qApp->exit_loop();
5088 int NetHackQtExtCmdRequestor::get()
5090 const int none = -10;
5091 char str[32];
5092 int cursor=0;
5093 resize(1,1); // pack
5094 centerOnMain(this);
5095 show();
5096 setResult(none);
5097 while (result()==none) {
5098 while (result()==none && !keysource.Empty()) {
5099 char k=keysource.GetAscii();
5100 if (k=='\r' || k=='\n' || k==' ' || k=='\033') {
5101 setResult(-1);
5102 } else {
5103 str[cursor++] = k;
5104 int r=-1;
5105 for (int i=0; extcmdlist[i].ef_txt; i++) {
5106 if (qstrnicmp(str, extcmdlist[i].ef_txt, cursor)==0) {
5107 if ( r == -1 )
5108 r = i;
5109 else
5110 r = -2;
5113 if ( r == -1 ) { // no match!
5114 QApplication::beep();
5115 cursor=0;
5116 } else if ( r != -2 ) { // only one match
5117 setResult(r);
5121 if (result()==none)
5122 qApp->enter_loop();
5124 hide();
5125 return result();
5129 int NetHackQtBind::qt_get_ext_cmd()
5131 NetHackQtExtCmdRequestor requestor(keybuffer);
5132 return requestor.get();
5135 void NetHackQtBind::qt_number_pad(int)
5137 // Ignore.
5140 void NetHackQtBind::qt_delay_output()
5142 NetHackQtDelay delay(15);
5143 delay.wait();
5146 void NetHackQtBind::qt_start_screen()
5148 // Ignore.
5151 void NetHackQtBind::qt_end_screen()
5153 // Ignore.
5156 void NetHackQtBind::qt_outrip(winid wid, int how, time_t when)
5158 NetHackQtWindow* window=id_to_window[wid];
5160 window->UseRIP(how, when);
5163 bool NetHackQtBind::notify(QObject *receiver, QEvent *event)
5165 // Ignore Alt-key navigation to menubar, it's annoying when you
5166 // use Alt-Direction to move around.
5167 if ( main && event->type()==QEvent::KeyRelease && main==receiver
5168 && ((QKeyEvent*)event)->key() == Key_Alt )
5169 return TRUE;
5171 bool result=QApplication::notify(receiver,event);
5172 #ifdef SAFERHANGUP
5173 if (program_state.done_hup) {
5174 keybuffer.Put('\033');
5175 qApp->exit_loop();
5176 return TRUE;
5178 #endif
5179 if (event->type()==QEvent::KeyPress) {
5180 QKeyEvent* key_event=(QKeyEvent*)event;
5182 if (!key_event->isAccepted()) {
5183 const int k=key_event->key();
5184 bool macro=FALSE;
5185 for (int i=0; !macro && key_macro[i].key; i++) {
5186 if (key_macro[i].key==k
5187 && ((key_macro[i].state&key_event->state())==key_macro[i].state))
5189 keybuffer.Put(key_macro[i].macro);
5190 macro=TRUE;
5193 char ch=key_event->ascii();
5194 if ( !ch && (key_event->state() & Qt::ControlButton) ) {
5195 // On Mac, it aint-ncessarily-control
5196 if ( k>=Qt::Key_A && k<=Qt::Key_Z )
5197 ch = k - Qt::Key_A + 1;
5199 if (!macro && ch) {
5200 bool alt = (key_event->state()&AltButton) ||
5201 (k >= Key_0 && k <= Key_9 && (key_event->state()&ControlButton));
5202 keybuffer.Put(key_event->key(),ch + (alt ? 128 : 0),
5203 key_event->state());
5204 key_event->accept();
5205 result=TRUE;
5208 if (ch || macro) {
5209 qApp->exit_loop();
5213 return result;
5216 NetHackQtBind* NetHackQtBind::instance=0;
5217 NetHackQtKeyBuffer NetHackQtBind::keybuffer;
5218 NetHackQtClickBuffer NetHackQtBind::clickbuffer;
5219 NetHackQtMainWindow* NetHackQtBind::main=0;
5220 QWidget* NetHackQtBind::splash=0;
5223 extern "C" struct window_procs Qt_procs;
5225 struct window_procs Qt_procs = {
5226 "Qt",
5227 WC_COLOR|WC_HILITE_PET|
5228 WC_ASCII_MAP|WC_TILED_MAP|
5229 WC_FONT_MAP|WC_TILE_FILE|WC_TILE_WIDTH|WC_TILE_HEIGHT|
5230 WC_PLAYER_SELECTION|WC_SPLASH_SCREEN,
5232 NetHackQtBind::qt_init_nhwindows,
5233 NetHackQtBind::qt_player_selection,
5234 NetHackQtBind::qt_askname,
5235 NetHackQtBind::qt_get_nh_event,
5236 NetHackQtBind::qt_exit_nhwindows,
5237 NetHackQtBind::qt_suspend_nhwindows,
5238 NetHackQtBind::qt_resume_nhwindows,
5239 NetHackQtBind::qt_create_nhwindow,
5240 NetHackQtBind::qt_clear_nhwindow,
5241 NetHackQtBind::qt_display_nhwindow,
5242 NetHackQtBind::qt_destroy_nhwindow,
5243 NetHackQtBind::qt_curs,
5244 NetHackQtBind::qt_putstr,
5245 genl_putmixed,
5246 NetHackQtBind::qt_display_file,
5247 NetHackQtBind::qt_start_menu,
5248 NetHackQtBind::qt_add_menu,
5249 NetHackQtBind::qt_end_menu,
5250 NetHackQtBind::qt_select_menu,
5251 genl_message_menu, /* no need for X-specific handling */
5252 NetHackQtBind::qt_update_inventory,
5253 NetHackQtBind::qt_mark_synch,
5254 NetHackQtBind::qt_wait_synch,
5255 #ifdef CLIPPING
5256 NetHackQtBind::qt_cliparound,
5257 #endif
5258 #ifdef POSITIONBAR
5259 donull,
5260 #endif
5261 NetHackQtBind::qt_print_glyph,
5262 //NetHackQtBind::qt_print_glyph_compose,
5263 NetHackQtBind::qt_raw_print,
5264 NetHackQtBind::qt_raw_print_bold,
5265 NetHackQtBind::qt_nhgetch,
5266 NetHackQtBind::qt_nh_poskey,
5267 NetHackQtBind::qt_nhbell,
5268 NetHackQtBind::qt_doprev_message,
5269 NetHackQtBind::qt_yn_function,
5270 NetHackQtBind::qt_getlin,
5271 NetHackQtBind::qt_get_ext_cmd,
5272 NetHackQtBind::qt_number_pad,
5273 NetHackQtBind::qt_delay_output,
5274 #ifdef CHANGE_COLOR /* only a Mac option currently */
5275 donull,
5276 donull,
5277 #endif
5278 /* other defs that really should go away (they're tty specific) */
5279 NetHackQtBind::qt_start_screen,
5280 NetHackQtBind::qt_end_screen,
5281 #ifdef GRAPHIC_TOMBSTONE
5282 NetHackQtBind::qt_outrip,
5283 #else
5284 genl_outrip,
5285 #endif
5286 genl_preference_update,
5287 genl_getmsghistory,
5288 genl_putmsghistory,
5289 #ifdef STATUS_VIA_WINDOWPORT
5290 genl_status_init,
5291 genl_status_finish,
5292 genl_status_enablefield,
5293 genl_status_update,
5294 # ifdef STATUS_HILITES
5295 genl_status_threshold,
5296 # endif
5297 #endif
5300 extern "C" void play_usersound(const char* filename, int volume)
5302 #ifdef USER_SOUNDS
5303 #ifndef QT_NO_SOUND
5304 QSound::play(filename);
5305 #endif
5306 #endif
5309 #include "qt_win.moc"
5310 #ifndef KDE
5311 #include "qt_kde0.moc"
5312 #endif
5313 #if QT_VERSION >= 300
5314 #include "qttableview.moc"
5315 #endif