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