1 //	SCCS Id: @(#)qt_win.cpp	3.3	1999/11/19
2 // Copyright (c) Warwick Allison, 1999.
3 // NetHack may be freely redistributed.  See license for details.
4 
5 #define VERSION_QT_MAJOR 2
6 #define VERSION_QT_MINOR 0
7 #define VERSION_QT_PATCH 0
8 
9 // Qt Binding for NetHack 3.3
10 //
11 // Copyright (C) 1996,1997 by Warwick W. Allison (warwick@troll.no)
12 //
13 // Contributors:
14 //    Michael Hohmuth <hohmuth@inf.tu-dresden.de>
15 //       - Userid control
16 //    Svante Gerhard <svante@algonet.se>
17 //       - .nethackrc tile and font size settings
18 //    Dirk Schoenberger <schoenberger@signsoft.com>
19 //       - KDE support
20 //       - SlashEm support
21 //    and many others for bug reports.
22 //
23 // Unfortunately, this doesn't use Qt as well as I would like,
24 // primarily because NetHack is fundamentally a getkey-type program
25 // rather than being event driven (hence the ugly key and click buffer)
26 // and also because this is my first major application of Qt.
27 //
28 // The problem of NetHack's getkey requirement is solved by intercepting
29 // key events by overiding QApplicion::notify(...), and putting them in
30 // a buffer.  Mouse clicks on the map window are treated with a similar
31 // buffer.  When the NetHack engine calls for a key, one is taken from
32 // the buffer, or if that is empty, QApplication::enter_loop() is called.
33 // Whenever keys or clicks go into the buffer, QApplication::exit_loop()
34 // is called.
35 //
36 // Another problem is that some NetHack players are decade-long players who
37 // demand complete keyboard control (while Qt and X11 conspire to make this
38 // difficult by having widget-based focus rather than application based -
39 // a good thing in general).  This problem is solved by again using the key
40 // event buffer.
41 //
42 // Out of all this hackery comes a silver lining however, as macros for
43 // the super-expert and menus for the ultra-newbie are also made possible
44 // by the key event buffer.
45 //
46 
47 extern "C" {
48 
49 // This includes all the definitions we need from the NetHack main
50 // engine.  We pretend MSC is a STDC compiler, because C++ is close
51 // enough, and we undefine NetHack macros which conflict with Qt
52 // identifiers.
53 
54 #ifdef _MSC_VER
55 #define NHSTDC
56 #endif
57 #include "hack.h"
58 #include "func_tab.h"
59 #include "dlb.h"
60 #include "patchlevel.h"
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 
72 }
73 
74 #include "qt_win.h"
75 #include <qpainter.h>
76 #include <qbitmap.h>
77 #include <qkeycode.h>
78 #include <qmenubar.h>
79 #include <qpopupmenu.h>
80 #include <qlayout.h>
81 #include <qheader.h>
82 #include <qradiobutton.h>
83 #include <qcombobox.h>
84 #include <qvbox.h>
85 #include <qhbox.h>
86 //#include <qgrid.h>
87 //#include <qlabelled.h>
88 
89 #include <ctype.h>
90 
91 #include "qt_clust.h"
92 #include "qt_xpms.h"
93 
94 //#include <stdlib.h>
95 #include <stdlib.h>
96 
97 #ifdef _WS_X11_
98 // For userid control
99 #include <unistd.h>
100 #endif
101 
102 #ifdef USER_SOUNDS
103 #include <qaudio.h>
104 #endif
105 
106 
107 #ifdef USER_SOUNDS
108 extern "C" void play_sound_for_message(const char* str);
109 #endif
110 
111 // Warwick prefers it this way...
112 #define QT_CHOOSE_RACE_FIRST
113 
114 static QString
aboutMsg()115 aboutMsg()
116 {
117     QString msg;
118     msg.sprintf(
119     "Qt NetHack is a version of NetHack built using\n"
120 #ifdef KDE
121     "KDE and the Qt GUI toolkit for the user interface.\n"
122 #else
123     "the Qt GUI toolkit for the user interface.\n"
124 #endif
125     "This is version %d.%d.%d.%d.%d.%d\n\n"
126     "Homepage:\n     http://trolls.troll.no/warwick/nethack/\n\n"
127 #ifdef KDE
128 	  "KDE:\n     http://www.kde.org\n"
129 #endif
130 	  "Qt:\n     http://www.troll.no",
131 	VERSION_MAJOR,
132 	VERSION_MINOR,
133 	PATCHLEVEL,
134 	VERSION_QT_MAJOR,
135 	VERSION_QT_MINOR,
136 	VERSION_QT_PATCH);
137     return msg;
138 }
139 
140 static void
centerOnMain(QWidget * w)141 centerOnMain( QWidget* w )
142 {
143     QWidget* m = qApp->mainWidget();
144     QPoint p = m->mapToGlobal(QPoint(0,0));
145     w->move( p.x() + m->width()/2  - w->width()/2,
146               p.y() + m->height()/2 - w->height()/2 );
147 }
148 
NetHackQtLineEdit()149 NetHackQtLineEdit::NetHackQtLineEdit() :
150     QLineEdit(0)
151 {
152 }
153 
NetHackQtLineEdit(QWidget * parent,const char * name)154 NetHackQtLineEdit::NetHackQtLineEdit(QWidget* parent, const char* name) :
155     QLineEdit(parent,name)
156 {
157 }
158 
fakeEvent(int key,int ascii,int state)159 void NetHackQtLineEdit::fakeEvent(int key, int ascii, int state)
160 {
161     QKeyEvent fake(QEvent::KeyPress,key,ascii,state);
162     keyPressEvent(&fake);
163 }
164 
165 extern "C" {
166 /* Used by tile/font-size patch below and in ../../src/files.c */
167 char *qt_tilewidth=NULL;
168 char *qt_tileheight=NULL;
169 char *qt_fontsize=NULL;
170 extern const char *enc_stat[]; /* from botl.c */
171 extern const char *hu_stat[]; /* from eat.c */
172 extern const char *killed_by_prefix[];
173 extern int total_tiles_used; // from tile.c
174 extern short glyph2tile[]; // from tile.c
175 }
176 
177 // ### Try to replace these by looking at image size
178 #define TILEWBASE 16
179 #define TILEHBASE 16
180 
181 #define TILEWMIN 4
182 #define TILEHMIN 4
183 
184 
185 /* XPM */
186 static const char * nh_icon[] = {
187 "40 40 6 1",
188 " 	s None c none",
189 ".	c #ffffff",
190 "X	c #dadab6",
191 "o	c #6c91b6",
192 "O	c #476c6c",
193 "+	c #000000",
194 "                                        ",
195 "                                        ",
196 "                                        ",
197 "        .      .X..XX.XX      X         ",
198 "        ..   .....X.XXXXXX   XX         ",
199 "        ... ....X..XX.XXXXX XXX         ",
200 "   ..   ..........X.XXXXXXXXXXX   XX    ",
201 "   .... ........X..XX.XXXXXXXXX XXXX    ",
202 "   .... ..........X.XXXXXXXXXXX XXXX    ",
203 "   ooOOO..ooooooOooOOoOOOOOOOXX+++OO++  ",
204 "   ooOOO..ooooooooOoOOOOOOOOOXX+++OO++  ",
205 "   ....O..ooooooOooOOoOOOOOOOXX+XXXX++  ",
206 "   ....O..ooooooooOoOOOOOOOOOXX+XXXX++  ",
207 "   ..OOO..ooooooOooOOoOOOOOOOXX+++XX++  ",
208 "    ++++..ooooooooOoOOOOOOOOOXX+++ +++  ",
209 "     +++..ooooooOooOOoOOOOOOOXX+++  +   ",
210 "      ++..ooooooooOoOOOOOOOOOXX+++      ",
211 "        ..ooooooOooOOoOOOOOOOXX+++      ",
212 "        ..ooooooooOoOOOOOOOOOXX+++      ",
213 "        ..ooooooOooOOoOOOOOOOXX+++      ",
214 "        ..ooooooooOoOOOOOOOOOXX+++      ",
215 "         ..oooooOooOOoOOOOOOXX+++       ",
216 "         ..oooooooOoOOOOOOOOXX+++       ",
217 "          ..ooooOooOOoOOOOOXX+++        ",
218 "          ..ooooooOoOOOOOOOXX++++       ",
219 "        ..o..oooOooOOoOOOOXX+XX+++      ",
220 "       ...o..oooooOoOOOOOXX++XXX++      ",
221 "      ....OO..ooOooOOoOOXX+++XXXX++     ",
222 "     ...oo..+..oooOoOOOXX++XXooXXX++    ",
223 "    ...ooo..++..OooOOoXX+++XXooOXXX+    ",
224 "   ..oooOOXX+++....XXXX++++XXOOoOOXX+   ",
225 "   ..oooOOXX+++ ...XXX+++++XXOOooOXX++  ",
226 "   ..oooOXXX+++  ..XX+++  +XXOOooOXX++  ",
227 "   .....XXX++++             XXXXXXX++   ",
228 "    ....XX++++              XXXXXXX+    ",
229 "     ...XX+++                XXXXX++    ",
230 "                                        ",
231 "                                        ",
232 "                                        ",
233 "                                        "};
234 
NetHackQtSettings(int w,int h)235 NetHackQtSettings::NetHackQtSettings(int w, int h) :
236     tilewidth(TILEWMIN,32,1,this),
237     tileheight(TILEHMIN,32,1,this),
238     widthlbl(&tilewidth,"&Width:",this),
239     heightlbl(&tileheight,"&Height:",this),
240     fontsize(this),
241     normal("times"),
242 #ifdef WS_WIN
243     normalfixed("courier new"),
244 #else
245     normalfixed("fixed"),
246 #endif
247     large("times"),
248     theglyphs(0)
249 
250 {
251     int default_fontsize;
252 
253     if (w<=700) {
254 	// ~640x480
255 	default_fontsize=3;
256 	tilewidth.setValue(8);
257 	tileheight.setValue(14);
258     } else if (w<=900) {
259 	// ~800x600
260 	default_fontsize=3;
261 	tilewidth.setValue(10);
262 	tileheight.setValue(17);
263     } else if (w<=1100) {
264 	// ~1024x768
265 	default_fontsize=2;
266 	tilewidth.setValue(12);
267 	tileheight.setValue(22);
268     } else if (w<=1200) {
269 	// ~1152x900
270 	default_fontsize=1;
271 	tilewidth.setValue(14);
272 	tileheight.setValue(26);
273     } else {
274 	// ~1280x1024 and larger
275 	default_fontsize=0;
276 	tilewidth.setValue(16);
277 	tileheight.setValue(30);
278     }
279 
280     // Tile/font sizes read from .nethackrc
281     if (qt_tilewidth != NULL) {
282 	tilewidth.setValue(atoi(qt_tilewidth));
283 	free(qt_tilewidth);
284     }
285     if (qt_tileheight != NULL) {
286 	tileheight.setValue(atoi(qt_tileheight));
287 	free(qt_tileheight);
288     }
289     if (qt_fontsize != NULL) {
290 	switch (tolower(qt_fontsize[0])) {
291 	  case 'h': default_fontsize = 0; break;
292 	  case 'l': default_fontsize = 1; break;
293 	  case 'm': default_fontsize = 2; break;
294 	  case 's': default_fontsize = 3; break;
295 	}
296 	free(qt_fontsize);
297     }
298 
299     theglyphs=new NetHackQtGlyphs();
300     resizeTiles();
301 
302     connect(&tilewidth,SIGNAL(valueChanged(int)),this,SLOT(resizeTiles()));
303     connect(&tileheight,SIGNAL(valueChanged(int)),this,SLOT(resizeTiles()));
304 
305     fontsize.insertItem("Huge");
306     fontsize.insertItem("Large");
307     fontsize.insertItem("Medium");
308     fontsize.insertItem("Small");
309     fontsize.setCurrentItem(default_fontsize);
310     connect(&fontsize,SIGNAL(activated(int)),this,SIGNAL(fontChanged()));
311 
312     QGridLayout* grid = new QGridLayout(this, 4, 2, 8);
313     grid->addWidget(&tilewidth, 0, 1);  grid->addWidget(&widthlbl, 0, 0);
314     grid->addWidget(&tileheight, 1, 1); grid->addWidget(&heightlbl, 1, 0);
315     QLabel* flabel=new QLabel(&fontsize, "&Font:",this);
316     grid->addWidget(flabel, 2, 0); grid->addWidget(&fontsize, 2, 1);
317     QPushButton* dismiss=new QPushButton("Dismiss",this);
318     dismiss->setDefault(TRUE);
319     grid->addMultiCellWidget(dismiss, 3, 3, 0, 1);
320     grid->setRowStretch(3,0);
321     grid->setColStretch(0,1);
322     grid->setColStretch(1,2);
323     grid->activate();
324 
325     connect(dismiss,SIGNAL(clicked()),this,SLOT(accept()));
326     resize(150,140);
327 }
328 
glyphs()329 NetHackQtGlyphs& NetHackQtSettings::glyphs()
330 {
331     return *theglyphs;
332 }
333 
resizeTiles()334 void NetHackQtSettings::resizeTiles()
335 {
336     int w = tilewidth.value();
337     int h = tileheight.value();
338 
339     theglyphs->resize(w,h);
340     emit tilesChanged();
341 }
342 
normalFont()343 const QFont& NetHackQtSettings::normalFont()
344 {
345     static int size[]={ 18, 14, 12, 10 };
346     normal.setPointSize(size[fontsize.currentItem()]);
347     return normal;
348 }
349 
normalFixedFont()350 const QFont& NetHackQtSettings::normalFixedFont()
351 {
352     static int size[]={ 18, 14, 13, 10 };
353     normalfixed.setPointSize(size[fontsize.currentItem()]);
354     return normalfixed;
355 }
356 
largeFont()357 const QFont& NetHackQtSettings::largeFont()
358 {
359     static int size[]={ 24, 18, 14, 12 };
360     large.setPointSize(size[fontsize.currentItem()]);
361     return large;
362 }
363 
ynInMessages()364 bool NetHackQtSettings::ynInMessages()
365 {
366     // XXX Two out of two users prefer True.
367     return TRUE;
368 }
369 
370 
371 NetHackQtSettings* qt_settings;
372 
373 
374 
NetHackQtKeyBuffer()375 NetHackQtKeyBuffer::NetHackQtKeyBuffer() :
376     in(0), out(0)
377 {
378 }
379 
Empty() const380 bool NetHackQtKeyBuffer::Empty() const { return in==out; }
Full() const381 bool NetHackQtKeyBuffer::Full() const { return (in+1)%maxkey==out; }
382 
Put(int k,int a,int state)383 void NetHackQtKeyBuffer::Put(int k, int a, int state)
384 {
385     if ( Full() ) return;	// Safety
386     key[in]=k;
387     ascii[in]=a;
388     in=(in+1)%maxkey;
389 }
390 
Put(const char * str)391 void NetHackQtKeyBuffer::Put(const char* str)
392 {
393     while (*str) Put(0,*str++,0);
394 }
395 
GetKey()396 int NetHackQtKeyBuffer::GetKey()
397 {
398     if ( Empty() ) return 0;
399     int r=TopKey();
400     out=(out+1)%maxkey;
401     return r;
402 }
403 
GetAscii()404 int NetHackQtKeyBuffer::GetAscii()
405 {
406     if ( Empty() ) return 0; // Safety
407     int r=TopAscii();
408     out=(out+1)%maxkey;
409     return r;
410 }
411 
GetState()412 int NetHackQtKeyBuffer::GetState()
413 {
414     if ( Empty() ) return 0;
415     int r=TopState();
416     out=(out+1)%maxkey;
417     return r;
418 }
419 
TopKey() const420 int NetHackQtKeyBuffer::TopKey() const
421 {
422     if ( Empty() ) return 0;
423     return key[out];
424 }
425 
TopAscii() const426 int NetHackQtKeyBuffer::TopAscii() const
427 {
428     if ( Empty() ) return 0;
429     return ascii[out];
430 }
431 
TopState() const432 int NetHackQtKeyBuffer::TopState() const
433 {
434     if ( Empty() ) return 0;
435     return state[out];
436 }
437 
438 
NetHackQtClickBuffer()439 NetHackQtClickBuffer::NetHackQtClickBuffer() :
440     in(0), out(0)
441 {
442 }
443 
Empty() const444 bool NetHackQtClickBuffer::Empty() const { return in==out; }
Full() const445 bool NetHackQtClickBuffer::Full() const { return (in+1)%maxclick==out; }
446 
Put(int x,int y,int mod)447 void NetHackQtClickBuffer::Put(int x, int y, int mod)
448 {
449     click[in].x=x;
450     click[in].y=y;
451     click[in].mod=mod;
452     in=(in+1)%maxclick;
453 }
454 
NextX() const455 int NetHackQtClickBuffer::NextX() const { return click[out].x; }
NextY() const456 int NetHackQtClickBuffer::NextY() const { return click[out].y; }
NextMod() const457 int NetHackQtClickBuffer::NextMod() const { return click[out].mod; }
458 
Get()459 void NetHackQtClickBuffer::Get()
460 {
461     out=(out+1)%maxclick;
462 }
463 
464 class NhPSListViewItem : public QListViewItem {
465 public:
NhPSListViewItem(QListView * parent,const QString & name)466     NhPSListViewItem( QListView* parent, const QString& name ) :
467 	QListViewItem(parent, name)
468     {
469     }
470 
setGlyph(int g)471     void setGlyph(int g)
472     {
473 	NetHackQtGlyphs& glyphs = qt_settings->glyphs();
474 	int gw = glyphs.width();
475 	int gh = glyphs.height();
476 	QPixmap pm(gw,gh);
477 	QPainter p(&pm);
478 	glyphs.drawGlyph(p, g, 0, 0);
479 	p.end();
480 	setPixmap(0,pm);
481 	setHeight(QMAX(pm.height()+1,height()));
482     }
483 
paintCell(QPainter * p,const QColorGroup & cg,int column,int width,int alignment)484     void paintCell( QPainter *p, const QColorGroup &cg,
485 		    int column, int width, int alignment )
486     {
487 	if ( isSelectable() ) {
488 	    QListViewItem::paintCell( p, cg, column, width, alignment );
489 	} else {
490 	    QColorGroup disabled(
491 		cg.foreground().light(),
492 		cg.button().light(),
493 		cg.light(), cg.dark(), cg.mid(),
494 		gray, cg.base() );
495 	    QListViewItem::paintCell( p, disabled, column, width, alignment );
496 	}
497     }
498 };
499 
500 class NhPSListViewRole : public NhPSListViewItem {
501 public:
NhPSListViewRole(QListView * parent,int id)502     NhPSListViewRole( QListView* parent, int id ) :
503 	NhPSListViewItem(parent,
504 #ifdef QT_CHOOSE_RACE_FIRST // Lowerize - looks better
505 	    QString(QChar(roles[id].name.m[0])).lower()+QString(roles[id].name.m+1)
506 #else
507 	    roles[id].name.m
508 #endif
509 	)
510     {
511 	setGlyph(monnum_to_glyph(roles[id].malenum));
512     }
513 };
514 
515 class NhPSListViewRace : public NhPSListViewItem {
516 public:
NhPSListViewRace(QListView * parent,int id)517     NhPSListViewRace( QListView* parent, int id ) :
518 	NhPSListViewItem(parent,
519 #ifdef QT_CHOOSE_RACE_FIRST // Capitalize - looks better
520 	    QString(QChar(races[id].noun[0])).upper()+QString(races[id].noun+1)
521 #else
522 	    QString(QChar(races[id].noun[0])+QString(races[id].noun+1))
523 #endif
524 	)
525     {
526 	setGlyph(monnum_to_glyph(races[id].malenum));
527     }
528 };
529 
530 class NhPSListView : public QListView {
531 public:
NhPSListView(QWidget * parent)532     NhPSListView( QWidget* parent ) :
533 	QListView(parent)
534     {
535 	setSorting(-1); // order is identity
536 	header()->setClickEnabled(FALSE);
537     }
538 
sizePolicy() const539     QSizePolicy sizePolicy() const
540     {
541 	return QSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
542     }
543 
minimumSizeHint() const544     QSize minimumSizeHint() const
545     {
546 	return sizeHint();
547     }
548 
sizeHint() const549     QSize sizeHint() const
550     {
551 	QListView::sizeHint();
552 	QSize sz = header()->sizeHint();
553 	int h=0;
554 	QListViewItem* c=firstChild();
555 	while (c) h+=c->height(),c = c->nextSibling();
556 	sz += QSize(frameWidth()*2, h+frameWidth()*2);
557 	return sz;
558     }
559 
selectedItemNumber() const560     int selectedItemNumber() const
561     {
562 	int i=0;
563 	QListViewItem* c=firstChild();
564 	while (c && c != selectedItem())
565 	    i++,c = c->nextSibling();
566 	return i;
567     }
568 
setSelectedItemNumber(int i)569     void setSelectedItemNumber(int i)
570     {
571 	QListViewItem* c=firstChild();
572 	while (i--)
573 	    c = c->nextSibling();
574 	c->setSelected(TRUE);
575     }
576 };
577 
NetHackQtPlayerSelector(NetHackQtKeyBuffer & ks)578 NetHackQtPlayerSelector::NetHackQtPlayerSelector(NetHackQtKeyBuffer& ks) :
579     QDialog(0,"plsel",TRUE),
580     keysource(ks)
581 {
582     /*
583                0             1             2
584 	  + Name ------------------------------------+
585 	0 |                                          |
586 	  + ---- ------------------------------------+
587 	  + Role ---+   + Race ---+   + Gender ------+
588 	  |         |   |         |   |  * Male      |
589 	1 |         |   |         |   |  * Female    |
590 	  |         |   |         |   +--------------+
591 	  |         |   |         |
592 	  |         |   |         |   + Alignment ---+
593 	2 |         |   |         |   |  * Male      |
594 	  |         |   |         |   |  * Female    |
595 	  |         |   |         |   +--------------+
596 	3 |         |   |         |   ...stretch...
597 	  |         |   |         |
598 	4 |         |   |         |   [  Play  ]
599 	5 |         |   |         |   [  Quit  ]
600 	  +---------+   +---------+
601     */
602 
603     int marg=4;
604     QGridLayout *l = new QGridLayout(this,6,3,marg,marg);
605 
606     QButtonGroup* namebox = new QButtonGroup(1,Horizontal,"Name",this);
607     QLineEdit* name = new QLineEdit(namebox);
608     name->setMaxLength(sizeof(plname)-1);
609     if ( strncmp(plname,"player",6) && strncmp(plname,"games",5) )
610 	name->setText(plname);
611     connect(name, SIGNAL(textChanged(const QString&)),
612 	    this, SLOT(selectName(const QString&)) );
613     name->setFocus();
614     role = new NhPSListView(this);
615     race = new NhPSListView(this);
616     role->addColumn("Role");
617     race->addColumn("Race");
618     QButtonGroup* genderbox = new QButtonGroup(1,Horizontal,"Sex",this);
619     QButtonGroup* alignbox = new QButtonGroup(1,Horizontal,"Alignment",this);
620 
621     QLabel* logo = new QLabel("<big>Qt NetHack</big>"
622 	"<br><i>by Warwick Allison<br>and the NetHack DevTeam", this);
623 
624     l->addMultiCellWidget( namebox, 0,0,0,2 );
625 #ifdef QT_CHOOSE_RACE_FIRST
626     l->addMultiCellWidget( race, 1,5,0,0 );
627     l->addMultiCellWidget( role, 1,5,1,1 );
628 #else
629     l->addMultiCellWidget( role, 1,5,0,0 );
630     l->addMultiCellWidget( race, 1,5,1,1 );
631 #endif
632     l->addWidget( genderbox, 1, 2 );
633     l->addWidget( alignbox, 2, 2 );
634     l->addWidget( logo, 3, 2, AlignCenter );
635     l->setRowStretch( 3, 5 );
636 
637     int i;
638     int nrole;
639 
640     for (nrole=0; roles[nrole].name.m; nrole++)
641 	;
642     for (i=nrole-1; i>=0; i--) { // XXX QListView unsorted goes in rev.
643 	new NhPSListViewRole( role, i );
644     }
645     connect( role, SIGNAL(selectionChanged()), this, SLOT(selectRole()) );
646 
647     int nrace;
648     for (nrace=0; races[nrace].noun; nrace++)
649 	;
650     for (i=nrace-1; i>=0; i--) {
651 	new NhPSListViewRace( race, i );
652     }
653     connect( race, SIGNAL(selectionChanged()), this, SLOT(selectRace()) );
654 
655     gender = new QRadioButton*[ROLE_GENDERS];
656     for (i=0; i<ROLE_GENDERS; i++) {
657 	gender[i] = new QRadioButton( genders[i].adj, genderbox );
658     }
659     connect( genderbox, SIGNAL(clicked(int)), this, SLOT(selectGender(int)) );
660 
661     alignment = new QRadioButton*[ROLE_ALIGNS];
662     for (i=0; i<ROLE_ALIGNS; i++) {
663 	alignment[i] = new QRadioButton( aligns[i].adj, alignbox );
664     }
665     connect( alignbox, SIGNAL(clicked(int)), this, SLOT(selectAlignment(int)) );
666 
667     QPushButton* ok = new QPushButton("Play",this);
668     l->addWidget( ok, 4, 2 );
669     ok->setDefault(TRUE);
670     connect( ok, SIGNAL(clicked()), this, SLOT(accept()) );
671 
672     QPushButton* cancel = new QPushButton("Quit",this);
673     l->addWidget( cancel, 5, 2 );
674     connect( cancel, SIGNAL(clicked()), this, SLOT(reject()) );
675 
676     // Randomize
677     int ro = rn2(nrole);
678     int ra = rn2(nrace);
679 
680 #ifdef QT_CHOOSE_RACE_FIRST
681     while (!validrace(ro,ra))
682 	ro = rn2(nrole);
683 #else
684     while (!validrace(ro,ra))
685 	ra = rn2(nrace);
686 #endif
687 
688     int g;
689     do {
690 	g = rn2(ROLE_GENDERS);
691     } while (!validgend(ro,ra,g));
692     gender[g]->setChecked(TRUE);
693     selectGender(g);
694 
695     int a;
696     do {
697 	a = rn2(ROLE_ALIGNS);
698     } while (!validalign(ro,ra,a));
699     alignment[a]->setChecked(TRUE);
700     selectAlignment(g);
701 
702     QListViewItem* li;
703 
704     li = role->firstChild();
705     while (ro--) li=li->nextSibling();
706     role->setSelected(li,TRUE);
707 
708     li = race->firstChild();
709     while (ra--) li=li->nextSibling();
710     race->setSelected(li,TRUE);
711 }
712 
713 
selectName(const QString & n)714 void NetHackQtPlayerSelector::selectName(const QString& n)
715 {
716     strncpy(plname,n.latin1(),sizeof(plname)-1);
717 }
718 
selectRole()719 void NetHackQtPlayerSelector::selectRole()
720 {
721 #ifndef QT_CHOOSE_RACE_FIRST
722     selectRace();
723 #else
724     QListViewItem* i=role->currentItem();
725     QListViewItem* valid=0;
726     int j;
727     NhPSListViewItem* item;
728     item = (NhPSListViewItem*)role->firstChild();
729     int ra = race->selectedItemNumber();
730     for (j=0; roles[j].name.m; j++) {
731 	bool v = validrace(j,ra);
732 	item->setSelectable(TRUE);
733 	if ( !valid && v ) valid = item;
734 	item=(NhPSListViewItem*)item->nextSibling();
735     }
736     if ( !validrace(role->selectedItemNumber(),ra) )
737 	i = valid;
738     role->setSelected(i,TRUE);
739     item = (NhPSListViewItem*)role->firstChild();
740     for (j=0; roles[j].name.m; j++) {
741 	bool v = validrace(j,ra);
742 	item->setSelectable(v);
743 	item->repaint();
744 	item=(NhPSListViewItem*)item->nextSibling();
745     }
746 #endif
747 
748     flags.initrole = role->selectedItemNumber();
749     setupOthers();
750 }
751 
selectRace()752 void NetHackQtPlayerSelector::selectRace()
753 {
754 #ifdef QT_CHOOSE_RACE_FIRST
755     selectRole();
756     flags.initrace = race->selectedItemNumber();
757 #else
758     QListViewItem* i=race->currentItem();
759     QListViewItem* valid=0;
760     int j;
761     NhPSListViewItem* item;
762     item = (NhPSListViewItem*)race->firstChild();
763     int ro = role->selectedItemNumber();
764     for (j=0; races[j].noun; j++) {
765 	bool v = validrace(ro,j);
766 	item->setSelectable(TRUE);
767 	if ( !valid && v ) valid = item;
768 	item=(NhPSListViewItem*)item->nextSibling();
769     }
770     if ( !validrace(ro,race->selectedItemNumber()) )
771 	i = valid;
772     race->setSelected(i,TRUE);
773     item = (NhPSListViewItem*)race->firstChild();
774     for (j=0; races[j].noun; j++) {
775 	bool v = validrace(ro,j);
776 	item->setSelectable(v);
777 	item->repaint();
778 	item=(NhPSListViewItem*)item->nextSibling();
779     }
780 #endif
781 
782     flags.initrace = race->selectedItemNumber();
783     setupOthers();
784 }
785 
setupOthers()786 void NetHackQtPlayerSelector::setupOthers()
787 {
788     int ro = role->selectedItemNumber();
789     int ra = race->selectedItemNumber();
790     int valid=-1;
791     int c=0;
792     int j;
793     for (j=0; j<ROLE_GENDERS; j++) {
794 	bool v = validgend(ro,ra,j);
795 	if ( gender[j]->isChecked() )
796 	    c = j;
797 	gender[j]->setEnabled(v);
798 	if ( valid<0 && v ) valid = j;
799     }
800     if ( !validgend(ro,ra,c) )
801 	c = valid;
802     int k;
803     for (k=0; k<ROLE_GENDERS; k++) {
804 	gender[k]->setChecked(c==k);
805     }
806     selectGender(c);
807 
808     valid=-1;
809     for (j=0; j<ROLE_ALIGNS; j++) {
810 	bool v = validalign(ro,ra,j);
811 	if ( alignment[j]->isChecked() )
812 	    c = j;
813 	alignment[j]->setEnabled(v);
814 	if ( valid<0 && v ) valid = j;
815     }
816     if ( !validalign(ro,ra,c) )
817 	c = valid;
818     for (k=0; k<ROLE_ALIGNS; k++) {
819 	alignment[k]->setChecked(c==k);
820     }
821     selectAlignment(c);
822 }
823 
selectGender(int i)824 void NetHackQtPlayerSelector::selectGender(int i)
825 {
826     flags.initgend = i;
827 }
828 
selectAlignment(int i)829 void NetHackQtPlayerSelector::selectAlignment(int i)
830 {
831     flags.initalign = i;
832 }
833 
834 
done(int i)835 void NetHackQtPlayerSelector::done(int i)
836 {
837     setResult(i);
838     qApp->exit_loop();
839 }
840 
Quit()841 void NetHackQtPlayerSelector::Quit()
842 {
843     done(R_Quit);
844     qApp->exit_loop();
845 }
846 
Random()847 void NetHackQtPlayerSelector::Random()
848 {
849     done(R_Rand);
850     qApp->exit_loop();
851 }
852 
Choose()853 bool NetHackQtPlayerSelector::Choose()
854 {
855     centerOnMain(this);
856     if ( exec() ) {
857 	return TRUE;
858     } else
859 	return FALSE;
860 }
861 
862 
NetHackQtStringRequestor(NetHackQtKeyBuffer & ks,const char * p,const char * cancelstr)863 NetHackQtStringRequestor::NetHackQtStringRequestor(NetHackQtKeyBuffer& ks, const char* p, const char* cancelstr) :
864     QDialog(0,"string",FALSE),
865     prompt(p,this,"prompt"),
866     input(this,"input"),
867     keysource(ks)
868 {
869     cancel=new QPushButton(cancelstr,this);
870     connect(cancel,SIGNAL(clicked()),this,SLOT(reject()));
871 
872     okay=new QPushButton("Okay",this);
873     connect(okay,SIGNAL(clicked()),this,SLOT(accept()));
874     connect(&input,SIGNAL(returnPressed()),this,SLOT(accept()));
875 
876     setFocusPolicy(StrongFocus);
877 }
878 
resizeEvent(QResizeEvent *)879 void NetHackQtStringRequestor::resizeEvent(QResizeEvent*)
880 {
881     const int margin=5;
882     const int gutter=5;
883 
884     int h=(height()-margin*2-gutter);
885 
886     if (strlen(prompt.text()) > 16) {
887 	h/=3;
888 	prompt.setGeometry(margin,margin,width()-margin*2,h);
889 	input.setGeometry(width()*1/5,margin+h+gutter,
890 	    (width()-margin-2-gutter)*4/5,h);
891     } else {
892 	h/=2;
893 	prompt.setGeometry(margin,margin,(width()-margin*2-gutter)*2/5,h);
894 	input.setGeometry(prompt.geometry().right()+gutter,margin,
895 	    (width()-margin-2-gutter)*3/5,h);
896     }
897 
898     cancel->setGeometry(margin,input.geometry().bottom()+gutter,
899 	(width()-margin*2-gutter)/2,h);
900     okay->setGeometry(cancel->geometry().right()+gutter,cancel->geometry().y(),
901 	cancel->width(),h);
902 }
903 
SetDefault(const char * d)904 void NetHackQtStringRequestor::SetDefault(const char* d)
905 {
906     input.setText(d);
907 }
Get(char * buffer,int maxchar)908 bool NetHackQtStringRequestor::Get(char* buffer, int maxchar)
909 {
910     input.setMaxLength(maxchar);
911     if (strlen(prompt.text()) > 16) {
912 	resize(fontMetrics().width(prompt.text())+50,fontMetrics().height()*6);
913     } else {
914 	resize(fontMetrics().width(prompt.text())*2+50,fontMetrics().height()*4);
915     }
916 
917     centerOnMain(this);
918     show();
919     input.setFocus();
920     setResult(-1);
921     while (result()==-1) {
922 	// Put keys in buffer (eg. from macros, from out-of-focus input)
923 	if (!keysource.Empty()) {
924 	    while (!keysource.Empty()) {
925 		int key=keysource.TopKey();
926 		int ascii=keysource.TopAscii();
927 		int state=keysource.GetState();
928 		if (ascii=='\r' || ascii=='\n') {
929 		    // CR or LF in buffer causes confirmation
930 		    strcpy(buffer,input.text());
931 		    return TRUE;
932 		} else if (ascii=='\033') {
933 		    return FALSE;
934 		} else {
935 		    input.fakeEvent(key,ascii,state);
936 		}
937 	    }
938 	}
939 	qApp->enter_loop();
940     }
941     // XXX Get rid of extra keys, since we couldn't get focus!
942     while (!keysource.Empty()) keysource.GetKey();
943 
944     if (result()) {
945 	strcpy(buffer,input.text());
946 	return TRUE;
947     } else {
948 	return FALSE;
949     }
950 }
done(int i)951 void NetHackQtStringRequestor::done(int i)
952 {
953     setResult(i);
954     qApp->exit_loop();
955 }
956 
957 
NetHackQtWindow()958 NetHackQtWindow::NetHackQtWindow()
959 {
960 }
~NetHackQtWindow()961 NetHackQtWindow::~NetHackQtWindow()
962 {
963 }
964 
965 // XXX Use "expected ..." for now, abort or default later.
966 //
Clear()967 void NetHackQtWindow::Clear() { puts("unexpected Clear"); }
Display(bool block)968 void NetHackQtWindow::Display(bool block) { puts("unexpected Display"); }
Destroy()969 bool NetHackQtWindow::Destroy() { return TRUE; }
CursorTo(int x,int y)970 void NetHackQtWindow::CursorTo(int x,int y) { puts("unexpected CursorTo"); }
PutStr(int attr,const char * text)971 void NetHackQtWindow::PutStr(int attr, const char* text) { puts("unexpected PutStr"); }
StartMenu()972 void NetHackQtWindow::StartMenu() { puts("unexpected StartMenu"); }
AddMenu(int glyph,const ANY_P * identifier,char ch,char gch,int attr,const char * str,bool presel)973 void NetHackQtWindow::AddMenu(int glyph, const ANY_P* identifier, char ch, char gch, int attr,
974     const char* str, bool presel) { puts("unexpected AddMenu"); }
EndMenu(const char * prompt)975 void NetHackQtWindow::EndMenu(const char* prompt) { puts("unexpected EndMenu"); }
SelectMenu(int how,MENU_ITEM_P ** menu_list)976 int NetHackQtWindow::SelectMenu(int how, MENU_ITEM_P **menu_list) { puts("unexpected SelectMenu"); return 0; }
ClipAround(int x,int y)977 void NetHackQtWindow::ClipAround(int x,int y) { puts("unexpected ClipAround"); }
PrintGlyph(int x,int y,int glyph)978 void NetHackQtWindow::PrintGlyph(int x,int y,int glyph) { puts("unexpected PrintGlyph"); }
979 //void NetHackQtWindow::PrintGlyphCompose(int x,int y,int,int) { puts("unexpected PrintGlyphCompose"); }
UseRIP(int how)980 void NetHackQtWindow::UseRIP(int how) { puts("unexpected UseRIP"); }
981 
982 
983 
984 // XXX Hmmm... crash after saving bones file if Map window is
985 // XXX deleted.  Strange bug somewhere.
Destroy()986 bool NetHackQtMapWindow::Destroy() { return FALSE; }
987 
NetHackQtMapWindow(NetHackQtClickBuffer & click_sink)988 NetHackQtMapWindow::NetHackQtMapWindow(NetHackQtClickBuffer& click_sink) :
989     clicksink(click_sink),
990     change(10),
991     rogue_font(0)
992 {
993     viewport.addChild(this);
994 
995     setBackgroundColor(black);
996     viewport.setBackgroundColor(black);
997 
998     pet_annotation = QPixmap(pet_mark_xpm);
999 
1000     cursor.setX(0);
1001     cursor.setY(0);
1002     Clear();
1003 
1004     connect(qt_settings,SIGNAL(tilesChanged()),this,SLOT(updateTiles()));
1005 
1006     updateTiles();
1007     //setFocusPolicy(StrongFocus);
1008 }
1009 
updateTiles()1010 void NetHackQtMapWindow::updateTiles()
1011 {
1012     NetHackQtGlyphs& glyphs = qt_settings->glyphs();
1013     int gw = glyphs.width();
1014     int gh = glyphs.height();
1015     // Be exactly the size we want to be - full map...
1016     resize(COLNO*gw,ROWNO*gh);
1017 
1018     viewport.verticalScrollBar()->setSteps(gh,gh);
1019     viewport.horizontalScrollBar()->setSteps(gw,gw);
1020     viewport.setMaximumSize(
1021 	gw*COLNO + viewport.verticalScrollBar()->width(),
1022 	gh*ROWNO + viewport.horizontalScrollBar()->height()
1023     );
1024     viewport.updateScrollBars();
1025 
1026     change.clear();
1027     change.add(0,0,COLNO,ROWNO);
1028     delete rogue_font; rogue_font = 0;
1029     Display(FALSE);
1030 
1031     emit resized();
1032 }
1033 
~NetHackQtMapWindow()1034 NetHackQtMapWindow::~NetHackQtMapWindow()
1035 {
1036     // Remove from viewport porthole, since that is a destructible member.
1037     viewport.removeChild(this);
1038     recreate(0,0,QPoint(0,0));
1039 }
1040 
Widget()1041 QWidget* NetHackQtMapWindow::Widget()
1042 {
1043     return &viewport;
1044 }
1045 
Scroll(int dx,int dy)1046 void NetHackQtMapWindow::Scroll(int dx, int dy)
1047 {
1048     if (viewport.horizontalScrollBar()->isVisible()) {
1049 	while (dx<0) { viewport.horizontalScrollBar()->subtractPage(); dx++; }
1050 	while (dx>0) { viewport.horizontalScrollBar()->addPage(); dx--; }
1051     }
1052     if (viewport.verticalScrollBar()->isVisible()) {
1053 	while (dy<0) { viewport.verticalScrollBar()->subtractPage(); dy++; }
1054 	while (dy>0) { viewport.verticalScrollBar()->addPage(); dy--; }
1055     }
1056 }
1057 
Clear()1058 void NetHackQtMapWindow::Clear()
1059 {
1060     unsigned short stone=cmap_to_glyph(S_stone);
1061 
1062     for (int j=0; j<ROWNO; j++) {
1063 	for (int i=0; i<COLNO; i++) {
1064 	    Glyph(i,j)=stone;
1065 	}
1066     }
1067 
1068     change.clear();
1069     change.add(0,0,COLNO,ROWNO);
1070 }
1071 
mousePressEvent(QMouseEvent * event)1072 void NetHackQtMapWindow::mousePressEvent(QMouseEvent* event)
1073 {
1074     clicksink.Put(
1075 	event->pos().x()/qt_settings->glyphs().width(),
1076 	event->pos().y()/qt_settings->glyphs().height(),
1077 	event->button()==LeftButton ? CLICK_1 : CLICK_2
1078     );
1079     qApp->exit_loop();
1080 }
1081 
1082 #ifdef TEXTCOLOR
1083 static
nhcolor_to_pen(int c)1084 const QPen& nhcolor_to_pen(int c)
1085 {
1086     static QPen* pen=0;
1087     if ( !pen ) {
1088 	pen = new QPen[17];
1089 	pen[0] = Qt::black;
1090 	pen[1] = Qt::red;
1091 	pen[2] = QColor(0,191,0);
1092 	pen[3] = QColor(127,127,0);
1093 	pen[4] = Qt::blue;
1094 	pen[5] = Qt::magenta;
1095 	pen[6] = Qt::cyan;
1096 	pen[7] = Qt::gray;
1097 	pen[8] = Qt::white; // no color
1098 	pen[9] = QColor(255,127,0);
1099 	pen[10] = QColor(127,255,127);
1100 	pen[11] = Qt::yellow;
1101 	pen[12] = QColor(127,127,255);
1102 	pen[13] = QColor(255,127,255);
1103 	pen[14] = QColor(127,255,255);
1104 	pen[15] = Qt::white;
1105 	pen[16] = Qt::black;
1106     }
1107 
1108     return pen[c];
1109 }
1110 #endif
1111 
paintEvent(QPaintEvent * event)1112 void NetHackQtMapWindow::paintEvent(QPaintEvent* event)
1113 {
1114     QRect area=event->rect();
1115     QRect garea;
1116     garea.setCoords(
1117 	QMAX(0,area.left()/qt_settings->glyphs().width()),
1118 	QMAX(0,area.top()/qt_settings->glyphs().height()),
1119 	QMIN(COLNO-1,area.right()/qt_settings->glyphs().width()),
1120 	QMIN(ROWNO-1,area.bottom()/qt_settings->glyphs().height())
1121     );
1122 
1123     QPainter painter;
1124 
1125     painter.begin(this);
1126 
1127 #ifdef REINCARNATION
1128     if (Is_rogue_level(&u.uz)) {
1129 	// You enter a VERY primitive world!
1130 
1131 	painter.setClipRect( event->rect() ); // (normally we don't clip)
1132 	painter.fillRect( event->rect(), black );
1133 
1134 	int offset;
1135 
1136 	if ( !rogue_font ) {
1137 	    // Find font...
1138 	    int pts = 5;
1139 	    while ( pts < 32 ) {
1140 		painter.setFont(QFont("Courier", pts));
1141 		QFontMetrics fm = painter.fontMetrics();
1142 		if ( fm.width("M") > qt_settings->glyphs().width() )
1143 		    break;
1144 		if ( fm.height() > qt_settings->glyphs().height() )
1145 		    break;
1146 		pts++;
1147 	    }
1148 	    rogue_font = new QFont("Courier",pts-1);
1149 	}
1150 	painter.setFont(*rogue_font);
1151 
1152 	for (int j=garea.top(); j<=garea.bottom(); j++) {
1153 	    for (int i=garea.left(); i<=garea.right(); i++) {
1154 		unsigned short g=Glyph(i,j);
1155 		uchar ch;
1156 
1157 		/* (from wintty, naturally)
1158 		 *
1159 		 *  Map the glyph back to a character.
1160 		 *
1161 		 *  Warning:  For speed, this makes an assumption on the order of
1162 		 *		  offsets.  The order is set in display.h.
1163 		 */
1164 
1165 
1166 #ifdef TEXTCOLOR
1167 		int	    color;
1168 
1169 #define zap_color(n)  color = iflags.use_color ? zapcolors[n] : NO_COLOR
1170 #define cmap_color(n) color = iflags.use_color ? defsyms[n].color : NO_COLOR
1171 #define obj_color(n)  color = iflags.use_color ? objects[n].oc_color : NO_COLOR
1172 #define mon_color(n)  color = iflags.use_color ? mons[n].mcolor : NO_COLOR
1173 #define pet_color(n)  color = iflags.use_color ? mons[n].mcolor : NO_COLOR
1174 #define warn_color(n) color = iflags.use_color ? def_warnsyms[n].color : NO_COLOR
1175 
1176 # else /* no text color */
1177 
1178 #define zap_color(n)
1179 #define cmap_color(n)
1180 #define obj_color(n)
1181 #define mon_color(n)
1182 #define pet_color(c)
1183 #define warn_color(c)
1184 		painter.setPen( green );
1185 #endif
1186 
1187 		if ((offset = (g - GLYPH_WARNING_OFF)) >= 0) { 	  /* a warning flash */
1188 		    ch = warnsyms[offset];
1189 		    warn_color(offset);
1190 		} else if ((offset = (g - GLYPH_SWALLOW_OFF)) >= 0) {	/* swallow */
1191 		    /* see swallow_to_glyph() in display.c */
1192 		    ch = (uchar) showsyms[S_sw_tl + (offset & 0x7)];
1193 		    mon_color(offset >> 3);
1194 		} else if ((offset = (g - GLYPH_ZAP_OFF)) >= 0) {	/* zap beam */
1195 		    /* see zapdir_to_glyph() in display.c */
1196 		    ch = showsyms[S_vbeam + (offset & 0x3)];
1197 		    zap_color((offset >> 2));
1198 		} else if ((offset = (g - GLYPH_CMAP_OFF)) >= 0) {	/* cmap */
1199 		    ch = showsyms[offset];
1200 		    cmap_color(offset);
1201 		} else if ((offset = (g - GLYPH_OBJ_OFF)) >= 0) {	/* object */
1202 		    ch = oc_syms[(int)objects[offset].oc_class];
1203 		    obj_color(offset);
1204 		} else if ((offset = (g - GLYPH_BODY_OFF)) >= 0) {	/* a corpse */
1205 		    ch = oc_syms[(int)objects[CORPSE].oc_class];
1206 		    mon_color(offset);
1207 		} else if ((offset = (g - GLYPH_PET_OFF)) >= 0) {	/* a pet */
1208 		    ch = monsyms[(int)mons[offset].mlet];
1209 		    pet_color(offset);
1210 		} else {							/* a monster */
1211 		    ch = monsyms[(int)mons[g].mlet];
1212 		    mon_color(g);
1213 		}
1214 		// end of wintty code
1215 
1216 #ifdef TEXTCOLOR
1217 		painter.setPen( nhcolor_to_pen(color) );
1218 #endif
1219 		painter.drawText(
1220 		    i*qt_settings->glyphs().width(),
1221 		    j*qt_settings->glyphs().height(),
1222 		    qt_settings->glyphs().width(),
1223 		    qt_settings->glyphs().height(),
1224 		    AlignCenter,
1225 		    (const char*)&ch, 1
1226 		);
1227 		if (glyph_is_pet(g)
1228 #ifdef TEXTCOLOR
1229 		    && ::iflags.hilite_pet
1230 #endif
1231 		) {
1232 		    painter.drawPixmap(QPoint(i*qt_settings->glyphs().width(), j*qt_settings->glyphs().height()), pet_annotation);
1233 		}
1234 	    }
1235 	}
1236     } else
1237 #endif
1238     {
1239 	for (int j=garea.top(); j<=garea.bottom(); j++) {
1240 	    for (int i=garea.left(); i<=garea.right(); i++) {
1241 		unsigned short g=Glyph(i,j);
1242 		qt_settings->glyphs().drawCell(painter, g, i, j);
1243 		if (glyph_is_pet(g)
1244 #ifdef TEXTCOLOR
1245 		    && ::iflags.hilite_pet
1246 #endif
1247 		) {
1248 		    painter.drawPixmap(QPoint(i*qt_settings->glyphs().width(), j*qt_settings->glyphs().height()), pet_annotation);
1249 		}
1250 	    }
1251 	}
1252     }
1253 
1254     if (garea.contains(cursor)) {
1255 #ifdef REINCARNATION
1256 	if (Is_rogue_level(&u.uz)) {
1257 #ifdef TEXTCOLOR
1258 	    painter.setPen( white );
1259 #else
1260 	    painter.setPen( green ); // REALLY primitive
1261 #endif
1262 	} else
1263 #endif
1264 	{
1265 	    int hp100;
1266 	    if (u.mtimedone) {
1267 		hp100=u.mhmax ? u.mh*100/u.mhmax : 100;
1268 	    } else {
1269 		hp100=u.uhpmax ? u.uhp*100/u.uhpmax : 100;
1270 	    }
1271 
1272 	    if (hp100 > 75) painter.setPen(white);
1273 	    else if (hp100 > 50) painter.setPen(yellow);
1274 	    else if (hp100 > 25) painter.setPen(QColor(0xff,0xbf,0x00)); // orange
1275 	    else if (hp100 > 10) painter.setPen(red);
1276 	    else painter.setPen(magenta);
1277 	}
1278 
1279 	painter.drawRect(
1280 	    cursor.x()*qt_settings->glyphs().width(),cursor.y()*qt_settings->glyphs().height(),
1281 	    qt_settings->glyphs().width(),qt_settings->glyphs().height());
1282     }
1283 
1284     painter.end();
1285 }
1286 
Display(bool block)1287 void NetHackQtMapWindow::Display(bool block)
1288 {
1289     for (int i=0; i<change.clusters(); i++) {
1290 	const QRect& ch=change[i];
1291 	repaint(
1292 	    ch.x()*qt_settings->glyphs().width(),
1293 	    ch.y()*qt_settings->glyphs().height(),
1294 	    ch.width()*qt_settings->glyphs().width(),
1295 	    ch.height()*qt_settings->glyphs().height(),
1296 	    FALSE
1297 	);
1298     }
1299 
1300     change.clear();
1301 
1302     if (block) {
1303 	yn_function("Press a key when done viewing",0,'\0');
1304     }
1305 }
1306 
CursorTo(int x,int y)1307 void NetHackQtMapWindow::CursorTo(int x,int y)
1308 {
1309     Changed(cursor.x(),cursor.y());
1310     cursor.setX(x);
1311     cursor.setY(y);
1312     Changed(cursor.x(),cursor.y());
1313 }
1314 
PutStr(int attr,const char * text)1315 void NetHackQtMapWindow::PutStr(int attr, const char* text)
1316 {
1317     puts("unexpected PutStr in MapWindow");
1318 }
1319 
ClipAround(int x,int y)1320 void NetHackQtMapWindow::ClipAround(int x,int y)
1321 {
1322     // Convert to pixel of center of tile
1323     x=x*qt_settings->glyphs().width()+qt_settings->glyphs().width()/2;
1324     y=y*qt_settings->glyphs().height()+qt_settings->glyphs().height()/2;
1325 
1326     // Then ensure that pixel is visible
1327     viewport.center(x,y,0.45,0.45);
1328 }
1329 
PrintGlyph(int x,int y,int glyph)1330 void NetHackQtMapWindow::PrintGlyph(int x,int y,int glyph)
1331 {
1332     Glyph(x,y)=glyph;
1333     Changed(x,y);
1334 }
1335 
1336 //void NetHackQtMapWindow::PrintGlyphCompose(int x,int y,int glyph1, int glyph2)
1337 //{
1338     // TODO: composed graphics
1339 //}
1340 
Changed(int x,int y)1341 void NetHackQtMapWindow::Changed(int x, int y)
1342 {
1343     change.add(x,y);
1344 }
1345 
1346 
1347 class NetHackQtScrollText : public QTableView {
1348     struct UData {
UDataNetHackQtScrollText::UData1349 	UData() : text(0), attr(0) { }
~UDataNetHackQtScrollText::UData1350 	~UData() { if (text) free(text); }
1351 
1352 	char* text;
1353 	int attr;
1354     };
1355 public:
1356     int uncleared;
1357 
NetHackQtScrollText(int maxlength)1358     NetHackQtScrollText(int maxlength) :
1359 	maxitems(maxlength),
1360 	first(0),
1361 	count(0),
1362 	uncleared(0),
1363 	item_cycle(maxlength)
1364     {
1365 	setNumCols(1);
1366 	setCellWidth(200);
1367 	setCellHeight(fontMetrics().height());
1368 	setBackgroundColor(white);
1369 	setTableFlags(Tbl_vScrollBar
1370 	    |Tbl_autoHScrollBar
1371 	    |Tbl_clipCellPainting
1372 	    |Tbl_smoothScrolling);
1373     }
1374 
~NetHackQtScrollText()1375     ~NetHackQtScrollText()
1376     {
1377     }
1378 
Scroll(int dx,int dy)1379     void Scroll(int dx, int dy)
1380     {
1381 	setXOffset(xOffset()+dx*viewWidth());
1382 	setYOffset(yOffset()+dy*viewHeight());
1383     }
1384 
insertItem(int attr,const char * text)1385     void insertItem(int attr, const char* text)
1386     {
1387 	setTopCell(count);
1388 
1389 	setAutoUpdate(FALSE);
1390 
1391 	int i;
1392 	if (count<maxitems) {
1393 	    i=count++;
1394 	    setNumRows(count);
1395 	} else {
1396 	    i=count-1;
1397 	    first=(first+1)%maxitems;
1398 	}
1399 	item(i).attr=attr;
1400 	item(i).text=strdup(text);
1401 	int w=datumWidth(item(i));
1402 
1403 	if (w > cellWidth()) {
1404 	    // Get wider.
1405 	    setCellWidth(w);
1406 	}
1407 	setTopCell(count);
1408 
1409 	setAutoUpdate(TRUE);
1410 
1411 	if (viewHeight() >= totalHeight()-cellHeight()) {
1412 	    repaint();
1413 	} else {
1414 	    scroll(0,cellHeight());
1415 	}
1416     }
1417 
setFont(const QFont & font)1418     virtual void setFont(const QFont& font)
1419     {
1420 	QTableView::setFont(font);
1421 	setCellHeight(fontMetrics().height());
1422     }
1423 
1424 protected:
1425 
item(int i)1426     UData& item(int i)
1427     {
1428 	return item_cycle[(first+i)%maxitems];
1429     }
1430 
1431     const int maxitems;
1432     int first, count;
1433     QArray<UData> item_cycle;
1434 
datumWidth(const UData & uitem)1435     int datumWidth(const UData& uitem)
1436     {
1437 	if (uitem.text) {
1438 	    int width=fontMetrics().width(uitem.text)+3;
1439 	    if (uitem.attr) {
1440 		// XXX Too expensive to do properly, because
1441 		// XXX we have to set the font of the widget
1442 		// XXX just to get the font metrics information!
1443 		// XXX Could hold a fake widget for that
1444 		// XXX purpose, but this hack is less ugly.
1445 		width+=width/10;
1446 	    }
1447 	    return width;
1448 	} else {
1449 	    return 0;
1450 	}
1451     }
1452 
setupPainter(QPainter * p)1453     virtual void setupPainter(QPainter *p)
1454     {
1455 	// XXX This shouldn't be needed - we set the bg in the constructor.
1456 	p->setBackgroundColor(white);
1457     }
1458 
paintCell(QPainter * p,int row,int col)1459     virtual void paintCell(QPainter *p, int row, int col)
1460     {
1461 	bool sel=FALSE;
1462 	UData& uitem=item(row);
1463 
1464 	if (!sel && row < count-uncleared) {
1465 	    p->setPen(darkGray);
1466 	} else {
1467 	    p->setPen(black);
1468 	}
1469 
1470 	if (uitem.attr) {
1471 	    // XXX only bold
1472 	    QFont bold(font().family(),font().pointSize(),QFont::Bold);
1473 	    p->setFont(bold);
1474 	}
1475 
1476 	p->drawText(3, 0, cellWidth(), cellHeight(),
1477 		AlignLeft|AlignVCenter, uitem.text);
1478 
1479 	if (uitem.attr) {
1480 	    p->setFont(font());
1481 	}
1482     }
1483 };
1484 
NetHackQtMessageWindow()1485 NetHackQtMessageWindow::NetHackQtMessageWindow() :
1486     list(new NetHackQtScrollText(::iflags.msg_history))
1487 {
1488     ::iflags.window_inited = 1;
1489     connect(qt_settings,SIGNAL(fontChanged()),this,SLOT(updateFont()));
1490     updateFont();
1491 }
1492 
~NetHackQtMessageWindow()1493 NetHackQtMessageWindow::~NetHackQtMessageWindow()
1494 {
1495     ::iflags.window_inited = 0;
1496     delete list;
1497 }
1498 
Widget()1499 QWidget* NetHackQtMessageWindow::Widget() { return list; }
1500 
updateFont()1501 void NetHackQtMessageWindow::updateFont()
1502 {
1503     list->setFont(qt_settings->normalFont());
1504 }
1505 
Scroll(int dx,int dy)1506 void NetHackQtMessageWindow::Scroll(int dx, int dy)
1507 {
1508     list->Scroll(dx,dy);
1509 }
1510 
Clear()1511 void NetHackQtMessageWindow::Clear()
1512 {
1513     if (list->uncleared) {
1514 	list->uncleared=0;
1515 	changed=TRUE;
1516 	Display(FALSE);
1517     }
1518 }
1519 
Display(bool block)1520 void NetHackQtMessageWindow::Display(bool block)
1521 {
1522     if (changed) {
1523 	list->repaint();
1524 	changed=FALSE;
1525     }
1526 }
1527 
PutStr(int attr,const char * text)1528 void NetHackQtMessageWindow::PutStr(int attr, const char* text)
1529 {
1530 #ifdef USER_SOUNDS
1531     play_sound_for_message(text);
1532 #endif
1533 
1534     changed=TRUE;
1535     list->uncleared++;
1536     list->insertItem(attr,text);
1537 
1538     // Force scrollbar to bottom
1539     // XXX list->setTopItem(list->count());
1540 }
1541 
1542 
1543 
NetHackQtLabelledIcon(QWidget * parent,const char * l)1544 NetHackQtLabelledIcon::NetHackQtLabelledIcon(QWidget* parent, const char* l) :
1545     QWidget(parent),
1546     turn_count(-1),
1547     label(new QLabel(l,this)),
1548     icon(0),
1549     low_is_good(FALSE),
1550     prev_value(-123)
1551 {
1552     initHighlight();
1553 }
NetHackQtLabelledIcon(QWidget * parent,const char * l,const QPixmap & i)1554 NetHackQtLabelledIcon::NetHackQtLabelledIcon(QWidget* parent, const char* l, const QPixmap& i) :
1555     QWidget(parent),
1556     low_is_good(FALSE),
1557     turn_count(-1),
1558     label(new QLabel(l,this)),
1559     icon(new QLabel(this)),
1560     prev_value(-123)
1561 {
1562     setIcon(i);
1563     initHighlight();
1564 }
initHighlight()1565 void NetHackQtLabelledIcon::initHighlight()
1566 {
1567     const QPalette& pal=palette();
1568     const QColorGroup& pa=pal.normal();
1569     //QColorGroup good(white,darkGreen,pa.light(),pa.dark(),pa.mid(),white,pa.base());
1570     QColorGroup good(black,green,pa.light(),pa.dark(),pa.mid(),black,pa.base());
1571     QColorGroup bad(white,red,pa.light(),pa.dark(),pa.mid(),white,pa.base());
1572     hl_good=pal.copy();
1573     hl_good.setNormal(good);
1574     hl_good.setActive(good);
1575     hl_bad=pal.copy();
1576     hl_bad.setNormal(bad);
1577     hl_bad.setActive(bad);
1578 }
1579 
setLabel(const char * t,bool lower)1580 void NetHackQtLabelledIcon::setLabel(const char* t, bool lower)
1581 {
1582     if (!label) {
1583 	label=new QLabel(this);
1584 	label->setFont(font());
1585 	resizeEvent(0);
1586     }
1587     if (0!=strcmp(label->text(),t)) {
1588 	label->setText(t);
1589 	highlight(lower==low_is_good ? hl_good : hl_bad);
1590     }
1591 }
setLabel(const char * t,long v,long cv,const char * tail)1592 void NetHackQtLabelledIcon::setLabel(const char* t, long v, long cv, const char* tail)
1593 {
1594     char buf[BUFSZ];
1595     if (v==NoNum) {
1596 	Sprintf(buf,"%s%s",t,tail);
1597     } else {
1598 	Sprintf(buf,"%s%ld%s",t,v,tail);
1599     }
1600     setLabel(buf,cv<prev_value);
1601     prev_value=cv;
1602 }
setLabel(const char * t,long v,const char * tail)1603 void NetHackQtLabelledIcon::setLabel(const char* t, long v, const char* tail)
1604 {
1605     setLabel(t,v,v,tail);
1606 }
setIcon(const QPixmap & i)1607 void NetHackQtLabelledIcon::setIcon(const QPixmap& i)
1608 {
1609     if (icon) icon->setPixmap(i);
1610     else { icon=new QLabel(this); icon->setPixmap(i); resizeEvent(0); }
1611     icon->resize(i.width(),i.height());
1612 }
setFont(const QFont & f)1613 void NetHackQtLabelledIcon::setFont(const QFont& f)
1614 {
1615     QWidget::setFont(f);
1616     if (label) label->setFont(f);
1617 }
show()1618 void NetHackQtLabelledIcon::show()
1619 {
1620     if (!isVisible()) highlight(hl_bad);
1621     QWidget::show();
1622 }
highlightWhenChanging()1623 void NetHackQtLabelledIcon::highlightWhenChanging()
1624 {
1625     turn_count=0;
1626 }
lowIsGood()1627 void NetHackQtLabelledIcon::lowIsGood()
1628 {
1629     low_is_good=TRUE;
1630 }
dissipateHighlight()1631 void NetHackQtLabelledIcon::dissipateHighlight()
1632 {
1633     if (turn_count>0) {
1634 	turn_count--;
1635 	if (!turn_count)
1636 	    unhighlight();
1637     }
1638 }
highlight(const QPalette & hl)1639 void NetHackQtLabelledIcon::highlight(const QPalette& hl)
1640 {
1641     if (label) { // Surely it is?!
1642 	if (turn_count>=0) {
1643 	    label->setPalette(hl);
1644 	    turn_count=4;
1645 	    // `4' includes this turn, so dissipates after
1646 	    // 3 more keypresses.
1647 	} else {
1648 	    label->setPalette(palette());
1649 	}
1650     }
1651 }
unhighlight()1652 void NetHackQtLabelledIcon::unhighlight()
1653 {
1654     if (label) { // Surely it is?!
1655 	label->setPalette(palette());
1656     }
1657 }
resizeEvent(QResizeEvent *)1658 void NetHackQtLabelledIcon::resizeEvent(QResizeEvent*)
1659 {
1660     setAlignments();
1661 
1662     //int labw=label ? label->fontMetrics().width(label->text()) : 0;
1663     int labh=label ? label->fontMetrics().height() : 0;
1664     int icoh=icon ? icon->height() : 0;
1665     int h=icoh+labh;
1666     int icoy=(h>height() ? height()-labh-icoh : height()/2-h/2);
1667     int laby=icoy+icoh;
1668     if (icon) {
1669 	icon->setGeometry(0,icoy,width(),icoh);
1670     }
1671     if (label) {
1672 	label->setGeometry(0,laby,width(),labh);
1673     }
1674 }
1675 
setAlignments()1676 void NetHackQtLabelledIcon::setAlignments()
1677 {
1678     if (label) label->setAlignment(AlignHCenter|AlignVCenter);
1679     if (icon) icon->setAlignment(AlignHCenter|AlignVCenter);
1680 }
1681 
1682 static void
tryload(QPixmap & pm,const char * fn)1683 tryload(QPixmap& pm, const char* fn)
1684 {
1685     if (!pm.load(fn)) {
1686 	QString msg;
1687 	msg.sprintf("Cannot load \"%s\"", fn);
1688 	QMessageBox::warning(0, "IO Error", msg);
1689     }
1690 }
1691 
NetHackQtStatusWindow()1692 NetHackQtStatusWindow::NetHackQtStatusWindow() :
1693     // Notes:
1694     //  Alignment needs -2 init value, because -1 is an alignment.
1695     //  Armor Class is an schar, so 256 is out of range.
1696     //  Blank value is 0 and should never change.
1697     str(this,"STR"),
1698     dex(this,"DEX"),
1699     con(this,"CON"),
1700     intel(this,"INT"),
1701     wis(this,"WIS"),
1702     cha(this,"CHA"),
1703     name(this,"(name)"),
1704     dlevel(this,"(dlevel)"),
1705     gold(this,"Gold"),
1706     hp(this,"Hit Points"),
1707     power(this,"Power"),
1708     ac(this,"Armour Class"),
1709     level(this,"Level"),
1710     exp(this,"Experience"),
1711     align(this,"Alignment"),
1712     time(this,"Time"),
1713     score(this,"Score"),
1714     hunger(this,""),
1715     confused(this,"Confused"),
1716     sick_fp(this,"Sick"),
1717     sick_il(this,"Ill"),
1718     blind(this,"Blind"),
1719     stunned(this,"Stunned"),
1720     hallu(this,"Hallu"),
1721     encumber(this,""),
1722     hline1(this),
1723     hline2(this),
1724     hline3(this),
1725     first_set(TRUE)
1726 {
1727     p_str = QPixmap(str_xpm);
1728     p_str = QPixmap(str_xpm);
1729     p_dex = QPixmap(dex_xpm);
1730     p_con = QPixmap(cns_xpm);
1731     p_int = QPixmap(int_xpm);
1732     p_wis = QPixmap(wis_xpm);
1733     p_cha = QPixmap(cha_xpm);
1734 
1735     p_chaotic = QPixmap(chaotic_xpm);
1736     p_neutral = QPixmap(neutral_xpm);
1737     p_lawful = QPixmap(lawful_xpm);
1738 
1739     p_satiated = QPixmap(satiated_xpm);
1740     p_hungry = QPixmap(hungry_xpm);
1741 
1742     p_confused = QPixmap(confused_xpm);
1743     p_sick_fp = QPixmap(sick_fp_xpm);
1744     p_sick_il = QPixmap(sick_il_xpm);
1745     p_blind = QPixmap(blind_xpm);
1746     p_stunned = QPixmap(stunned_xpm);
1747     p_hallu = QPixmap(hallu_xpm);
1748 
1749     p_encumber[0] = QPixmap(slt_enc_xpm);
1750     p_encumber[1] = QPixmap(mod_enc_xpm);
1751     p_encumber[2] = QPixmap(hvy_enc_xpm);
1752     p_encumber[3] = QPixmap(ext_enc_xpm);
1753     p_encumber[4] = QPixmap(ovr_enc_xpm);
1754 
1755     str.setIcon(p_str);
1756     dex.setIcon(p_dex);
1757     con.setIcon(p_con);
1758     intel.setIcon(p_int);
1759     wis.setIcon(p_wis);
1760     cha.setIcon(p_cha);
1761 
1762     align.setIcon(p_neutral);
1763     hunger.setIcon(p_hungry);
1764 
1765     confused.setIcon(p_confused);
1766     sick_fp.setIcon(p_sick_fp);
1767     sick_il.setIcon(p_sick_il);
1768     blind.setIcon(p_blind);
1769     stunned.setIcon(p_stunned);
1770     hallu.setIcon(p_hallu);
1771 
1772     encumber.setIcon(p_encumber[0]);
1773 
1774     hline1.setFrameStyle(QFrame::HLine|QFrame::Sunken);
1775     hline2.setFrameStyle(QFrame::HLine|QFrame::Sunken);
1776     hline3.setFrameStyle(QFrame::HLine|QFrame::Sunken);
1777     hline1.setLineWidth(1);
1778     hline2.setLineWidth(1);
1779     hline3.setLineWidth(1);
1780 
1781     connect(qt_settings,SIGNAL(fontChanged()),this,SLOT(doUpdate()));
1782     doUpdate();
1783 }
1784 
doUpdate()1785 void NetHackQtStatusWindow::doUpdate()
1786 {
1787     const QFont& large=qt_settings->largeFont();
1788     name.setFont(large);
1789     dlevel.setFont(large);
1790 
1791     const QFont& normal=qt_settings->normalFont();
1792     str.setFont(normal);
1793     dex.setFont(normal);
1794     con.setFont(normal);
1795     intel.setFont(normal);
1796     wis.setFont(normal);
1797     cha.setFont(normal);
1798     gold.setFont(normal);
1799     hp.setFont(normal);
1800     power.setFont(normal);
1801     ac.setFont(normal);
1802     level.setFont(normal);
1803     exp.setFont(normal);
1804     align.setFont(normal);
1805     time.setFont(normal);
1806     score.setFont(normal);
1807     hunger.setFont(normal);
1808     confused.setFont(normal);
1809     sick_fp.setFont(normal);
1810     sick_il.setFont(normal);
1811     blind.setFont(normal);
1812     stunned.setFont(normal);
1813     hallu.setFont(normal);
1814     encumber.setFont(normal);
1815 
1816     updateStats();
1817 }
1818 
Widget()1819 QWidget* NetHackQtStatusWindow::Widget() { return this; }
1820 
Clear()1821 void NetHackQtStatusWindow::Clear()
1822 {
1823 }
Display(bool block)1824 void NetHackQtStatusWindow::Display(bool block)
1825 {
1826 }
CursorTo(int,int y)1827 void NetHackQtStatusWindow::CursorTo(int,int y)
1828 {
1829     cursy=y;
1830 }
PutStr(int attr,const char * text)1831 void NetHackQtStatusWindow::PutStr(int attr, const char* text)
1832 {
1833     // do a complete update when line 0 is done (as per X11 fancy status)
1834     if (cursy==0) updateStats();
1835 }
1836 
resizeEvent(QResizeEvent *)1837 void NetHackQtStatusWindow::resizeEvent(QResizeEvent*)
1838 {
1839     const float SP_name=0.13; //     <Name> the <Class> (large)
1840     const float SP_dlev=0.13; //   Level 3 in The Dungeons of Doom (large)
1841     const float SP_atr1=0.25; //  STR   DEX   CON   INT   WIS   CHA
1842     const float SP_hln1=0.02; // ---
1843     const float SP_atr2=0.09; //  Au    HP    PW    AC    LVL   EXP
1844     const float SP_hln2=0.02; // ---
1845     const float SP_time=0.09; //      time    score
1846     const float SP_hln3=0.02; // ---
1847     const float SP_stat=0.25; // Alignment, Poisoned, Hungry, Sick, etc.
1848 
1849     int h=height();
1850     int x=0,y=0;
1851     double space=1.0;
1852 
1853     int iw; // Width of an item across line
1854     int lh; // Height of a line of values
1855 
1856     lh=int(h*SP_name);
1857     name.setGeometry(0,0,width(),lh); y+=lh;
1858     lh=int(h*SP_dlev);
1859     dlevel.setGeometry(0,y,width(),lh); y+=lh;
1860 
1861     lh=int(h*SP_hln1);
1862     hline1.setGeometry(0,y,width(),lh); y+=lh;
1863 
1864     lh=int(h*SP_atr1);
1865     iw=width()/6;
1866     str.setGeometry(x,y,iw,lh); x+=iw;
1867     dex.setGeometry(x,y,iw,lh); x+=iw;
1868     con.setGeometry(x,y,iw,lh); x+=iw;
1869     intel.setGeometry(x,y,iw,lh); x+=iw;
1870     wis.setGeometry(x,y,iw,lh); x+=iw;
1871     cha.setGeometry(x,y,iw,lh); x+=iw;
1872     x=0; y+=lh;
1873 
1874     lh=int(h*SP_hln2);
1875     hline2.setGeometry(0,y,width(),lh); y+=lh;
1876 
1877     lh=int(h*SP_atr2);
1878     iw=width()/6;
1879     gold.setGeometry(x,y,iw,lh); x+=iw;
1880     hp.setGeometry(x,y,iw,lh); x+=iw;
1881     power.setGeometry(x,y,iw,lh); x+=iw;
1882     ac.setGeometry(x,y,iw,lh); x+=iw;
1883     level.setGeometry(x,y,iw,lh); x+=iw;
1884     exp.setGeometry(x,y,iw,lh); x+=iw;
1885     x=0; y+=lh;
1886 
1887     lh=int(h*SP_hln3);
1888     hline3.setGeometry(0,y,width(),lh); y+=lh;
1889 
1890     lh=int(h*SP_time);
1891     iw=width()/3; x+=iw/2;
1892     time.setGeometry(x,y,iw,lh); x+=iw;
1893     score.setGeometry(x,y,iw,lh); x+=iw;
1894     x=0; y+=lh;
1895 
1896     lh=int(h*SP_stat);
1897     iw=width()/9;
1898     align.setGeometry(x,y,iw,lh); x+=iw;
1899     hunger.setGeometry(x,y,iw,lh); x+=iw;
1900     confused.setGeometry(x,y,iw,lh); x+=iw;
1901     sick_fp.setGeometry(x,y,iw,lh); x+=iw;
1902     sick_il.setGeometry(x,y,iw,lh); x+=iw;
1903     blind.setGeometry(x,y,iw,lh); x+=iw;
1904     stunned.setGeometry(x,y,iw,lh); x+=iw;
1905     hallu.setGeometry(x,y,iw,lh); x+=iw;
1906     encumber.setGeometry(x,y,iw,lh); x+=iw;
1907     x=0; y+=lh;
1908 }
1909 
1910 
1911 /*
1912  * Set all widget values to a null string.  This is used after all spacings
1913  * have been calculated so that when the window is popped up we don't get all
1914  * kinds of funny values being displayed.
1915  */
nullOut()1916 void NetHackQtStatusWindow::nullOut()
1917 {
1918 }
1919 
fadeHighlighting()1920 void NetHackQtStatusWindow::fadeHighlighting()
1921 {
1922     name.dissipateHighlight();
1923     dlevel.dissipateHighlight();
1924 
1925     str.dissipateHighlight();
1926     dex.dissipateHighlight();
1927     con.dissipateHighlight();
1928     intel.dissipateHighlight();
1929     wis.dissipateHighlight();
1930     cha.dissipateHighlight();
1931 
1932     gold.dissipateHighlight();
1933     hp.dissipateHighlight();
1934     power.dissipateHighlight();
1935     ac.dissipateHighlight();
1936     level.dissipateHighlight();
1937     exp.dissipateHighlight();
1938     align.dissipateHighlight();
1939 
1940     time.dissipateHighlight();
1941     score.dissipateHighlight();
1942 
1943     hunger.dissipateHighlight();
1944     confused.dissipateHighlight();
1945     sick_fp.dissipateHighlight();
1946     sick_il.dissipateHighlight();
1947     blind.dissipateHighlight();
1948     stunned.dissipateHighlight();
1949     hallu.dissipateHighlight();
1950     encumber.dissipateHighlight();
1951 }
1952 
1953 /*
1954  * Update the displayed status.  The current code in botl.c updates
1955  * two lines of information.  Both lines are always updated one after
1956  * the other.  So only do our update when we update the second line.
1957  *
1958  * Information on the first line:
1959  *    name, attributes, alignment, score
1960  *
1961  * Information on the second line:
1962  *    dlvl, gold, hp, power, ac, {level & exp or HD **}
1963  *    status (hunger, conf, halu, stun, sick, blind), time, encumbrance
1964  *
1965  * [**] HD is shown instead of level and exp if mtimedone is non-zero.
1966  */
updateStats()1967 void NetHackQtStatusWindow::updateStats()
1968 {
1969     if (!isVisible()) return;
1970 
1971     char buf[BUFSZ];
1972 
1973     if (cursy != 0) return;    /* do a complete update when line 0 is done */
1974 
1975     if (ACURR(A_STR) > 118) {
1976 	Sprintf(buf,"STR:%d",ACURR(A_STR)-100);
1977     } else if (ACURR(A_STR)==118) {
1978 	Sprintf(buf,"STR:18/**");
1979     } else if(ACURR(A_STR) > 18) {
1980 	Sprintf(buf,"STR:18/%02d",ACURR(A_STR)-18);
1981     } else {
1982 	Sprintf(buf,"STR:%d",ACURR(A_STR));
1983     }
1984     str.setLabel(buf,NetHackQtLabelledIcon::NoNum,ACURR(A_STR));
1985 
1986     dex.setLabel("DEX:",(long)ACURR(A_DEX));
1987     con.setLabel("CON:",(long)ACURR(A_CON));
1988     intel.setLabel("INT:",(long)ACURR(A_INT));
1989     wis.setLabel("WIS:",(long)ACURR(A_WIS));
1990     cha.setLabel("CHA:",(long)ACURR(A_CHA));
1991     const char* hung=hu_stat[u.uhs];
1992     if (hung[0]==' ') {
1993 	hunger.hide();
1994     } else {
1995 	hunger.setIcon(u.uhs ? p_hungry : p_satiated);
1996 	hunger.setLabel(hung);
1997 	hunger.show();
1998     }
1999     if (Confusion) confused.show(); else confused.hide();
2000     if (Sick) {
2001 	if (u.usick_type & SICK_VOMITABLE) {
2002 	    sick_fp.show();
2003 	} else {
2004 	    sick_fp.hide();
2005 	}
2006 	if (u.usick_type & SICK_NONVOMITABLE) {
2007 	    sick_il.show();
2008 	} else {
2009 	    sick_il.hide();
2010 	}
2011     } else {
2012 	sick_fp.hide();
2013 	sick_il.hide();
2014     }
2015     if (Blind) blind.show(); else blind.hide();
2016     if (Stunned) stunned.show(); else stunned.hide();
2017     if (Hallucination) hallu.show(); else hallu.hide();
2018     const char* enc=enc_stat[near_capacity()];
2019     if (enc[0]==' ' || !enc[0]) {
2020 	encumber.hide();
2021     } else {
2022 	encumber.setIcon(p_encumber[near_capacity()-1]);
2023 	encumber.setLabel(enc);
2024 	encumber.show();
2025     }
2026     Strcpy(buf, plname);
2027     if ('a' <= buf[0] && buf[0] <= 'z') buf[0] += 'A'-'a';
2028     Strcat(buf, " the ");
2029     if (u.mtimedone) {
2030 	char mname[BUFSZ];
2031 	int k = 0;
2032 
2033 	Strcpy(mname, mons[u.umonnum].mname);
2034 	while(mname[k] != 0) {
2035 	    if ((k == 0 || (k > 0 && mname[k-1] == ' '))
2036 	     && 'a' <= mname[k] && mname[k] <= 'z')
2037 	    {
2038 		mname[k] += 'A' - 'a';
2039 	    }
2040 	    k++;
2041 	}
2042 	Strcat(buf, mname);
2043     } else {
2044 	Strcat(buf, rank_of(u.ulevel, pl_character[0], ::flags.female));
2045     }
2046     name.setLabel(buf,NetHackQtLabelledIcon::NoNum,u.ulevel);
2047 
2048     if (describe_level(buf)) {
2049 	dlevel.setLabel(buf,TRUE);
2050     } else {
2051 	Sprintf(buf, "%s, level ", dungeons[u.uz.dnum].dname);
2052 	dlevel.setLabel(buf,(long)depth(&u.uz));
2053     }
2054 
2055     gold.setLabel("Au:",(long)u.ugold);
2056     if (u.mtimedone) {
2057 	// You're a monster!
2058 
2059 	Sprintf(buf, "/%d", u.mhmax);
2060 	hp.setLabel("HP:",u.mh  > 0 ? u.mh  : 0,buf);
2061 	level.setLabel("HD:",(long)mons[u.umonnum].mlevel);
2062     } else {
2063 	// You're normal.
2064 
2065 	Sprintf(buf, "/%d", u.uhpmax);
2066 	hp.setLabel("HP:",u.uhp > 0 ? u.uhp : 0,buf);
2067 	level.setLabel("Level:",(long)u.ulevel);
2068     }
2069     Sprintf(buf, "/%d", u.uenmax);
2070     power.setLabel("Pow:",u.uen,buf);
2071     ac.setLabel("AC:",(long)u.uac);
2072 #ifdef EXP_ON_BOTL
2073     if (::flags.showexp) {
2074 	exp.setLabel("Exp:",(long)u.uexp);
2075     } else
2076 #endif
2077     {
2078 	exp.setLabel("");
2079     }
2080     if (u.ualign.type==A_CHAOTIC) {
2081 	align.setIcon(p_chaotic);
2082 	align.setLabel("Chaotic");
2083     } else if (u.ualign.type==A_NEUTRAL) {
2084 	align.setIcon(p_neutral);
2085 	align.setLabel("Neutral");
2086     } else {
2087 	align.setIcon(p_lawful);
2088 	align.setLabel("Lawful");
2089     }
2090 
2091     if (::flags.time) time.setLabel("Time:",(long)moves);
2092     else time.setLabel("");
2093 #ifdef SCORE_ON_BOTL
2094     if (::flags.showscore) {
2095 	score.setLabel("Score:",(long)botl_score());
2096     } else
2097 #endif
2098     {
2099 	score.setLabel("");
2100     }
2101 
2102     if (first_set)
2103     {
2104 	first_set=FALSE;
2105 
2106 	name.highlightWhenChanging();
2107 	dlevel.highlightWhenChanging();
2108 
2109 	str.highlightWhenChanging();
2110 	dex.highlightWhenChanging();
2111 	con.highlightWhenChanging();
2112 	intel.highlightWhenChanging();
2113 	wis.highlightWhenChanging();
2114 	cha.highlightWhenChanging();
2115 
2116 	gold.highlightWhenChanging();
2117 	hp.highlightWhenChanging();
2118 	power.highlightWhenChanging();
2119 	ac.highlightWhenChanging(); ac.lowIsGood();
2120 	level.highlightWhenChanging();
2121 	exp.highlightWhenChanging();
2122 	align.highlightWhenChanging();
2123 
2124 	//time.highlightWhenChanging();
2125 	score.highlightWhenChanging();
2126 
2127 	hunger.highlightWhenChanging();
2128 	confused.highlightWhenChanging();
2129 	sick_fp.highlightWhenChanging();
2130 	sick_il.highlightWhenChanging();
2131 	blind.highlightWhenChanging();
2132 	stunned.highlightWhenChanging();
2133 	hallu.highlightWhenChanging();
2134 	encumber.highlightWhenChanging();
2135     }
2136 }
2137 
2138 /*
2139  * Turn off hilighted status values after a certain amount of turns.
2140  */
checkTurnEvents()2141 void NetHackQtStatusWindow::checkTurnEvents()
2142 {
2143 }
2144 
2145 
2146 
NetHackQtMenuDialog()2147 NetHackQtMenuDialog::NetHackQtMenuDialog() :
2148     QDialog(0,0,FALSE)
2149 {
2150 }
2151 
resizeEvent(QResizeEvent *)2152 void NetHackQtMenuDialog::resizeEvent(QResizeEvent*)
2153 {
2154     emit Resized();
2155 }
2156 
Accept()2157 void NetHackQtMenuDialog::Accept()
2158 {
2159     accept();
2160 }
2161 
Reject()2162 void NetHackQtMenuDialog::Reject()
2163 {
2164     reject();
2165 }
2166 
SetResult(int r)2167 void NetHackQtMenuDialog::SetResult(int r)
2168 {
2169     setResult(r);
2170 }
2171 
done(int i)2172 void NetHackQtMenuDialog::done(int i)
2173 {
2174     setResult(i);
2175     qApp->exit_loop();
2176 }
2177 
2178 // Table view columns:
2179 //
2180 // [pick-count] [accel] [glyph] [string]
2181 //
2182 // Maybe accel should be near string.  We'll see.
2183 // pick-count normally blank.
2184 //   double-clicking or click-on-count gives pop-up entry
2185 // string is green when selected
2186 //
NetHackQtMenuWindow(NetHackQtKeyBuffer & ks)2187 NetHackQtMenuWindow::NetHackQtMenuWindow(NetHackQtKeyBuffer& ks) :
2188     QTableView(),
2189     keysource(ks),
2190     dialog(new NetHackQtMenuDialog()),
2191     pressed(-1),
2192     prompt(0)
2193 {
2194     setNumCols(4);
2195     setCellHeight(QMAX(qt_settings->glyphs().height()+1,fontMetrics().height()));
2196     setBackgroundColor(lightGray);
2197     setFrameStyle(Panel|Sunken);
2198     setLineWidth(2);
2199 
2200     int x=0;
2201 
2202     ok=new QPushButton("Ok",dialog);
2203     connect(ok,SIGNAL(clicked()),dialog,SLOT(accept()));
2204 
2205     cancel=new QPushButton("Cancel",dialog);
2206     connect(cancel,SIGNAL(clicked()),dialog,SLOT(reject()));
2207 
2208     all=new QPushButton("All",dialog);
2209     connect(all,SIGNAL(clicked()),this,SLOT(All()));
2210 
2211     none=new QPushButton("None",dialog);
2212     connect(none,SIGNAL(clicked()),this,SLOT(ChooseNone()));
2213 
2214     invert=new QPushButton("Invert",dialog);
2215     connect(invert,SIGNAL(clicked()),this,SLOT(Invert()));
2216 
2217     search=new QPushButton("Search",dialog);
2218     connect(search,SIGNAL(clicked()),this,SLOT(Search()));
2219 
2220     QPoint pos(0,ok->height());
2221     recreate(dialog,0,pos);
2222     prompt.recreate(dialog,0,pos);
2223 
2224     setBackgroundColor(lightGray);
2225 
2226     connect(dialog,SIGNAL(Resized()),this,SLOT(Layout()));
2227 
2228     setTableFlags(Tbl_autoHScrollBar|Tbl_autoVScrollBar
2229 	    |Tbl_smoothScrolling|Tbl_clipCellPainting);
2230     setFocusPolicy(StrongFocus);
2231 }
2232 
~NetHackQtMenuWindow()2233 NetHackQtMenuWindow::~NetHackQtMenuWindow()
2234 {
2235     // Remove from dialog before we destruct it
2236     recreate(0,0,QPoint(0,0));
2237     delete dialog;
2238 }
2239 
focusInEvent(QFocusEvent *)2240 void NetHackQtMenuWindow::focusInEvent(QFocusEvent *)
2241 {
2242     // Don't repaint at all, since nothing is using the focus colour
2243 }
focusOutEvent(QFocusEvent *)2244 void NetHackQtMenuWindow::focusOutEvent(QFocusEvent *)
2245 {
2246     // Don't repaint at all, since nothing is using the focus colour
2247 }
2248 
cellWidth(int col)2249 int NetHackQtMenuWindow::cellWidth(int col)
2250 {
2251     switch (col) {
2252      case 0:
2253 	return 20;
2254     break; case 1:
2255 	return 16;
2256     break; case 2:
2257 	return qt_settings->glyphs().width();
2258     break; case 3:
2259 	return str_width;
2260     }
2261     impossible("Extra column (#%d) in MenuWindow",col);
2262     return 0;
2263 }
2264 
Widget()2265 QWidget* NetHackQtMenuWindow::Widget() { return dialog; }
2266 
StartMenu()2267 void NetHackQtMenuWindow::StartMenu()
2268 {
2269     setNumRows((itemcount=0));
2270     str_width=200;
2271     str_fixed=FALSE;
2272     next_accel=0;
2273     has_glyphs=FALSE;
2274 }
2275 
MenuItem()2276 NetHackQtMenuWindow::MenuItem::MenuItem() :
2277     str(0)
2278 {
2279 }
2280 
~MenuItem()2281 NetHackQtMenuWindow::MenuItem::~MenuItem()
2282 {
2283     if (str) free((void*)str);
2284 }
2285 
2286 #define STR_MARGIN 4
2287 
AddMenu(int glyph,const ANY_P * identifier,char ch,char gch,int attr,const char * str,bool presel)2288 void NetHackQtMenuWindow::AddMenu(int glyph, const ANY_P* identifier,
2289 	char ch, char gch, int attr, const char* str, bool presel)
2290 {
2291     if (!ch && identifier->a_void!=0) {
2292 	// Supply a keyboard accelerator.  Limited supply.
2293 	static char accel[]="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
2294 	if (accel[next_accel]) {
2295 	    ch=accel[next_accel++];
2296 	}
2297     }
2298 
2299     if (item.size()<itemcount+1) {
2300 	item.resize(itemcount*4+10);
2301     }
2302     item[itemcount].glyph=glyph;
2303     item[itemcount].identifier=*identifier;
2304     item[itemcount].ch=ch;
2305     item[itemcount].attr=attr;
2306     item[itemcount].str=strdup(str);
2307     item[itemcount].selected=presel;
2308     item[itemcount].count=-1;
2309     ++itemcount;
2310 
2311     str_fixed=str_fixed || strstr(str,"     ");
2312     if (glyph!=NO_GLYPH) has_glyphs=TRUE;
2313 }
EndMenu(const char * p)2314 void NetHackQtMenuWindow::EndMenu(const char* p)
2315 {
2316     prompt.setText(p ? p : "");
2317 }
Layout()2318 void NetHackQtMenuWindow::Layout()
2319 {
2320     int butw=totalWidth()/6; // 6 buttons
2321     int buth=fontMetrics().height()+8; // 8 for spacing & mitres
2322     int prompth=(prompt.text().isNull() ? 0 : buth);
2323 
2324     prompt.setGeometry(6,buth,dialog->width()-6,prompth);
2325     int h=dialog->height()-buth-prompth;
2326     setGeometry(0,buth+prompth, dialog->width(), h);
2327 
2328     // Below, we take care to use up full width
2329     int x=0;
2330     ok->setGeometry(x,0,butw,buth); x+=butw; butw=(dialog->width()-x)/5;
2331     cancel->setGeometry(x,0,butw,buth); x+=butw; butw=(dialog->width()-x)/4;
2332     all->setGeometry(x,0,butw,buth); x+=butw; butw=(dialog->width()-x)/3;
2333     none->setGeometry(x,0,butw,buth); x+=butw; butw=(dialog->width()-x)/2;
2334     invert->setGeometry(x,0,butw,buth); x+=butw; butw=(dialog->width()-x)/1;
2335     search->setGeometry(x,0,butw,buth);
2336 }
SelectMenu(int h,MENU_ITEM_P ** menu_list)2337 int NetHackQtMenuWindow::SelectMenu(int h, MENU_ITEM_P **menu_list)
2338 {
2339     setFont(str_fixed ?
2340 	qt_settings->normalFixedFont() : qt_settings->normalFont());
2341 
2342     for (int i=0; i<itemcount; i++) {
2343 	str_width=QMAX(str_width,
2344 	    STR_MARGIN+fontMetrics().width(item[i].str));
2345     }
2346 
2347     setCellHeight(QMAX(qt_settings->glyphs().height()+1,fontMetrics().height()));
2348     setNumRows(itemcount);
2349 
2350     int buth=fontMetrics().height()+8; // 8 for spacing & mitres
2351 
2352     how=h;
2353 
2354     ok->setEnabled(how!=PICK_ONE);ok->setDefault(how!=PICK_ONE);
2355     cancel->setEnabled(how!=PICK_NONE);
2356     all->setEnabled(how==PICK_ANY);
2357     none->setEnabled(how==PICK_ANY);
2358     invert->setEnabled(how==PICK_ANY);
2359     search->setEnabled(how!=PICK_NONE);
2360 
2361     // 20 allows for scrollbar or spacing
2362     // 4 for frame borders
2363     int mh = QApplication::desktop()->height()*3/5;
2364     dialog->resize(totalWidth()+20,
2365 	QMIN(totalHeight(), mh)+buth+4+(prompt.text().isNull() ? 0 : buth));
2366 
2367     dialog->SetResult(-1);
2368     centerOnMain(dialog);
2369     dialog->show();
2370     setFocus();
2371     while (dialog->result()<0) {
2372 	qApp->enter_loop();
2373 	// changed the defaults below to the values in wintype.h 000119 - azy
2374 	if (dialog->result()<0 && !keysource.Empty()) {
2375 	    char k=keysource.GetAscii();
2376 	    k=map_menu_cmd(k); /* added 000119 - azy */
2377 	    if (k=='\033')
2378 		dialog->Reject();
2379 	    else if (k=='\r' || k=='\n' || k==' ')
2380 		dialog->Accept();
2381 	    else if (k==MENU_SEARCH)
2382 		Search();
2383 	    else if (k==MENU_SELECT_ALL)
2384 		All();
2385 	    else if (k==MENU_INVERT_ALL)
2386 		Invert();
2387 	    else if (k==MENU_UNSELECT_ALL)
2388 		ChooseNone();
2389 	    else {
2390 		for (int i=0; i<itemcount; i++) {
2391 		    if (item[i].ch==k)
2392 			ToggleSelect(i);
2393 		}
2394 	    }
2395 	}
2396     }
2397     dialog->hide();
2398     int result=dialog->result();
2399 
2400     // Consume ^M (which QDialog steals for default button)
2401     while (!keysource.Empty() &&
2402 	(keysource.TopAscii()=='\n' || keysource.TopAscii()=='\r'))
2403 	keysource.GetAscii();
2404 
2405     *menu_list=0;
2406     if (result>0 && how!=PICK_NONE) {
2407 	if (how==PICK_ONE) {
2408 	    int i;
2409 	    for (i=0; i<itemcount && !item[i].selected; i++)
2410 		;
2411 	    if (i<itemcount) {
2412 		*menu_list=(MENU_ITEM_P*)malloc(sizeof(MENU_ITEM_P));
2413 		(*menu_list)[0].item=item[i].identifier;
2414 		(*menu_list)[0].count=item[i].count;
2415 		return 1;
2416 	    } else {
2417 		return 0;
2418 	    }
2419 	} else {
2420 	    int count=0;
2421 	    for (int i=0; i<itemcount; i++)
2422 		if (item[i].selected) count++;
2423 	    if (count) {
2424 		*menu_list=(MENU_ITEM_P*)malloc(count*sizeof(MENU_ITEM_P));
2425 		int j=0;
2426 		for (int i=0; i<itemcount; i++) {
2427 		    if (item[i].selected) {
2428 			(*menu_list)[j].item=item[i].identifier;
2429 			(*menu_list)[j].count=item[i].count;
2430 			j++;
2431 		    }
2432 		}
2433 		return count;
2434 	    } else {
2435 		return 0;
2436 	    }
2437 	}
2438     } else {
2439 	return -1;
2440     }
2441 }
keyPressEvent(QKeyEvent * event)2442 void NetHackQtMenuWindow::keyPressEvent(QKeyEvent* event)
2443 {
2444     if (viewHeight() < totalHeight() && !(event->state()&ShiftButton)) {
2445 	if (event->key()==Key_Prior) {
2446 	    setYOffset(yOffset()-viewHeight());
2447 	} else if (event->key()==Key_Next) {
2448 	    setYOffset(yOffset()+viewHeight());
2449 	} else {
2450 	    event->ignore();
2451 	}
2452     } else {
2453 	event->ignore();
2454     }
2455 }
2456 
All()2457 void NetHackQtMenuWindow::All()
2458 {
2459     for (int i=0; i<itemcount; i++) {
2460 	if (!item[i].selected) ToggleSelect(i);
2461     }
2462 }
ChooseNone()2463 void NetHackQtMenuWindow::ChooseNone()
2464 {
2465     for (int i=0; i<itemcount; i++) {
2466 	if (item[i].selected) ToggleSelect(i);
2467     }
2468 }
Invert()2469 void NetHackQtMenuWindow::Invert()
2470 {
2471     for (int i=0; i<itemcount; i++) {
2472 	ToggleSelect(i);
2473     }
2474 }
Search()2475 void NetHackQtMenuWindow::Search()
2476 {
2477     NetHackQtStringRequestor requestor(keysource,"Search for:");
2478     char line[256];
2479     if (requestor.Get(line)) {
2480 	for (int i=0; i<itemcount; i++) {
2481 	    if (strstr(item[i].str,line))
2482 		ToggleSelect(i);
2483 	}
2484     }
2485 }
ToggleSelect(int i)2486 void NetHackQtMenuWindow::ToggleSelect(int i)
2487 {
2488     if (item[i].Selectable()) {
2489 	item[i].selected = !item[i].selected;
2490 	updateCell(i,3);
2491 	if (how==PICK_ONE) {
2492 	    dialog->Accept();
2493 	}
2494     }
2495 }
2496 
2497 
paintCell(QPainter * painter,int row,int col)2498 void NetHackQtMenuWindow::paintCell(QPainter* painter, int row, int col)
2499 {
2500     // [pick-count] [accel] [glyph] [string]
2501 
2502     MenuItem& i = item[row];
2503 
2504     painter->setPen(black);
2505     painter->setFont(font());
2506 
2507     if (i.selected) {
2508 	painter->setPen(darkGreen);
2509     }
2510 
2511     switch (col) {
2512      case 0:
2513 	if (i.count>=0) {
2514 	    char text[16];
2515 	    sprintf(text,"%d",i.count);
2516 	    painter->drawText(0,0,cellWidth(col),cellHeight(),
2517 		AlignHCenter|AlignVCenter,text);
2518 	}
2519     break; case 1:
2520 	if (i.ch>=0) {
2521 	    char text[2]={i.ch,0};
2522 	    painter->drawText(0,0,cellWidth(col),cellHeight(),
2523 		AlignHCenter|AlignVCenter,text);
2524 	}
2525     break; case 2:
2526 	if (i.glyph!=NO_GLYPH) {
2527 	    // Centered in height
2528 	    int y=(cellHeight()-qt_settings->glyphs().height())/2;
2529 	    if (y<0) y=0;
2530 	    qt_settings->glyphs().drawGlyph(*painter, i.glyph, 0, y);
2531 	}
2532     break; case 3:
2533 	// XXX should qt_settings have ALL the various fonts
2534 	QFont newfont=font();
2535 
2536 	if (i.attr) {
2537 	    switch(i.attr) {
2538 	     case ATR_ULINE:
2539 		newfont.setUnderline(TRUE);
2540 	    break; case ATR_BOLD:
2541 		painter->setPen(red);
2542 	    break; case ATR_BLINK:
2543 		newfont.setItalic(TRUE);
2544 	    break; case ATR_INVERSE:
2545 		newfont=qt_settings->largeFont();
2546 		newfont.setWeight(QFont::Bold);
2547 
2548 		if (i.selected) {
2549 		    painter->setPen(blue);
2550 		} else {
2551 		    painter->setPen(darkBlue);
2552 		}
2553 	    }
2554 	}
2555 	painter->setFont(newfont);
2556 
2557 	painter->drawText(STR_MARGIN,0,cellWidth(col),cellHeight(),
2558 	    AlignLeft|AlignVCenter,i.str);
2559     }
2560 }
2561 
mousePressEvent(QMouseEvent * event)2562 void NetHackQtMenuWindow::mousePressEvent(QMouseEvent* event)
2563 {
2564     int col=findCol(event->pos().x());
2565     int row=findRow(event->pos().y());
2566 
2567     if (col<0 || row<0 || !item[row].Selectable()) return;
2568 
2569     if (how!=PICK_NONE) {
2570 	if (col==0) {
2571 	    // Changing count.
2572 	    NetHackQtStringRequestor requestor(keysource,"Count:");
2573 	    char buf[BUFSZ];
2574 
2575 	    if (item[row].count>0)
2576 		Sprintf(buf,"%d", item[row].count);
2577 	    else
2578 		Sprintf(buf,"");
2579 
2580 	    requestor.SetDefault(buf);
2581 	    if (requestor.Get(buf)) {
2582 		item[row].count=atoi(buf);
2583 		if (item[row].count==0) {
2584 		    item[row].count=-1;
2585 		    if (item[row].selected) ToggleSelect(row);
2586 		} else {
2587 		    if (!item[row].selected) ToggleSelect(row);
2588 		}
2589 		updateCell(row,0);
2590 	    }
2591 	} else {
2592 	    pressed=row;
2593 	    was_sel=item[row].selected;
2594 	    ToggleSelect(row);
2595 	}
2596     }
2597 }
mouseReleaseEvent(QMouseEvent * event)2598 void NetHackQtMenuWindow::mouseReleaseEvent(QMouseEvent* event)
2599 {
2600     if (pressed>=0) {
2601 	int p=pressed;
2602 	pressed=-1;
2603 	updateCell(p,3);
2604     }
2605 }
mouseMoveEvent(QMouseEvent * event)2606 void NetHackQtMenuWindow::mouseMoveEvent(QMouseEvent* event)
2607 {
2608     if (pressed>=0) {
2609 	int col=findCol(event->pos().x());
2610 	int row=findRow(event->pos().y());
2611 
2612 	if (row>=0 && col>=0) {
2613 	    if (pressed!=row) {
2614 		// reset to initial state
2615 		if (item[pressed].selected!=was_sel)
2616 		    ToggleSelect(pressed);
2617 	    } else {
2618 		// reset to new state
2619 		if (item[pressed].selected==was_sel)
2620 		    ToggleSelect(pressed);
2621 	    }
2622 	}
2623     }
2624 }
2625 
2626 
2627 class NetHackQtTextListBox : public QListBox {
2628 public:
NetHackQtTextListBox(QWidget * parent)2629     NetHackQtTextListBox(QWidget* parent) : QListBox(parent) { }
2630 
TotalWidth()2631     int TotalWidth()
2632     {
2633 	doLayout();
2634 	return contentsWidth();
2635     }
TotalHeight()2636     int TotalHeight()
2637     {
2638 	doLayout();
2639 	return contentsHeight();
2640     }
2641 
setFont(const QFont & font)2642     virtual void setFont(const QFont &font)
2643     {
2644 	QListBox::setFont(font);
2645     }
keyPressEvent(QKeyEvent * e)2646     void keyPressEvent(QKeyEvent* e)
2647     {
2648 	QListBox::keyPressEvent(e);
2649     }
2650 };
2651 
2652 
2653 QPixmap* NetHackQtRIP::pixmap=0;
2654 
NetHackQtRIP(QWidget * parent)2655 NetHackQtRIP::NetHackQtRIP(QWidget* parent) :
2656     QWidget(parent)
2657 {
2658     if (!pixmap) {
2659 	pixmap=new QPixmap;
2660 	tryload(*pixmap, "rip.xpm");
2661     }
2662     riplines=0;
2663     resize(pixmap->width(),pixmap->height());
2664     setFont(QFont("times",12)); // XXX may need to be configurable
2665 }
2666 
setLines(char ** l,int n)2667 void NetHackQtRIP::setLines(char** l, int n)
2668 {
2669     line=l;
2670     riplines=n;
2671 }
2672 
paintEvent(QPaintEvent * event)2673 void NetHackQtRIP::paintEvent(QPaintEvent* event)
2674 {
2675     if ( riplines ) {
2676 	int pix_x=(width()-pixmap->width())/2;
2677 	int pix_y=(height()-pixmap->height())/2;
2678 
2679 	// XXX positions based on RIP image
2680 	int rip_text_x=pix_x+156;
2681 	int rip_text_y=pix_y+67;
2682 	int rip_text_h=94/riplines;
2683 
2684 	QPainter painter;
2685 	painter.begin(this);
2686 	painter.drawPixmap(pix_x,pix_y,*pixmap);
2687 	for (int i=0; i<riplines; i++) {
2688 	    painter.drawText(rip_text_x-i/2,rip_text_y+i*rip_text_h,
2689 		1,1,DontClip|AlignHCenter,line[i]);
2690 	}
2691 	painter.end();
2692     }
2693 }
2694 
NetHackQtTextWindow(NetHackQtKeyBuffer & ks)2695 NetHackQtTextWindow::NetHackQtTextWindow(NetHackQtKeyBuffer& ks) :
2696     QDialog(0,0,FALSE),
2697     keysource(ks),
2698     use_rip(FALSE),
2699     str_fixed(FALSE),
2700     ok("Dismiss",this),
2701     search("Search",this),
2702     lines(new NetHackQtTextListBox(this)),
2703     rip(this)
2704 {
2705     ok.setDefault(TRUE);
2706     connect(&ok,SIGNAL(clicked()),this,SLOT(accept()));
2707     connect(&search,SIGNAL(clicked()),this,SLOT(Search()));
2708 
2709     connect(qt_settings,SIGNAL(fontChanged()),this,SLOT(doUpdate()));
2710 }
2711 
doUpdate()2712 void NetHackQtTextWindow::doUpdate()
2713 {
2714     update();
2715 }
2716 
2717 
~NetHackQtTextWindow()2718 NetHackQtTextWindow::~NetHackQtTextWindow()
2719 {
2720 
2721 }
2722 
Widget()2723 QWidget* NetHackQtTextWindow::Widget()
2724 {
2725     return this;
2726 }
2727 
Destroy()2728 bool NetHackQtTextWindow::Destroy()
2729 {
2730     return !isVisible();
2731 }
2732 
UseRIP(int how)2733 void NetHackQtTextWindow::UseRIP(int how)
2734 {
2735 // Code from X11 windowport
2736 #define STONE_LINE_LEN 16    /* # chars that fit on one line */
2737 #define NAME_LINE 0	/* line # for player name */
2738 #define GOLD_LINE 1	/* line # for amount of gold */
2739 #define DEATH_LINE 2	/* line # for death description */
2740 #define YEAR_LINE 6	/* line # for year */
2741 
2742 static char** rip_line=0;
2743     if (!rip_line) {
2744 	rip_line=new char*[YEAR_LINE+1];
2745 	for (int i=0; i<YEAR_LINE+1; i++) {
2746 	    rip_line[i]=new char[STONE_LINE_LEN+1];
2747 	}
2748     }
2749 
2750     /* Follows same algorithm as genl_outrip() */
2751 
2752     char buf[BUFSZ];
2753     char *dpx;
2754     int line;
2755 
2756     /* Put name on stone */
2757     Sprintf(rip_line[NAME_LINE], "%s", plname);
2758 
2759     /* Put $ on stone */
2760     Sprintf(rip_line[GOLD_LINE], "%ld Au", u.ugold);
2761 
2762     /* Put together death description */
2763     switch (killer_format) {
2764 	default: impossible("bad killer format?");
2765 	case KILLED_BY_AN:
2766 	    Strcpy(buf, killed_by_prefix[how]);
2767 	    Strcat(buf, an(killer));
2768 	    break;
2769 	case KILLED_BY:
2770 	    Strcpy(buf, killed_by_prefix[how]);
2771 	    Strcat(buf, killer);
2772 	    break;
2773 	case NO_KILLER_PREFIX:
2774 	    Strcpy(buf, killer);
2775 	    break;
2776     }
2777 
2778     /* Put death type on stone */
2779     for (line=DEATH_LINE, dpx = buf; line<YEAR_LINE; line++) {
2780 	register int i,i0;
2781 	char tmpchar;
2782 
2783 	if ( (i0=strlen(dpx)) > STONE_LINE_LEN) {
2784 	    for(i = STONE_LINE_LEN;
2785 		((i0 > STONE_LINE_LEN) && i); i--)
2786 		if(dpx[i] == ' ') i0 = i;
2787 	    if(!i) i0 = STONE_LINE_LEN;
2788 	}
2789 	tmpchar = dpx[i0];
2790 	dpx[i0] = 0;
2791 	strcpy(rip_line[line], dpx);
2792 	if (tmpchar != ' ') {
2793 	    dpx[i0] = tmpchar;
2794 	    dpx= &dpx[i0];
2795 	} else  dpx= &dpx[i0+1];
2796     }
2797 
2798     /* Put year on stone */
2799     Sprintf(rip_line[YEAR_LINE], "%4d", getyear());
2800 
2801     rip.setLines(rip_line,YEAR_LINE+1);
2802 
2803     use_rip=TRUE;
2804 }
2805 
Clear()2806 void NetHackQtTextWindow::Clear()
2807 {
2808     lines->clear();
2809     use_rip=FALSE;
2810     str_fixed=FALSE;
2811 }
2812 
Display(bool block)2813 void NetHackQtTextWindow::Display(bool block)
2814 {
2815     if (str_fixed) {
2816 	lines->setFont(qt_settings->normalFixedFont());
2817     }
2818 
2819     int h=ok.height()*2;
2820     if (use_rip) {
2821 	h+=rip.height();
2822 	rip.show();
2823     } else {
2824 	rip.hide();
2825     }
2826     int mh = QApplication::desktop()->height()*3/5;
2827     resize(QMAX(use_rip ? rip.width() : 200,
2828 	    lines->TotalWidth()+24),
2829 	QMIN(mh, lines->TotalHeight()+h));
2830     centerOnMain(this);
2831     show();
2832     if (block) {
2833 	setResult(-1);
2834 	while (result()==-1) {
2835 	    qApp->enter_loop();
2836 	    if (result()==-1 && !keysource.Empty()) {
2837 		char k=keysource.GetAscii();
2838 		if (k=='\033' || k==' ' || k=='\r' || k=='\n') {
2839 		    accept();
2840 		} else if (k=='/') {
2841 		    Search();
2842 		}
2843 	    }
2844 	}
2845     }
2846 }
2847 
PutStr(int attr,const char * text)2848 void NetHackQtTextWindow::PutStr(int attr, const char* text)
2849 {
2850     str_fixed=str_fixed || strstr(text,"    ");
2851     lines->insertItem(text);
2852 }
2853 
done(int i)2854 void NetHackQtTextWindow::done(int i)
2855 {
2856     setResult(i+1000);
2857     hide();
2858     qApp->exit_loop();
2859 }
2860 
resizeEvent(QResizeEvent *)2861 void NetHackQtTextWindow::resizeEvent(QResizeEvent*)
2862 {
2863     const int margin=8;
2864     const int gutter=8;
2865     const int butw = (width()-margin*2-gutter)/2;
2866     const int buth=fontMetrics().height()*2;
2867     int y=0;
2868     if (use_rip) {
2869 	rip.setGeometry(0,0,width(),rip.height());
2870 	y+=rip.height();
2871     }
2872     y+=margin;
2873     ok.setGeometry(margin, y, butw, buth);
2874     search.setGeometry(margin+butw+gutter, y, butw, buth);
2875     y+=buth+margin;
2876     lines->setGeometry(0,y,width(),height()-y);
2877 }
keyPressEvent(QKeyEvent * e)2878 void NetHackQtTextWindow::keyPressEvent(QKeyEvent* e)
2879 {
2880     if ( e->ascii() != '\r' && e->ascii() != '\n' && e->ascii() != '\033' )
2881 	lines->keyPressEvent(e);
2882     else
2883 	QDialog::keyPressEvent(e);
2884 }
2885 
Search()2886 void NetHackQtTextWindow::Search()
2887 {
2888     NetHackQtStringRequestor requestor(keysource,"Search for:");
2889     static char line[256]="";
2890     requestor.SetDefault(line);
2891     if (requestor.Get(line)) {
2892 	int current=lines->currentItem();
2893 	for (int i=1; i<lines->count(); i++) {
2894 	    int lnum=(i+current)%lines->count();
2895 	    const char* str=lines->text(lnum);
2896 	    if (strstr(str,line)) {
2897 		lines->setCurrentItem(lnum);
2898 		lines->centerCurrentItem();
2899 		return;
2900 	    }
2901 	}
2902 	lines->setCurrentItem(-1);
2903     }
2904 }
2905 
2906 
NetHackQtDelay(int ms)2907 NetHackQtDelay::NetHackQtDelay(int ms) :
2908     msec(ms)
2909 {
2910 }
2911 
wait()2912 void NetHackQtDelay::wait()
2913 {
2914     startTimer(msec);
2915     qApp->enter_loop();
2916 }
2917 
timerEvent(QTimerEvent * timer)2918 void NetHackQtDelay::timerEvent(QTimerEvent* timer)
2919 {
2920     qApp->exit_loop();
2921     killTimers();
2922 }
2923 
NetHackQtInvUsageWindow(QWidget * parent)2924 NetHackQtInvUsageWindow::NetHackQtInvUsageWindow(QWidget* parent) :
2925     QWidget(parent)
2926 {
2927 }
2928 
drawWorn(QPainter & painter,obj * nhobj,int x,int y,bool canbe)2929 void NetHackQtInvUsageWindow::drawWorn(QPainter& painter, obj* nhobj, int x, int y, bool canbe)
2930 {
2931     short int glyph;
2932     if (nhobj)
2933 	glyph=obj_to_glyph(nhobj);
2934     else if (canbe)
2935 	glyph=cmap_to_glyph(S_room);
2936     else
2937 	glyph=cmap_to_glyph(S_stone);
2938 
2939     qt_settings->glyphs().drawCell(painter,glyph,x,y);
2940 }
2941 
paintEvent(QPaintEvent *)2942 void NetHackQtInvUsageWindow::paintEvent(QPaintEvent*)
2943 {
2944     //  012
2945     //
2946     //0 WhB
2947     //1 s"w
2948     //2 gCg
2949     //3 =A=
2950     //4  T
2951     //5  S
2952 
2953     QPainter painter;
2954     painter.begin(this);
2955 
2956     // Blanks
2957     drawWorn(painter,0,0,4,FALSE);
2958     drawWorn(painter,0,0,5,FALSE);
2959     drawWorn(painter,0,2,4,FALSE);
2960     drawWorn(painter,0,2,5,FALSE);
2961 
2962     drawWorn(painter,uarm,1,3); // Armour
2963     drawWorn(painter,uarmc,1,2); // Cloak
2964     drawWorn(painter,uarmh,1,0); // Helmet
2965     drawWorn(painter,uarms,0,1); // Shield
2966     drawWorn(painter,uarmg,0,2); // Gloves - repeated
2967     drawWorn(painter,uarmg,2,2); // Gloves - repeated
2968 #ifdef TOURIST
2969     drawWorn(painter,uarmf,1,5); // Shoes (feet)
2970     drawWorn(painter,uarmu,1,4); // Undershirt
2971 #else
2972     drawWorn(painter,0    ,1,5,FALSE);
2973     drawWorn(painter,uarmf,1,4); // Shoes (feet)
2974 #endif
2975     drawWorn(painter,uleft,0,3); // RingL
2976     drawWorn(painter,uright,2,3); // RingR
2977 
2978     drawWorn(painter,uwep,2,1); // Weapon
2979     drawWorn(painter,uswapwep,0,0); // Secondary weapon
2980     drawWorn(painter,uamul,1,1); // Amulet
2981     drawWorn(painter,ublindf,2,0); // Blindfold
2982 
2983     painter.end();
2984 }
2985 
2986 
2987 
NetHackQtMainWindow(NetHackQtKeyBuffer & ks)2988 NetHackQtMainWindow::NetHackQtMainWindow(NetHackQtKeyBuffer& ks) :
2989     message(0), map(0), status(0), invusage(this),
2990 #ifdef KDE
2991     menubar(new KMenuBar(this)),
2992 #else
2993     menubar(new QMenuBar(this)),
2994 #endif
2995     keysink(ks)
2996 {
2997     setCaption("Qt NetHack");
2998     setIcon(QPixmap(nh_icon));
2999 
3000     setBackgroundColor(black);
3001 #ifndef KDE
3002     menubar->setSeparator(QMenuBar::InWindowsStyle);
3003 #endif
3004 
3005     QPopupMenu* game=new QPopupMenu;
3006     QPopupMenu* apparel=new QPopupMenu;
3007     QPopupMenu* action=new QPopupMenu;
3008     QPopupMenu* magic=new QPopupMenu;
3009     QPopupMenu* nonaction=new QPopupMenu;
3010 
3011 #ifdef KDE
3012     QPopupMenu *help = kapp->getHelpMenu( TRUE, "" );
3013     help->insertSeparator();
3014 #else
3015     QPopupMenu* help = new QPopupMenu;
3016 #endif
3017 
3018     struct Macro {
3019 	QPopupMenu* menu;
3020 	const char* name;
3021 	const char* action;
3022     } item[] = {
3023 	{ game,		0, 0 },
3024 	{ game,		"Version\tv",           "v" },
3025 	{ game,		"Compilation\tAlt-V",     "\366" },
3026 	{ game,		"History\tShift-V",           "V" },
3027 	{ game,		"Redraw\tCtrl-R",          "\022" },
3028 	{ game,		"Options\tShift-O",           "O" },
3029 	{ game,		"Explore mode\tShift-X",      "X" },
3030 	{ game,		0, 0 },
3031 	{ game,		"Save\tShift-S",              "S" },
3032 	{ game,		"Quit\tAlt-Q",                "\361" },
3033 
3034 	{ apparel,	"Apparel off\tShift-A",       "A" },
3035 	{ apparel,	0, 0 },
3036 	{ apparel,	"Wield weapon\tw",      "w" },
3037 	{ apparel,	"Exchange weapons\tx",      "x" },
3038 	{ apparel,	"Two weapon combat\t#two",      "#tw" },
3039 	{ apparel,	0, 0 },
3040 	{ apparel,	"Wear armour\tShift-W",       "W" },
3041 	{ apparel,	"Take off armour\tShift-T",   "T" },
3042 	{ apparel,	0, 0 },
3043 	{ apparel,	"Put on non-armour\tShift-P", "P" },
3044 	{ apparel,	"Remove non-armour\tShift-R", "R" },
3045 
3046 	{ action,	"Again\tCtrl-A",           "\001" },
3047 	{ action,	0, 0 },
3048 	{ action,	"Get\t,",               "," },
3049 	{ action,	"Loot\tAlt-L",            "\354" },
3050 	{ action,	"Sit\tAlt-S",             "\363" },
3051 	{ action,	"Force\tAlt-F",           "\346" },
3052 	{ action,	"Kick\tCtrl-D",              "\004" },
3053 	{ action,	"Jump\tAlt-J",            "\352" },
3054 	{ action,	"Wipe face\tAlt-W",       "\367" },
3055 	{ action,	"Throw\tt",             "t" },
3056 	{ action,	"Load quiver\tQ",       "Q" },
3057 	{ action,	"Fire from quiver\tf",  "f" },
3058 	{ action,	"Fight\tF",             "F" },
3059 	{ action,	"Open door\to",         "o" },
3060 	{ action,	"Close door\tc",        "c" },
3061 	{ action,	"Drop\td?",             "d?" },
3062 	{ action,	"Drop many\tShift-D",         "D" },
3063 	{ action,	"Eat\te?",              "e?" },
3064 	{ action,	"Engrave\tShift-E",           "E" },
3065 	{ action,	"Apply\ta?",            "a?" },
3066 	{ action,	0, 0 },
3067 	{ action,	"Ride\t#ri",            "#ri" },
3068 	{ action,	"Up\t<",                "<" },
3069 	{ action,	"Down\t>",              ">" },
3070 	{ action,	"Rest\t.",              "." },
3071 	{ action,	"Search\ts",            "s" },
3072 	{ action,	0, 0 },
3073 	{ action,	"Chat\tAlt-C",            "\343" },
3074 	{ action,	"Pay\tp",               "p" },
3075 
3076 	{ magic,	"Quaff potion\tq",      "q?" },
3077 	{ magic,	"Read scroll/book\tr?", "r?" },
3078 	{ magic,	"Zap wand\tz?",         "z?" },
3079 	{ magic,	"Zap spell\tShift-Z?",        "Z?" },
3080 	{ magic,	"Dip\tAlt-D",             "\344" },
3081 	{ magic,	"Rub\tAlt-R",             "\362" },
3082 	{ magic,	"Invoke\tAlt-I",          "\351" },
3083 	{ magic,	0, 0 },
3084 	{ magic,	"Offer\tAlt-O",           "\357" },
3085 	{ magic,	"Pray\tAlt-P",            "\360" },
3086 	{ magic,	0, 0 },
3087 	{ magic,	"Teleport\tCtrl-T",        "\024" },
3088 	{ magic,	"Monster action\tAlt-M",  "\355" },
3089 	{ magic,	"Turn undead\tAlt-T",     "\364" },
3090 
3091 	{ nonaction,	"Inventory\ti",         "i" },
3092 #ifdef SLASHEM
3093 	{ nonaction,	"Angbandish inventory\t*",    "*" },
3094 #endif
3095 	{ nonaction,	"Conduct\t#co",         "#co" },
3096 	{ nonaction,	"Discoveries\t\\",      "\\" },
3097 	{ nonaction,	"List/reorder spells\t+",     "+" },
3098 	{ nonaction,	"Adjust letters\tAlt-A",  "\341" },
3099 	{ nonaction,	0, 0 },
3100 	{ nonaction,	"Name objects\tAlt-N",    "\356" },
3101 	{ nonaction,	"Name creature\tShift-C",      "C" },
3102 	{ nonaction,	0, 0 },
3103 	{ nonaction,	"Qualifications\tAlt-E",  "\345" },
3104 
3105 	{ help,		"Help\t?",              "?" },
3106 	{ help,		0, 0 },
3107 	{ help,		"What is here\t:",      ":" },
3108 	{ help,		"What is that\t;",      ";" },
3109 	{ help,		"What is...\t/",        "/y" },
3110 
3111 	{ 0, 0, 0 }
3112     };
3113 
3114     int i;
3115     int count=0;
3116     for (i=0; item[i].menu; i++)
3117 	if (item[i].name) count++;
3118 
3119     macro=new const char* [count];
3120 
3121     game->insertItem("Qt settings...",1000);
3122     help->insertItem("About Qt NetHack...",2000);
3123 
3124     count=0;
3125     for (i=0; item[i].menu; i++) {
3126 	if (item[i].name) {
3127 	    item[i].menu->insertItem(item[i].name,count);
3128 	    macro[count++]=item[i].action;
3129 	} else {
3130 	    item[i].menu->insertSeparator();
3131 	}
3132     }
3133 
3134     menubar->insertItem("Game",game);
3135     menubar->insertItem("Apparel",apparel);
3136     menubar->insertItem("Action",action);
3137     menubar->insertItem("Magic",magic);
3138     menubar->insertItem("Non-action",nonaction);
3139     menubar->insertSeparator();
3140     menubar->insertItem("Help",help);
3141 
3142     connect(menubar,SIGNAL(activated(int)),this,SLOT(doMenuItem(int)));
3143 
3144 #ifdef KDE
3145     setMenu (menubar);
3146 #endif
3147 
3148     int x=0,y=0;
3149     int w=QApplication::desktop()->width()-10; // XXX arbitrary extra space for frame
3150     int h=QApplication::desktop()->height()-50;
3151 
3152     int maxwn;
3153     int maxhn;
3154     if (qt_tilewidth != NULL) {
3155 	maxwn = atoi(qt_tilewidth) * COLNO + 10;
3156     } else {
3157 	maxwn = 1400;
3158     }
3159     if (qt_tileheight != NULL) {
3160 	maxhn = atoi(qt_tileheight) * ROWNO * 6/4;
3161     } else {
3162 	maxhn = 1024;
3163     }
3164 
3165     // Be exactly the size we want to be - full map...
3166     if (w>maxwn) {
3167 	x+=(w-maxwn)/2;
3168 	w=maxwn; // Doesn't need to be any wider
3169     }
3170     if (h>maxhn) {
3171 	y+=(h-maxhn)/2;
3172 	h=maxhn; // Doesn't need to be any taller
3173     }
3174 
3175     setGeometry(x,y,w,h);
3176 }
doMenuItem(int id)3177 void NetHackQtMainWindow::doMenuItem(int id)
3178 {
3179     switch (id) {
3180       case 1000:
3181 	centerOnMain(qt_settings);
3182 	qt_settings->show();
3183 	break;
3184       case 2000: {
3185 	    QMessageBox::about(this,  "About Qt NetHack", aboutMsg());
3186 	} break;
3187       default:
3188 	keysink.Put(macro[id]);
3189 	qApp->exit_loop();
3190     }
3191 }
3192 
AddMessageWindow(NetHackQtMessageWindow * window)3193 void NetHackQtMainWindow::AddMessageWindow(NetHackQtMessageWindow* window)
3194 {
3195     message=window;
3196     ShowIfReady();
3197 }
3198 
AddMapWindow(NetHackQtMapWindow * window)3199 void NetHackQtMainWindow::AddMapWindow(NetHackQtMapWindow* window)
3200 {
3201     map=window;
3202     ShowIfReady();
3203     connect(map,SIGNAL(resized()),this,SLOT(layout()));
3204 }
3205 
AddStatusWindow(NetHackQtStatusWindow * window)3206 void NetHackQtMainWindow::AddStatusWindow(NetHackQtStatusWindow* window)
3207 {
3208     status=window;
3209     ShowIfReady();
3210 }
3211 
RemoveWindow(NetHackQtWindow * window)3212 void NetHackQtMainWindow::RemoveWindow(NetHackQtWindow* window)
3213 {
3214     if (window==status) {
3215 	status=0;
3216 	ShowIfReady();
3217     } else if (window==map) {
3218 	map=0;
3219 	ShowIfReady();
3220     } else if (window==message) {
3221 	message=0;
3222 	ShowIfReady();
3223     }
3224 }
3225 
updateInventory()3226 void NetHackQtMainWindow::updateInventory()
3227 {
3228     invusage.repaint(FALSE);
3229 }
3230 
fadeHighlighting()3231 void NetHackQtMainWindow::fadeHighlighting()
3232 {
3233     if (status) {
3234 	status->fadeHighlighting();
3235     }
3236 }
3237 
layout()3238 void NetHackQtMainWindow::layout()
3239 {
3240     if (message && map && status) {
3241 	QSize maxs=map->Widget()->maximumSize();
3242 	int maph=QMIN(height()*2/3,maxs.height());
3243 
3244 	int y=menubar->height();
3245 	int h=height()-y;
3246 	int toph=h-maph;
3247 	int iuw=3*qt_settings->glyphs().width();
3248 	int topw=(width()-iuw)/2;
3249 
3250 	message->Widget()->setGeometry(0,y,topw,toph);
3251 	invusage.setGeometry(topw,y,iuw,toph);
3252 	status->Widget()->setGeometry(topw+iuw,y,topw,toph);
3253 	map->Widget()->setGeometry(QMAX(0,(width()-maxs.width())/2),
3254 				   y+toph,width(),maph);
3255     }
3256 }
3257 
resizeEvent(QResizeEvent *)3258 void NetHackQtMainWindow::resizeEvent(QResizeEvent*)
3259 {
3260     layout();
3261 #ifdef KDE
3262     updateRects();
3263 #endif
3264 }
3265 
keyPressEvent(QKeyEvent * event)3266 void NetHackQtMainWindow::keyPressEvent(QKeyEvent* event)
3267 {
3268     // Global key controls
3269 
3270     switch (event->key()) {
3271      case Key_Up:
3272 	if (map) map->Scroll(0,-1);
3273     break; case Key_Down:
3274 	if (map) map->Scroll(0,+1);
3275     break; case Key_Left:
3276 	if (map) map->Scroll(-1,0);
3277     break; case Key_Right:
3278 	if (map) map->Scroll(+1,0);
3279     break; case Key_Prior:
3280 	if (message) message->Scroll(0,-1);
3281     break; case Key_Next:
3282 	if (message) message->Scroll(0,+1);
3283     break; default:
3284 	event->ignore();
3285     }
3286 }
3287 
closeEvent(QCloseEvent * e)3288 void NetHackQtMainWindow::closeEvent(QCloseEvent* e)
3289 {
3290     if ( program_state.something_worth_saving ) {
3291 	switch ( QMessageBox::information( this, "NetHack",
3292 	    "This will end your NetHack session",
3293 	    "&Save", "&Quit", "&Cancel", 0, 2 ) )
3294 	{
3295 	    case 0:
3296 		// See dosave() function
3297 		if (dosave0()) {
3298 		    u.uhp = -1;
3299 		    terminate(EXIT_SUCCESS);
3300 		}
3301 		break;
3302 	    case 1:
3303 		u.uhp = -1;
3304 		terminate(EXIT_SUCCESS);
3305 		break;
3306 	    case 2:
3307 		break; // ignore the event
3308 	}
3309     } else {
3310 	e->accept();
3311     }
3312 }
3313 
ShowIfReady()3314 void NetHackQtMainWindow::ShowIfReady()
3315 {
3316     if (message && map && status) {
3317 	QPoint pos(0,0);
3318 	message->Widget()->recreate(this,0,pos);
3319 	map->Widget()->recreate(this,0,pos);
3320 	status->Widget()->recreate(this,0,pos);
3321 
3322 	layout();
3323 	show();
3324     } else if (isVisible()) {
3325 	hide();
3326     }
3327 }
3328 
3329 
NetHackQtYnDialog(NetHackQtKeyBuffer & keysrc,const char * q,const char * ch,char df)3330 NetHackQtYnDialog::NetHackQtYnDialog(NetHackQtKeyBuffer& keysrc,const char* q,const char* ch,char df) :
3331     QDialog(0,0,FALSE),
3332     keysource(keysrc),
3333     question(q), choices(ch), def(df)
3334 {
3335     setCaption("NetHack: Question");
3336 }
3337 
Exec()3338 char NetHackQtYnDialog::Exec()
3339 {
3340     if (choices) {
3341 	QButtonGroup group(question, this);
3342 
3343 	int nchoices=strlen(choices);
3344 
3345 	bool allow_count=strchr(choices,'#')!=0;
3346 
3347 	const int margin=8;
3348 	const int gutter=8;
3349 	const int extra=fontMetrics().height(); // Extra for group
3350 	int row=0;
3351 	int x=margin, y=extra+margin;
3352 	int butsize=fontMetrics().height()*2+5;
3353 
3354 	QPushButton* button;
3355 	for (int i=0; i<nchoices && choices[i]!='\033'; i++) {
3356 	    char text[2]={choices[i],0};
3357 	    button=new QPushButton(text,&group);
3358 	    button->setGeometry(x,y,butsize,butsize); // Square
3359 	    if (choices[i]==def) button->setDefault(TRUE);
3360 	    if (i%10==9) {
3361 		// last in row
3362 		x=margin;
3363 		y+=butsize+gutter;
3364 	    } else {
3365 		x+=butsize+gutter;
3366 	    }
3367 	}
3368 
3369 	group.resize(margin*2+(gutter+button->width())*10,
3370 	    extra+margin*2+(gutter+button->height())*(nchoices/10+1));
3371 	connect(&group,SIGNAL(clicked(int)),this,SLOT(done(int)));
3372 
3373 	QLabel* lb=0;
3374 	QLineEdit* le=0;
3375 
3376 	if (allow_count) {
3377 	    lb=new QLabel("Count: ",this);
3378 	    le=new QLineEdit(this);
3379 	    lb->setGeometry(margin,group.height()+margin,group.width()*1/3,le->height());
3380 	    le->setGeometry(lb->geometry().right()+margin,lb->geometry().top(),
3381 		group.width()*2/3,le->height());
3382 	}
3383 
3384 	centerOnMain(this);
3385 	show();
3386 	char choice=0;
3387 	while (!choice) {
3388 	    if (!result()) {
3389 		if (!keysource.Empty()) {
3390 		    char k=keysource.GetAscii();
3391 		    char ch_esc=0;
3392 		    for (int i=0; choices[i]; i++) {
3393 			if (choices[i]==k)
3394 			    choice=k;
3395 			if (choices[i]=='q') ch_esc='q';
3396 			else if (!ch_esc && choices[i]=='n') ch_esc='n';
3397 		    }
3398 		    if (!choice) {
3399 			if (k=='\033' && ch_esc)
3400 			    choice=ch_esc;
3401 			else if (k==' ' || k=='\r' || k=='\n')
3402 			    choice=def;
3403 			// else choice remains 0
3404 		    }
3405 		}
3406 	    } else {
3407 		choice=choices[result()-1000];
3408 	    }
3409 	    qApp->enter_loop();
3410 	}
3411 	hide();
3412 	if (allow_count && !le->text().isEmpty()) {
3413 	    yn_number=atoi(le->text());
3414 	    choice='#';
3415 	}
3416 	return choice;
3417     } else {
3418 	QLabel label(question,this);
3419 	QPushButton cancel("Dismiss",this);
3420 	label.setFrameStyle(QFrame::Box|QFrame::Sunken);
3421 	label.setAlignment(AlignCenter);
3422 	label.resize(fontMetrics().width(question)+60,30+fontMetrics().height());
3423 	cancel.move(width()/2-cancel.width()/2,label.geometry().bottom()+8);
3424 	connect(&cancel,SIGNAL(clicked()),this,SLOT(reject()));
3425 	centerOnMain(this);
3426 	show();
3427 	while (!result() && keysource.Empty()) {
3428 	    qApp->enter_loop();
3429 	}
3430 	hide();
3431 	if (keysource.Empty()) {
3432 	    return '\033';
3433 	} else {
3434 	    return keysource.GetAscii();
3435 	}
3436     }
3437 }
keyPressEvent(QKeyEvent * event)3438 void NetHackQtYnDialog::keyPressEvent(QKeyEvent* event)
3439 {
3440     // Don't want QDialog's Return/Esc behaviour
3441     event->ignore();
3442 }
3443 
done(int i)3444 void NetHackQtYnDialog::done(int i)
3445 {
3446     setResult(i+1000);
3447     qApp->exit_loop();
3448 }
3449 
NetHackQtGlyphs()3450 NetHackQtGlyphs::NetHackQtGlyphs()
3451 {
3452     const char* tile_file = "x11tiles";
3453 
3454     int tw = TILEWBASE;
3455     int th = TILEHBASE;
3456 
3457 
3458     if (!img.load(tile_file)) {
3459 	tile_file = "nhtiles.bmp";
3460 	if (!img.load(tile_file)) {
3461 	    QString msg;
3462 	    msg.sprintf("Cannot load x11tiles or nhtiles.bmp");
3463 	    QMessageBox::warning(0, "IO Error", msg);
3464 	} else {
3465 	    tiles_per_row = 40;
3466 	}
3467     } else {
3468         tiles_per_row = 1;
3469         if (img.height()%total_tiles_used) {
3470             impossible("Tile file \"%s\" has %d lines, not multiple of glyph count (%d)",
3471                tile_file, img.height(), total_tiles_used);
3472         }
3473     }
3474     int rows = ((total_tiles_used+tiles_per_row-1) / tiles_per_row);
3475     tw = img.width() / tiles_per_row;
3476     th = img.height() / rows;
3477 
3478     resize(tw, th);
3479 }
3480 
drawGlyph(QPainter & painter,int glyph,int x,int y)3481 void NetHackQtGlyphs::drawGlyph(QPainter& painter, int glyph, int x, int y)
3482 {
3483     int tile = glyph2tile[glyph];
3484     int px = (tile%tiles_per_row)*width();
3485     int py = tile/tiles_per_row*height();
3486 
3487     painter.drawPixmap(
3488 	x,
3489 	y,
3490 	pm,
3491 	px,py,
3492 	width(),height()
3493     );
3494 }
drawCell(QPainter & painter,int glyph,int cellx,int celly)3495 void NetHackQtGlyphs::drawCell(QPainter& painter, int glyph, int cellx, int celly)
3496 {
3497     drawGlyph(painter,glyph,cellx*width(),celly*height());
3498 }
resize(int w,int h)3499 void NetHackQtGlyphs::resize(int w, int h)
3500 {
3501     size = QSize(w,h);
3502     if (!w || !h)
3503 	return; // Still not decided
3504 
3505     if (w==TILEWBASE && h==TILEHBASE) {
3506 	pm.convertFromImage(img);
3507     } else {
3508 	QApplication::setOverrideCursor( Qt::waitCursor );
3509 	QImage scaled = img.smoothScale(
3510 	    w*img.width()/TILEWBASE,
3511 	    h*img.height()/TILEHBASE
3512 	);
3513 	pm.convertFromImage(scaled,Qt::ThresholdDither|Qt::PreferDither);
3514 	QApplication::restoreOverrideCursor();
3515     }
3516 }
3517 
3518 
3519 //////////////////////////////////////////////////////////////
3520 //
3521 //  The ugly C binding classes...
3522 //
3523 //////////////////////////////////////////////////////////////
3524 
3525 
NetHackQtMenuOrTextWindow(NetHackQtKeyBuffer & ks)3526 NetHackQtMenuOrTextWindow::NetHackQtMenuOrTextWindow(NetHackQtKeyBuffer& ks) :
3527     keysource(ks),
3528     actual(0)
3529 {
3530 }
3531 
Widget()3532 QWidget* NetHackQtMenuOrTextWindow::Widget()
3533 {
3534     if (!actual) impossible("Widget called before we know if Menu or Text");
3535     return actual->Widget();
3536 }
3537 
3538 // Text
Clear()3539 void NetHackQtMenuOrTextWindow::Clear()
3540 {
3541     if (!actual) impossible("Clear called before we know if Menu or Text");
3542     actual->Clear();
3543 }
Display(bool block)3544 void NetHackQtMenuOrTextWindow::Display(bool block)
3545 {
3546     if (!actual) impossible("Display called before we know if Menu or Text");
3547     actual->Display(block);
3548 }
Destroy()3549 bool NetHackQtMenuOrTextWindow::Destroy()
3550 {
3551     if (!actual) impossible("Destroy called before we know if Menu or Text");
3552     return actual->Destroy();
3553 }
3554 
PutStr(int attr,const char * text)3555 void NetHackQtMenuOrTextWindow::PutStr(int attr, const char* text)
3556 {
3557     if (!actual) actual=new NetHackQtTextWindow(keysource);
3558     actual->PutStr(attr,text);
3559 }
3560 
3561 // Menu
StartMenu()3562 void NetHackQtMenuOrTextWindow::StartMenu()
3563 {
3564     if (!actual) actual=new NetHackQtMenuWindow(keysource);
3565     actual->StartMenu();
3566 }
AddMenu(int glyph,const ANY_P * identifier,char ch,char gch,int attr,const char * str,bool presel)3567 void NetHackQtMenuOrTextWindow::AddMenu(int glyph, const ANY_P* identifier, char ch, char gch, int attr,
3568 	const char* str, bool presel)
3569 {
3570     if (!actual) impossible("AddMenu called before we know if Menu or Text");
3571     actual->AddMenu(glyph,identifier,ch,gch,attr,str,presel);
3572 }
EndMenu(const char * prompt)3573 void NetHackQtMenuOrTextWindow::EndMenu(const char* prompt)
3574 {
3575     if (!actual) impossible("EndMenu called before we know if Menu or Text");
3576     actual->EndMenu(prompt);
3577 }
SelectMenu(int how,MENU_ITEM_P ** menu_list)3578 int NetHackQtMenuOrTextWindow::SelectMenu(int how, MENU_ITEM_P **menu_list)
3579 {
3580     if (!actual) impossible("SelectMenu called before we know if Menu or Text");
3581     return actual->SelectMenu(how,menu_list);
3582 }
3583 
3584 
3585 // XXX Should be from Options
3586 //
3587 // XXX Hmm.  Tricky part is that perhaps some macros should only be active
3588 // XXX       when a key is about to be gotten.  For example, the user could
3589 // XXX       define "-" to do "E-yyyyyyyy\r", but would still need "-" for
3590 // XXX       other purposes.  Maybe just too bad.
3591 //
3592 struct {
3593     int key;
3594     int state;
3595     const char* macro;
3596 } key_macro[]={
3597     { Qt::Key_F1, 0, "n100." }, // Rest (x100)
3598     { Qt::Key_F2, 0, "n20s" },  // Search (x20)
3599     { Qt::Key_F3, 0, "o8o4o6o2o8o4o6o2o8o4o6o2" }, // Open all doors (x3)
3600     { Qt::Key_Tab, 0, "\001" },
3601     { 0, 0, 0 }
3602 };
3603 
3604 
NetHackQtBind(int & argc,char ** argv)3605 NetHackQtBind::NetHackQtBind(int& argc, char** argv) :
3606 #ifdef KDE
3607     KApplication(argc,argv)
3608 #else
3609     QApplication(argc,argv)
3610 #endif
3611 {
3612     main = new NetHackQtMainWindow(keybuffer);
3613     setMainWidget(main);
3614     qt_settings=new NetHackQtSettings(main->width(),main->height());
3615 }
3616 
qt_init_nhwindows(int * argc,char ** argv)3617 void NetHackQtBind::qt_init_nhwindows(int* argc, char** argv)
3618 {
3619 #ifdef _WS_X11_
3620 // Userid control
3621 //
3622 // Michael Hohmuth <hohmuth@inf.tu-dresden.de>...
3623 //
3624 // As the game runs setuid games, it must seteuid(getuid()) before
3625 // calling XOpenDisplay(), and reset the euid afterwards.
3626 // Otherwise, it can't read the $HOME/.Xauthority file and whines about
3627 // not being able to open the X display (if a magic-cookie
3628 // authorization mechanism is being used).
3629 
3630     uid_t gamesuid=geteuid();
3631     seteuid(getuid());
3632 #endif
3633 
3634     QApplication::setColorSpec(ManyColor);
3635     instance=new NetHackQtBind(*argc,argv);
3636 
3637 #ifdef _WS_X11_
3638     seteuid(gamesuid);
3639 #endif
3640 
3641 #ifdef _WS_WIN_
3642     // This nethack engine feature should be moved into windowport API
3643     nt_kbhit = NetHackQtBind::qt_kbhit;
3644 #endif
3645 }
3646 
qt_kbhit()3647 int NetHackQtBind::qt_kbhit()
3648 {
3649     return !keybuffer.Empty();
3650 }
3651 
3652 static bool have_asked = FALSE;
3653 
qt_player_selection()3654 void NetHackQtBind::qt_player_selection()
3655 {
3656     if ( !have_asked )
3657 	qt_askname();
3658 }
3659 
qt_askname()3660 void NetHackQtBind::qt_askname()
3661 {
3662     have_asked = TRUE;
3663 
3664     // We do it all here, and nothing in askname
3665 
3666     NetHackQtPlayerSelector selector(keybuffer);
3667 
3668     if (selector.Choose()) {
3669 	// ...
3670     } else {
3671 	clearlocks();
3672 	qt_exit_nhwindows(0);
3673 	terminate(0);
3674     }
3675 }
3676 
qt_get_nh_event()3677 void NetHackQtBind::qt_get_nh_event()
3678 {
3679 }
3680 
qt_exit_nhwindows(const char *)3681 void NetHackQtBind::qt_exit_nhwindows(const char *)
3682 {
3683 }
3684 
qt_suspend_nhwindows(const char *)3685 void NetHackQtBind::qt_suspend_nhwindows(const char *)
3686 {
3687 }
3688 
qt_resume_nhwindows()3689 void NetHackQtBind::qt_resume_nhwindows()
3690 {
3691 }
3692 
3693 static QArray<NetHackQtWindow*> id_to_window;
3694 
qt_create_nhwindow(int type)3695 winid NetHackQtBind::qt_create_nhwindow(int type)
3696 {
3697     winid id;
3698     for (id = 0; id < id_to_window.size(); id++) {
3699 	if ( !id_to_window[id] )
3700 	    break;
3701     }
3702     if ( id == id_to_window.size() )
3703 	id_to_window.resize(id+1);
3704 
3705     NetHackQtWindow* window=0;
3706 
3707     switch (type) {
3708      case NHW_MAP: {
3709 	NetHackQtMapWindow* w=new NetHackQtMapWindow(clickbuffer);
3710 	main->AddMapWindow(w);
3711 	window=w;
3712     } break; case NHW_MESSAGE: {
3713 	NetHackQtMessageWindow* w=new NetHackQtMessageWindow;
3714 	main->AddMessageWindow(w);
3715 	window=w;
3716     } break; case NHW_STATUS: {
3717 	NetHackQtStatusWindow* w=new NetHackQtStatusWindow;
3718 	main->AddStatusWindow(w);
3719 	window=w;
3720     } break; case NHW_MENU:
3721 	window=new NetHackQtMenuOrTextWindow(keybuffer);
3722     break; case NHW_TEXT:
3723 	window=new NetHackQtTextWindow(keybuffer);
3724     }
3725 
3726     id_to_window[id] = window;
3727     return id;
3728 }
3729 
qt_clear_nhwindow(winid wid)3730 void NetHackQtBind::qt_clear_nhwindow(winid wid)
3731 {
3732     NetHackQtWindow* window=id_to_window[wid];
3733     window->Clear();
3734 }
3735 
qt_display_nhwindow(winid wid,BOOLEAN_P block)3736 void NetHackQtBind::qt_display_nhwindow(winid wid, BOOLEAN_P block)
3737 {
3738     NetHackQtWindow* window=id_to_window[wid];
3739     window->Display(block);
3740 }
3741 
qt_destroy_nhwindow(winid wid)3742 void NetHackQtBind::qt_destroy_nhwindow(winid wid)
3743 {
3744     NetHackQtWindow* window=id_to_window[wid];
3745     main->RemoveWindow(window);
3746     if (window->Destroy())
3747 	delete window;
3748     id_to_window[wid] = 0;
3749 }
3750 
qt_curs(winid wid,int x,int y)3751 void NetHackQtBind::qt_curs(winid wid, int x, int y)
3752 {
3753     NetHackQtWindow* window=id_to_window[wid];
3754     window->CursorTo(x,y);
3755 }
3756 
qt_putstr(winid wid,int attr,const char * text)3757 void NetHackQtBind::qt_putstr(winid wid, int attr, const char *text)
3758 {
3759     NetHackQtWindow* window=id_to_window[wid];
3760     window->PutStr(attr,text);
3761 }
3762 
qt_display_file(const char * filename,BOOLEAN_P must_exist)3763 void NetHackQtBind::qt_display_file(const char *filename, BOOLEAN_P must_exist)
3764 {
3765     NetHackQtTextWindow* window=new NetHackQtTextWindow(keybuffer);
3766     bool complain = FALSE;
3767 
3768 #ifdef DLB
3769     {
3770 	dlb *f;
3771 	char buf[BUFSZ];
3772 	char *cr;
3773 
3774 	window->Clear();
3775 	f = dlb_fopen(filename, "r");
3776 	if (!f) {
3777 	    complain = must_exist;
3778 	} else {
3779 	    while (dlb_fgets(buf, BUFSZ, f)) {
3780 		if ((cr = index(buf, '\n')) != 0) *cr = 0;
3781 #ifdef MSDOS
3782 		if ((cr = index(buf, '\r')) != 0) *cr = 0;
3783 #endif
3784 		if (index(buf, '\t') != 0) (void) tabexpand(buf);
3785 		window->PutStr(ATR_NONE, buf);
3786 	    }
3787 	    window->Display(FALSE);
3788 	    (void) dlb_fclose(f);
3789 	}
3790     }
3791 #else
3792     QFile file(filename);
3793 
3794     if (file.open(IO_ReadOnly)) {
3795 	char line[128];
3796 	while (file.readLine(line,127) >= 0) {
3797 	    line[strlen(line)-1]=0;// remove newline
3798 	    window->PutStr(ATR_NONE,line);
3799 	}
3800 	window->Display(FALSE);
3801     } else {
3802 	complain = must_exist;
3803     }
3804 #endif
3805 
3806     if (complain) {
3807 	QString message;
3808 	message.sprintf("File not found: %s\n",filename);
3809 	QMessageBox::message("File Error", (const char*)message, "Ignore");
3810     }
3811 }
3812 
qt_start_menu(winid wid)3813 void NetHackQtBind::qt_start_menu(winid wid)
3814 {
3815     NetHackQtWindow* window=id_to_window[wid];
3816     window->StartMenu();
3817 }
3818 
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)3819 void NetHackQtBind::qt_add_menu(winid wid, int glyph,
3820     const ANY_P * identifier, CHAR_P ch, CHAR_P gch, int attr,
3821     const char *str, BOOLEAN_P presel)
3822 {
3823     NetHackQtWindow* window=id_to_window[wid];
3824     window->AddMenu(glyph, identifier, ch, gch, attr, str, presel);
3825 }
3826 
qt_end_menu(winid wid,const char * prompt)3827 void NetHackQtBind::qt_end_menu(winid wid, const char *prompt)
3828 {
3829     NetHackQtWindow* window=id_to_window[wid];
3830     window->EndMenu(prompt);
3831 }
3832 
qt_select_menu(winid wid,int how,MENU_ITEM_P ** menu_list)3833 int NetHackQtBind::qt_select_menu(winid wid, int how, MENU_ITEM_P **menu_list)
3834 {
3835     NetHackQtWindow* window=id_to_window[wid];
3836     return window->SelectMenu(how,menu_list);
3837 }
3838 
qt_update_inventory()3839 void NetHackQtBind::qt_update_inventory()
3840 {
3841     if (main)
3842 	main->updateInventory();
3843 }
3844 
qt_mark_synch()3845 void NetHackQtBind::qt_mark_synch()
3846 {
3847 }
3848 
qt_wait_synch()3849 void NetHackQtBind::qt_wait_synch()
3850 {
3851 }
3852 
qt_cliparound(int x,int y)3853 void NetHackQtBind::qt_cliparound(int x, int y)
3854 {
3855     // XXXNH - winid should be a parameter!
3856     qt_cliparound_window(WIN_MAP,x,y);
3857 }
3858 
qt_cliparound_window(winid wid,int x,int y)3859 void NetHackQtBind::qt_cliparound_window(winid wid, int x, int y)
3860 {
3861     NetHackQtWindow* window=id_to_window[wid];
3862     window->ClipAround(x,y);
3863 }
qt_print_glyph(winid wid,XCHAR_P x,XCHAR_P y,int glyph)3864 void NetHackQtBind::qt_print_glyph(winid wid,XCHAR_P x,XCHAR_P y,int glyph)
3865 {
3866     NetHackQtWindow* window=id_to_window[wid];
3867     window->PrintGlyph(x,y,glyph);
3868 }
3869 //void NetHackQtBind::qt_print_glyph_compose(winid wid,XCHAR_P x,XCHAR_P y,int glyph1, int glyph2)
3870 //{
3871     //NetHackQtWindow* window=id_to_window[wid];
3872     //window->PrintGlyphCompose(x,y,glyph1,glyph2);
3873 //}
3874 
qt_raw_print(const char * str)3875 void NetHackQtBind::qt_raw_print(const char *str)
3876 {
3877     puts(str);
3878 }
3879 
qt_raw_print_bold(const char * str)3880 void NetHackQtBind::qt_raw_print_bold(const char *str)
3881 {
3882     puts(str);
3883 }
3884 
qt_nhgetch()3885 int NetHackQtBind::qt_nhgetch()
3886 {
3887     if (main)
3888 	main->fadeHighlighting();
3889 
3890     // Process events until a key arrives.
3891     //
3892     while (keybuffer.Empty()) {
3893 	qApp->enter_loop();
3894     }
3895 
3896     return keybuffer.GetAscii();
3897 }
3898 
qt_nh_poskey(int * x,int * y,int * mod)3899 int NetHackQtBind::qt_nh_poskey(int *x, int *y, int *mod)
3900 {
3901     if (main)
3902 	main->fadeHighlighting();
3903 
3904     // Process events until a key or map-click arrives.
3905     //
3906     while (keybuffer.Empty() && clickbuffer.Empty()) {
3907 	qApp->enter_loop();
3908     }
3909     if (!keybuffer.Empty()) {
3910 	return keybuffer.GetAscii();
3911     } else {
3912 	*x=clickbuffer.NextX();
3913 	*y=clickbuffer.NextY();
3914 	*mod=clickbuffer.NextMod();
3915 	clickbuffer.Get();
3916 	return 0;
3917     }
3918 }
3919 
qt_nhbell()3920 void NetHackQtBind::qt_nhbell()
3921 {
3922     QApplication::beep();
3923 }
3924 
qt_doprev_message()3925 int NetHackQtBind::qt_doprev_message()
3926 {
3927     // Don't need it - uses scrollbar
3928     // XXX but could make this a shortcut
3929     return 0;
3930 }
3931 
qt_yn_function(const char * question,const char * choices,CHAR_P def)3932 char NetHackQtBind::qt_yn_function(const char *question, const char *choices, CHAR_P def)
3933 {
3934     if (qt_settings->ynInMessages() && WIN_MESSAGE!=WIN_ERR) {
3935 	// Similar to X11 windowport `slow' feature.
3936 
3937 	char message[BUFSZ];
3938 	char yn_esc_map='\033';
3939 
3940 	if (choices) {
3941 	    char *cb, choicebuf[QBUFSZ];
3942 	    Strcpy(choicebuf, choices);
3943 	    if ((cb = index(choicebuf, '\033')) != 0) {
3944 		// anything beyond <esc> is hidden
3945 		*cb = '\0';
3946 	    }
3947 	    Sprintf(message, "%s [%s] ", question, choicebuf);
3948 	    if (def) Sprintf(eos(message), "(%c) ", def);
3949 	    // escape maps to 'q' or 'n' or default, in that order
3950 	    yn_esc_map = (index(choices, 'q') ? 'q' :
3951 		     (index(choices, 'n') ? 'n' : def));
3952 	} else {
3953 	    Strcpy(message, question);
3954 	}
3955 
3956 #ifdef USE_POPUPS
3957 	// Improve some special-cases (DIRKS 08/02/23)
3958 	if (strcmp (choices,"ynq") == 0) {
3959 	    switch (QMessageBox::information (0,"NetHack",question,"&Yes","&No","&Quit",0,2))
3960 	    {
3961 	      case 0: return 'y';
3962 	      case 1: return 'n';
3963 	      case 2: return 'q';
3964 	    }
3965 	}
3966 
3967 	if (strcmp (choices,"yn") == 0) {
3968 	    switch (QMessageBox::information(0,"NetHack",question,"&Yes", "&No",0,1))
3969 	    {
3970 	      case 0: return 'y';
3971 	      case 1: return 'n';
3972 	    }
3973 	}
3974 #endif
3975 
3976 	NetHackQtBind::qt_putstr(WIN_MESSAGE, ATR_BOLD, message);
3977 
3978 	int result=-1;
3979 	while (result<0) {
3980 	    char ch=NetHackQtBind::qt_nhgetch();
3981 	    if (ch=='\033') {
3982 		result=yn_esc_map;
3983 	    } else if (choices && !index(choices,ch)) {
3984 		if (def && (ch==' ' || ch=='\r' || ch=='\n')) {
3985 		    result=def;
3986 		} else {
3987 		    NetHackQtBind::qt_nhbell();
3988 		    // and try again...
3989 		}
3990 	    } else {
3991 		result=ch;
3992 	    }
3993 	}
3994 
3995 	NetHackQtBind::qt_clear_nhwindow(WIN_MESSAGE);
3996 
3997 	return result;
3998     } else {
3999 	NetHackQtYnDialog dialog(keybuffer,question,choices,def);
4000 	return dialog.Exec();
4001     }
4002 }
4003 
qt_getlin(const char * prompt,char * line)4004 void NetHackQtBind::qt_getlin(const char *prompt, char *line)
4005 {
4006     NetHackQtStringRequestor requestor(keybuffer,prompt);
4007     if (!requestor.Get(line)) {
4008 	line[0]=0;
4009     }
4010 }
4011 
NetHackQtExtCmdRequestor(NetHackQtKeyBuffer & ks)4012 NetHackQtExtCmdRequestor::NetHackQtExtCmdRequestor(NetHackQtKeyBuffer& ks) :
4013     QDialog(0, "ext-cmd", FALSE),
4014     keysource(ks)
4015 {
4016     int marg=4;
4017     QVBoxLayout *l = new QVBoxLayout(this,marg,marg);
4018 
4019     QPushButton* can = new QPushButton("Cancel", this);
4020     can->setDefault(TRUE);
4021     can->setMinimumSize(can->sizeHint());
4022     l->addWidget(can);
4023 
4024     QButtonGroup *group=new QButtonGroup("",0);
4025     QGroupBox *grid=new QGroupBox("Extended commands",this);
4026     l->addWidget(grid);
4027 
4028     int i;
4029     int butw=50;
4030     QFontMetrics fm = fontMetrics();
4031     for (i=0; extcmdlist[i].ef_txt; i++) {
4032 	butw = QMAX(butw,30+fm.width(extcmdlist[i].ef_txt));
4033     }
4034     int ncols=4;
4035     int nrows=(i+ncols-1)/ncols;
4036 
4037     QVBoxLayout* bl = new QVBoxLayout(grid,marg);
4038     bl->addSpacing(fm.height());
4039     QGridLayout* gl = new QGridLayout(nrows,ncols,marg);
4040     bl->addLayout(gl);
4041     for (i=0; extcmdlist[i].ef_txt; i++) {
4042 	QPushButton* pb=new QPushButton(extcmdlist[i].ef_txt, grid);
4043 	pb->setMinimumSize(butw,pb->sizeHint().height());
4044 	group->insert(pb);
4045 	gl->addWidget(pb,i/ncols,i%ncols);
4046     }
4047     connect(group,SIGNAL(clicked(int)),this,SLOT(done(int)));
4048 
4049     bl->activate();
4050     l->activate();
4051     resize(1,1);
4052 
4053     connect(can,SIGNAL(clicked()),this,SLOT(cancel()));
4054 }
4055 
cancel()4056 void NetHackQtExtCmdRequestor::cancel()
4057 {
4058     setResult(-1);
4059     qApp->exit_loop();
4060 }
4061 
done(int i)4062 void NetHackQtExtCmdRequestor::done(int i)
4063 {
4064     setResult(i);
4065     qApp->exit_loop();
4066 }
4067 
get()4068 int NetHackQtExtCmdRequestor::get()
4069 {
4070     const int none = -10;
4071     char str[32];
4072     int cursor=0;
4073     resize(1,1); // pack
4074     centerOnMain(this);
4075     show();
4076     setResult(none);
4077     while (result()==none) {
4078 	while (result()==none && !keysource.Empty()) {
4079 	    char k=keysource.GetAscii();
4080 	    if (k=='\r' || k=='\n' || k==' ' || k=='\033') {
4081 		setResult(-1);
4082 	    } else {
4083 		str[cursor++] = k;
4084 		int r=-1;
4085 		for (int i=0; extcmdlist[i].ef_txt; i++) {
4086 		    if (qstrnicmp(str, extcmdlist[i].ef_txt, cursor)==0) {
4087 			if ( r == -1 )
4088 			    r = i;
4089 			else
4090 			    r = -2;
4091 		    }
4092 		}
4093 		if ( r == -1 ) { // no match!
4094 		    QApplication::beep();
4095 		    cursor=0;
4096 		} else if ( r != -2 ) { // only one match
4097 		    setResult(r);
4098 		}
4099 	    }
4100 	}
4101 	if (result()==none)
4102 	    qApp->enter_loop();
4103     }
4104     hide();
4105     return result();
4106 }
4107 
4108 
qt_get_ext_cmd()4109 int NetHackQtBind::qt_get_ext_cmd()
4110 {
4111     NetHackQtExtCmdRequestor requestor(keybuffer);
4112     return requestor.get();
4113 }
4114 
qt_number_pad(int)4115 void NetHackQtBind::qt_number_pad(int)
4116 {
4117     // Ignore.
4118 }
4119 
qt_delay_output()4120 void NetHackQtBind::qt_delay_output()
4121 {
4122     NetHackQtDelay delay(15);
4123     delay.wait();
4124 }
4125 
qt_start_screen()4126 void NetHackQtBind::qt_start_screen()
4127 {
4128     // Ignore.
4129 }
4130 
qt_end_screen()4131 void NetHackQtBind::qt_end_screen()
4132 {
4133     // Ignore.
4134 }
4135 
qt_outrip(winid wid,int how)4136 void NetHackQtBind::qt_outrip(winid wid, int how)
4137 {
4138     NetHackQtWindow* window=id_to_window[wid];
4139 
4140     window->UseRIP(how);
4141 }
4142 
notify(QObject * receiver,QEvent * event)4143 bool NetHackQtBind::notify(QObject *receiver, QEvent *event)
4144 {
4145     bool result=QApplication::notify(receiver,event);
4146     if (event->type()==QEvent::KeyPress) {
4147 	QKeyEvent* key_event=(QKeyEvent*)event;
4148 
4149 	if (!key_event->isAccepted()) {
4150 	    const int k=key_event->key();
4151 	    bool macro=FALSE;
4152 	    for (int i=0; !macro && key_macro[i].key; i++) {
4153 		if (key_macro[i].key==k
4154 		 && ((key_macro[i].state&key_event->state())==key_macro[i].state))
4155 		{
4156 		    keybuffer.Put(key_macro[i].macro);
4157 		    macro=TRUE;
4158 		}
4159 	    }
4160 	    char ch=key_event->ascii();
4161 	    if (!macro && ch) {
4162 		int k = key_event->key();
4163 		bool alt = (key_event->state()&AltButton) ||
4164 		   (k >= Key_0 && k <= Key_9 && (key_event->state()&ControlButton));
4165 		keybuffer.Put(key_event->key(),ch + (alt ? 128 : 0),
4166 		    key_event->state());
4167 		key_event->accept();
4168 		result=TRUE;
4169 	    }
4170 
4171 	    if (ch || macro) {
4172 		qApp->exit_loop();
4173 	    }
4174 	}
4175     }
4176     return result;
4177 }
4178 
4179 NetHackQtBind* NetHackQtBind::instance=0;
4180 NetHackQtKeyBuffer NetHackQtBind::keybuffer;
4181 NetHackQtClickBuffer NetHackQtBind::clickbuffer;
4182 NetHackQtMainWindow* NetHackQtBind::main=0;
4183 
4184 
4185 extern "C" struct window_procs Qt_procs;
4186 
4187 struct window_procs Qt_procs = {
4188     "Qt",
4189     NetHackQtBind::qt_init_nhwindows,
4190     NetHackQtBind::qt_player_selection,
4191     NetHackQtBind::qt_askname,
4192     NetHackQtBind::qt_get_nh_event,
4193     NetHackQtBind::qt_exit_nhwindows,
4194     NetHackQtBind::qt_suspend_nhwindows,
4195     NetHackQtBind::qt_resume_nhwindows,
4196     NetHackQtBind::qt_create_nhwindow,
4197     NetHackQtBind::qt_clear_nhwindow,
4198     NetHackQtBind::qt_display_nhwindow,
4199     NetHackQtBind::qt_destroy_nhwindow,
4200     NetHackQtBind::qt_curs,
4201     NetHackQtBind::qt_putstr,
4202     NetHackQtBind::qt_display_file,
4203     NetHackQtBind::qt_start_menu,
4204     NetHackQtBind::qt_add_menu,
4205     NetHackQtBind::qt_end_menu,
4206     NetHackQtBind::qt_select_menu,
4207     genl_message_menu,      /* no need for X-specific handling */
4208     NetHackQtBind::qt_update_inventory,
4209     NetHackQtBind::qt_mark_synch,
4210     NetHackQtBind::qt_wait_synch,
4211 #ifdef CLIPPING
4212     NetHackQtBind::qt_cliparound,
4213 #endif
4214 #ifdef POSITIONBAR
4215     donull,
4216 #endif
4217     NetHackQtBind::qt_print_glyph,
4218     //NetHackQtBind::qt_print_glyph_compose,
4219     NetHackQtBind::qt_raw_print,
4220     NetHackQtBind::qt_raw_print_bold,
4221     NetHackQtBind::qt_nhgetch,
4222     NetHackQtBind::qt_nh_poskey,
4223     NetHackQtBind::qt_nhbell,
4224     NetHackQtBind::qt_doprev_message,
4225     NetHackQtBind::qt_yn_function,
4226     NetHackQtBind::qt_getlin,
4227     NetHackQtBind::qt_get_ext_cmd,
4228     NetHackQtBind::qt_number_pad,
4229     NetHackQtBind::qt_delay_output,
4230 #ifdef CHANGE_COLOR     /* only a Mac option currently */
4231     donull,
4232     donull,
4233 #endif
4234     /* other defs that really should go away (they're tty specific) */
4235     NetHackQtBind::qt_start_screen,
4236     NetHackQtBind::qt_end_screen,
4237 #ifdef GRAPHIC_TOMBSTONE
4238     NetHackQtBind::qt_outrip,
4239 #else
4240     genl_outrip,
4241 #endif
4242 };
4243 
play_usersound(const char * filename,int volume)4244 extern "C" void play_usersound(const char* filename, int volume)
4245 {
4246 #ifdef USER_SOUNDS
4247     // Qt 2.2 has sound
4248     //QSound::play(filename,volume);
4249 #endif
4250 }
4251 
4252 #include "qt_win.moc"
4253 #ifndef KDE
4254 #include "qt_kde0.moc"
4255 #endif
4256