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