// 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,1997 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 "qt_clust.h" #include "qt_xpms.h" //#include #include #ifdef _WS_X11_ // For userid control #include #endif #ifdef USER_SOUNDS #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; 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 4 #define TILEHMIN 4 /* 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++ ", " ", " ", " ", " "}; 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("fixed"), #endif large("times"), theglyphs(0) { int default_fontsize; 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; } 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.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 }; normal.setPointSize(size[fontsize.currentItem()]); return normal; } const QFont& NetHackQtSettings::normalFixedFont() { static int size[]={ 18, 14, 13, 10 }; normalfixed.setPointSize(size[fontsize.currentItem()]); return normalfixed; } const QFont& NetHackQtSettings::largeFont() { static int size[]={ 24, 18, 14, 12 }; large.setPointSize(size[fontsize.currentItem()]); return large; } bool NetHackQtSettings::ynInMessages() { // XXX Two out of two users prefer True. return TRUE; } 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(const char* str) { while (*str) Put(0,*str++,0); } 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 && c != selectedItem()) i++,c = c->nextSibling(); return i; } 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) { /* 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(1,Horizontal,"Sex",this); QButtonGroup* alignbox = new QButtonGroup(1,Horizontal,"Alignment",this); 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 int ro = rn2(nrole); int ra = rn2(nrace); #ifdef QT_CHOOSE_RACE_FIRST while (!validrace(ro,ra)) ro = rn2(nrole); #else while (!validrace(ro,ra)) ra = rn2(nrace); #endif int g; do { g = rn2(ROLE_GENDERS); } while (!validgend(ro,ra,g)); gender[g]->setChecked(TRUE); selectGender(g); int a; do { a = rn2(ROLE_ALIGNS); } while (!validalign(ro,ra,a)); 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() { #ifndef QT_CHOOSE_RACE_FIRST selectRace(); #else QListViewItem* i=role->currentItem(); QListViewItem* valid=0; int j; NhPSListViewItem* item; item = (NhPSListViewItem*)role->firstChild(); int ra = race->selectedItemNumber(); 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() { #ifdef QT_CHOOSE_RACE_FIRST selectRole(); flags.initrace = race->selectedItemNumber(); #else QListViewItem* i=race->currentItem(); QListViewItem* valid=0; int j; NhPSListViewItem* item; item = (NhPSListViewItem*)race->firstChild(); int ro = role->selectedItemNumber(); 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() { 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(pet_mark_xpm); cursor.setX(0); cursor.setY(0); Clear(); connect(qt_settings,SIGNAL(tilesChanged()),this,SLOT(updateTiles())); updateTiles(); //setFocusPolicy(StrongFocus); } 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; /* (from wintty, naturally) * * Map the glyph back to a character. * * Warning: For speed, this makes an assumption on the order of * offsets. The order is set in display.h. */ #ifdef TEXTCOLOR int color; #define zap_color(n) color = iflags.use_color ? zapcolors[n] : NO_COLOR #define cmap_color(n) color = iflags.use_color ? defsyms[n].color : NO_COLOR #define obj_color(n) color = iflags.use_color ? objects[n].oc_color : NO_COLOR #define mon_color(n) color = iflags.use_color ? mons[n].mcolor : NO_COLOR #define pet_color(n) color = iflags.use_color ? mons[n].mcolor : NO_COLOR #define warn_color(n) color = iflags.use_color ? def_warnsyms[n].color : NO_COLOR # else /* no text color */ #define zap_color(n) #define cmap_color(n) #define obj_color(n) #define mon_color(n) #define pet_color(c) #define warn_color(c) painter.setPen( green ); #endif if ((offset = (g - GLYPH_WARNING_OFF)) >= 0) { /* a warning flash */ ch = warnsyms[offset]; warn_color(offset); } else if ((offset = (g - GLYPH_SWALLOW_OFF)) >= 0) { /* swallow */ /* see swallow_to_glyph() in display.c */ ch = (uchar) showsyms[S_sw_tl + (offset & 0x7)]; mon_color(offset >> 3); } else if ((offset = (g - GLYPH_ZAP_OFF)) >= 0) { /* zap beam */ /* see zapdir_to_glyph() in display.c */ ch = showsyms[S_vbeam + (offset & 0x3)]; zap_color((offset >> 2)); } else if ((offset = (g - GLYPH_CMAP_OFF)) >= 0) { /* cmap */ ch = showsyms[offset]; cmap_color(offset); } else if ((offset = (g - GLYPH_OBJ_OFF)) >= 0) { /* object */ ch = oc_syms[(int)objects[offset].oc_class]; obj_color(offset); } else if ((offset = (g - GLYPH_BODY_OFF)) >= 0) { /* a corpse */ ch = oc_syms[(int)objects[CORPSE].oc_class]; mon_color(offset); } else if ((offset = (g - GLYPH_PET_OFF)) >= 0) { /* a pet */ ch = monsyms[(int)mons[offset].mlet]; pet_color(offset); } else { /* a monster */ ch = monsyms[(int)mons[g].mlet]; mon_color(g); } // end of wintty code #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()); } 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; connect(qt_settings,SIGNAL(fontChanged()),this,SLOT(updateFont())); updateFont(); } NetHackQtMessageWindow::~NetHackQtMessageWindow() { ::iflags.window_inited = 0; delete list; } QWidget* NetHackQtMessageWindow::Widget() { return list; } void NetHackQtMessageWindow::updateFont() { list->setFont(qt_settings->normalFont()); } void NetHackQtMessageWindow::Scroll(int dx, int dy) { list->Scroll(dx,dy); } void NetHackQtMessageWindow::Clear() { 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()); } 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); updateStats(); } 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); // 20 allows for scrollbar or spacing // 4 for frame borders int mh = QApplication::desktop()->height()*3/5; dialog->resize(totalWidth()+20, QMIN(totalHeight(), mh)+buth+4+(prompt.text().isNull() ? 0 : buth)); dialog->SetResult(-1); centerOnMain(dialog); dialog->show(); setFocus(); while (dialog->result()<0) { qApp->enter_loop(); // changed the defaults below to the values in wintype.h 000119 - azy if (dialog->result()<0 && !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; ihide(); 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 (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; } 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; 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()); } int h=ok.height()*2; if (use_rip) { h+=rip.height(); rip.show(); } else { rip.hide(); } int mh = QApplication::desktop()->height()*3/5; 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::resizeEvent(QResizeEvent*) { const int margin=8; const int gutter=8; const int butw = (width()-margin*2-gutter)/2; const int buth=fontMetrics().height()*2; int y=0; if (use_rip) { rip.setGeometry(0,0,width(),rip.height()); y+=rip.height(); } y+=margin; ok.setGeometry(margin, y, butw, buth); search.setGeometry(margin+butw+gutter, y, butw, buth); y+=buth+margin; lines->setGeometry(0,y,width(),height()-y); } 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(); } NetHackQtMainWindow::NetHackQtMainWindow(NetHackQtKeyBuffer& ks) : message(0), map(0), status(0), invusage(this), #ifdef KDE menubar(new KMenuBar(this)), #else menubar(new QMenuBar(this)), #endif keysink(ks) { setCaption("Qt NetHack"); setIcon(QPixmap(nh_icon)); setBackgroundColor(black); #ifndef KDE menubar->setSeparator(QMenuBar::InWindowsStyle); #endif QPopupMenu* game=new QPopupMenu; QPopupMenu* apparel=new QPopupMenu; QPopupMenu* action=new QPopupMenu; QPopupMenu* magic=new QPopupMenu; QPopupMenu* nonaction=new QPopupMenu; #ifdef KDE QPopupMenu *help = kapp->getHelpMenu( TRUE, "" ); help->insertSeparator(); #else QPopupMenu* help = new QPopupMenu; #endif struct Macro { QPopupMenu* menu; const char* name; const char* action; } item[] = { { game, 0, 0 }, { game, "Version\tv", "v" }, { game, "Compilation\tAlt-V", "\366" }, { game, "History\tShift-V", "V" }, { game, "Redraw\tCtrl-R", "\022" }, { game, "Options\tShift-O", "O" }, { game, "Explore mode\tShift-X", "X" }, { game, 0, 0 }, { game, "Save\tShift-S", "S" }, { game, "Quit\tAlt-Q", "\361" }, { apparel, "Apparel off\tShift-A", "A" }, { apparel, 0, 0 }, { apparel, "Wield weapon\tw", "w" }, { apparel, "Exchange weapons\tx", "x" }, { apparel, "Two weapon combat\t#two", "#tw" }, { apparel, 0, 0 }, { apparel, "Wear armour\tShift-W", "W" }, { apparel, "Take off armour\tShift-T", "T" }, { apparel, 0, 0 }, { apparel, "Put on non-armour\tShift-P", "P" }, { apparel, "Remove non-armour\tShift-R", "R" }, { action, "Again\tCtrl-A", "\001" }, { action, 0, 0 }, { action, "Get\t,", "," }, { action, "Loot\tAlt-L", "\354" }, { action, "Sit\tAlt-S", "\363" }, { action, "Force\tAlt-F", "\346" }, { action, "Kick\tCtrl-D", "\004" }, { action, "Jump\tAlt-J", "\352" }, { action, "Wipe face\tAlt-W", "\367" }, { action, "Throw\tt", "t" }, { action, "Load quiver\tQ", "Q" }, { action, "Fire from quiver\tf", "f" }, { action, "Fight\tF", "F" }, { action, "Open door\to", "o" }, { action, "Close door\tc", "c" }, { action, "Drop\td?", "d?" }, { action, "Drop many\tShift-D", "D" }, { action, "Eat\te?", "e?" }, { action, "Engrave\tShift-E", "E" }, { action, "Apply\ta?", "a?" }, { action, 0, 0 }, { action, "Ride\t#ri", "#ri" }, { action, "Up\t<", "<" }, { action, "Down\t>", ">" }, { action, "Rest\t.", "." }, { action, "Search\ts", "s" }, { action, 0, 0 }, { action, "Chat\tAlt-C", "\343" }, { action, "Pay\tp", "p" }, { magic, "Quaff potion\tq", "q?" }, { magic, "Read scroll/book\tr?", "r?" }, { magic, "Zap wand\tz?", "z?" }, { magic, "Zap spell\tShift-Z?", "Z?" }, { magic, "Dip\tAlt-D", "\344" }, { magic, "Rub\tAlt-R", "\362" }, { magic, "Invoke\tAlt-I", "\351" }, { magic, 0, 0 }, { magic, "Offer\tAlt-O", "\357" }, { magic, "Pray\tAlt-P", "\360" }, { magic, 0, 0 }, { magic, "Teleport\tCtrl-T", "\024" }, { magic, "Monster action\tAlt-M", "\355" }, { magic, "Turn undead\tAlt-T", "\364" }, { nonaction, "Inventory\ti", "i" }, #ifdef SLASHEM { nonaction, "Angbandish inventory\t*", "*" }, #endif { nonaction, "Conduct\t#co", "#co" }, { nonaction, "Discoveries\t\\", "\\" }, { nonaction, "List/reorder spells\t+", "+" }, { nonaction, "Adjust letters\tAlt-A", "\341" }, { nonaction, 0, 0 }, { nonaction, "Name objects\tAlt-N", "\356" }, { nonaction, "Name creature\tShift-C", "C" }, { nonaction, 0, 0 }, { nonaction, "Qualifications\tAlt-E", "\345" }, { help, "Help\t?", "?" }, { help, 0, 0 }, { help, "What is here\t:", ":" }, { help, "What is that\t;", ";" }, { help, "What is...\t/", "/y" }, { 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].name) { item[i].menu->insertItem(item[i].name,count); macro[count++]=item[i].action; } else { item[i].menu->insertSeparator(); } } menubar->insertItem("Game",game); menubar->insertItem("Apparel",apparel); menubar->insertItem("Action",action); menubar->insertItem("Magic",magic); menubar->insertItem("Non-action",nonaction); 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); } 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: keysink.Put(macro[id]); 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() { invusage.repaint(FALSE); } void NetHackQtMainWindow::fadeHighlighting() { if (status) { status->fadeHighlighting(); } } void NetHackQtMainWindow::layout() { 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 switch (event->key()) { case Key_Up: if (map) map->Scroll(0,-1); break; case Key_Down: if (map) map->Scroll(0,+1); break; case Key_Left: if (map) map->Scroll(-1,0); break; case Key_Right: 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); message->Widget()->recreate(this,0,pos); map->Widget()->recreate(this,0,pos); status->Widget()->recreate(this,0,pos); 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() { if (choices) { QButtonGroup group(question, this); int nchoices=strlen(choices); bool allow_count=strchr(choices,'#')!=0; 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; isetGeometry(x,y,butsize,butsize); // Square if (choices[i]==def) button->setDefault(TRUE); if (i%10==9) { // last in row x=margin; y+=butsize+gutter; } else { x+=butsize+gutter; } } group.resize(margin*2+(gutter+button->width())*10, extra+margin*2+(gutter+button->height())*(nchoices/10+1)); connect(&group,SIGNAL(clicked(int)),this,SLOT(done(int))); QLabel* lb=0; QLineEdit* le=0; if (allow_count) { lb=new QLabel("Count: ",this); le=new QLineEdit(this); lb->setGeometry(margin,group.height()+margin,group.width()*1/3,le->height()); le->setGeometry(lb->geometry().right()+margin,lb->geometry().top(), group.width()*2/3,le->height()); } centerOnMain(this); show(); char choice=0; while (!choice) { if (!result()) { if (!keysource.Empty()) { char k=keysource.GetAscii(); char ch_esc=0; for (int i=0; choices[i]; i++) { if (choices[i]==k) choice=k; if (choices[i]=='q') ch_esc='q'; else if (!ch_esc && choices[i]=='n') ch_esc='n'; } if (!choice) { if (k=='\033' && ch_esc) choice=ch_esc; else if (k==' ' || k=='\r' || k=='\n') choice=def; // else choice remains 0 } } } else { choice=choices[result()-1000]; } qApp->enter_loop(); } hide(); if (allow_count && !le->text().isEmpty()) { yn_number=atoi(le->text()); choice='#'; } return choice; } else { QLabel label(question,this); QPushButton cancel("Dismiss",this); label.setFrameStyle(QFrame::Box|QFrame::Sunken); label.setAlignment(AlignCenter); label.resize(fontMetrics().width(question)+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::done(int i) { setResult(i+1000); 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) #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 (0,"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(0,"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