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