From: jwalz Date: Sat, 5 Jan 2002 21:06:01 +0000 (+0000) Subject: *** empty log message *** X-Git-Tag: MOVE2GIT~3573 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=c389853b09d40f9f4982add86912ce9976ee81de;p=nethack *** empty log message *** --- diff --git a/win/Qt/qt_win.cpp b/win/Qt/qt_win.cpp new file mode 100644 index 000000000..dd0f190e2 --- /dev/null +++ b/win/Qt/qt_win.cpp @@ -0,0 +1,4866 @@ +// SCCS Id: @(#)qt_win.cpp 3.3 1999/11/19 +// Copyright (c) Warwick Allison, 1999. +// NetHack may be freely redistributed. See license for details. + +#define VERSION_QT_MAJOR 2 +#define VERSION_QT_MINOR 0 +#define VERSION_QT_PATCH 0 + +// Qt Binding for NetHack 3.3 +// +// Copyright (C) 1996-2001 by Warwick W. Allison (warwick@troll.no) +// +// Contributors: +// Michael Hohmuth +// - Userid control +// Svante Gerhard +// - .nethackrc tile and font size settings +// Dirk Schoenberger +// - KDE support +// - SlashEm support +// and many others for bug reports. +// +// Unfortunately, this doesn't use Qt as well as I would like, +// primarily because NetHack is fundamentally a getkey-type program +// rather than being event driven (hence the ugly key and click buffer) +// and also because this is my first major application of Qt. +// +// The problem of NetHack's getkey requirement is solved by intercepting +// key events by overiding QApplicion::notify(...), and putting them in +// a buffer. Mouse clicks on the map window are treated with a similar +// buffer. When the NetHack engine calls for a key, one is taken from +// the buffer, or if that is empty, QApplication::enter_loop() is called. +// Whenever keys or clicks go into the buffer, QApplication::exit_loop() +// is called. +// +// Another problem is that some NetHack players are decade-long players who +// demand complete keyboard control (while Qt and X11 conspire to make this +// difficult by having widget-based focus rather than application based - +// a good thing in general). This problem is solved by again using the key +// event buffer. +// +// Out of all this hackery comes a silver lining however, as macros for +// the super-expert and menus for the ultra-newbie are also made possible +// by the key event buffer. +// + +extern "C" { + +// This includes all the definitions we need from the NetHack main +// engine. We pretend MSC is a STDC compiler, because C++ is close +// enough, and we undefine NetHack macros which conflict with Qt +// identifiers. + +#ifdef _MSC_VER +#define NHSTDC +#endif +#include "hack.h" +#include "func_tab.h" +#include "dlb.h" +#include "patchlevel.h" +#undef Warning +#undef red +#undef green +#undef blue +#undef Black +#undef curs +#undef TRUE +#undef FALSE +#undef min +#undef max + +} + +#include "qt_win.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include +//#include + +#include + +#include "qt_clust.h" +#include "qt_xpms.h" + +#include + +#ifdef _WS_X11_ +// For userid control +#include +#endif + +#ifdef USER_SOUNDS +extern "C" void play_sound_for_message(const char* str); +#endif + +// Warwick prefers it this way... +#define QT_CHOOSE_RACE_FIRST + +static QString +aboutMsg() +{ + QString msg; + msg.sprintf( + "Qt NetHack is a version of NetHack built using\n" +#ifdef KDE + "KDE and the Qt GUI toolkit for the user interface.\n" +#else + "the Qt GUI toolkit for the user interface.\n" +#endif + "This is version %d.%d.%d.%d.%d.%d\n\n" + "Homepage:\n http://trolls.troll.no/warwick/nethack/\n\n" +#ifdef KDE + "KDE:\n http://www.kde.org\n" +#endif + "Qt:\n http://www.troll.no", + VERSION_MAJOR, + VERSION_MINOR, + PATCHLEVEL, + VERSION_QT_MAJOR, + VERSION_QT_MINOR, + VERSION_QT_PATCH); + return msg; +} + +static void +centerOnMain( QWidget* w ) +{ + QWidget* m = qApp->mainWidget(); + QPoint p = m->mapToGlobal(QPoint(0,0)); + w->move( p.x() + m->width()/2 - w->width()/2, + p.y() + m->height()/2 - w->height()/2 ); +} + +NetHackQtLineEdit::NetHackQtLineEdit() : + QLineEdit(0) +{ +} + +NetHackQtLineEdit::NetHackQtLineEdit(QWidget* parent, const char* name) : + QLineEdit(parent,name) +{ +} + +void NetHackQtLineEdit::fakeEvent(int key, int ascii, int state) +{ + QKeyEvent fake(QEvent::KeyPress,key,ascii,state); + keyPressEvent(&fake); +} + +extern "C" { +/* Used by tile/font-size patch below and in ../../src/files.c */ +char *qt_tilewidth=NULL; +char *qt_tileheight=NULL; +char *qt_fontsize=NULL; +int qt_compact_mode = 0; +extern const char *enc_stat[]; /* from botl.c */ +extern const char *hu_stat[]; /* from eat.c */ +extern const char *killed_by_prefix[]; +extern int total_tiles_used; // from tile.c +extern short glyph2tile[]; // from tile.c +} + +// ### Try to replace these by looking at image size +#define TILEWBASE 16 +#define TILEHBASE 16 + +#define TILEWMIN 1 +#define TILEHMIN 1 + + +/* XPM */ +static const char * nh_icon[] = { +"40 40 6 1", +" s None c none", +". c #ffffff", +"X c #dadab6", +"o c #6c91b6", +"O c #476c6c", +"+ c #000000", +" ", +" ", +" ", +" . .X..XX.XX X ", +" .. .....X.XXXXXX XX ", +" ... ....X..XX.XXXXX XXX ", +" .. ..........X.XXXXXXXXXXX XX ", +" .... ........X..XX.XXXXXXXXX XXXX ", +" .... ..........X.XXXXXXXXXXX XXXX ", +" ooOOO..ooooooOooOOoOOOOOOOXX+++OO++ ", +" ooOOO..ooooooooOoOOOOOOOOOXX+++OO++ ", +" ....O..ooooooOooOOoOOOOOOOXX+XXXX++ ", +" ....O..ooooooooOoOOOOOOOOOXX+XXXX++ ", +" ..OOO..ooooooOooOOoOOOOOOOXX+++XX++ ", +" ++++..ooooooooOoOOOOOOOOOXX+++ +++ ", +" +++..ooooooOooOOoOOOOOOOXX+++ + ", +" ++..ooooooooOoOOOOOOOOOXX+++ ", +" ..ooooooOooOOoOOOOOOOXX+++ ", +" ..ooooooooOoOOOOOOOOOXX+++ ", +" ..ooooooOooOOoOOOOOOOXX+++ ", +" ..ooooooooOoOOOOOOOOOXX+++ ", +" ..oooooOooOOoOOOOOOXX+++ ", +" ..oooooooOoOOOOOOOOXX+++ ", +" ..ooooOooOOoOOOOOXX+++ ", +" ..ooooooOoOOOOOOOXX++++ ", +" ..o..oooOooOOoOOOOXX+XX+++ ", +" ...o..oooooOoOOOOOXX++XXX++ ", +" ....OO..ooOooOOoOOXX+++XXXX++ ", +" ...oo..+..oooOoOOOXX++XXooXXX++ ", +" ...ooo..++..OooOOoXX+++XXooOXXX+ ", +" ..oooOOXX+++....XXXX++++XXOOoOOXX+ ", +" ..oooOOXX+++ ...XXX+++++XXOOooOXX++ ", +" ..oooOXXX+++ ..XX+++ +XXOOooOXX++ ", +" .....XXX++++ XXXXXXX++ ", +" ....XX++++ XXXXXXX+ ", +" ...XX+++ XXXXX++ ", +" ", +" ", +" ", +" "}; +/* XPM */ +static const char * nh_icon_small[] = { +/* width height ncolors chars_per_pixel */ +"16 16 16 1", +/* colors */ +" c #587070", +". c #D1D5C9", +"X c #8B8C84", +"o c #2A2A28", +"O c #9AABA9", +"+ c #6A8FB2", +"@ c #C4CAC4", +"# c #B6BEB6", +"$ c None", +"% c #54564E", +"& c #476C6C", +"* c #ADB2AB", +"= c #ABABA2", +"- c #5E8295", +"; c #8B988F", +": c #E8EAE7", +/* pixels */ +"$$$$$$$$$$$$$$$$", +"$$$.$#::.#==*$$$", +"$.*:::::....#*=$", +"$@#:..@#*==#;XX;", +"$@O:+++- &&; X%X", +"$#%.+++- &&;% oX", +"$$o.++-- &&;%%X$", +"$$$:++-- &&;%%$$", +"$$$.O++- &&=o $$", +"$$$=:++- & XoX$$", +"$$*:@O-- ;%Xo$$", +"$*:O#$+--;oOOX $", +"$:+ =o::=oo=-;%X", +"$::.%o$*;X;##@%$", +"$$@# ;$$$$$=*;X$", +"$$$$$$$$$$$$$$$$" +}; + +/* XPM */ +static const char * map_xpm[] = { +"12 13 4 1", +". c None", +" c #000000000000", +"X c #0000B6DAFFFF", +"o c #69A69248B6DA", +" .", +" XXXXX ooo ", +" XoooX o ", +" XoooX o o ", +" XoooX ooo ", +" XXoXX o ", +" oooooXXX ", +" oo o oooX ", +" o XooX ", +" oooo XooX ", +" o o XXXX ", +" ", +". "}; +/* XPM */ +static const char * msg_xpm[] = { +"12 13 4 1", +". c None", +" c #FFFFFFFFFFFF", +"X c #69A69248B6DA", +"o c #000000000000", +" .", +" XXX XXX X o", +" o", +" XXXXX XX o", +" o", +" XX XXXXX o", +" o", +" XXXXXX o", +" o", +" XX XXX XX o", +" o", +" o", +".ooooooooooo"}; +/* XPM */ +static const char * stat_xpm[] = { +"12 13 5 1", +" c None", +". c #FFFF00000000", +"X c #000000000000", +"o c #FFFFFFFF0000", +"O c #69A6FFFF0000", +" ", +" ", +"... ", +"...X ", +"...X ... ", +"oooX oooX", +"oooXooo oooX", +"OOOXOOOXOOOX", +"OOOXOOOXOOOX", +"OOOXOOOXOOOX", +"OOOXOOOXOOOX", +"OOOXOOOXOOOX", +" XXXXXXXXXXX"}; +/* XPM */ +static const char * info_xpm[] = { +"12 13 4 1", +" c None", +". c #00000000FFFF", +"X c #FFFFFFFFFFFF", +"o c #000000000000", +" ... ", +" ....... ", +" ...XXX... ", +" .........o ", +"...XXXX.... ", +"....XXX....o", +"....XXX....o", +"....XXX....o", +" ...XXX...oo", +" ..XXXXX..o ", +" .......oo ", +" o...ooo ", +" ooo "}; + + +/* XPM */ +static const char * again_xpm[] = { +"12 13 2 1", +" c None", +". c #000000000000", +" .. ", +" .. ", +" ..... ", +" ....... ", +"... .. .. ", +".. .. .. ", +".. ..", +".. ..", +".. ..", +" .. .. ", +" .......... ", +" ...... ", +" "}; +/* XPM */ +static const char * kick_xpm[] = { +"12 13 3 1", +" c None", +". c #000000000000", +"X c #FFFF6DB60000", +" ", +" ", +" . . . ", +" ... . . ", +" ... . ", +" ... . ", +" ... ", +"XXX ... ", +"XXX. ... ", +"XXX. ... ", +"XXX. .. ", +" ... ", +" "}; +/* XPM */ +static const char * throw_xpm[] = { +"12 13 3 1", +" c None", +". c #FFFF6DB60000", +"X c #000000000000", +" ", +" ", +" ", +" ", +".... X ", +"....X X ", +"....X XXXXXX", +"....X X ", +" XXXX X ", +" ", +" ", +" ", +" "}; +/* XPM */ +static const char * fire_xpm[] = { +"12 13 5 1", +" c None", +". c #B6DA45140000", +"X c #FFFFB6DA9658", +"o c #000000000000", +"O c #FFFF6DB60000", +" . ", +" X. ", +" X . ", +" X .o ", +" X . o ", +" X .o o ", +"OOOOOOOOoooo", +" X .o o ", +" X . o o ", +" X .o ", +" X. o ", +" . o ", +" o "}; +/* XPM */ +static const char * get_xpm[] = { +"12 13 3 1", +" c None", +". c #000000000000", +"X c #FFFF6DB60000", +" ", +" . ", +" ... ", +" . . . ", +" . ", +" . ", +" ", +" XXXXX ", +" XXXXX. ", +" XXXXX. ", +" XXXXX. ", +" ..... ", +" "}; +/* XPM */ +static const char * drop_xpm[] = { +"12 13 3 1", +" c None", +". c #FFFF6DB60000", +"X c #000000000000", +" ", +" ..... ", +" .....X ", +" .....X ", +" .....X ", +" XXXXX ", +" ", +" X ", +" X ", +" X X X ", +" XXX ", +" X ", +" "}; +/* XPM */ +static const char * eat_xpm[] = { +"12 13 4 1", +" c None", +". c #000000000000", +"X c #FFFFB6DA9658", +"o c #FFFF6DB60000", +" .X. .. ", +" .X. .. ", +" .X. .. ", +" .X. .. ", +" ... .. ", +" .. .. ", +" .. .. ", +" oo oo ", +" oo oo ", +" oo oo ", +" oo oo ", +" oo oo ", +" oo oo "}; +/* XPM */ +static const char * rest_xpm[] = { +"12 13 2 1", +" c None", +". c #000000000000", +" ..... ", +" . ", +" . ", +" . ....", +" ..... . ", +" . ", +" ....", +" ", +" .... ", +" . ", +" . ", +" .... ", +" "}; +/* XPM */ +static const char * cast_a_xpm[] = { +"12 13 3 1", +" c None", +". c #FFFF6DB60000", +"X c #000000000000", +" . ", +" . ", +" .. ", +" .. ", +" .. . ", +" .. . ", +" ...... ", +" .. .. XX ", +" .. X X ", +" .. X X ", +" .. XXXX ", +" . X X ", +" . X X "}; +/* XPM */ +static const char * cast_b_xpm[] = { +"12 13 3 1", +" c None", +". c #FFFF6DB60000", +"X c #000000000000", +" . ", +" . ", +" .. ", +" .. ", +" .. . ", +" .. . ", +" ...... ", +" .. .. XXX ", +" .. X X ", +" .. XXX ", +" .. X X ", +" . X X ", +" . XXX "}; +/* XPM */ +static const char * cast_c_xpm[] = { +"12 13 3 1", +" c None", +". c #FFFF6DB60000", +"X c #000000000000", +" . ", +" . ", +" .. ", +" .. ", +" .. . ", +" .. . ", +" ...... ", +" .. .. XX ", +" .. X X ", +" .. X ", +" .. X ", +" . X X ", +" . XX "}; + +NetHackQtSettings::NetHackQtSettings(int w, int h) : + tilewidth(TILEWMIN,32,1,this), + tileheight(TILEHMIN,32,1,this), + widthlbl(&tilewidth,"&Width:",this), + heightlbl(&tileheight,"&Height:",this), + fontsize(this), + normal("times"), +#ifdef WS_WIN + normalfixed("courier new"), +#else + normalfixed("helvetica"), // ################# normally fixed, just testing +#endif + large("times"), + theglyphs(0) + +{ + int default_fontsize; + + if (w<=300) { + // ~240x320 + default_fontsize=4; + tilewidth.setValue(8); + tileheight.setValue(12); + } else if (w<=700) { + // ~640x480 + default_fontsize=3; + tilewidth.setValue(8); + tileheight.setValue(14); + } else if (w<=900) { + // ~800x600 + default_fontsize=3; + tilewidth.setValue(10); + tileheight.setValue(17); + } else if (w<=1100) { + // ~1024x768 + default_fontsize=2; + tilewidth.setValue(12); + tileheight.setValue(22); + } else if (w<=1200) { + // ~1152x900 + default_fontsize=1; + tilewidth.setValue(14); + tileheight.setValue(26); + } else { + // ~1280x1024 and larger + default_fontsize=0; + tilewidth.setValue(16); + tileheight.setValue(30); + } + + // Tile/font sizes read from .nethackrc + if (qt_tilewidth != NULL) { + tilewidth.setValue(atoi(qt_tilewidth)); + free(qt_tilewidth); + } + if (qt_tileheight != NULL) { + tileheight.setValue(atoi(qt_tileheight)); + free(qt_tileheight); + } + if (qt_fontsize != NULL) { + switch (tolower(qt_fontsize[0])) { + case 'h': default_fontsize = 0; break; + case 'l': default_fontsize = 1; break; + case 'm': default_fontsize = 2; break; + case 's': default_fontsize = 3; break; + case 't': default_fontsize = 4; break; + } + free(qt_fontsize); + } + + theglyphs=new NetHackQtGlyphs(); + resizeTiles(); + + connect(&tilewidth,SIGNAL(valueChanged(int)),this,SLOT(resizeTiles())); + connect(&tileheight,SIGNAL(valueChanged(int)),this,SLOT(resizeTiles())); + + fontsize.insertItem("Huge"); + fontsize.insertItem("Large"); + fontsize.insertItem("Medium"); + fontsize.insertItem("Small"); + fontsize.insertItem("Tiny"); + fontsize.setCurrentItem(default_fontsize); + connect(&fontsize,SIGNAL(activated(int)),this,SIGNAL(fontChanged())); + + QGridLayout* grid = new QGridLayout(this, 4, 2, 8); + grid->addWidget(&tilewidth, 0, 1); grid->addWidget(&widthlbl, 0, 0); + grid->addWidget(&tileheight, 1, 1); grid->addWidget(&heightlbl, 1, 0); + QLabel* flabel=new QLabel(&fontsize, "&Font:",this); + grid->addWidget(flabel, 2, 0); grid->addWidget(&fontsize, 2, 1); + QPushButton* dismiss=new QPushButton("Dismiss",this); + dismiss->setDefault(TRUE); + grid->addMultiCellWidget(dismiss, 3, 3, 0, 1); + grid->setRowStretch(3,0); + grid->setColStretch(0,1); + grid->setColStretch(1,2); + grid->activate(); + + connect(dismiss,SIGNAL(clicked()),this,SLOT(accept())); + resize(150,140); +} + +NetHackQtGlyphs& NetHackQtSettings::glyphs() +{ + return *theglyphs; +} + +void NetHackQtSettings::resizeTiles() +{ + int w = tilewidth.value(); + int h = tileheight.value(); + + theglyphs->resize(w,h); + emit tilesChanged(); +} + +const QFont& NetHackQtSettings::normalFont() +{ + static int size[]={ 18, 14, 12, 10, 8 }; + normal.setPointSize(size[fontsize.currentItem()]); + return normal; +} + +const QFont& NetHackQtSettings::normalFixedFont() +{ + static int size[]={ 18, 14, 13, 10, 8 }; + normalfixed.setPointSize(size[fontsize.currentItem()]); + return normalfixed; +} + +const QFont& NetHackQtSettings::largeFont() +{ + static int size[]={ 24, 18, 14, 12, 10 }; + large.setPointSize(size[fontsize.currentItem()]); + return large; +} + +bool NetHackQtSettings::ynInMessages() +{ + return !qt_compact_mode; +} + + +NetHackQtSettings* qt_settings; + + + +NetHackQtKeyBuffer::NetHackQtKeyBuffer() : + in(0), out(0) +{ +} + +bool NetHackQtKeyBuffer::Empty() const { return in==out; } +bool NetHackQtKeyBuffer::Full() const { return (in+1)%maxkey==out; } + +void NetHackQtKeyBuffer::Put(int k, int a, int state) +{ + if ( Full() ) return; // Safety + key[in]=k; + ascii[in]=a; + in=(in+1)%maxkey; +} + +void NetHackQtKeyBuffer::Put(char a) +{ + Put(0,a,0); +} + +void NetHackQtKeyBuffer::Put(const char* str) +{ + while (*str) Put(*str++); +} + +int NetHackQtKeyBuffer::GetKey() +{ + if ( Empty() ) return 0; + int r=TopKey(); + out=(out+1)%maxkey; + return r; +} + +int NetHackQtKeyBuffer::GetAscii() +{ + if ( Empty() ) return 0; // Safety + int r=TopAscii(); + out=(out+1)%maxkey; + return r; +} + +int NetHackQtKeyBuffer::GetState() +{ + if ( Empty() ) return 0; + int r=TopState(); + out=(out+1)%maxkey; + return r; +} + +int NetHackQtKeyBuffer::TopKey() const +{ + if ( Empty() ) return 0; + return key[out]; +} + +int NetHackQtKeyBuffer::TopAscii() const +{ + if ( Empty() ) return 0; + return ascii[out]; +} + +int NetHackQtKeyBuffer::TopState() const +{ + if ( Empty() ) return 0; + return state[out]; +} + + +NetHackQtClickBuffer::NetHackQtClickBuffer() : + in(0), out(0) +{ +} + +bool NetHackQtClickBuffer::Empty() const { return in==out; } +bool NetHackQtClickBuffer::Full() const { return (in+1)%maxclick==out; } + +void NetHackQtClickBuffer::Put(int x, int y, int mod) +{ + click[in].x=x; + click[in].y=y; + click[in].mod=mod; + in=(in+1)%maxclick; +} + +int NetHackQtClickBuffer::NextX() const { return click[out].x; } +int NetHackQtClickBuffer::NextY() const { return click[out].y; } +int NetHackQtClickBuffer::NextMod() const { return click[out].mod; } + +void NetHackQtClickBuffer::Get() +{ + out=(out+1)%maxclick; +} + +class NhPSListViewItem : public QListViewItem { +public: + NhPSListViewItem( QListView* parent, const QString& name ) : + QListViewItem(parent, name) + { + } + + void setGlyph(int g) + { + NetHackQtGlyphs& glyphs = qt_settings->glyphs(); + int gw = glyphs.width(); + int gh = glyphs.height(); + QPixmap pm(gw,gh); + QPainter p(&pm); + glyphs.drawGlyph(p, g, 0, 0); + p.end(); + setPixmap(0,pm); + setHeight(QMAX(pm.height()+1,height())); + } + + void paintCell( QPainter *p, const QColorGroup &cg, + int column, int width, int alignment ) + { + if ( isSelectable() ) { + QListViewItem::paintCell( p, cg, column, width, alignment ); + } else { + QColorGroup disabled( + cg.foreground().light(), + cg.button().light(), + cg.light(), cg.dark(), cg.mid(), + gray, cg.base() ); + QListViewItem::paintCell( p, disabled, column, width, alignment ); + } + } +}; + +class NhPSListViewRole : public NhPSListViewItem { +public: + NhPSListViewRole( QListView* parent, int id ) : + NhPSListViewItem(parent, +#ifdef QT_CHOOSE_RACE_FIRST // Lowerize - looks better + QString(QChar(roles[id].name.m[0])).lower()+QString(roles[id].name.m+1) +#else + roles[id].name.m +#endif + ) + { + setGlyph(monnum_to_glyph(roles[id].malenum)); + } +}; + +class NhPSListViewRace : public NhPSListViewItem { +public: + NhPSListViewRace( QListView* parent, int id ) : + NhPSListViewItem(parent, +#ifdef QT_CHOOSE_RACE_FIRST // Capitalize - looks better + QString(QChar(races[id].noun[0])).upper()+QString(races[id].noun+1) +#else + QString(QChar(races[id].noun[0])+QString(races[id].noun+1)) +#endif + ) + { + setGlyph(monnum_to_glyph(races[id].malenum)); + } +}; + +class NhPSListView : public QListView { +public: + NhPSListView( QWidget* parent ) : + QListView(parent) + { + setSorting(-1); // order is identity + header()->setClickEnabled(FALSE); + } + + QSizePolicy sizePolicy() const + { + return QSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding); + } + + QSize minimumSizeHint() const + { + return sizeHint(); + } + + QSize sizeHint() const + { + QListView::sizeHint(); + QSize sz = header()->sizeHint(); + int h=0; + QListViewItem* c=firstChild(); + while (c) h+=c->height(),c = c->nextSibling(); + sz += QSize(frameWidth()*2, h+frameWidth()*2); + return sz; + } + + int selectedItemNumber() const + { + int i=0; + QListViewItem* c = firstChild(); + while (c) { + if (c == selectedItem()) { + return i; + } + i++; + c = c->nextSibling(); + } + return -1; + } + + void setSelectedItemNumber(int i) + { + QListViewItem* c=firstChild(); + while (i--) + c = c->nextSibling(); + c->setSelected(TRUE); + } +}; + +NetHackQtPlayerSelector::NetHackQtPlayerSelector(NetHackQtKeyBuffer& ks) : + QDialog(0,"plsel",TRUE), + keysource(ks), + fully_specified_role(TRUE) +{ + /* + 0 1 2 + + Name ------------------------------------+ + 0 | | + + ---- ------------------------------------+ + + Role ---+ + Race ---+ + Gender ------+ + | | | | | * Male | + 1 | | | | | * Female | + | | | | +--------------+ + | | | | + | | | | + Alignment ---+ + 2 | | | | | * Male | + | | | | | * Female | + | | | | +--------------+ + 3 | | | | ...stretch... + | | | | + 4 | | | | [ Play ] + 5 | | | | [ Quit ] + +---------+ +---------+ + */ + + int marg=4; + QGridLayout *l = new QGridLayout(this,6,3,marg,marg); + + QButtonGroup* namebox = new QButtonGroup(1,Horizontal,"Name",this); + QLineEdit* name = new QLineEdit(namebox); + name->setMaxLength(sizeof(plname)-1); + if ( strncmp(plname,"player",6) && strncmp(plname,"games",5) ) + name->setText(plname); + connect(name, SIGNAL(textChanged(const QString&)), + this, SLOT(selectName(const QString&)) ); + name->setFocus(); + role = new NhPSListView(this); + race = new NhPSListView(this); + role->addColumn("Role"); + race->addColumn("Race"); + QButtonGroup* genderbox = new QButtonGroup("Sex",this); + QButtonGroup* alignbox = new QButtonGroup("Alignment",this); + QVBoxLayout* vbgb = new QVBoxLayout(genderbox,3,1); + vbgb->setAutoAdd(TRUE); + vbgb->addSpacing(fontMetrics().height()*3/4); + QVBoxLayout* vbab = new QVBoxLayout(alignbox,3,1); + vbab->setAutoAdd(TRUE); + vbab->addSpacing(fontMetrics().height()); + QLabel* logo = new QLabel("Qt NetHack" + "
by Warwick Allison
and the NetHack DevTeam
", this); + + l->addMultiCellWidget( namebox, 0,0,0,2 ); +#ifdef QT_CHOOSE_RACE_FIRST + l->addMultiCellWidget( race, 1,5,0,0 ); + l->addMultiCellWidget( role, 1,5,1,1 ); +#else + l->addMultiCellWidget( role, 1,5,0,0 ); + l->addMultiCellWidget( race, 1,5,1,1 ); +#endif + l->addWidget( genderbox, 1, 2 ); + l->addWidget( alignbox, 2, 2 ); + l->addWidget( logo, 3, 2, AlignCenter ); + l->setRowStretch( 3, 5 ); + + int i; + int nrole; + + for (nrole=0; roles[nrole].name.m; nrole++) + ; + for (i=nrole-1; i>=0; i--) { // XXX QListView unsorted goes in rev. + new NhPSListViewRole( role, i ); + } + connect( role, SIGNAL(selectionChanged()), this, SLOT(selectRole()) ); + + int nrace; + for (nrace=0; races[nrace].noun; nrace++) + ; + for (i=nrace-1; i>=0; i--) { + new NhPSListViewRace( race, i ); + } + connect( race, SIGNAL(selectionChanged()), this, SLOT(selectRace()) ); + + gender = new QRadioButton*[ROLE_GENDERS]; + for (i=0; iaddWidget( ok, 4, 2 ); + ok->setDefault(TRUE); + connect( ok, SIGNAL(clicked()), this, SLOT(accept()) ); + + QPushButton* cancel = new QPushButton("Quit",this); + l->addWidget( cancel, 5, 2 ); + connect( cancel, SIGNAL(clicked()), this, SLOT(reject()) ); + + // Randomize race and role, unless specified in config + int ro = flags.initrole; + if (ro == -1) { + ro = rn2(nrole); + fully_specified_role = FALSE; + } + int ra = flags.initrace; + if (ra == -1) { + ra = rn2(nrace); + fully_specified_role = FALSE; + } + + // make sure we have a valid combination, honoring + // the users request if possible. + bool choose_race_first; +#ifdef QT_CHOOSE_RACE_FIRST + choose_race_first = TRUE; + if (flags.initrole != -1 && flags.initrace == -1) { + choose_race_first = FALSE; + } +#else + choose_race_first = FALSE; + if (flags.initrace != -1 && flags.initrole == -1) { + choose_race_first = TRUE; + } +#endif + while (!validrace(ro,ra)) { + fully_specified_role = FALSE; + if (choose_race_first) { + ro = rn2(nrole); + } else { + ra = rn2(nrace); + } + } + + int g = flags.initgend; + if (g == -1) { + g = rn2(ROLE_GENDERS); + } + while (!validgend(ro,ra,g)) { + g = rn2(ROLE_GENDERS); + } + gender[g]->setChecked(TRUE); + selectGender(g); + + int a = flags.initalign; + if (a == -1) { + a = rn2(ROLE_ALIGNS); + } + while (!validalign(ro,ra,a)) { + a = rn2(ROLE_ALIGNS); + } + alignment[a]->setChecked(TRUE); + selectAlignment(g); + + QListViewItem* li; + + li = role->firstChild(); + while (ro--) li=li->nextSibling(); + role->setSelected(li,TRUE); + + li = race->firstChild(); + while (ra--) li=li->nextSibling(); + race->setSelected(li,TRUE); +} + + +void NetHackQtPlayerSelector::selectName(const QString& n) +{ + strncpy(plname,n.latin1(),sizeof(plname)-1); +} + +void NetHackQtPlayerSelector::selectRole() +{ + int ra = race->selectedItemNumber(); + int ro = role->selectedItemNumber(); + if (ra == -1 || ro == -1) return; + +#ifndef QT_CHOOSE_RACE_FIRST + selectRace(); +#else + QListViewItem* i=role->currentItem(); + QListViewItem* valid=0; + int j; + NhPSListViewItem* item; + item = (NhPSListViewItem*)role->firstChild(); + for (j=0; roles[j].name.m; j++) { + bool v = validrace(j,ra); + item->setSelectable(TRUE); + if ( !valid && v ) valid = item; + item=(NhPSListViewItem*)item->nextSibling(); + } + if ( !validrace(role->selectedItemNumber(),ra) ) + i = valid; + role->setSelected(i,TRUE); + item = (NhPSListViewItem*)role->firstChild(); + for (j=0; roles[j].name.m; j++) { + bool v = validrace(j,ra); + item->setSelectable(v); + item->repaint(); + item=(NhPSListViewItem*)item->nextSibling(); + } +#endif + + flags.initrole = role->selectedItemNumber(); + setupOthers(); +} + +void NetHackQtPlayerSelector::selectRace() +{ + int ra = race->selectedItemNumber(); + int ro = role->selectedItemNumber(); + if (ra == -1 || ro == -1) return; + +#ifdef QT_CHOOSE_RACE_FIRST + selectRole(); +#else + QListViewItem* i=race->currentItem(); + QListViewItem* valid=0; + int j; + NhPSListViewItem* item; + item = (NhPSListViewItem*)race->firstChild(); + for (j=0; races[j].noun; j++) { + bool v = validrace(ro,j); + item->setSelectable(TRUE); + if ( !valid && v ) valid = item; + item=(NhPSListViewItem*)item->nextSibling(); + } + if ( !validrace(ro,race->selectedItemNumber()) ) + i = valid; + race->setSelected(i,TRUE); + item = (NhPSListViewItem*)race->firstChild(); + for (j=0; races[j].noun; j++) { + bool v = validrace(ro,j); + item->setSelectable(v); + item->repaint(); + item=(NhPSListViewItem*)item->nextSibling(); + } +#endif + + flags.initrace = race->selectedItemNumber(); + setupOthers(); +} + +void NetHackQtPlayerSelector::setupOthers() +{ + int ro = role->selectedItemNumber(); + int ra = race->selectedItemNumber(); + int valid=-1; + int c=0; + int j; + for (j=0; jisChecked() ) + c = j; + gender[j]->setEnabled(v); + if ( valid<0 && v ) valid = j; + } + if ( !validgend(ro,ra,c) ) + c = valid; + int k; + for (k=0; ksetChecked(c==k); + } + selectGender(c); + + valid=-1; + for (j=0; jisChecked() ) + c = j; + alignment[j]->setEnabled(v); + if ( valid<0 && v ) valid = j; + } + if ( !validalign(ro,ra,c) ) + c = valid; + for (k=0; ksetChecked(c==k); + } + selectAlignment(c); +} + +void NetHackQtPlayerSelector::selectGender(int i) +{ + flags.initgend = i; +} + +void NetHackQtPlayerSelector::selectAlignment(int i) +{ + flags.initalign = i; +} + + +void NetHackQtPlayerSelector::done(int i) +{ + setResult(i); + qApp->exit_loop(); +} + +void NetHackQtPlayerSelector::Quit() +{ + done(R_Quit); + qApp->exit_loop(); +} + +void NetHackQtPlayerSelector::Random() +{ + done(R_Rand); + qApp->exit_loop(); +} + +bool NetHackQtPlayerSelector::Choose() +{ + if (fully_specified_role) return TRUE; + + if ( qt_compact_mode ) { + showMaximized(); + } else { + adjustSize(); + centerOnMain(this); + } + + if ( exec() ) { + return TRUE; + } else + return FALSE; +} + + +NetHackQtStringRequestor::NetHackQtStringRequestor(NetHackQtKeyBuffer& ks, const char* p, const char* cancelstr) : + QDialog(0,"string",FALSE), + prompt(p,this,"prompt"), + input(this,"input"), + keysource(ks) +{ + cancel=new QPushButton(cancelstr,this); + connect(cancel,SIGNAL(clicked()),this,SLOT(reject())); + + okay=new QPushButton("Okay",this); + connect(okay,SIGNAL(clicked()),this,SLOT(accept())); + connect(&input,SIGNAL(returnPressed()),this,SLOT(accept())); + + setFocusPolicy(StrongFocus); +} + +void NetHackQtStringRequestor::resizeEvent(QResizeEvent*) +{ + const int margin=5; + const int gutter=5; + + int h=(height()-margin*2-gutter); + + if (strlen(prompt.text()) > 16) { + h/=3; + prompt.setGeometry(margin,margin,width()-margin*2,h); + input.setGeometry(width()*1/5,margin+h+gutter, + (width()-margin-2-gutter)*4/5,h); + } else { + h/=2; + prompt.setGeometry(margin,margin,(width()-margin*2-gutter)*2/5,h); + input.setGeometry(prompt.geometry().right()+gutter,margin, + (width()-margin-2-gutter)*3/5,h); + } + + cancel->setGeometry(margin,input.geometry().bottom()+gutter, + (width()-margin*2-gutter)/2,h); + okay->setGeometry(cancel->geometry().right()+gutter,cancel->geometry().y(), + cancel->width(),h); +} + +void NetHackQtStringRequestor::SetDefault(const char* d) +{ + input.setText(d); +} +bool NetHackQtStringRequestor::Get(char* buffer, int maxchar) +{ + input.setMaxLength(maxchar); + if (strlen(prompt.text()) > 16) { + resize(fontMetrics().width(prompt.text())+50,fontMetrics().height()*6); + } else { + resize(fontMetrics().width(prompt.text())*2+50,fontMetrics().height()*4); + } + + centerOnMain(this); + show(); + input.setFocus(); + setResult(-1); + while (result()==-1) { + // Put keys in buffer (eg. from macros, from out-of-focus input) + if (!keysource.Empty()) { + while (!keysource.Empty()) { + int key=keysource.TopKey(); + int ascii=keysource.TopAscii(); + int state=keysource.GetState(); + if (ascii=='\r' || ascii=='\n') { + // CR or LF in buffer causes confirmation + strcpy(buffer,input.text()); + return TRUE; + } else if (ascii=='\033') { + return FALSE; + } else { + input.fakeEvent(key,ascii,state); + } + } + } + qApp->enter_loop(); + } + // XXX Get rid of extra keys, since we couldn't get focus! + while (!keysource.Empty()) keysource.GetKey(); + + if (result()) { + strcpy(buffer,input.text()); + return TRUE; + } else { + return FALSE; + } +} +void NetHackQtStringRequestor::done(int i) +{ + setResult(i); + qApp->exit_loop(); +} + + +NetHackQtWindow::NetHackQtWindow() +{ +} +NetHackQtWindow::~NetHackQtWindow() +{ +} + +// XXX Use "expected ..." for now, abort or default later. +// +void NetHackQtWindow::Clear() { puts("unexpected Clear"); } +void NetHackQtWindow::Display(bool block) { puts("unexpected Display"); } +bool NetHackQtWindow::Destroy() { return TRUE; } +void NetHackQtWindow::CursorTo(int x,int y) { puts("unexpected CursorTo"); } +void NetHackQtWindow::PutStr(int attr, const char* text) { puts("unexpected PutStr"); } +void NetHackQtWindow::StartMenu() { puts("unexpected StartMenu"); } +void NetHackQtWindow::AddMenu(int glyph, const ANY_P* identifier, char ch, char gch, int attr, + const char* str, bool presel) { puts("unexpected AddMenu"); } +void NetHackQtWindow::EndMenu(const char* prompt) { puts("unexpected EndMenu"); } +int NetHackQtWindow::SelectMenu(int how, MENU_ITEM_P **menu_list) { puts("unexpected SelectMenu"); return 0; } +void NetHackQtWindow::ClipAround(int x,int y) { puts("unexpected ClipAround"); } +void NetHackQtWindow::PrintGlyph(int x,int y,int glyph) { puts("unexpected PrintGlyph"); } +//void NetHackQtWindow::PrintGlyphCompose(int x,int y,int,int) { puts("unexpected PrintGlyphCompose"); } +void NetHackQtWindow::UseRIP(int how) { puts("unexpected UseRIP"); } + + + +// XXX Hmmm... crash after saving bones file if Map window is +// XXX deleted. Strange bug somewhere. +bool NetHackQtMapWindow::Destroy() { return FALSE; } + +NetHackQtMapWindow::NetHackQtMapWindow(NetHackQtClickBuffer& click_sink) : + clicksink(click_sink), + change(10), + rogue_font(0) +{ + viewport.addChild(this); + + setBackgroundColor(black); + viewport.setBackgroundColor(black); + + pet_annotation = QPixmap(qt_compact_mode ? pet_mark_small_xpm : pet_mark_xpm); + + cursor.setX(0); + cursor.setY(0); + Clear(); + + connect(qt_settings,SIGNAL(tilesChanged()),this,SLOT(updateTiles())); + connect(&viewport, SIGNAL(contentsMoving(int,int)), this, + SLOT(moveMessages(int,int))); + + updateTiles(); + //setFocusPolicy(StrongFocus); +} + +void NetHackQtMapWindow::moveMessages(int x, int y) +{ + QRect u = messages_rect; + messages_rect.moveTopLeft(QPoint(x,y)); + u |= messages_rect; + update(u); +} + +void NetHackQtMapWindow::clearMessages() +{ + messages = ""; + update(messages_rect); + messages_rect = QRect(); +} + +void NetHackQtMapWindow::putMessage(int attr, const char* text) +{ + if ( !messages.isEmpty() ) + messages += "\n"; + messages += text; + QFontMetrics fm = fontMetrics(); + messages_rect = fm.boundingRect(viewport.contentsX(),viewport.contentsY(),viewport.width(),0, WordBreak|AlignTop|AlignLeft|DontClip, messages); + update(messages_rect); +} + +void NetHackQtMapWindow::updateTiles() +{ + NetHackQtGlyphs& glyphs = qt_settings->glyphs(); + int gw = glyphs.width(); + int gh = glyphs.height(); + // Be exactly the size we want to be - full map... + resize(COLNO*gw,ROWNO*gh); + + viewport.verticalScrollBar()->setSteps(gh,gh); + viewport.horizontalScrollBar()->setSteps(gw,gw); + /* + viewport.setMaximumSize( + gw*COLNO + viewport.verticalScrollBar()->width(), + gh*ROWNO + viewport.horizontalScrollBar()->height() + ); + */ + viewport.updateScrollBars(); + + change.clear(); + change.add(0,0,COLNO,ROWNO); + delete rogue_font; rogue_font = 0; + Display(FALSE); + + emit resized(); +} + +NetHackQtMapWindow::~NetHackQtMapWindow() +{ + // Remove from viewport porthole, since that is a destructible member. + viewport.removeChild(this); + recreate(0,0,QPoint(0,0)); +} + +QWidget* NetHackQtMapWindow::Widget() +{ + return &viewport; +} + +void NetHackQtMapWindow::Scroll(int dx, int dy) +{ + if (viewport.horizontalScrollBar()->isVisible()) { + while (dx<0) { viewport.horizontalScrollBar()->subtractPage(); dx++; } + while (dx>0) { viewport.horizontalScrollBar()->addPage(); dx--; } + } + if (viewport.verticalScrollBar()->isVisible()) { + while (dy<0) { viewport.verticalScrollBar()->subtractPage(); dy++; } + while (dy>0) { viewport.verticalScrollBar()->addPage(); dy--; } + } +} + +void NetHackQtMapWindow::Clear() +{ + unsigned short stone=cmap_to_glyph(S_stone); + + for (int j=0; jpos().x()/qt_settings->glyphs().width(), + event->pos().y()/qt_settings->glyphs().height(), + event->button()==LeftButton ? CLICK_1 : CLICK_2 + ); + qApp->exit_loop(); +} + +#ifdef TEXTCOLOR +static +const QPen& nhcolor_to_pen(int c) +{ + static QPen* pen=0; + if ( !pen ) { + pen = new QPen[17]; + pen[0] = Qt::black; + pen[1] = Qt::red; + pen[2] = QColor(0,191,0); + pen[3] = QColor(127,127,0); + pen[4] = Qt::blue; + pen[5] = Qt::magenta; + pen[6] = Qt::cyan; + pen[7] = Qt::gray; + pen[8] = Qt::white; // no color + pen[9] = QColor(255,127,0); + pen[10] = QColor(127,255,127); + pen[11] = Qt::yellow; + pen[12] = QColor(127,127,255); + pen[13] = QColor(255,127,255); + pen[14] = QColor(127,255,255); + pen[15] = Qt::white; + pen[16] = Qt::black; + } + + return pen[c]; +} +#endif + +void NetHackQtMapWindow::paintEvent(QPaintEvent* event) +{ + QRect area=event->rect(); + QRect garea; + garea.setCoords( + QMAX(0,area.left()/qt_settings->glyphs().width()), + QMAX(0,area.top()/qt_settings->glyphs().height()), + QMIN(COLNO-1,area.right()/qt_settings->glyphs().width()), + QMIN(ROWNO-1,area.bottom()/qt_settings->glyphs().height()) + ); + + QPainter painter; + + painter.begin(this); + +#ifdef REINCARNATION + if (Is_rogue_level(&u.uz)) { + // You enter a VERY primitive world! + + painter.setClipRect( event->rect() ); // (normally we don't clip) + painter.fillRect( event->rect(), black ); + + int offset; + + if ( !rogue_font ) { + // Find font... + int pts = 5; + while ( pts < 32 ) { + painter.setFont(QFont("Courier", pts)); + QFontMetrics fm = painter.fontMetrics(); + if ( fm.width("M") > qt_settings->glyphs().width() ) + break; + if ( fm.height() > qt_settings->glyphs().height() ) + break; + pts++; + } + rogue_font = new QFont("Courier",pts-1); + } + painter.setFont(*rogue_font); + + for (int j=garea.top(); j<=garea.bottom(); j++) { + for (int i=garea.left(); i<=garea.right(); i++) { + unsigned short g=Glyph(i,j); + uchar ch; + int color, och; + unsigned special; + + painter.setPen( green ); + /* map glyph to character and color */ + mapglyph(g, &och, &color, &special, i, j); + ch = (uchar)och; +#ifdef TEXTCOLOR + painter.setPen( nhcolor_to_pen(color) ); +#endif + painter.drawText( + i*qt_settings->glyphs().width(), + j*qt_settings->glyphs().height(), + qt_settings->glyphs().width(), + qt_settings->glyphs().height(), + AlignCenter, + (const char*)&ch, 1 + ); + if (glyph_is_pet(g) +#ifdef TEXTCOLOR + && ::iflags.hilite_pet +#endif + ) { + painter.drawPixmap(QPoint(i*qt_settings->glyphs().width(), j*qt_settings->glyphs().height()), pet_annotation); + } + } + } + } else +#endif + { + for (int j=garea.top(); j<=garea.bottom(); j++) { + for (int i=garea.left(); i<=garea.right(); i++) { + unsigned short g=Glyph(i,j); + qt_settings->glyphs().drawCell(painter, g, i, j); + if (glyph_is_pet(g) +#ifdef TEXTCOLOR + && ::iflags.hilite_pet +#endif + ) { + painter.drawPixmap(QPoint(i*qt_settings->glyphs().width(), j*qt_settings->glyphs().height()), pet_annotation); + } + } + } + } + + if (garea.contains(cursor)) { +#ifdef REINCARNATION + if (Is_rogue_level(&u.uz)) { +#ifdef TEXTCOLOR + painter.setPen( white ); +#else + painter.setPen( green ); // REALLY primitive +#endif + } else +#endif + { + int hp100; + if (u.mtimedone) { + hp100=u.mhmax ? u.mh*100/u.mhmax : 100; + } else { + hp100=u.uhpmax ? u.uhp*100/u.uhpmax : 100; + } + + if (hp100 > 75) painter.setPen(white); + else if (hp100 > 50) painter.setPen(yellow); + else if (hp100 > 25) painter.setPen(QColor(0xff,0xbf,0x00)); // orange + else if (hp100 > 10) painter.setPen(red); + else painter.setPen(magenta); + } + + painter.drawRect( + cursor.x()*qt_settings->glyphs().width(),cursor.y()*qt_settings->glyphs().height(), + qt_settings->glyphs().width(),qt_settings->glyphs().height()); + } + + if (area.intersects(messages_rect)) { + painter.setPen(white); + painter.drawText(viewport.contentsX(),viewport.contentsY(), + viewport.width(),0, WordBreak|AlignTop|AlignLeft|DontClip, messages); + } + + painter.end(); +} + +void NetHackQtMapWindow::Display(bool block) +{ + for (int i=0; iglyphs().width(), + ch.y()*qt_settings->glyphs().height(), + ch.width()*qt_settings->glyphs().width(), + ch.height()*qt_settings->glyphs().height(), + FALSE + ); + } + + change.clear(); + + if (block) { + yn_function("Press a key when done viewing",0,'\0'); + } +} + +void NetHackQtMapWindow::CursorTo(int x,int y) +{ + Changed(cursor.x(),cursor.y()); + cursor.setX(x); + cursor.setY(y); + Changed(cursor.x(),cursor.y()); +} + +void NetHackQtMapWindow::PutStr(int attr, const char* text) +{ + puts("unexpected PutStr in MapWindow"); +} + +void NetHackQtMapWindow::ClipAround(int x,int y) +{ + // Convert to pixel of center of tile + x=x*qt_settings->glyphs().width()+qt_settings->glyphs().width()/2; + y=y*qt_settings->glyphs().height()+qt_settings->glyphs().height()/2; + + // Then ensure that pixel is visible + viewport.center(x,y,0.45,0.45); +} + +void NetHackQtMapWindow::PrintGlyph(int x,int y,int glyph) +{ + Glyph(x,y)=glyph; + Changed(x,y); +} + +//void NetHackQtMapWindow::PrintGlyphCompose(int x,int y,int glyph1, int glyph2) +//{ + // TODO: composed graphics +//} + +void NetHackQtMapWindow::Changed(int x, int y) +{ + change.add(x,y); +} + + +class NetHackQtScrollText : public QTableView { + struct UData { + UData() : text(0), attr(0) { } + ~UData() { if (text) free(text); } + + char* text; + int attr; + }; +public: + int uncleared; + + NetHackQtScrollText(int maxlength) : + maxitems(maxlength), + first(0), + count(0), + uncleared(0), + item_cycle(maxlength) + { + setNumCols(1); + setCellWidth(200); + setCellHeight(fontMetrics().height()); + setBackgroundColor(white); + setTableFlags(Tbl_vScrollBar + |Tbl_autoHScrollBar + |Tbl_clipCellPainting + |Tbl_smoothScrolling); + } + + ~NetHackQtScrollText() + { + } + + void Scroll(int dx, int dy) + { + setXOffset(xOffset()+dx*viewWidth()); + setYOffset(yOffset()+dy*viewHeight()); + } + + void insertItem(int attr, const char* text) + { + setTopCell(count); + + setAutoUpdate(FALSE); + + int i; + if (count cellWidth()) { + // Get wider. + setCellWidth(w); + } + setTopCell(count); + + setAutoUpdate(TRUE); + + if (viewHeight() >= totalHeight()-cellHeight()) { + repaint(); + } else { + scroll(0,cellHeight()); + } + } + + virtual void setFont(const QFont& font) + { + QTableView::setFont(font); + setCellHeight(fontMetrics().height()); + } + +protected: + + UData& item(int i) + { + return item_cycle[(first+i)%maxitems]; + } + + const int maxitems; + int first, count; + QArray item_cycle; + + int datumWidth(const UData& uitem) + { + if (uitem.text) { + int width=fontMetrics().width(uitem.text)+3; + if (uitem.attr) { + // XXX Too expensive to do properly, because + // XXX we have to set the font of the widget + // XXX just to get the font metrics information! + // XXX Could hold a fake widget for that + // XXX purpose, but this hack is less ugly. + width+=width/10; + } + return width; + } else { + return 0; + } + } + + virtual void setupPainter(QPainter *p) + { + // XXX This shouldn't be needed - we set the bg in the constructor. + p->setBackgroundColor(white); + } + + virtual void paintCell(QPainter *p, int row, int col) + { + bool sel=FALSE; + UData& uitem=item(row); + + if (!sel && row < count-uncleared) { + p->setPen(darkGray); + } else { + p->setPen(black); + } + + if (uitem.attr) { + // XXX only bold + QFont bold(font().family(),font().pointSize(),QFont::Bold); + p->setFont(bold); + } + + p->drawText(3, 0, cellWidth(), cellHeight(), + AlignLeft|AlignVCenter, uitem.text); + + if (uitem.attr) { + p->setFont(font()); + } + } +}; + +NetHackQtMessageWindow::NetHackQtMessageWindow() : + list(new NetHackQtScrollText(::iflags.msg_history)) +{ + ::iflags.window_inited = 1; + map = 0; + connect(qt_settings,SIGNAL(fontChanged()),this,SLOT(updateFont())); + updateFont(); +} + +NetHackQtMessageWindow::~NetHackQtMessageWindow() +{ + ::iflags.window_inited = 0; + delete list; +} + +QWidget* NetHackQtMessageWindow::Widget() { return list; } + +void NetHackQtMessageWindow::setMap(NetHackQtMapWindow* m) +{ + map = m; + updateFont(); +} + +void NetHackQtMessageWindow::updateFont() +{ + list->setFont(qt_settings->normalFont()); + if ( map ) + map->setFont(qt_settings->normalFont()); +} + +void NetHackQtMessageWindow::Scroll(int dx, int dy) +{ + list->Scroll(dx,dy); +} + +void NetHackQtMessageWindow::Clear() +{ + if ( map ) + map->clearMessages(); + if (list->uncleared) { + list->uncleared=0; + changed=TRUE; + Display(FALSE); + } +} + +void NetHackQtMessageWindow::Display(bool block) +{ + if (changed) { + list->repaint(); + changed=FALSE; + } +} + +void NetHackQtMessageWindow::PutStr(int attr, const char* text) +{ +#ifdef USER_SOUNDS + play_sound_for_message(text); +#endif + + changed=TRUE; + list->uncleared++; + list->insertItem(attr,text); + + // Force scrollbar to bottom + // XXX list->setTopItem(list->count()); + + if ( map ) + map->putMessage(attr, text); +} + + + +NetHackQtLabelledIcon::NetHackQtLabelledIcon(QWidget* parent, const char* l) : + QWidget(parent), + turn_count(-1), + label(new QLabel(l,this)), + icon(0), + low_is_good(FALSE), + prev_value(-123) +{ + initHighlight(); +} +NetHackQtLabelledIcon::NetHackQtLabelledIcon(QWidget* parent, const char* l, const QPixmap& i) : + QWidget(parent), + low_is_good(FALSE), + turn_count(-1), + label(new QLabel(l,this)), + icon(new QLabel(this)), + prev_value(-123) +{ + setIcon(i); + initHighlight(); +} +void NetHackQtLabelledIcon::initHighlight() +{ + const QPalette& pal=palette(); + const QColorGroup& pa=pal.normal(); + //QColorGroup good(white,darkGreen,pa.light(),pa.dark(),pa.mid(),white,pa.base()); + QColorGroup good(black,green,pa.light(),pa.dark(),pa.mid(),black,pa.base()); + QColorGroup bad(white,red,pa.light(),pa.dark(),pa.mid(),white,pa.base()); + hl_good=pal.copy(); + hl_good.setNormal(good); + hl_good.setActive(good); + hl_bad=pal.copy(); + hl_bad.setNormal(bad); + hl_bad.setActive(bad); +} + +void NetHackQtLabelledIcon::setLabel(const char* t, bool lower) +{ + if (!label) { + label=new QLabel(this); + label->setFont(font()); + resizeEvent(0); + } + if (0!=strcmp(label->text(),t)) { + label->setText(t); + highlight(lower==low_is_good ? hl_good : hl_bad); + } +} +void NetHackQtLabelledIcon::setLabel(const char* t, long v, long cv, const char* tail) +{ + char buf[BUFSZ]; + if (v==NoNum) { + Sprintf(buf,"%s%s",t,tail); + } else { + Sprintf(buf,"%s%ld%s",t,v,tail); + } + setLabel(buf,cvsetPixmap(i); + else { icon=new QLabel(this); icon->setPixmap(i); resizeEvent(0); } + icon->resize(i.width(),i.height()); +} +void NetHackQtLabelledIcon::setFont(const QFont& f) +{ + QWidget::setFont(f); + if (label) label->setFont(f); +} +void NetHackQtLabelledIcon::show() +{ + if (!isVisible()) highlight(hl_bad); + QWidget::show(); +} +void NetHackQtLabelledIcon::highlightWhenChanging() +{ + turn_count=0; +} +void NetHackQtLabelledIcon::lowIsGood() +{ + low_is_good=TRUE; +} +void NetHackQtLabelledIcon::dissipateHighlight() +{ + if (turn_count>0) { + turn_count--; + if (!turn_count) + unhighlight(); + } +} +void NetHackQtLabelledIcon::highlight(const QPalette& hl) +{ + if (label) { // Surely it is?! + if (turn_count>=0) { + label->setPalette(hl); + turn_count=4; + // `4' includes this turn, so dissipates after + // 3 more keypresses. + } else { + label->setPalette(palette()); + } + } +} +void NetHackQtLabelledIcon::unhighlight() +{ + if (label) { // Surely it is?! + label->setPalette(palette()); + } +} +void NetHackQtLabelledIcon::resizeEvent(QResizeEvent*) +{ + setAlignments(); + + //int labw=label ? label->fontMetrics().width(label->text()) : 0; + int labh=label ? label->fontMetrics().height() : 0; + int icoh=icon ? icon->height() : 0; + int h=icoh+labh; + int icoy=(h>height() ? height()-labh-icoh : height()/2-h/2); + int laby=icoy+icoh; + if (icon) { + icon->setGeometry(0,icoy,width(),icoh); + } + if (label) { + label->setGeometry(0,laby,width(),labh); + } +} + +void NetHackQtLabelledIcon::setAlignments() +{ + if (label) label->setAlignment(AlignHCenter|AlignVCenter); + if (icon) icon->setAlignment(AlignHCenter|AlignVCenter); +} + +static void +tryload(QPixmap& pm, const char* fn) +{ + if (!pm.load(fn)) { + QString msg; + msg.sprintf("Cannot load \"%s\"", fn); + QMessageBox::warning(0, "IO Error", msg); + } +} + +NetHackQtStatusWindow::NetHackQtStatusWindow() : + // Notes: + // Alignment needs -2 init value, because -1 is an alignment. + // Armor Class is an schar, so 256 is out of range. + // Blank value is 0 and should never change. + str(this,"STR"), + dex(this,"DEX"), + con(this,"CON"), + intel(this,"INT"), + wis(this,"WIS"), + cha(this,"CHA"), + name(this,"(name)"), + dlevel(this,"(dlevel)"), + gold(this,"Gold"), + hp(this,"Hit Points"), + power(this,"Power"), + ac(this,"Armour Class"), + level(this,"Level"), + exp(this,"Experience"), + align(this,"Alignment"), + time(this,"Time"), + score(this,"Score"), + hunger(this,""), + confused(this,"Confused"), + sick_fp(this,"Sick"), + sick_il(this,"Ill"), + blind(this,"Blind"), + stunned(this,"Stunned"), + hallu(this,"Hallu"), + encumber(this,""), + hline1(this), + hline2(this), + hline3(this), + first_set(TRUE) +{ + p_str = QPixmap(str_xpm); + p_str = QPixmap(str_xpm); + p_dex = QPixmap(dex_xpm); + p_con = QPixmap(cns_xpm); + p_int = QPixmap(int_xpm); + p_wis = QPixmap(wis_xpm); + p_cha = QPixmap(cha_xpm); + + p_chaotic = QPixmap(chaotic_xpm); + p_neutral = QPixmap(neutral_xpm); + p_lawful = QPixmap(lawful_xpm); + + p_satiated = QPixmap(satiated_xpm); + p_hungry = QPixmap(hungry_xpm); + + p_confused = QPixmap(confused_xpm); + p_sick_fp = QPixmap(sick_fp_xpm); + p_sick_il = QPixmap(sick_il_xpm); + p_blind = QPixmap(blind_xpm); + p_stunned = QPixmap(stunned_xpm); + p_hallu = QPixmap(hallu_xpm); + + p_encumber[0] = QPixmap(slt_enc_xpm); + p_encumber[1] = QPixmap(mod_enc_xpm); + p_encumber[2] = QPixmap(hvy_enc_xpm); + p_encumber[3] = QPixmap(ext_enc_xpm); + p_encumber[4] = QPixmap(ovr_enc_xpm); + + str.setIcon(p_str); + dex.setIcon(p_dex); + con.setIcon(p_con); + intel.setIcon(p_int); + wis.setIcon(p_wis); + cha.setIcon(p_cha); + + align.setIcon(p_neutral); + hunger.setIcon(p_hungry); + + confused.setIcon(p_confused); + sick_fp.setIcon(p_sick_fp); + sick_il.setIcon(p_sick_il); + blind.setIcon(p_blind); + stunned.setIcon(p_stunned); + hallu.setIcon(p_hallu); + + encumber.setIcon(p_encumber[0]); + + hline1.setFrameStyle(QFrame::HLine|QFrame::Sunken); + hline2.setFrameStyle(QFrame::HLine|QFrame::Sunken); + hline3.setFrameStyle(QFrame::HLine|QFrame::Sunken); + hline1.setLineWidth(1); + hline2.setLineWidth(1); + hline3.setLineWidth(1); + + connect(qt_settings,SIGNAL(fontChanged()),this,SLOT(doUpdate())); + doUpdate(); +} + +void NetHackQtStatusWindow::doUpdate() +{ + const QFont& large=qt_settings->largeFont(); + name.setFont(large); + dlevel.setFont(large); + + const QFont& normal=qt_settings->normalFont(); + str.setFont(normal); + dex.setFont(normal); + con.setFont(normal); + intel.setFont(normal); + wis.setFont(normal); + cha.setFont(normal); + gold.setFont(normal); + hp.setFont(normal); + power.setFont(normal); + ac.setFont(normal); + level.setFont(normal); + exp.setFont(normal); + align.setFont(normal); + time.setFont(normal); + score.setFont(normal); + hunger.setFont(normal); + confused.setFont(normal); + sick_fp.setFont(normal); + sick_il.setFont(normal); + blind.setFont(normal); + stunned.setFont(normal); + hallu.setFont(normal); + encumber.setFont(normal); +} + +QWidget* NetHackQtStatusWindow::Widget() { return this; } + +void NetHackQtStatusWindow::Clear() +{ +} +void NetHackQtStatusWindow::Display(bool block) +{ +} +void NetHackQtStatusWindow::CursorTo(int,int y) +{ + cursy=y; +} +void NetHackQtStatusWindow::PutStr(int attr, const char* text) +{ + // do a complete update when line 0 is done (as per X11 fancy status) + if (cursy==0) updateStats(); +} + +void NetHackQtStatusWindow::resizeEvent(QResizeEvent*) +{ + const float SP_name=0.13; // the (large) + const float SP_dlev=0.13; // Level 3 in The Dungeons of Doom (large) + const float SP_atr1=0.25; // STR DEX CON INT WIS CHA + const float SP_hln1=0.02; // --- + const float SP_atr2=0.09; // Au HP PW AC LVL EXP + const float SP_hln2=0.02; // --- + const float SP_time=0.09; // time score + const float SP_hln3=0.02; // --- + const float SP_stat=0.25; // Alignment, Poisoned, Hungry, Sick, etc. + + int h=height(); + int x=0,y=0; + double space=1.0; + + int iw; // Width of an item across line + int lh; // Height of a line of values + + lh=int(h*SP_name); + name.setGeometry(0,0,width(),lh); y+=lh; + lh=int(h*SP_dlev); + dlevel.setGeometry(0,y,width(),lh); y+=lh; + + lh=int(h*SP_hln1); + hline1.setGeometry(0,y,width(),lh); y+=lh; + + lh=int(h*SP_atr1); + iw=width()/6; + str.setGeometry(x,y,iw,lh); x+=iw; + dex.setGeometry(x,y,iw,lh); x+=iw; + con.setGeometry(x,y,iw,lh); x+=iw; + intel.setGeometry(x,y,iw,lh); x+=iw; + wis.setGeometry(x,y,iw,lh); x+=iw; + cha.setGeometry(x,y,iw,lh); x+=iw; + x=0; y+=lh; + + lh=int(h*SP_hln2); + hline2.setGeometry(0,y,width(),lh); y+=lh; + + lh=int(h*SP_atr2); + iw=width()/6; + gold.setGeometry(x,y,iw,lh); x+=iw; + hp.setGeometry(x,y,iw,lh); x+=iw; + power.setGeometry(x,y,iw,lh); x+=iw; + ac.setGeometry(x,y,iw,lh); x+=iw; + level.setGeometry(x,y,iw,lh); x+=iw; + exp.setGeometry(x,y,iw,lh); x+=iw; + x=0; y+=lh; + + lh=int(h*SP_hln3); + hline3.setGeometry(0,y,width(),lh); y+=lh; + + lh=int(h*SP_time); + iw=width()/3; x+=iw/2; + time.setGeometry(x,y,iw,lh); x+=iw; + score.setGeometry(x,y,iw,lh); x+=iw; + x=0; y+=lh; + + lh=int(h*SP_stat); + iw=width()/9; + align.setGeometry(x,y,iw,lh); x+=iw; + hunger.setGeometry(x,y,iw,lh); x+=iw; + confused.setGeometry(x,y,iw,lh); x+=iw; + sick_fp.setGeometry(x,y,iw,lh); x+=iw; + sick_il.setGeometry(x,y,iw,lh); x+=iw; + blind.setGeometry(x,y,iw,lh); x+=iw; + stunned.setGeometry(x,y,iw,lh); x+=iw; + hallu.setGeometry(x,y,iw,lh); x+=iw; + encumber.setGeometry(x,y,iw,lh); x+=iw; + x=0; y+=lh; +} + + +/* + * Set all widget values to a null string. This is used after all spacings + * have been calculated so that when the window is popped up we don't get all + * kinds of funny values being displayed. + */ +void NetHackQtStatusWindow::nullOut() +{ +} + +void NetHackQtStatusWindow::fadeHighlighting() +{ + name.dissipateHighlight(); + dlevel.dissipateHighlight(); + + str.dissipateHighlight(); + dex.dissipateHighlight(); + con.dissipateHighlight(); + intel.dissipateHighlight(); + wis.dissipateHighlight(); + cha.dissipateHighlight(); + + gold.dissipateHighlight(); + hp.dissipateHighlight(); + power.dissipateHighlight(); + ac.dissipateHighlight(); + level.dissipateHighlight(); + exp.dissipateHighlight(); + align.dissipateHighlight(); + + time.dissipateHighlight(); + score.dissipateHighlight(); + + hunger.dissipateHighlight(); + confused.dissipateHighlight(); + sick_fp.dissipateHighlight(); + sick_il.dissipateHighlight(); + blind.dissipateHighlight(); + stunned.dissipateHighlight(); + hallu.dissipateHighlight(); + encumber.dissipateHighlight(); +} + +/* + * Update the displayed status. The current code in botl.c updates + * two lines of information. Both lines are always updated one after + * the other. So only do our update when we update the second line. + * + * Information on the first line: + * name, attributes, alignment, score + * + * Information on the second line: + * dlvl, gold, hp, power, ac, {level & exp or HD **} + * status (hunger, conf, halu, stun, sick, blind), time, encumbrance + * + * [**] HD is shown instead of level and exp if mtimedone is non-zero. + */ +void NetHackQtStatusWindow::updateStats() +{ + //if (!isVisible()) return; + + char buf[BUFSZ]; + + if (cursy != 0) return; /* do a complete update when line 0 is done */ + + if (ACURR(A_STR) > 118) { + Sprintf(buf,"STR:%d",ACURR(A_STR)-100); + } else if (ACURR(A_STR)==118) { + Sprintf(buf,"STR:18/**"); + } else if(ACURR(A_STR) > 18) { + Sprintf(buf,"STR:18/%02d",ACURR(A_STR)-18); + } else { + Sprintf(buf,"STR:%d",ACURR(A_STR)); + } + str.setLabel(buf,NetHackQtLabelledIcon::NoNum,ACURR(A_STR)); + + dex.setLabel("DEX:",(long)ACURR(A_DEX)); + con.setLabel("CON:",(long)ACURR(A_CON)); + intel.setLabel("INT:",(long)ACURR(A_INT)); + wis.setLabel("WIS:",(long)ACURR(A_WIS)); + cha.setLabel("CHA:",(long)ACURR(A_CHA)); + const char* hung=hu_stat[u.uhs]; + if (hung[0]==' ') { + hunger.hide(); + } else { + hunger.setIcon(u.uhs ? p_hungry : p_satiated); + hunger.setLabel(hung); + hunger.show(); + } + if (Confusion) confused.show(); else confused.hide(); + if (Sick) { + if (u.usick_type & SICK_VOMITABLE) { + sick_fp.show(); + } else { + sick_fp.hide(); + } + if (u.usick_type & SICK_NONVOMITABLE) { + sick_il.show(); + } else { + sick_il.hide(); + } + } else { + sick_fp.hide(); + sick_il.hide(); + } + if (Blind) blind.show(); else blind.hide(); + if (Stunned) stunned.show(); else stunned.hide(); + if (Hallucination) hallu.show(); else hallu.hide(); + const char* enc=enc_stat[near_capacity()]; + if (enc[0]==' ' || !enc[0]) { + encumber.hide(); + } else { + encumber.setIcon(p_encumber[near_capacity()-1]); + encumber.setLabel(enc); + encumber.show(); + } + Strcpy(buf, plname); + if ('a' <= buf[0] && buf[0] <= 'z') buf[0] += 'A'-'a'; + Strcat(buf, " the "); + if (u.mtimedone) { + char mname[BUFSZ]; + int k = 0; + + Strcpy(mname, mons[u.umonnum].mname); + while(mname[k] != 0) { + if ((k == 0 || (k > 0 && mname[k-1] == ' ')) + && 'a' <= mname[k] && mname[k] <= 'z') + { + mname[k] += 'A' - 'a'; + } + k++; + } + Strcat(buf, mname); + } else { + Strcat(buf, rank_of(u.ulevel, pl_character[0], ::flags.female)); + } + name.setLabel(buf,NetHackQtLabelledIcon::NoNum,u.ulevel); + + if (describe_level(buf)) { + dlevel.setLabel(buf,TRUE); + } else { + Sprintf(buf, "%s, level ", dungeons[u.uz.dnum].dname); + dlevel.setLabel(buf,(long)depth(&u.uz)); + } + + gold.setLabel("Au:",(long)u.ugold); + if (u.mtimedone) { + // You're a monster! + + Sprintf(buf, "/%d", u.mhmax); + hp.setLabel("HP:",u.mh > 0 ? u.mh : 0,buf); + level.setLabel("HD:",(long)mons[u.umonnum].mlevel); + } else { + // You're normal. + + Sprintf(buf, "/%d", u.uhpmax); + hp.setLabel("HP:",u.uhp > 0 ? u.uhp : 0,buf); + level.setLabel("Level:",(long)u.ulevel); + } + Sprintf(buf, "/%d", u.uenmax); + power.setLabel("Pow:",u.uen,buf); + ac.setLabel("AC:",(long)u.uac); +#ifdef EXP_ON_BOTL + if (::flags.showexp) { + exp.setLabel("Exp:",(long)u.uexp); + } else +#endif + { + exp.setLabel(""); + } + if (u.ualign.type==A_CHAOTIC) { + align.setIcon(p_chaotic); + align.setLabel("Chaotic"); + } else if (u.ualign.type==A_NEUTRAL) { + align.setIcon(p_neutral); + align.setLabel("Neutral"); + } else { + align.setIcon(p_lawful); + align.setLabel("Lawful"); + } + + if (::flags.time) time.setLabel("Time:",(long)moves); + else time.setLabel(""); +#ifdef SCORE_ON_BOTL + if (::flags.showscore) { + score.setLabel("Score:",(long)botl_score()); + } else +#endif + { + score.setLabel(""); + } + + if (first_set) + { + first_set=FALSE; + + name.highlightWhenChanging(); + dlevel.highlightWhenChanging(); + + str.highlightWhenChanging(); + dex.highlightWhenChanging(); + con.highlightWhenChanging(); + intel.highlightWhenChanging(); + wis.highlightWhenChanging(); + cha.highlightWhenChanging(); + + gold.highlightWhenChanging(); + hp.highlightWhenChanging(); + power.highlightWhenChanging(); + ac.highlightWhenChanging(); ac.lowIsGood(); + level.highlightWhenChanging(); + exp.highlightWhenChanging(); + align.highlightWhenChanging(); + + //time.highlightWhenChanging(); + score.highlightWhenChanging(); + + hunger.highlightWhenChanging(); + confused.highlightWhenChanging(); + sick_fp.highlightWhenChanging(); + sick_il.highlightWhenChanging(); + blind.highlightWhenChanging(); + stunned.highlightWhenChanging(); + hallu.highlightWhenChanging(); + encumber.highlightWhenChanging(); + } +} + +/* + * Turn off hilighted status values after a certain amount of turns. + */ +void NetHackQtStatusWindow::checkTurnEvents() +{ +} + + + +NetHackQtMenuDialog::NetHackQtMenuDialog() : + QDialog(0,0,FALSE) +{ +} + +void NetHackQtMenuDialog::resizeEvent(QResizeEvent*) +{ + emit Resized(); +} + +void NetHackQtMenuDialog::Accept() +{ + accept(); +} + +void NetHackQtMenuDialog::Reject() +{ + reject(); +} + +void NetHackQtMenuDialog::SetResult(int r) +{ + setResult(r); +} + +void NetHackQtMenuDialog::done(int i) +{ + setResult(i); + qApp->exit_loop(); +} + +// Table view columns: +// +// [pick-count] [accel] [glyph] [string] +// +// Maybe accel should be near string. We'll see. +// pick-count normally blank. +// double-clicking or click-on-count gives pop-up entry +// string is green when selected +// +NetHackQtMenuWindow::NetHackQtMenuWindow(NetHackQtKeyBuffer& ks) : + QTableView(), + keysource(ks), + dialog(new NetHackQtMenuDialog()), + pressed(-1), + prompt(0) +{ + setNumCols(4); + setCellHeight(QMAX(qt_settings->glyphs().height()+1,fontMetrics().height())); + setBackgroundColor(lightGray); + setFrameStyle(Panel|Sunken); + setLineWidth(2); + + int x=0; + + ok=new QPushButton("Ok",dialog); + connect(ok,SIGNAL(clicked()),dialog,SLOT(accept())); + + cancel=new QPushButton("Cancel",dialog); + connect(cancel,SIGNAL(clicked()),dialog,SLOT(reject())); + + all=new QPushButton("All",dialog); + connect(all,SIGNAL(clicked()),this,SLOT(All())); + + none=new QPushButton("None",dialog); + connect(none,SIGNAL(clicked()),this,SLOT(ChooseNone())); + + invert=new QPushButton("Invert",dialog); + connect(invert,SIGNAL(clicked()),this,SLOT(Invert())); + + search=new QPushButton("Search",dialog); + connect(search,SIGNAL(clicked()),this,SLOT(Search())); + + QPoint pos(0,ok->height()); + recreate(dialog,0,pos); + prompt.recreate(dialog,0,pos); + + setBackgroundColor(lightGray); + + connect(dialog,SIGNAL(Resized()),this,SLOT(Layout())); + + setTableFlags(Tbl_autoHScrollBar|Tbl_autoVScrollBar + |Tbl_smoothScrolling|Tbl_clipCellPainting); + setFocusPolicy(StrongFocus); +} + +NetHackQtMenuWindow::~NetHackQtMenuWindow() +{ + // Remove from dialog before we destruct it + recreate(0,0,QPoint(0,0)); + delete dialog; +} + +void NetHackQtMenuWindow::focusInEvent(QFocusEvent *) +{ + // Don't repaint at all, since nothing is using the focus colour +} +void NetHackQtMenuWindow::focusOutEvent(QFocusEvent *) +{ + // Don't repaint at all, since nothing is using the focus colour +} + +int NetHackQtMenuWindow::cellWidth(int col) +{ + switch (col) { + case 0: + return 20; + break; case 1: + return 16; + break; case 2: + return qt_settings->glyphs().width(); + break; case 3: + return str_width; + } + impossible("Extra column (#%d) in MenuWindow",col); + return 0; +} + +QWidget* NetHackQtMenuWindow::Widget() { return dialog; } + +void NetHackQtMenuWindow::StartMenu() +{ + setNumRows((itemcount=0)); + str_width=200; + str_fixed=FALSE; + next_accel=0; + has_glyphs=FALSE; +} + +NetHackQtMenuWindow::MenuItem::MenuItem() : + str(0) +{ +} + +NetHackQtMenuWindow::MenuItem::~MenuItem() +{ + if (str) free((void*)str); +} + +#define STR_MARGIN 4 + +void NetHackQtMenuWindow::AddMenu(int glyph, const ANY_P* identifier, + char ch, char gch, int attr, const char* str, bool presel) +{ + if (!ch && identifier->a_void!=0) { + // Supply a keyboard accelerator. Limited supply. + static char accel[]="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + if (accel[next_accel]) { + ch=accel[next_accel++]; + } + } + + if (item.size()width()-6,prompth); + int h=dialog->height()-buth-prompth; + setGeometry(0,buth+prompth, dialog->width(), h); + + // Below, we take care to use up full width + int x=0; + ok->setGeometry(x,0,butw,buth); x+=butw; butw=(dialog->width()-x)/5; + cancel->setGeometry(x,0,butw,buth); x+=butw; butw=(dialog->width()-x)/4; + all->setGeometry(x,0,butw,buth); x+=butw; butw=(dialog->width()-x)/3; + none->setGeometry(x,0,butw,buth); x+=butw; butw=(dialog->width()-x)/2; + invert->setGeometry(x,0,butw,buth); x+=butw; butw=(dialog->width()-x)/1; + search->setGeometry(x,0,butw,buth); +} +int NetHackQtMenuWindow::SelectMenu(int h, MENU_ITEM_P **menu_list) +{ + setFont(str_fixed ? + qt_settings->normalFixedFont() : qt_settings->normalFont()); + + for (int i=0; iglyphs().height()+1,fontMetrics().height())); + setNumRows(itemcount); + + int buth=fontMetrics().height()+8; // 8 for spacing & mitres + + how=h; + + ok->setEnabled(how!=PICK_ONE);ok->setDefault(how!=PICK_ONE); + cancel->setEnabled(how!=PICK_NONE); + all->setEnabled(how==PICK_ANY); + none->setEnabled(how==PICK_ANY); + invert->setEnabled(how==PICK_ANY); + search->setEnabled(how!=PICK_NONE); + + dialog->SetResult(-1); + + // 20 allows for scrollbar or spacing + // 4 for frame borders + int mh = QApplication::desktop()->height()*3/5; + if ( qt_compact_mode && totalHeight() > mh ) { + // big, so make it fill + dialog->showMaximized(); + } else { + dialog->resize(totalWidth()+20, + QMIN(totalHeight(), mh)+buth+4+(prompt.text().isNull() ? 0 : buth)); + if ( dialog->width() > QApplication::desktop()->width() ) + dialog->resize(QApplication::desktop()->width(),dialog->height()+16); + centerOnMain(dialog); + dialog->show(); + } + + setFocus(); + while (dialog->result()<0) { + // changed the defaults below to the values in wintype.h 000119 - azy + if (!keysource.Empty()) { + char k=keysource.GetAscii(); + k=map_menu_cmd(k); /* added 000119 - azy */ + if (k=='\033') + dialog->Reject(); + else if (k=='\r' || k=='\n' || k==' ') + dialog->Accept(); + else if (k==MENU_SEARCH) + Search(); + else if (k==MENU_SELECT_ALL) + All(); + else if (k==MENU_INVERT_ALL) + Invert(); + else if (k==MENU_UNSELECT_ALL) + ChooseNone(); + else { + for (int i=0; iresult()<0) + qApp->enter_loop(); + } + dialog->hide(); + int result=dialog->result(); + + // Consume ^M (which QDialog steals for default button) + while (!keysource.Empty() && + (keysource.TopAscii()=='\n' || keysource.TopAscii()=='\r')) + keysource.GetAscii(); + + *menu_list=0; + if (result>0 && how!=PICK_NONE) { + if (how==PICK_ONE) { + int i; + for (i=0; istate()&ShiftButton)) { + if (event->key()==Key_Prior) { + setYOffset(yOffset()-viewHeight()); + } else if (event->key()==Key_Next) { + setYOffset(yOffset()+viewHeight()); + } else { + event->ignore(); + } + } else { + event->ignore(); + } +} + +void NetHackQtMenuWindow::All() +{ + for (int i=0; iAccept(); + } + } +} + + +void NetHackQtMenuWindow::paintCell(QPainter* painter, int row, int col) +{ + // [pick-count] [accel] [glyph] [string] + + MenuItem& i = item[row]; + + painter->setPen(black); + painter->setFont(font()); + + if (i.selected) { + painter->setPen(darkGreen); + } + + switch (col) { + case 0: + if (i.count>=0) { + char text[16]; + sprintf(text,"%d",i.count); + painter->drawText(0,0,cellWidth(col),cellHeight(), + AlignHCenter|AlignVCenter,text); + } + break; case 1: + if ((signed char)i.ch >= 0) { + char text[2]={i.ch,0}; + painter->drawText(0,0,cellWidth(col),cellHeight(), + AlignHCenter|AlignVCenter,text); + } + break; case 2: + if (i.glyph!=NO_GLYPH) { + // Centered in height + int y=(cellHeight()-qt_settings->glyphs().height())/2; + if (y<0) y=0; + qt_settings->glyphs().drawGlyph(*painter, i.glyph, 0, y); + } + break; case 3: + // XXX should qt_settings have ALL the various fonts + QFont newfont=font(); + + if (i.attr) { + switch(i.attr) { + case ATR_ULINE: + newfont.setUnderline(TRUE); + break; case ATR_BOLD: + painter->setPen(red); + break; case ATR_BLINK: + newfont.setItalic(TRUE); + break; case ATR_INVERSE: + newfont=qt_settings->largeFont(); + newfont.setWeight(QFont::Bold); + + if (i.selected) { + painter->setPen(blue); + } else { + painter->setPen(darkBlue); + } + } + } + painter->setFont(newfont); + + painter->drawText(STR_MARGIN,0,cellWidth(col),cellHeight(), + AlignLeft|AlignVCenter,i.str); + } +} + +void NetHackQtMenuWindow::mousePressEvent(QMouseEvent* event) +{ + int col=findCol(event->pos().x()); + int row=findRow(event->pos().y()); + + if (col<0 || row<0 || !item[row].Selectable()) return; + + if (how!=PICK_NONE) { + if (col==0) { + // Changing count. + NetHackQtStringRequestor requestor(keysource,"Count:"); + char buf[BUFSZ]; + + if (item[row].count>0) + Sprintf(buf,"%d", item[row].count); + else + Sprintf(buf,""); + + requestor.SetDefault(buf); + if (requestor.Get(buf)) { + item[row].count=atoi(buf); + if (item[row].count==0) { + item[row].count=-1; + if (item[row].selected) ToggleSelect(row); + } else { + if (!item[row].selected) ToggleSelect(row); + } + updateCell(row,0); + } + } else { + pressed=row; + was_sel=item[row].selected; + ToggleSelect(row); + } + } +} +void NetHackQtMenuWindow::mouseReleaseEvent(QMouseEvent* event) +{ + if (pressed>=0) { + int p=pressed; + pressed=-1; + updateCell(p,3); + } +} +void NetHackQtMenuWindow::mouseMoveEvent(QMouseEvent* event) +{ + if (pressed>=0) { + int col=findCol(event->pos().x()); + int row=findRow(event->pos().y()); + + if (row>=0 && col>=0) { + if (pressed!=row) { + // reset to initial state + if (item[pressed].selected!=was_sel) + ToggleSelect(pressed); + } else { + // reset to new state + if (item[pressed].selected==was_sel) + ToggleSelect(pressed); + } + } + } +} + + +class NetHackQtTextListBox : public QListBox { +public: + NetHackQtTextListBox(QWidget* parent) : QListBox(parent) { } + + int TotalWidth() + { + doLayout(); + return contentsWidth(); + } + int TotalHeight() + { + doLayout(); + return contentsHeight(); + } + + virtual void setFont(const QFont &font) + { + QListBox::setFont(font); + } + void keyPressEvent(QKeyEvent* e) + { + QListBox::keyPressEvent(e); + } +}; + + +QPixmap* NetHackQtRIP::pixmap=0; + +NetHackQtRIP::NetHackQtRIP(QWidget* parent) : + QWidget(parent) +{ + if (!pixmap) { + pixmap=new QPixmap; + tryload(*pixmap, "rip.xpm"); + } + riplines=0; + resize(pixmap->width(),pixmap->height()); + setFont(QFont("times",12)); // XXX may need to be configurable +} + +void NetHackQtRIP::setLines(char** l, int n) +{ + line=l; + riplines=n; +} + +QSize NetHackQtRIP::sizeHint() const +{ + return pixmap->size(); +} + +void NetHackQtRIP::paintEvent(QPaintEvent* event) +{ + if ( riplines ) { + int pix_x=(width()-pixmap->width())/2; + int pix_y=(height()-pixmap->height())/2; + + // XXX positions based on RIP image + int rip_text_x=pix_x+156; + int rip_text_y=pix_y+67; + int rip_text_h=94/riplines; + + QPainter painter; + painter.begin(this); + painter.drawPixmap(pix_x,pix_y,*pixmap); + for (int i=0; iaddWidget(&rip); + QHBoxLayout* hb = new QHBoxLayout(vb); + hb->addWidget(&ok); + hb->addWidget(&search); + vb->addWidget(lines); +} + +void NetHackQtTextWindow::doUpdate() +{ + update(); +} + + +NetHackQtTextWindow::~NetHackQtTextWindow() +{ + +} + +QWidget* NetHackQtTextWindow::Widget() +{ + return this; +} + +bool NetHackQtTextWindow::Destroy() +{ + return !isVisible(); +} + +void NetHackQtTextWindow::UseRIP(int how) +{ +// Code from X11 windowport +#define STONE_LINE_LEN 16 /* # chars that fit on one line */ +#define NAME_LINE 0 /* line # for player name */ +#define GOLD_LINE 1 /* line # for amount of gold */ +#define DEATH_LINE 2 /* line # for death description */ +#define YEAR_LINE 6 /* line # for year */ + +static char** rip_line=0; + if (!rip_line) { + rip_line=new char*[YEAR_LINE+1]; + for (int i=0; i STONE_LINE_LEN) { + for(i = STONE_LINE_LEN; + ((i0 > STONE_LINE_LEN) && i); i--) + if(dpx[i] == ' ') i0 = i; + if(!i) i0 = STONE_LINE_LEN; + } + tmpchar = dpx[i0]; + dpx[i0] = 0; + strcpy(rip_line[line], dpx); + if (tmpchar != ' ') { + dpx[i0] = tmpchar; + dpx= &dpx[i0]; + } else dpx= &dpx[i0+1]; + } + + /* Put year on stone */ + Sprintf(rip_line[YEAR_LINE], "%4d", getyear()); + + rip.setLines(rip_line,YEAR_LINE+1); + + use_rip=TRUE; +} + +void NetHackQtTextWindow::Clear() +{ + lines->clear(); + use_rip=FALSE; + str_fixed=FALSE; +} + +void NetHackQtTextWindow::Display(bool block) +{ + if (str_fixed) { + lines->setFont(qt_settings->normalFixedFont()); + } else { + lines->setFont(qt_settings->normalFont()); + } + + int h=0; + if (use_rip) { + h+=rip.height(); + ok.hide(); + search.hide(); + rip.show(); + } else { + h+=ok.height()*2; + ok.show(); + search.show(); + rip.hide(); + } + int mh = QApplication::desktop()->height()*3/5; + if ( qt_compact_mode && lines->TotalHeight() > mh || use_rip ) { + // big, so make it fill + showMaximized(); + } else { + resize(QMAX(use_rip ? rip.width() : 200, + lines->TotalWidth()+24), + QMIN(mh, lines->TotalHeight()+h)); + centerOnMain(this); + show(); + } + if (block) { + setResult(-1); + while (result()==-1) { + qApp->enter_loop(); + if (result()==-1 && !keysource.Empty()) { + char k=keysource.GetAscii(); + if (k=='\033' || k==' ' || k=='\r' || k=='\n') { + accept(); + } else if (k=='/') { + Search(); + } + } + } + } +} + +void NetHackQtTextWindow::PutStr(int attr, const char* text) +{ + str_fixed=str_fixed || strstr(text," "); + lines->insertItem(text); +} + +void NetHackQtTextWindow::done(int i) +{ + setResult(i+1000); + hide(); + qApp->exit_loop(); +} + +void NetHackQtTextWindow::keyPressEvent(QKeyEvent* e) +{ + if ( e->ascii() != '\r' && e->ascii() != '\n' && e->ascii() != '\033' ) + lines->keyPressEvent(e); + else + QDialog::keyPressEvent(e); +} + +void NetHackQtTextWindow::Search() +{ + NetHackQtStringRequestor requestor(keysource,"Search for:"); + static char line[256]=""; + requestor.SetDefault(line); + if (requestor.Get(line)) { + int current=lines->currentItem(); + for (int i=1; icount(); i++) { + int lnum=(i+current)%lines->count(); + const char* str=lines->text(lnum); + if (strstr(str,line)) { + lines->setCurrentItem(lnum); + lines->centerCurrentItem(); + return; + } + } + lines->setCurrentItem(-1); + } +} + + +NetHackQtDelay::NetHackQtDelay(int ms) : + msec(ms) +{ +} + +void NetHackQtDelay::wait() +{ + startTimer(msec); + qApp->enter_loop(); +} + +void NetHackQtDelay::timerEvent(QTimerEvent* timer) +{ + qApp->exit_loop(); + killTimers(); +} + +NetHackQtInvUsageWindow::NetHackQtInvUsageWindow(QWidget* parent) : + QWidget(parent) +{ +} + +void NetHackQtInvUsageWindow::drawWorn(QPainter& painter, obj* nhobj, int x, int y, bool canbe) +{ + short int glyph; + if (nhobj) + glyph=obj_to_glyph(nhobj); + else if (canbe) + glyph=cmap_to_glyph(S_room); + else + glyph=cmap_to_glyph(S_stone); + + qt_settings->glyphs().drawCell(painter,glyph,x,y); +} + +void NetHackQtInvUsageWindow::paintEvent(QPaintEvent*) +{ + // 012 + // + //0 WhB + //1 s"w + //2 gCg + //3 =A= + //4 T + //5 S + + QPainter painter; + painter.begin(this); + + // Blanks + drawWorn(painter,0,0,4,FALSE); + drawWorn(painter,0,0,5,FALSE); + drawWorn(painter,0,2,4,FALSE); + drawWorn(painter,0,2,5,FALSE); + + drawWorn(painter,uarm,1,3); // Armour + drawWorn(painter,uarmc,1,2); // Cloak + drawWorn(painter,uarmh,1,0); // Helmet + drawWorn(painter,uarms,0,1); // Shield + drawWorn(painter,uarmg,0,2); // Gloves - repeated + drawWorn(painter,uarmg,2,2); // Gloves - repeated +#ifdef TOURIST + drawWorn(painter,uarmf,1,5); // Shoes (feet) + drawWorn(painter,uarmu,1,4); // Undershirt +#else + drawWorn(painter,0 ,1,5,FALSE); + drawWorn(painter,uarmf,1,4); // Shoes (feet) +#endif + drawWorn(painter,uleft,0,3); // RingL + drawWorn(painter,uright,2,3); // RingR + + drawWorn(painter,uwep,2,1); // Weapon + drawWorn(painter,uswapwep,0,0); // Secondary weapon + drawWorn(painter,uamul,1,1); // Amulet + drawWorn(painter,ublindf,2,0); // Blindfold + + painter.end(); +} + +class SmallToolButton : public QToolButton { +public: + SmallToolButton(const QPixmap & pm, const QString &textLabel, + const QString& grouptext, + QObject * receiver, const char* slot, + QToolBar * parent) : + QToolButton(pm, textLabel, grouptext, receiver, slot, parent) + { + } + + QSize sizeHint() const + { + // get just a couple more pixels for the map + return QToolButton::sizeHint()-QSize(0,2); + } +}; + + +NetHackQtMainWindow::NetHackQtMainWindow(NetHackQtKeyBuffer& ks) : + message(0), map(0), status(0), invusage(0), + keysink(ks) +{ + if ( !qt_compact_mode ) + invusage = new NetHackQtInvUsageWindow(this); + + setToolBarsMovable(FALSE); + QToolBar* toolbar = new QToolBar(this); + toolbar->setHorizontalStretchable(TRUE); + toolbar->setVerticalStretchable(TRUE); + addToolBar(toolbar); + menubar = menuBar(); + + setCaption("Qt NetHack"); + if ( qt_compact_mode ) + setIcon(QPixmap(nh_icon_small)); + else + setIcon(QPixmap(nh_icon)); + + QPopupMenu* game=new QPopupMenu; + QPopupMenu* apparel=new QPopupMenu; + QPopupMenu* act1=new QPopupMenu; + QPopupMenu* act2 = qt_compact_mode ? new QPopupMenu : act1; + QPopupMenu* magic=new QPopupMenu; + QPopupMenu* info=new QPopupMenu; + + QPopupMenu *help; + +#ifdef KDE + help = kapp->getHelpMenu( TRUE, "" ); + help->insertSeparator(); +#else + help = qt_compact_mode ? info : new QPopupMenu; +#endif + + enum { OnDesktop=1, OnHandhelds=2 }; + struct Macro { + QPopupMenu* menu; + const char* name; + const char* action; + int flags; + } item[] = { + { game, 0, 0, 3}, + { game, "Version\tv", "v", 3}, + { game, "Compilation\tAlt-V", "\366", 3}, + { game, "History\tShift-V", "V", 3}, + { game, "Redraw\tCtrl-R", "\022", 3}, + { game, "Options\tShift-O", "O", 3}, + { game, "Explore mode\tShift-X", "X", 3}, + { game, 0, 0, 3}, + { game, "Save\tShift-S", "Sy", 3}, + { game, "Quit\tAlt-Q", "\361", 3}, + + { apparel, "Apparel off\tShift-A", "A", 3}, + { apparel, 0, 0, 3}, + { apparel, "Wield weapon\tw", "w", 3}, + { apparel, "Exchange weapons\tx", "x", 3}, + { apparel, "Two weapon combat\t#two", "#tw", 3}, + { apparel, "Load quiver\tQ", "Q", 3}, + { apparel, 0, 0, 3}, + { apparel, "Wear armour\tShift-W", "W", 3}, + { apparel, "Take off armour\tShift-T", "T", 3}, + { apparel, 0, 0, 3}, + { apparel, "Put on non-armour\tShift-P", "P", 3}, + { apparel, "Remove non-armour\tShift-R", "R", 3}, + + { act1, "Again\tCtrl-A", "\001", 2}, + { act1, 0, 0, 3}, + { act1, "Apply\ta?", "a?", 3}, + { act1, "Chat\tAlt-C", "\343", 3}, + { act1, "Close door\tc", "c", 3}, + { act1, "Down\t>", ">", 3}, + { act1, "Drop many\tShift-D", "D", 2}, + { act1, "Drop\td?", "d?", 3}, + { act1, "Eat\te?", "e?", 2}, + { act1, "Engrave\tShift-E", "E", 3}, + { act1, "Fight\tF", "F", 3}, + { act1, "Fire from quiver\tf", "f", 3}, + { act1, "Force\tAlt-F", "\346", 3}, + { act2, "Get\t,", ",", 2}, + { act2, "Jump\tAlt-J", "\352", 3}, + { act2, "Kick\tCtrl-D", "\004", 2}, + { act2, "Loot\tAlt-L", "\354", 3}, + { act2, "Open door\to", "o", 3}, + { act2, "Pay\tp", "p", 3}, + { act2, "Rest\t.", ".", 2}, + { act2, "Ride\t#ri", "#ri", 3}, + { act2, "Search\ts", "s", 3}, + { act2, "Sit\tAlt-S", "\363", 3}, + { act2, "Throw\tt", "t", 3}, + { act2, "Up\t<", "<", 3}, + { act2, "Wipe face\tAlt-W", "\367", 3}, + + { magic, "Quaff potion\tq", "q?", 3}, + { magic, "Read scroll/book\tr?", "r?", 3}, + { magic, "Zap wand\tz?", "z?", 3}, + { magic, "Zap spell\tShift-Z", "Z", 3}, + { magic, "Dip\tAlt-D", "\344", 3}, + { magic, "Rub\tAlt-R", "\362", 3}, + { magic, "Invoke\tAlt-I", "\351", 3}, + { magic, 0, 0, 3}, + { magic, "Offer\tAlt-O", "\357", 3}, + { magic, "Pray\tAlt-P", "\360", 3}, + { magic, 0, 0, 3}, + { magic, "Teleport\tCtrl-T", "\024", 3}, + { magic, "Monster action\tAlt-M", "\355", 3}, + { magic, "Turn undead\tAlt-T", "\364", 3}, + + { help, "Help\t?", "?", 3}, + { help, 0, 0, 3}, + { help, "What is here\t:", ":", 3}, + { help, "What is there\t;", ";", 3}, + { help, "What is...\t/", "/y", 2}, + { help, 0, 0, 1}, + + { info, "Inventory\ti", "i", 3}, +#ifdef SLASHEM + { info, "Angbandish inventory\t*", "*", 3}, +#endif + { info, "Conduct\t#co", "#co", 3}, + { info, "Discoveries\t\\", "\\", 3}, + { info, "List/reorder spells\t+", "+", 3}, + { info, "Adjust letters\tAlt-A", "\341", 2}, + { info, 0, 0, 3}, + { info, "Name object\tAlt-N", "\356y?", 3}, + { info, "Name object type\tAlt-N", "\356n?", 3}, + { info, "Name creature\tShift-C", "C", 3}, + { info, 0, 0, 3}, + { info, "Qualifications\tAlt-E", "\345", 3}, + + { 0, 0, 0, 0 } + }; + + int i; + int count=0; + for (i=0; item[i].menu; i++) + if (item[i].name) count++; + + macro=new const char* [count]; + + game->insertItem("Qt settings...",1000); + help->insertItem("About Qt NetHack...",2000); + + count=0; + for (i=0; item[i].menu; i++) { + if ( item[i].flags & ((1<insertItem(name,count); + macro[count++]=item[i].action; + } else { + item[i].menu->insertSeparator(); + } + } + } + + menubar->insertItem("Game",game); + menubar->insertItem("Gear",apparel); + + if ( qt_compact_mode ) { + menubar->insertItem("A-F",act1); + menubar->insertItem("G-Z",act2); + menubar->insertItem("Magic",magic); + + QSignalMapper* sm = new QSignalMapper(this); + connect(sm, SIGNAL(mapped(const QString&)), this, SLOT(doKeys(const QString&))); + menubar->insertItem(QPixmap(info_xpm),info); + menubar->insertItem(QPixmap(map_xpm), this, SLOT(raiseMap())); + menubar->insertItem(QPixmap(msg_xpm), this, SLOT(raiseMessages())); + menubar->insertItem(QPixmap(stat_xpm), this, SLOT(raiseStatus())); + QToolButton* tb; + tb = new SmallToolButton( QPixmap(again_xpm),"Again","Action", sm, SLOT(map()), toolbar ); + sm->setMapping(tb, "\001" ); + tb = new SmallToolButton( QPixmap(get_xpm),"Get","Action", sm, SLOT(map()), toolbar ); + sm->setMapping(tb, "," ); + tb = new SmallToolButton( QPixmap(kick_xpm),"Kick","Action", sm, SLOT(map()), toolbar ); + sm->setMapping(tb, "\004" ); + tb = new SmallToolButton( QPixmap(throw_xpm),"Throw","Action", sm, SLOT(map()), toolbar ); + sm->setMapping(tb, "t" ); + tb = new SmallToolButton( QPixmap(fire_xpm),"Fire","Action", sm, SLOT(map()), toolbar ); + sm->setMapping(tb, "f" ); + tb = new SmallToolButton( QPixmap(drop_xpm),"Drop","Action", sm, SLOT(map()), toolbar ); + sm->setMapping(tb, "D" ); + tb = new SmallToolButton( QPixmap(eat_xpm),"Eat","Action", sm, SLOT(map()), toolbar ); + sm->setMapping(tb, "e" ); + tb = new SmallToolButton( QPixmap(rest_xpm),"Rest","Action", sm, SLOT(map()), toolbar ); + sm->setMapping(tb, "." ); + tb = new SmallToolButton( QPixmap(cast_a_xpm),"Cast A","Magic", sm, SLOT(map()), toolbar ); + sm->setMapping(tb, "Za" ); + tb = new SmallToolButton( QPixmap(cast_b_xpm),"Cast B","Magic", sm, SLOT(map()), toolbar ); + sm->setMapping(tb, "Zb" ); + tb = new SmallToolButton( QPixmap(cast_c_xpm),"Cast C","Magic", sm, SLOT(map()), toolbar ); + sm->setMapping(tb, "Zc" ); + } else { + menubar->insertItem("Action",act1); + menubar->insertItem("Magic",magic); + menubar->insertItem("Info",info); + menubar->insertSeparator(); + menubar->insertItem("Help",help); + } + + connect(menubar,SIGNAL(activated(int)),this,SLOT(doMenuItem(int))); + +#ifdef KDE + setMenu (menubar); +#endif + + int x=0,y=0; + int w=QApplication::desktop()->width()-10; // XXX arbitrary extra space for frame + int h=QApplication::desktop()->height()-50; + + int maxwn; + int maxhn; + if (qt_tilewidth != NULL) { + maxwn = atoi(qt_tilewidth) * COLNO + 10; + } else { + maxwn = 1400; + } + if (qt_tileheight != NULL) { + maxhn = atoi(qt_tileheight) * ROWNO * 6/4; + } else { + maxhn = 1024; + } + + // Be exactly the size we want to be - full map... + if (w>maxwn) { + x+=(w-maxwn)/2; + w=maxwn; // Doesn't need to be any wider + } + if (h>maxhn) { + y+=(h-maxhn)/2; + h=maxhn; // Doesn't need to be any taller + } + + setGeometry(x,y,w,h); + + if ( qt_compact_mode ) { + stack = new QWidgetStack(this); + setCentralWidget(stack); + } else { + setCentralWidget(new QWidget(this)); + } +} + +void NetHackQtMainWindow::raiseMap() +{ + stack->raiseWidget(0); +} + +void NetHackQtMainWindow::raiseMessages() +{ + stack->raiseWidget(1); +} + +void NetHackQtMainWindow::raiseStatus() +{ + stack->raiseWidget(2); +} + + +void NetHackQtMainWindow::doMenuItem(int id) +{ + switch (id) { + case 1000: + centerOnMain(qt_settings); + qt_settings->show(); + break; + case 2000: { + QMessageBox::about(this, "About Qt NetHack", aboutMsg()); + } break; + default: + if ( id >= 0 ) + doKeys(macro[id]); + } +} + +void NetHackQtMainWindow::doKeys(const QString& k) +{ + keysink.Put(k); + qApp->exit_loop(); +} + +void NetHackQtMainWindow::AddMessageWindow(NetHackQtMessageWindow* window) +{ + message=window; + ShowIfReady(); +} + +void NetHackQtMainWindow::AddMapWindow(NetHackQtMapWindow* window) +{ + map=window; + ShowIfReady(); + connect(map,SIGNAL(resized()),this,SLOT(layout())); +} + +void NetHackQtMainWindow::AddStatusWindow(NetHackQtStatusWindow* window) +{ + status=window; + ShowIfReady(); +} + +void NetHackQtMainWindow::RemoveWindow(NetHackQtWindow* window) +{ + if (window==status) { + status=0; + ShowIfReady(); + } else if (window==map) { + map=0; + ShowIfReady(); + } else if (window==message) { + message=0; + ShowIfReady(); + } +} + +void NetHackQtMainWindow::updateInventory() +{ + if ( invusage ) + invusage->repaint(FALSE); +} + +void NetHackQtMainWindow::fadeHighlighting() +{ + if (status) { + status->fadeHighlighting(); + } +} + +void NetHackQtMainWindow::layout() +{ + if ( qt_compact_mode ) + return; + if (message && map && status) { + QSize maxs=map->Widget()->maximumSize(); + int maph=QMIN(height()*2/3,maxs.height()); + + int y=menubar->height(); + int h=height()-y; + int toph=h-maph; + int iuw=3*qt_settings->glyphs().width(); + int topw=(width()-iuw)/2; + + message->Widget()->setGeometry(0,y,topw,toph); + invusage->setGeometry(topw,y,iuw,toph); + status->Widget()->setGeometry(topw+iuw,y,topw,toph); + map->Widget()->setGeometry(QMAX(0,(width()-maxs.width())/2), + y+toph,width(),maph); + } +} + +void NetHackQtMainWindow::resizeEvent(QResizeEvent*) +{ + layout(); +#ifdef KDE + updateRects(); +#endif +} + +void NetHackQtMainWindow::keyPressEvent(QKeyEvent* event) +{ + // Global key controls + + // For desktop, arrow keys scroll map, since we don't want players + // to think that's the way to move. For handhelds, the normal way is to + // click-to-travel, so we allow the cursor keys for fine movements. + + const char* d = iflags.num_pad ? ndir : sdir; + switch (event->key()) { + case Key_Up: + if (qt_compact_mode) + keysink.Put(d[2]); + else + if (map) map->Scroll(0,-1); + break; case Key_Down: + if (qt_compact_mode) + keysink.Put(d[6]); + else + if (map) map->Scroll(0,+1); + break; case Key_Left: + if (qt_compact_mode) + keysink.Put(d[0]); + else + if (map) map->Scroll(-1,0); + break; case Key_Right: + if (qt_compact_mode) + keysink.Put(d[4]); + else + if (map) map->Scroll(+1,0); + break; case Key_Prior: + if (message) message->Scroll(0,-1); + break; case Key_Next: + if (message) message->Scroll(0,+1); + break; default: + event->ignore(); + } +} + +void NetHackQtMainWindow::closeEvent(QCloseEvent* e) +{ + if ( program_state.something_worth_saving ) { + switch ( QMessageBox::information( this, "NetHack", + "This will end your NetHack session", + "&Save", "&Quit", "&Cancel", 0, 2 ) ) + { + case 0: + // See dosave() function + if (dosave0()) { + u.uhp = -1; + terminate(EXIT_SUCCESS); + } + break; + case 1: + u.uhp = -1; + terminate(EXIT_SUCCESS); + break; + case 2: + break; // ignore the event + } + } else { + e->accept(); + } +} + +void NetHackQtMainWindow::ShowIfReady() +{ + if (message && map && status) { + QPoint pos(0,0); + QWidget* p = qt_compact_mode ? stack : centralWidget(); + message->Widget()->recreate(p,0,pos); + map->Widget()->recreate(p,0,pos); + status->Widget()->recreate(p,0,pos); + if ( qt_compact_mode ) { + message->setMap(map); + stack->addWidget(map->Widget(), 0); + stack->addWidget(message->Widget(), 1); + stack->addWidget(status->Widget(), 2); + raiseMap(); + showMaximized(); + } else { + layout(); + show(); + } + } else if (isVisible()) { + hide(); + } +} + + +NetHackQtYnDialog::NetHackQtYnDialog(NetHackQtKeyBuffer& keysrc,const char* q,const char* ch,char df) : + QDialog(0,0,FALSE), + keysource(keysrc), + question(q), choices(ch), def(df) +{ + setCaption("NetHack: Question"); +} + +char NetHackQtYnDialog::Exec() +{ + QString ch(choices); + int ch_per_line=6; + QString qlabel; + QString enable; + if ( qt_compact_mode && !choices ) { + // expand choices from prompt + const char* c=question; + while ( *c && *c != '[' ) + c++; + qlabel = QString(question).left(c-question); + if ( *c ) { + c++; + if ( *c == '-' ) + ch.append(*c++); + char from=0; + while ( *c && *c != ']' && *c != ' ' ) { + if ( *c == '-' ) + from = c[-1]; + else if ( from ) + for (char f=from; f<=*c; f++) + ch.append(f); + else + ch.append(*c); + c++; + } + if ( *c == ' ' ) { + while ( *c && *c != ']' ) { + if ( *c == '*' || *c == '?' ) + ch.append(*c); + c++; + } + } + } + if ( strstr(question, "what direction") ) { + // We replace this regardless, since sometimes you get choices. + const char* d = iflags.num_pad ? ndir : sdir; + enable=ch; + ch=""; + ch.append(d[1]); + ch.append(d[2]); + ch.append(d[3]); + ch.append(d[0]); + ch.append('.'); + ch.append(d[4]); + ch.append(d[7]); + ch.append(d[6]); + ch.append(d[5]); + ch.append(d[8]); + ch.append(d[9]); + ch_per_line = 3; + def = ' '; + } else { + // Hmm... they'll have to use a virtual keyboard + } + } else { + qlabel = question; + } + if (!ch.isNull()) { + QVBoxLayout vb(this); + vb.setAutoAdd(TRUE); + bool bigq = qlabel.length()>40; + if ( bigq ) { + QLabel* q = new QLabel(qlabel,this); + q->setAlignment(AlignLeft|WordBreak); + q->setMargin(4); + } + QButtonGroup group(ch_per_line, Horizontal, + bigq ? QString::null : qlabel, this); + + int nchoices=ch.length(); + + bool allow_count=ch.contains('#'); + + const int margin=8; + const int gutter=8; + const int extra=fontMetrics().height(); // Extra for group + int row=0; + int x=margin, y=extra+margin; + int butsize=fontMetrics().height()*2+5; + + QPushButton* button; + for (int i=0; isetEnabled(FALSE); + } + button->setFixedSize(butsize,butsize); // Square + if (ch[i]==def) button->setDefault(TRUE); + if (i%10==9) { + // last in row + x=margin; + y+=butsize+gutter; + } else { + x+=butsize+gutter; + } + } + + connect(&group,SIGNAL(clicked(int)),this,SLOT(doneItem(int))); + + QLabel* lb=0; + QLineEdit* le=0; + + if (allow_count) { + QHBox *hb = new QHBox(this); + lb=new QLabel("Count: ",hb); + le=new QLineEdit(hb); + } + + adjustSize(); + centerOnMain(this); + show(); + char choice=0; + char ch_esc=0; + for (int i=0; i= 1000 ) { + choice = ch[result() - 1000].latin1(); + } + if ( !choice ) + qApp->enter_loop(); + } + hide(); + if (allow_count && !le->text().isEmpty()) { + yn_number=atoi(le->text()); + choice='#'; + } + return choice; + } else { + QLabel label(qlabel,this); + QPushButton cancel("Dismiss",this); + label.setFrameStyle(QFrame::Box|QFrame::Sunken); + label.setAlignment(AlignCenter); + label.resize(fontMetrics().width(qlabel)+60,30+fontMetrics().height()); + cancel.move(width()/2-cancel.width()/2,label.geometry().bottom()+8); + connect(&cancel,SIGNAL(clicked()),this,SLOT(reject())); + centerOnMain(this); + show(); + while (!result() && keysource.Empty()) { + qApp->enter_loop(); + } + hide(); + if (keysource.Empty()) { + return '\033'; + } else { + return keysource.GetAscii(); + } + } +} +void NetHackQtYnDialog::keyPressEvent(QKeyEvent* event) +{ + // Don't want QDialog's Return/Esc behaviour + event->ignore(); +} + +void NetHackQtYnDialog::doneItem(int i) +{ + done(i+1000); +} + +void NetHackQtYnDialog::done(int i) +{ + setResult(i); + qApp->exit_loop(); +} + +NetHackQtGlyphs::NetHackQtGlyphs() +{ + const char* tile_file = "x11tiles"; + + int tw = TILEWBASE; + int th = TILEHBASE; + + + if (!img.load(tile_file)) { + tile_file = "nhtiles.bmp"; + if (!img.load(tile_file)) { + QString msg; + msg.sprintf("Cannot load x11tiles or nhtiles.bmp"); + QMessageBox::warning(0, "IO Error", msg); + } else { + tiles_per_row = 40; + } + } else { + tiles_per_row = 1; + if (img.height()%total_tiles_used) { + impossible("Tile file \"%s\" has %d lines, not multiple of glyph count (%d)", + tile_file, img.height(), total_tiles_used); + } + } + int rows = ((total_tiles_used+tiles_per_row-1) / tiles_per_row); + tw = img.width() / tiles_per_row; + th = img.height() / rows; + + resize(tw, th); +} + +void NetHackQtGlyphs::drawGlyph(QPainter& painter, int glyph, int x, int y) +{ + int tile = glyph2tile[glyph]; + int px = (tile%tiles_per_row)*width(); + int py = tile/tiles_per_row*height(); + + painter.drawPixmap( + x, + y, + pm, + px,py, + width(),height() + ); +} +void NetHackQtGlyphs::drawCell(QPainter& painter, int glyph, int cellx, int celly) +{ + drawGlyph(painter,glyph,cellx*width(),celly*height()); +} +void NetHackQtGlyphs::resize(int w, int h) +{ + size = QSize(w,h); + if (!w || !h) + return; // Still not decided + + if (w==TILEWBASE && h==TILEHBASE) { + pm.convertFromImage(img); + } else { + QApplication::setOverrideCursor( Qt::waitCursor ); + QImage scaled = img.smoothScale( + w*img.width()/TILEWBASE, + h*img.height()/TILEHBASE + ); + pm.convertFromImage(scaled,Qt::ThresholdDither|Qt::PreferDither); + QApplication::restoreOverrideCursor(); + } +} + + +////////////////////////////////////////////////////////////// +// +// The ugly C binding classes... +// +////////////////////////////////////////////////////////////// + + +NetHackQtMenuOrTextWindow::NetHackQtMenuOrTextWindow(NetHackQtKeyBuffer& ks) : + keysource(ks), + actual(0) +{ +} + +QWidget* NetHackQtMenuOrTextWindow::Widget() +{ + if (!actual) impossible("Widget called before we know if Menu or Text"); + return actual->Widget(); +} + +// Text +void NetHackQtMenuOrTextWindow::Clear() +{ + if (!actual) impossible("Clear called before we know if Menu or Text"); + actual->Clear(); +} +void NetHackQtMenuOrTextWindow::Display(bool block) +{ + if (!actual) impossible("Display called before we know if Menu or Text"); + actual->Display(block); +} +bool NetHackQtMenuOrTextWindow::Destroy() +{ + if (!actual) impossible("Destroy called before we know if Menu or Text"); + return actual->Destroy(); +} + +void NetHackQtMenuOrTextWindow::PutStr(int attr, const char* text) +{ + if (!actual) actual=new NetHackQtTextWindow(keysource); + actual->PutStr(attr,text); +} + +// Menu +void NetHackQtMenuOrTextWindow::StartMenu() +{ + if (!actual) actual=new NetHackQtMenuWindow(keysource); + actual->StartMenu(); +} +void NetHackQtMenuOrTextWindow::AddMenu(int glyph, const ANY_P* identifier, char ch, char gch, int attr, + const char* str, bool presel) +{ + if (!actual) impossible("AddMenu called before we know if Menu or Text"); + actual->AddMenu(glyph,identifier,ch,gch,attr,str,presel); +} +void NetHackQtMenuOrTextWindow::EndMenu(const char* prompt) +{ + if (!actual) impossible("EndMenu called before we know if Menu or Text"); + actual->EndMenu(prompt); +} +int NetHackQtMenuOrTextWindow::SelectMenu(int how, MENU_ITEM_P **menu_list) +{ + if (!actual) impossible("SelectMenu called before we know if Menu or Text"); + return actual->SelectMenu(how,menu_list); +} + + +// XXX Should be from Options +// +// XXX Hmm. Tricky part is that perhaps some macros should only be active +// XXX when a key is about to be gotten. For example, the user could +// XXX define "-" to do "E-yyyyyyyy\r", but would still need "-" for +// XXX other purposes. Maybe just too bad. +// +struct { + int key; + int state; + const char* macro; +} key_macro[]={ + { Qt::Key_F1, 0, "n100." }, // Rest (x100) + { Qt::Key_F2, 0, "n20s" }, // Search (x20) + { Qt::Key_F3, 0, "o8o4o6o2o8o4o6o2o8o4o6o2" }, // Open all doors (x3) + { Qt::Key_Tab, 0, "\001" }, + { 0, 0, 0 } +}; + + +NetHackQtBind::NetHackQtBind(int& argc, char** argv) : +#ifdef KDE + KApplication(argc,argv) +#elif defined(QWS) // not quite the right condition + QPEApplication(argc,argv) +#else + QApplication(argc,argv) +#endif +{ + main = new NetHackQtMainWindow(keybuffer); + setMainWidget(main); + qt_settings=new NetHackQtSettings(main->width(),main->height()); +} + +void NetHackQtBind::qt_init_nhwindows(int* argc, char** argv) +{ +#ifdef _WS_X11_ +// Userid control +// +// Michael Hohmuth ... +// +// As the game runs setuid games, it must seteuid(getuid()) before +// calling XOpenDisplay(), and reset the euid afterwards. +// Otherwise, it can't read the $HOME/.Xauthority file and whines about +// not being able to open the X display (if a magic-cookie +// authorization mechanism is being used). + + uid_t gamesuid=geteuid(); + seteuid(getuid()); +#endif + + QApplication::setColorSpec(ManyColor); + instance=new NetHackQtBind(*argc,argv); + +#ifdef _WS_X11_ + seteuid(gamesuid); +#endif + +#ifdef _WS_WIN_ + // This nethack engine feature should be moved into windowport API + nt_kbhit = NetHackQtBind::qt_kbhit; +#endif +} + +int NetHackQtBind::qt_kbhit() +{ + return !keybuffer.Empty(); +} + +static bool have_asked = FALSE; + +void NetHackQtBind::qt_player_selection() +{ + if ( !have_asked ) + qt_askname(); +} + +void NetHackQtBind::qt_askname() +{ + have_asked = TRUE; + + // We do it all here, and nothing in askname + + NetHackQtPlayerSelector selector(keybuffer); + + if (selector.Choose()) { + // ... + } else { + clearlocks(); + qt_exit_nhwindows(0); + terminate(0); + } +} + +void NetHackQtBind::qt_get_nh_event() +{ +} + +void NetHackQtBind::qt_exit_nhwindows(const char *) +{ +} + +void NetHackQtBind::qt_suspend_nhwindows(const char *) +{ +} + +void NetHackQtBind::qt_resume_nhwindows() +{ +} + +static QArray id_to_window; + +winid NetHackQtBind::qt_create_nhwindow(int type) +{ + winid id; + for (id = 0; id < id_to_window.size(); id++) { + if ( !id_to_window[id] ) + break; + } + if ( id == id_to_window.size() ) + id_to_window.resize(id+1); + + NetHackQtWindow* window=0; + + switch (type) { + case NHW_MAP: { + NetHackQtMapWindow* w=new NetHackQtMapWindow(clickbuffer); + main->AddMapWindow(w); + window=w; + } break; case NHW_MESSAGE: { + NetHackQtMessageWindow* w=new NetHackQtMessageWindow; + main->AddMessageWindow(w); + window=w; + } break; case NHW_STATUS: { + NetHackQtStatusWindow* w=new NetHackQtStatusWindow; + main->AddStatusWindow(w); + window=w; + } break; case NHW_MENU: + window=new NetHackQtMenuOrTextWindow(keybuffer); + break; case NHW_TEXT: + window=new NetHackQtTextWindow(keybuffer); + } + + id_to_window[id] = window; + return id; +} + +void NetHackQtBind::qt_clear_nhwindow(winid wid) +{ + NetHackQtWindow* window=id_to_window[wid]; + window->Clear(); +} + +void NetHackQtBind::qt_display_nhwindow(winid wid, BOOLEAN_P block) +{ + NetHackQtWindow* window=id_to_window[wid]; + window->Display(block); +} + +void NetHackQtBind::qt_destroy_nhwindow(winid wid) +{ + NetHackQtWindow* window=id_to_window[wid]; + main->RemoveWindow(window); + if (window->Destroy()) + delete window; + id_to_window[wid] = 0; +} + +void NetHackQtBind::qt_curs(winid wid, int x, int y) +{ + NetHackQtWindow* window=id_to_window[wid]; + window->CursorTo(x,y); +} + +void NetHackQtBind::qt_putstr(winid wid, int attr, const char *text) +{ + NetHackQtWindow* window=id_to_window[wid]; + window->PutStr(attr,text); +} + +void NetHackQtBind::qt_display_file(const char *filename, BOOLEAN_P must_exist) +{ + NetHackQtTextWindow* window=new NetHackQtTextWindow(keybuffer); + bool complain = FALSE; + +#ifdef DLB + { + dlb *f; + char buf[BUFSZ]; + char *cr; + + window->Clear(); + f = dlb_fopen(filename, "r"); + if (!f) { + complain = must_exist; + } else { + while (dlb_fgets(buf, BUFSZ, f)) { + if ((cr = index(buf, '\n')) != 0) *cr = 0; +#ifdef MSDOS + if ((cr = index(buf, '\r')) != 0) *cr = 0; +#endif + if (index(buf, '\t') != 0) (void) tabexpand(buf); + window->PutStr(ATR_NONE, buf); + } + window->Display(FALSE); + (void) dlb_fclose(f); + } + } +#else + QFile file(filename); + + if (file.open(IO_ReadOnly)) { + char line[128]; + while (file.readLine(line,127) >= 0) { + line[strlen(line)-1]=0;// remove newline + window->PutStr(ATR_NONE,line); + } + window->Display(FALSE); + } else { + complain = must_exist; + } +#endif + + if (complain) { + QString message; + message.sprintf("File not found: %s\n",filename); + QMessageBox::message("File Error", (const char*)message, "Ignore"); + } +} + +void NetHackQtBind::qt_start_menu(winid wid) +{ + NetHackQtWindow* window=id_to_window[wid]; + window->StartMenu(); +} + +void NetHackQtBind::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) +{ + NetHackQtWindow* window=id_to_window[wid]; + window->AddMenu(glyph, identifier, ch, gch, attr, str, presel); +} + +void NetHackQtBind::qt_end_menu(winid wid, const char *prompt) +{ + NetHackQtWindow* window=id_to_window[wid]; + window->EndMenu(prompt); +} + +int NetHackQtBind::qt_select_menu(winid wid, int how, MENU_ITEM_P **menu_list) +{ + NetHackQtWindow* window=id_to_window[wid]; + return window->SelectMenu(how,menu_list); +} + +void NetHackQtBind::qt_update_inventory() +{ + if (main) + main->updateInventory(); +} + +void NetHackQtBind::qt_mark_synch() +{ +} + +void NetHackQtBind::qt_wait_synch() +{ +} + +void NetHackQtBind::qt_cliparound(int x, int y) +{ + // XXXNH - winid should be a parameter! + qt_cliparound_window(WIN_MAP,x,y); +} + +void NetHackQtBind::qt_cliparound_window(winid wid, int x, int y) +{ + NetHackQtWindow* window=id_to_window[wid]; + window->ClipAround(x,y); +} +void NetHackQtBind::qt_print_glyph(winid wid,XCHAR_P x,XCHAR_P y,int glyph) +{ + NetHackQtWindow* window=id_to_window[wid]; + window->PrintGlyph(x,y,glyph); +} +//void NetHackQtBind::qt_print_glyph_compose(winid wid,XCHAR_P x,XCHAR_P y,int glyph1, int glyph2) +//{ + //NetHackQtWindow* window=id_to_window[wid]; + //window->PrintGlyphCompose(x,y,glyph1,glyph2); +//} + +void NetHackQtBind::qt_raw_print(const char *str) +{ + puts(str); +} + +void NetHackQtBind::qt_raw_print_bold(const char *str) +{ + puts(str); +} + +int NetHackQtBind::qt_nhgetch() +{ + if (main) + main->fadeHighlighting(); + + // Process events until a key arrives. + // + while (keybuffer.Empty()) { + qApp->enter_loop(); + } + + return keybuffer.GetAscii(); +} + +int NetHackQtBind::qt_nh_poskey(int *x, int *y, int *mod) +{ + if (main) + main->fadeHighlighting(); + + // Process events until a key or map-click arrives. + // + while (keybuffer.Empty() && clickbuffer.Empty()) { + qApp->enter_loop(); + } + if (!keybuffer.Empty()) { + return keybuffer.GetAscii(); + } else { + *x=clickbuffer.NextX(); + *y=clickbuffer.NextY(); + *mod=clickbuffer.NextMod(); + clickbuffer.Get(); + return 0; + } +} + +void NetHackQtBind::qt_nhbell() +{ + QApplication::beep(); +} + +int NetHackQtBind::qt_doprev_message() +{ + // Don't need it - uses scrollbar + // XXX but could make this a shortcut + return 0; +} + +char NetHackQtBind::qt_yn_function(const char *question, const char *choices, CHAR_P def) +{ + if (qt_settings->ynInMessages() && WIN_MESSAGE!=WIN_ERR) { + // Similar to X11 windowport `slow' feature. + + char message[BUFSZ]; + char yn_esc_map='\033'; + + if (choices) { + char *cb, choicebuf[QBUFSZ]; + Strcpy(choicebuf, choices); + if ((cb = index(choicebuf, '\033')) != 0) { + // anything beyond is hidden + *cb = '\0'; + } + Sprintf(message, "%s [%s] ", question, choicebuf); + if (def) Sprintf(eos(message), "(%c) ", def); + // escape maps to 'q' or 'n' or default, in that order + yn_esc_map = (index(choices, 'q') ? 'q' : + (index(choices, 'n') ? 'n' : def)); + } else { + Strcpy(message, question); + } + +#ifdef USE_POPUPS + // Improve some special-cases (DIRKS 08/02/23) + if (strcmp (choices,"ynq") == 0) { + switch (QMessageBox::information (qApp->mainWidget(),"NetHack",question,"&Yes","&No","&Quit",0,2)) + { + case 0: return 'y'; + case 1: return 'n'; + case 2: return 'q'; + } + } + + if (strcmp (choices,"yn") == 0) { + switch (QMessageBox::information(qApp->mainWidget(),"NetHack",question,"&Yes", "&No",0,1)) + { + case 0: return 'y'; + case 1: return 'n'; + } + } +#endif + + NetHackQtBind::qt_putstr(WIN_MESSAGE, ATR_BOLD, message); + + int result=-1; + while (result<0) { + char ch=NetHackQtBind::qt_nhgetch(); + if (ch=='\033') { + result=yn_esc_map; + } else if (choices && !index(choices,ch)) { + if (def && (ch==' ' || ch=='\r' || ch=='\n')) { + result=def; + } else { + NetHackQtBind::qt_nhbell(); + // and try again... + } + } else { + result=ch; + } + } + + NetHackQtBind::qt_clear_nhwindow(WIN_MESSAGE); + + return result; + } else { + NetHackQtYnDialog dialog(keybuffer,question,choices,def); + return dialog.Exec(); + } +} + +void NetHackQtBind::qt_getlin(const char *prompt, char *line) +{ + NetHackQtStringRequestor requestor(keybuffer,prompt); + if (!requestor.Get(line)) { + line[0]=0; + } +} + +NetHackQtExtCmdRequestor::NetHackQtExtCmdRequestor(NetHackQtKeyBuffer& ks) : + QDialog(0, "ext-cmd", FALSE), + keysource(ks) +{ + int marg=4; + QVBoxLayout *l = new QVBoxLayout(this,marg,marg); + + QPushButton* can = new QPushButton("Cancel", this); + can->setDefault(TRUE); + can->setMinimumSize(can->sizeHint()); + l->addWidget(can); + + QButtonGroup *group=new QButtonGroup("",0); + QGroupBox *grid=new QGroupBox("Extended commands",this); + l->addWidget(grid); + + int i; + int butw=50; + QFontMetrics fm = fontMetrics(); + for (i=0; extcmdlist[i].ef_txt; i++) { + butw = QMAX(butw,30+fm.width(extcmdlist[i].ef_txt)); + } + int ncols=4; + int nrows=(i+ncols-1)/ncols; + + QVBoxLayout* bl = new QVBoxLayout(grid,marg); + bl->addSpacing(fm.height()); + QGridLayout* gl = new QGridLayout(nrows,ncols,marg); + bl->addLayout(gl); + for (i=0; extcmdlist[i].ef_txt; i++) { + QPushButton* pb=new QPushButton(extcmdlist[i].ef_txt, grid); + pb->setMinimumSize(butw,pb->sizeHint().height()); + group->insert(pb); + gl->addWidget(pb,i/ncols,i%ncols); + } + connect(group,SIGNAL(clicked(int)),this,SLOT(done(int))); + + bl->activate(); + l->activate(); + resize(1,1); + + connect(can,SIGNAL(clicked()),this,SLOT(cancel())); +} + +void NetHackQtExtCmdRequestor::cancel() +{ + setResult(-1); + qApp->exit_loop(); +} + +void NetHackQtExtCmdRequestor::done(int i) +{ + setResult(i); + qApp->exit_loop(); +} + +int NetHackQtExtCmdRequestor::get() +{ + const int none = -10; + char str[32]; + int cursor=0; + resize(1,1); // pack + centerOnMain(this); + show(); + setResult(none); + while (result()==none) { + while (result()==none && !keysource.Empty()) { + char k=keysource.GetAscii(); + if (k=='\r' || k=='\n' || k==' ' || k=='\033') { + setResult(-1); + } else { + str[cursor++] = k; + int r=-1; + for (int i=0; extcmdlist[i].ef_txt; i++) { + if (qstrnicmp(str, extcmdlist[i].ef_txt, cursor)==0) { + if ( r == -1 ) + r = i; + else + r = -2; + } + } + if ( r == -1 ) { // no match! + QApplication::beep(); + cursor=0; + } else if ( r != -2 ) { // only one match + setResult(r); + } + } + } + if (result()==none) + qApp->enter_loop(); + } + hide(); + return result(); +} + + +int NetHackQtBind::qt_get_ext_cmd() +{ + NetHackQtExtCmdRequestor requestor(keybuffer); + return requestor.get(); +} + +void NetHackQtBind::qt_number_pad(int) +{ + // Ignore. +} + +void NetHackQtBind::qt_delay_output() +{ + NetHackQtDelay delay(15); + delay.wait(); +} + +void NetHackQtBind::qt_start_screen() +{ + // Ignore. +} + +void NetHackQtBind::qt_end_screen() +{ + // Ignore. +} + +void NetHackQtBind::qt_outrip(winid wid, int how) +{ + NetHackQtWindow* window=id_to_window[wid]; + + window->UseRIP(how); +} + +bool NetHackQtBind::notify(QObject *receiver, QEvent *event) +{ + bool result=QApplication::notify(receiver,event); + if (event->type()==QEvent::KeyPress) { + QKeyEvent* key_event=(QKeyEvent*)event; + + if (!key_event->isAccepted()) { + const int k=key_event->key(); + bool macro=FALSE; + for (int i=0; !macro && key_macro[i].key; i++) { + if (key_macro[i].key==k + && ((key_macro[i].state&key_event->state())==key_macro[i].state)) + { + keybuffer.Put(key_macro[i].macro); + macro=TRUE; + } + } + char ch=key_event->ascii(); + if (!macro && ch) { + int k = key_event->key(); + bool alt = (key_event->state()&AltButton) || + (k >= Key_0 && k <= Key_9 && (key_event->state()&ControlButton)); + keybuffer.Put(key_event->key(),ch + (alt ? 128 : 0), + key_event->state()); + key_event->accept(); + result=TRUE; + } + + if (ch || macro) { + qApp->exit_loop(); + } + } + } + return result; +} + +NetHackQtBind* NetHackQtBind::instance=0; +NetHackQtKeyBuffer NetHackQtBind::keybuffer; +NetHackQtClickBuffer NetHackQtBind::clickbuffer; +NetHackQtMainWindow* NetHackQtBind::main=0; + + +extern "C" struct window_procs Qt_procs; + +struct window_procs Qt_procs = { + "Qt", + NetHackQtBind::qt_init_nhwindows, + NetHackQtBind::qt_player_selection, + NetHackQtBind::qt_askname, + NetHackQtBind::qt_get_nh_event, + NetHackQtBind::qt_exit_nhwindows, + NetHackQtBind::qt_suspend_nhwindows, + NetHackQtBind::qt_resume_nhwindows, + NetHackQtBind::qt_create_nhwindow, + NetHackQtBind::qt_clear_nhwindow, + NetHackQtBind::qt_display_nhwindow, + NetHackQtBind::qt_destroy_nhwindow, + NetHackQtBind::qt_curs, + NetHackQtBind::qt_putstr, + NetHackQtBind::qt_display_file, + NetHackQtBind::qt_start_menu, + NetHackQtBind::qt_add_menu, + NetHackQtBind::qt_end_menu, + NetHackQtBind::qt_select_menu, + genl_message_menu, /* no need for X-specific handling */ + NetHackQtBind::qt_update_inventory, + NetHackQtBind::qt_mark_synch, + NetHackQtBind::qt_wait_synch, +#ifdef CLIPPING + NetHackQtBind::qt_cliparound, +#endif +#ifdef POSITIONBAR + donull, +#endif + NetHackQtBind::qt_print_glyph, + //NetHackQtBind::qt_print_glyph_compose, + NetHackQtBind::qt_raw_print, + NetHackQtBind::qt_raw_print_bold, + NetHackQtBind::qt_nhgetch, + NetHackQtBind::qt_nh_poskey, + NetHackQtBind::qt_nhbell, + NetHackQtBind::qt_doprev_message, + NetHackQtBind::qt_yn_function, + NetHackQtBind::qt_getlin, + NetHackQtBind::qt_get_ext_cmd, + NetHackQtBind::qt_number_pad, + NetHackQtBind::qt_delay_output, +#ifdef CHANGE_COLOR /* only a Mac option currently */ + donull, + donull, +#endif + /* other defs that really should go away (they're tty specific) */ + NetHackQtBind::qt_start_screen, + NetHackQtBind::qt_end_screen, +#ifdef GRAPHIC_TOMBSTONE + NetHackQtBind::qt_outrip, +#else + genl_outrip, +#endif +}; + +extern "C" void play_usersound(const char* filename, int volume) +{ +#ifdef USER_SOUNDS + // Qt 2.2 has sound + //QSound::play(filename,volume); +#endif +} + +#include "qt_win.moc" +#ifndef KDE +#include "qt_kde0.moc" +#endif