1 // SCCS Id: @(#)qt_win.cpp 3.4 1999/11/19
2 // Copyright (c) Warwick Allison, 1999.
3 // NetHack may be freely redistributed. See license for details.
5 // Qt Binding for NetHack 3.4
7 // Copyright (C) 1996-2001 by Warwick W. Allison (warwick@troll.no)
10 // Michael Hohmuth <hohmuth@inf.tu-dresden.de>
12 // Svante Gerhard <svante@algonet.se>
13 // - .nethackrc tile and font size settings
14 // Dirk Schoenberger <schoenberger@signsoft.com>
17 // and many others for bug reports.
19 // Unfortunately, this doesn't use Qt as well as I would like,
20 // primarily because NetHack is fundamentally a getkey-type program
21 // rather than being event driven (hence the ugly key and click buffer)
22 // and also because this is my first major application of Qt.
24 // The problem of NetHack's getkey requirement is solved by intercepting
25 // key events by overiding QApplicion::notify(...), and putting them in
26 // a buffer. Mouse clicks on the map window are treated with a similar
27 // buffer. When the NetHack engine calls for a key, one is taken from
28 // the buffer, or if that is empty, QApplication::enter_loop() is called.
29 // Whenever keys or clicks go into the buffer, QApplication::exit_loop()
32 // Another problem is that some NetHack players are decade-long players who
33 // demand complete keyboard control (while Qt and X11 conspire to make this
34 // difficult by having widget-based focus rather than application based -
35 // a good thing in general). This problem is solved by again using the key
38 // Out of all this hackery comes a silver lining however, as macros for
39 // the super-expert and menus for the ultra-newbie are also made possible
40 // by the key event buffer.
45 // This includes all the definitions we need from the NetHack main
46 // engine. We pretend MSC is a STDC compiler, because C++ is close
47 // enough, and we undefine NetHack macros which conflict with Qt
50 #define alloc hide_alloc // avoid treading on STL symbol
51 #define lock hide_lock // avoid treading on STL symbol
58 #include "patchlevel.h"
84 #include <qpopupmenu.h>
87 #include <qradiobutton.h>
89 #include <qtoolbutton.h>
90 #include <qcombobox.h>
92 #include <qdragobject.h>
93 #include <qtextbrowser.h>
95 #include <qsignalmapper.h>
97 //#include <qlabelled.h>
101 #include "qt_clust.h"
106 # include <sys/malloc.h>
112 // For userid control
116 // Some distributors released Qt 2.1.0beta4
118 # define nh_WX11BypassWM 0x01000000
120 # define nh_WX11BypassWM WX11BypassWM
124 # if QT_VERSION < 220
133 extern "C" void play_sound_for_message(const char* str);
136 // Warwick prefers it this way...
137 #define QT_CHOOSE_RACE_FIRST
139 static const char nh_attribution[] = "<center><big>NetHack</big>"
140 "<br><small>by the NetHack DevTeam</small></center>";
147 "Qt NetHack is a version of NetHack built\n"
149 "using KDE and the Qt GUI toolkit.\n"
151 "using the Qt GUI toolkit.\n"
153 "This is version %d.%d.%d\n\n"
154 "Homepage:\n http://trolls.troll.no/warwick/nethack/\n\n"
156 "KDE:\n http://www.kde.org\n"
158 "Qt:\n http://www.troll.no",
166 centerOnMain( QWidget* w )
168 QWidget* m = qApp->mainWidget();
169 if (!m) m = qApp->desktop();
170 QPoint p = m->mapToGlobal(QPoint(0,0));
171 w->move( p.x() + m->width()/2 - w->width()/2,
172 p.y() + m->height()/2 - w->height()/2 );
175 NetHackQtLineEdit::NetHackQtLineEdit() :
180 NetHackQtLineEdit::NetHackQtLineEdit(QWidget* parent, const char* name) :
181 QLineEdit(parent,name)
185 void NetHackQtLineEdit::fakeEvent(int key, int ascii, int state)
187 QKeyEvent fake(QEvent::KeyPress,key,ascii,state);
188 keyPressEvent(&fake);
192 /* Used by tile/font-size patch below and in ../../src/files.c */
193 char *qt_tilewidth=NULL;
194 char *qt_tileheight=NULL;
195 char *qt_fontsize=NULL;
197 int qt_compact_mode = 1;
199 int qt_compact_mode = 0;
201 extern const char *enc_stat[]; /* from botl.c */
202 extern const char *hu_stat[]; /* from eat.c */
203 extern const char *killed_by_prefix[];
204 extern int total_tiles_used; // from tile.c
205 extern short glyph2tile[]; // from tile.c
208 static int tilefile_tile_W=16;
209 static int tilefile_tile_H=16;
216 static const char * nh_icon[] = {
228 " .. .....X.XXXXXX XX ",
229 " ... ....X..XX.XXXXX XXX ",
230 " .. ..........X.XXXXXXXXXXX XX ",
231 " .... ........X..XX.XXXXXXXXX XXXX ",
232 " .... ..........X.XXXXXXXXXXX XXXX ",
233 " ooOOO..ooooooOooOOoOOOOOOOXX+++OO++ ",
234 " ooOOO..ooooooooOoOOOOOOOOOXX+++OO++ ",
235 " ....O..ooooooOooOOoOOOOOOOXX+XXXX++ ",
236 " ....O..ooooooooOoOOOOOOOOOXX+XXXX++ ",
237 " ..OOO..ooooooOooOOoOOOOOOOXX+++XX++ ",
238 " ++++..ooooooooOoOOOOOOOOOXX+++ +++ ",
239 " +++..ooooooOooOOoOOOOOOOXX+++ + ",
240 " ++..ooooooooOoOOOOOOOOOXX+++ ",
241 " ..ooooooOooOOoOOOOOOOXX+++ ",
242 " ..ooooooooOoOOOOOOOOOXX+++ ",
243 " ..ooooooOooOOoOOOOOOOXX+++ ",
244 " ..ooooooooOoOOOOOOOOOXX+++ ",
245 " ..oooooOooOOoOOOOOOXX+++ ",
246 " ..oooooooOoOOOOOOOOXX+++ ",
247 " ..ooooOooOOoOOOOOXX+++ ",
248 " ..ooooooOoOOOOOOOXX++++ ",
249 " ..o..oooOooOOoOOOOXX+XX+++ ",
250 " ...o..oooooOoOOOOOXX++XXX++ ",
251 " ....OO..ooOooOOoOOXX+++XXXX++ ",
252 " ...oo..+..oooOoOOOXX++XXooXXX++ ",
253 " ...ooo..++..OooOOoXX+++XXooOXXX+ ",
254 " ..oooOOXX+++....XXXX++++XXOOoOOXX+ ",
255 " ..oooOOXX+++ ...XXX+++++XXOOooOXX++ ",
256 " ..oooOXXX+++ ..XX+++ +XXOOooOXX++ ",
257 " .....XXX++++ XXXXXXX++ ",
258 " ....XX++++ XXXXXXX+ ",
259 " ...XX+++ XXXXX++ ",
265 static const char * nh_icon_small[] = {
266 /* width height ncolors chars_per_pixel */
305 static const char * map_xpm[] = {
325 static const char * msg_xpm[] = {
345 static const char * stat_xpm[] = {
366 static const char * info_xpm[] = {
388 static const char * again_xpm[] = {
406 static const char * kick_xpm[] = {
425 static const char * throw_xpm[] = {
444 static const char * fire_xpm[] = {
465 static const char * get_xpm[] = {
484 static const char * drop_xpm[] = {
503 static const char * eat_xpm[] = {
523 static const char * rest_xpm[] = {
541 static const char * cast_a_xpm[] = {
560 static const char * cast_b_xpm[] = {
579 static const char * cast_c_xpm[] = {
598 NetHackQtSettings::NetHackQtSettings(int w, int h) :
599 tilewidth(TILEWMIN,64,1,this),
600 tileheight(TILEHMIN,64,1,this),
601 widthlbl(&tilewidth,"&Width:",this),
602 heightlbl(&tileheight,"&Height:",this),
603 whichsize("&Zoomed",this),
607 normalfixed("courier new"),
609 normalfixed("fixed"),
615 int default_fontsize;
620 tilewidth.setValue(8);
621 tileheight.setValue(12);
625 tilewidth.setValue(8);
626 tileheight.setValue(14);
630 tilewidth.setValue(10);
631 tileheight.setValue(17);
632 } else if (w<=1100) {
635 tilewidth.setValue(12);
636 tileheight.setValue(22);
637 } else if (w<=1200) {
640 tilewidth.setValue(14);
641 tileheight.setValue(26);
643 // ~1280x1024 and larger
645 tilewidth.setValue(16);
646 tileheight.setValue(30);
649 // Tile/font sizes read from .nethackrc
650 if (qt_tilewidth != NULL) {
651 tilewidth.setValue(atoi(qt_tilewidth));
654 if (qt_tileheight != NULL) {
655 tileheight.setValue(atoi(qt_tileheight));
658 if (qt_fontsize != NULL) {
659 switch (tolower(qt_fontsize[0])) {
660 case 'h': default_fontsize = 0; break;
661 case 'l': default_fontsize = 1; break;
662 case 'm': default_fontsize = 2; break;
663 case 's': default_fontsize = 3; break;
664 case 't': default_fontsize = 4; break;
669 theglyphs=new NetHackQtGlyphs();
672 connect(&tilewidth,SIGNAL(valueChanged(int)),this,SLOT(resizeTiles()));
673 connect(&tileheight,SIGNAL(valueChanged(int)),this,SLOT(resizeTiles()));
674 connect(&whichsize,SIGNAL(toggled(bool)),this,SLOT(setGlyphSize(bool)));
676 fontsize.insertItem("Huge");
677 fontsize.insertItem("Large");
678 fontsize.insertItem("Medium");
679 fontsize.insertItem("Small");
680 fontsize.insertItem("Tiny");
681 fontsize.setCurrentItem(default_fontsize);
682 connect(&fontsize,SIGNAL(activated(int)),this,SIGNAL(fontChanged()));
684 QGridLayout* grid = new QGridLayout(this, 5, 2, 8);
685 grid->addMultiCellWidget(&whichsize, 0, 0, 0, 1);
686 grid->addWidget(&tilewidth, 1, 1); grid->addWidget(&widthlbl, 1, 0);
687 grid->addWidget(&tileheight, 2, 1); grid->addWidget(&heightlbl, 2, 0);
688 QLabel* flabel=new QLabel(&fontsize, "&Font:",this);
689 grid->addWidget(flabel, 3, 0); grid->addWidget(&fontsize, 3, 1);
690 QPushButton* dismiss=new QPushButton("Dismiss",this);
691 dismiss->setDefault(TRUE);
692 grid->addMultiCellWidget(dismiss, 4, 4, 0, 1);
693 grid->setRowStretch(4,0);
694 grid->setColStretch(1,1);
695 grid->setColStretch(2,2);
698 connect(dismiss,SIGNAL(clicked()),this,SLOT(accept()));
702 NetHackQtGlyphs& NetHackQtSettings::glyphs()
707 void NetHackQtSettings::resizeTiles()
709 int w = tilewidth.value();
710 int h = tileheight.value();
712 theglyphs->setSize(w,h);
716 void NetHackQtSettings::toggleGlyphSize()
721 void NetHackQtSettings::setGlyphSize(bool which)
723 QSize n = QSize(tilewidth.value(),tileheight.value());
724 if ( othersize.isValid() ) {
725 tilewidth.blockSignals(TRUE);
726 tileheight.blockSignals(TRUE);
727 tilewidth.setValue(othersize.width());
728 tileheight.setValue(othersize.height());
729 tileheight.blockSignals(FALSE);
730 tilewidth.blockSignals(FALSE);
736 const QFont& NetHackQtSettings::normalFont()
738 static int size[]={ 18, 14, 12, 10, 8 };
739 normal.setPointSize(size[fontsize.currentItem()]);
743 const QFont& NetHackQtSettings::normalFixedFont()
745 static int size[]={ 18, 14, 13, 10, 8 };
746 normalfixed.setPointSize(size[fontsize.currentItem()]);
750 const QFont& NetHackQtSettings::largeFont()
752 static int size[]={ 24, 18, 14, 12, 10 };
753 large.setPointSize(size[fontsize.currentItem()]);
757 bool NetHackQtSettings::ynInMessages()
759 return !qt_compact_mode;
763 NetHackQtSettings* qt_settings;
767 NetHackQtKeyBuffer::NetHackQtKeyBuffer() :
772 bool NetHackQtKeyBuffer::Empty() const { return in==out; }
773 bool NetHackQtKeyBuffer::Full() const { return (in+1)%maxkey==out; }
775 void NetHackQtKeyBuffer::Put(int k, int a, int state)
777 if ( Full() ) return; // Safety
783 void NetHackQtKeyBuffer::Put(char a)
788 void NetHackQtKeyBuffer::Put(const char* str)
790 while (*str) Put(*str++);
793 int NetHackQtKeyBuffer::GetKey()
795 if ( Empty() ) return 0;
801 int NetHackQtKeyBuffer::GetAscii()
803 if ( Empty() ) return 0; // Safety
809 int NetHackQtKeyBuffer::GetState()
811 if ( Empty() ) return 0;
817 int NetHackQtKeyBuffer::TopKey() const
819 if ( Empty() ) return 0;
823 int NetHackQtKeyBuffer::TopAscii() const
825 if ( Empty() ) return 0;
829 int NetHackQtKeyBuffer::TopState() const
831 if ( Empty() ) return 0;
836 NetHackQtClickBuffer::NetHackQtClickBuffer() :
841 bool NetHackQtClickBuffer::Empty() const { return in==out; }
842 bool NetHackQtClickBuffer::Full() const { return (in+1)%maxclick==out; }
844 void NetHackQtClickBuffer::Put(int x, int y, int mod)
852 int NetHackQtClickBuffer::NextX() const { return click[out].x; }
853 int NetHackQtClickBuffer::NextY() const { return click[out].y; }
854 int NetHackQtClickBuffer::NextMod() const { return click[out].mod; }
856 void NetHackQtClickBuffer::Get()
858 out=(out+1)%maxclick;
861 class NhPSListViewItem : public QListViewItem {
863 NhPSListViewItem( QListView* parent, const QString& name ) :
864 QListViewItem(parent, name)
870 NetHackQtGlyphs& glyphs = qt_settings->glyphs();
871 int gw = glyphs.width();
872 int gh = glyphs.height();
875 glyphs.drawGlyph(p, g, 0, 0);
878 setHeight(QMAX(pm.height()+1,height()));
881 void paintCell( QPainter *p, const QColorGroup &cg,
882 int column, int width, int alignment )
884 if ( isSelectable() ) {
885 QListViewItem::paintCell( p, cg, column, width, alignment );
887 QColorGroup disabled(
888 cg.foreground().light(),
890 cg.light(), cg.dark(), cg.mid(),
892 QListViewItem::paintCell( p, disabled, column, width, alignment );
897 class NhPSListViewRole : public NhPSListViewItem {
899 NhPSListViewRole( QListView* parent, int id ) :
900 NhPSListViewItem(parent,
901 #ifdef QT_CHOOSE_RACE_FIRST // Lowerize - looks better
902 QString(QChar(roles[id].name.m[0])).lower()+QString(roles[id].name.m+1)
908 setGlyph(monnum_to_glyph(roles[id].malenum));
912 class NhPSListViewRace : public NhPSListViewItem {
914 NhPSListViewRace( QListView* parent, int id ) :
915 NhPSListViewItem(parent,
916 #ifdef QT_CHOOSE_RACE_FIRST // Capitalize - looks better
917 QString(QChar(races[id].noun[0])).upper()+QString(races[id].noun+1)
919 QString(QChar(races[id].noun[0])+QString(races[id].noun+1))
923 setGlyph(monnum_to_glyph(races[id].malenum));
927 class NhPSListView : public QListView {
929 NhPSListView( QWidget* parent ) :
932 setSorting(-1); // order is identity
933 header()->setClickEnabled(FALSE);
936 QSizePolicy sizePolicy() const
938 return QSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
941 QSize minimumSizeHint() const
946 QSize sizeHint() const
948 QListView::sizeHint();
949 QSize sz = header()->sizeHint();
951 QListViewItem* c=firstChild();
952 while (c) h+=c->height(),c = c->nextSibling();
953 sz += QSize(frameWidth()*2, h+frameWidth()*2);
957 int selectedItemNumber() const
960 QListViewItem* c = firstChild();
962 if (c == selectedItem()) {
966 c = c->nextSibling();
971 void setSelectedItemNumber(int i)
973 QListViewItem* c=firstChild();
975 c = c->nextSibling();
976 c->setSelected(TRUE);
980 NetHackQtPlayerSelector::NetHackQtPlayerSelector(NetHackQtKeyBuffer& ks) :
981 QDialog(qApp->mainWidget(),"plsel",TRUE),
983 fully_specified_role(TRUE)
987 + Name ------------------------------------+
989 + ---- ------------------------------------+
990 + Role ---+ + Race ---+ + Gender ------+
992 1 | | | | | * Female |
993 | | | | +--------------+
995 | | | | + Alignment ---+
998 | | | | +--------------+
999 3 | | | | ...stretch...
1003 +---------+ +---------+
1007 QGridLayout *l = new QGridLayout(this,6,3,marg,marg);
1009 QButtonGroup* namebox = new QButtonGroup(1,Horizontal,"Name",this);
1010 QLineEdit* name = new QLineEdit(namebox);
1011 name->setMaxLength(sizeof(plname)-1);
1012 if ( strncmp(plname,"player",6) && strncmp(plname,"games",5) )
1013 name->setText(plname);
1014 connect(name, SIGNAL(textChanged(const QString&)),
1015 this, SLOT(selectName(const QString&)) );
1017 QButtonGroup* genderbox = new QButtonGroup("Sex",this);
1018 QButtonGroup* alignbox = new QButtonGroup("Alignment",this);
1019 QVBoxLayout* vbgb = new QVBoxLayout(genderbox,3,1);
1020 vbgb->setAutoAdd(TRUE);
1021 vbgb->addSpacing(fontMetrics().height()*3/4);
1022 QVBoxLayout* vbab = new QVBoxLayout(alignbox,3,1);
1023 vbab->setAutoAdd(TRUE);
1024 vbab->addSpacing(fontMetrics().height());
1025 QLabel* logo = new QLabel(nh_attribution, this);
1027 l->addMultiCellWidget( namebox, 0,0,0,2 );
1028 #ifdef QT_CHOOSE_RACE_FIRST
1029 race = new NhPSListView(this);
1030 role = new NhPSListView(this);
1031 l->addMultiCellWidget( race, 1,5,0,0 );
1032 l->addMultiCellWidget( role, 1,5,1,1 );
1034 role = new NhPSListView(this);
1035 race = new NhPSListView(this);
1036 l->addMultiCellWidget( role, 1,5,0,0 );
1037 l->addMultiCellWidget( race, 1,5,1,1 );
1039 role->addColumn("Role");
1040 race->addColumn("Race");
1042 l->addWidget( genderbox, 1, 2 );
1043 l->addWidget( alignbox, 2, 2 );
1044 l->addWidget( logo, 3, 2, AlignCenter );
1045 l->setRowStretch( 3, 5 );
1050 for (nrole=0; roles[nrole].name.m; nrole++)
1052 for (i=nrole-1; i>=0; i--) { // XXX QListView unsorted goes in rev.
1053 new NhPSListViewRole( role, i );
1055 connect( role, SIGNAL(selectionChanged()), this, SLOT(selectRole()) );
1058 for (nrace=0; races[nrace].noun; nrace++)
1060 for (i=nrace-1; i>=0; i--) {
1061 new NhPSListViewRace( race, i );
1063 connect( race, SIGNAL(selectionChanged()), this, SLOT(selectRace()) );
1065 gender = new QRadioButton*[ROLE_GENDERS];
1066 for (i=0; i<ROLE_GENDERS; i++) {
1067 gender[i] = new QRadioButton( genders[i].adj, genderbox );
1069 connect( genderbox, SIGNAL(clicked(int)), this, SLOT(selectGender(int)) );
1071 alignment = new QRadioButton*[ROLE_ALIGNS];
1072 for (i=0; i<ROLE_ALIGNS; i++) {
1073 alignment[i] = new QRadioButton( aligns[i].adj, alignbox );
1075 connect( alignbox, SIGNAL(clicked(int)), this, SLOT(selectAlignment(int)) );
1077 QPushButton* ok = new QPushButton("Play",this);
1078 l->addWidget( ok, 4, 2 );
1079 ok->setDefault(TRUE);
1080 connect( ok, SIGNAL(clicked()), this, SLOT(accept()) );
1082 QPushButton* cancel = new QPushButton("Quit",this);
1083 l->addWidget( cancel, 5, 2 );
1084 connect( cancel, SIGNAL(clicked()), this, SLOT(reject()) );
1086 // Randomize race and role, unless specified in config
1087 int ro = flags.initrole;
1088 if (ro == ROLE_NONE || ro == ROLE_RANDOM) {
1090 if (flags.initrole != ROLE_RANDOM) {
1091 fully_specified_role = FALSE;
1094 int ra = flags.initrace;
1095 if (ra == ROLE_NONE || ra == ROLE_RANDOM) {
1097 if (flags.initrace != ROLE_RANDOM) {
1098 fully_specified_role = FALSE;
1102 // make sure we have a valid combination, honoring
1103 // the users request if possible.
1104 bool choose_race_first;
1105 #ifdef QT_CHOOSE_RACE_FIRST
1106 choose_race_first = TRUE;
1107 if (flags.initrole >= 0 && flags.initrace < 0) {
1108 choose_race_first = FALSE;
1111 choose_race_first = FALSE;
1112 if (flags.initrace >= 0 && flags.initrole < 0) {
1113 choose_race_first = TRUE;
1116 while (!validrace(ro,ra)) {
1117 if (choose_race_first) {
1119 if (flags.initrole != ROLE_RANDOM) {
1120 fully_specified_role = FALSE;
1124 if (flags.initrace != ROLE_RANDOM) {
1125 fully_specified_role = FALSE;
1130 int g = flags.initgend;
1132 g = rn2(ROLE_GENDERS);
1133 fully_specified_role = FALSE;
1135 while (!validgend(ro,ra,g)) {
1136 g = rn2(ROLE_GENDERS);
1138 gender[g]->setChecked(TRUE);
1141 int a = flags.initalign;
1143 a = rn2(ROLE_ALIGNS);
1144 fully_specified_role = FALSE;
1146 while (!validalign(ro,ra,a)) {
1147 a = rn2(ROLE_ALIGNS);
1149 alignment[a]->setChecked(TRUE);
1154 li = role->firstChild();
1155 while (ro--) li=li->nextSibling();
1156 role->setSelected(li,TRUE);
1158 li = race->firstChild();
1159 while (ra--) li=li->nextSibling();
1160 race->setSelected(li,TRUE);
1162 flags.initrace = race->selectedItemNumber();
1163 flags.initrole = role->selectedItemNumber();
1167 void NetHackQtPlayerSelector::selectName(const QString& n)
1169 strncpy(plname,n.latin1(),sizeof(plname)-1);
1172 void NetHackQtPlayerSelector::selectRole()
1174 int ra = race->selectedItemNumber();
1175 int ro = role->selectedItemNumber();
1176 if (ra == -1 || ro == -1) return;
1178 #ifdef QT_CHOOSE_RACE_FIRST
1181 QListViewItem* i=role->currentItem();
1182 QListViewItem* valid=0;
1184 NhPSListViewItem* item;
1185 item = (NhPSListViewItem*)role->firstChild();
1186 for (j=0; roles[j].name.m; j++) {
1187 bool v = validrace(j,ra);
1188 item->setSelectable(TRUE);
1189 if ( !valid && v ) valid = item;
1190 item=(NhPSListViewItem*)item->nextSibling();
1192 if ( !validrace(role->selectedItemNumber(),ra) )
1194 role->setSelected(i,TRUE);
1195 item = (NhPSListViewItem*)role->firstChild();
1196 for (j=0; roles[j].name.m; j++) {
1197 bool v = validrace(j,ra);
1198 item->setSelectable(v);
1200 item=(NhPSListViewItem*)item->nextSibling();
1204 flags.initrole = role->selectedItemNumber();
1208 void NetHackQtPlayerSelector::selectRace()
1210 int ra = race->selectedItemNumber();
1211 int ro = role->selectedItemNumber();
1212 if (ra == -1 || ro == -1) return;
1214 #ifndef QT_CHOOSE_RACE_FIRST
1217 QListViewItem* i=race->currentItem();
1218 QListViewItem* valid=0;
1220 NhPSListViewItem* item;
1221 item = (NhPSListViewItem*)race->firstChild();
1222 for (j=0; races[j].noun; j++) {
1223 bool v = validrace(ro,j);
1224 item->setSelectable(TRUE);
1225 if ( !valid && v ) valid = item;
1226 item=(NhPSListViewItem*)item->nextSibling();
1228 if ( !validrace(ro,race->selectedItemNumber()) )
1230 race->setSelected(i,TRUE);
1231 item = (NhPSListViewItem*)race->firstChild();
1232 for (j=0; races[j].noun; j++) {
1233 bool v = validrace(ro,j);
1234 item->setSelectable(v);
1236 item=(NhPSListViewItem*)item->nextSibling();
1240 flags.initrace = race->selectedItemNumber();
1244 void NetHackQtPlayerSelector::setupOthers()
1246 int ro = role->selectedItemNumber();
1247 int ra = race->selectedItemNumber();
1251 for (j=0; j<ROLE_GENDERS; j++) {
1252 bool v = validgend(ro,ra,j);
1253 if ( gender[j]->isChecked() )
1255 gender[j]->setEnabled(v);
1256 if ( valid<0 && v ) valid = j;
1258 if ( !validgend(ro,ra,c) )
1261 for (k=0; k<ROLE_GENDERS; k++) {
1262 gender[k]->setChecked(c==k);
1267 for (j=0; j<ROLE_ALIGNS; j++) {
1268 bool v = validalign(ro,ra,j);
1269 if ( alignment[j]->isChecked() )
1271 alignment[j]->setEnabled(v);
1272 if ( valid<0 && v ) valid = j;
1274 if ( !validalign(ro,ra,c) )
1276 for (k=0; k<ROLE_ALIGNS; k++) {
1277 alignment[k]->setChecked(c==k);
1282 void NetHackQtPlayerSelector::selectGender(int i)
1287 void NetHackQtPlayerSelector::selectAlignment(int i)
1289 flags.initalign = i;
1293 void NetHackQtPlayerSelector::done(int i)
1299 void NetHackQtPlayerSelector::Quit()
1305 void NetHackQtPlayerSelector::Random()
1311 bool NetHackQtPlayerSelector::Choose()
1313 if (fully_specified_role) return TRUE;
1315 #if defined(QWS) // probably safe with Qt 3, too (where show!=exec in QDialog).
1316 if ( qt_compact_mode ) {
1333 NetHackQtStringRequestor::NetHackQtStringRequestor(NetHackQtKeyBuffer& ks, const char* p, const char* cancelstr) :
1334 QDialog(qApp->mainWidget(),"string",FALSE),
1335 prompt(p,this,"prompt"),
1336 input(this,"input"),
1339 cancel=new QPushButton(cancelstr,this);
1340 connect(cancel,SIGNAL(clicked()),this,SLOT(reject()));
1342 okay=new QPushButton("Okay",this);
1343 connect(okay,SIGNAL(clicked()),this,SLOT(accept()));
1344 connect(&input,SIGNAL(returnPressed()),this,SLOT(accept()));
1345 okay->setDefault(TRUE);
1347 setFocusPolicy(StrongFocus);
1350 void NetHackQtStringRequestor::resizeEvent(QResizeEvent*)
1355 int h=(height()-margin*2-gutter);
1357 if (strlen(prompt.text()) > 16) {
1359 prompt.setGeometry(margin,margin,width()-margin*2,h);
1360 input.setGeometry(width()*1/5,margin+h+gutter,
1361 (width()-margin-2-gutter)*4/5,h);
1364 prompt.setGeometry(margin,margin,(width()-margin*2-gutter)*2/5,h);
1365 input.setGeometry(prompt.geometry().right()+gutter,margin,
1366 (width()-margin-2-gutter)*3/5,h);
1369 cancel->setGeometry(margin,input.geometry().bottom()+gutter,
1370 (width()-margin*2-gutter)/2,h);
1371 okay->setGeometry(cancel->geometry().right()+gutter,cancel->geometry().y(),
1375 void NetHackQtStringRequestor::SetDefault(const char* d)
1379 bool NetHackQtStringRequestor::Get(char* buffer, int maxchar)
1381 input.setMaxLength(maxchar);
1382 if (strlen(prompt.text()) > 16) {
1383 resize(fontMetrics().width(prompt.text())+50,fontMetrics().height()*6);
1385 resize(fontMetrics().width(prompt.text())*2+50,fontMetrics().height()*4);
1392 while (result()==-1) {
1393 // Put keys in buffer (eg. from macros, from out-of-focus input)
1394 if (!keysource.Empty()) {
1395 while (!keysource.Empty()) {
1396 int key=keysource.TopKey();
1397 int ascii=keysource.TopAscii();
1398 int state=keysource.GetState();
1399 if (ascii=='\r' || ascii=='\n') {
1400 // CR or LF in buffer causes confirmation
1401 strcpy(buffer,input.text());
1403 } else if (ascii=='\033') {
1406 input.fakeEvent(key,ascii,state);
1412 // XXX Get rid of extra keys, since we couldn't get focus!
1413 while (!keysource.Empty()) keysource.GetKey();
1416 strcpy(buffer,input.text());
1422 void NetHackQtStringRequestor::done(int i)
1429 NetHackQtWindow::NetHackQtWindow()
1432 NetHackQtWindow::~NetHackQtWindow()
1436 // XXX Use "expected ..." for now, abort or default later.
1438 void NetHackQtWindow::Clear() { puts("unexpected Clear"); }
1439 void NetHackQtWindow::Display(bool block) { puts("unexpected Display"); }
1440 bool NetHackQtWindow::Destroy() { return TRUE; }
1441 void NetHackQtWindow::CursorTo(int x,int y) { puts("unexpected CursorTo"); }
1442 void NetHackQtWindow::PutStr(int attr, const char* text) { puts("unexpected PutStr"); }
1443 void NetHackQtWindow::StartMenu() { puts("unexpected StartMenu"); }
1444 void NetHackQtWindow::AddMenu(int glyph, const ANY_P* identifier, char ch, char gch, int attr,
1445 const char* str, bool presel) { puts("unexpected AddMenu"); }
1446 void NetHackQtWindow::EndMenu(const char* prompt) { puts("unexpected EndMenu"); }
1447 int NetHackQtWindow::SelectMenu(int how, MENU_ITEM_P **menu_list) { puts("unexpected SelectMenu"); return 0; }
1448 void NetHackQtWindow::ClipAround(int x,int y) { puts("unexpected ClipAround"); }
1449 void NetHackQtWindow::PrintGlyph(int x,int y,int glyph) { puts("unexpected PrintGlyph"); }
1450 //void NetHackQtWindow::PrintGlyphCompose(int x,int y,int,int) { puts("unexpected PrintGlyphCompose"); }
1451 void NetHackQtWindow::UseRIP(int how) { puts("unexpected UseRIP"); }
1455 // XXX Hmmm... crash after saving bones file if Map window is
1456 // XXX deleted. Strange bug somewhere.
1457 bool NetHackQtMapWindow::Destroy() { return FALSE; }
1459 NetHackQtMapWindow::NetHackQtMapWindow(NetHackQtClickBuffer& click_sink) :
1460 clicksink(click_sink),
1464 viewport.addChild(this);
1466 setBackgroundColor(black);
1467 viewport.setBackgroundColor(black);
1469 pet_annotation = QPixmap(qt_compact_mode ? pet_mark_small_xpm : pet_mark_xpm);
1475 connect(qt_settings,SIGNAL(tilesChanged()),this,SLOT(updateTiles()));
1476 connect(&viewport, SIGNAL(contentsMoving(int,int)), this,
1477 SLOT(moveMessages(int,int)));
1480 //setFocusPolicy(StrongFocus);
1483 void NetHackQtMapWindow::moveMessages(int x, int y)
1485 QRect u = messages_rect;
1486 messages_rect.moveTopLeft(QPoint(x,y));
1491 void NetHackQtMapWindow::clearMessages()
1494 update(messages_rect);
1495 messages_rect = QRect();
1498 void NetHackQtMapWindow::putMessage(int attr, const char* text)
1500 if ( !messages.isEmpty() )
1503 QFontMetrics fm = fontMetrics();
1504 messages_rect = fm.boundingRect(viewport.contentsX(),viewport.contentsY(),viewport.width(),0, WordBreak|AlignTop|AlignLeft|DontClip, messages);
1505 update(messages_rect);
1508 void NetHackQtMapWindow::updateTiles()
1510 NetHackQtGlyphs& glyphs = qt_settings->glyphs();
1511 int gw = glyphs.width();
1512 int gh = glyphs.height();
1513 // Be exactly the size we want to be - full map...
1514 resize(COLNO*gw,ROWNO*gh);
1516 viewport.verticalScrollBar()->setSteps(gh,gh);
1517 viewport.horizontalScrollBar()->setSteps(gw,gw);
1519 viewport.setMaximumSize(
1520 gw*COLNO + viewport.verticalScrollBar()->width(),
1521 gh*ROWNO + viewport.horizontalScrollBar()->height()
1524 viewport.updateScrollBars();
1527 change.add(0,0,COLNO,ROWNO);
1528 delete rogue_font; rogue_font = 0;
1534 NetHackQtMapWindow::~NetHackQtMapWindow()
1536 // Remove from viewport porthole, since that is a destructible member.
1537 viewport.removeChild(this);
1538 recreate(0,0,QPoint(0,0));
1541 QWidget* NetHackQtMapWindow::Widget()
1546 void NetHackQtMapWindow::Scroll(int dx, int dy)
1548 if (viewport.horizontalScrollBar()->isVisible()) {
1549 while (dx<0) { viewport.horizontalScrollBar()->subtractPage(); dx++; }
1550 while (dx>0) { viewport.horizontalScrollBar()->addPage(); dx--; }
1552 if (viewport.verticalScrollBar()->isVisible()) {
1553 while (dy<0) { viewport.verticalScrollBar()->subtractPage(); dy++; }
1554 while (dy>0) { viewport.verticalScrollBar()->addPage(); dy--; }
1558 void NetHackQtMapWindow::Clear()
1560 unsigned short stone=cmap_to_glyph(S_stone);
1562 for (int j=0; j<ROWNO; j++) {
1563 for (int i=0; i<COLNO; i++) {
1569 change.add(0,0,COLNO,ROWNO);
1572 void NetHackQtMapWindow::clickCursor()
1574 clicksink.Put(cursor.x(),cursor.y(),CLICK_1);
1578 void NetHackQtMapWindow::mousePressEvent(QMouseEvent* event)
1581 event->pos().x()/qt_settings->glyphs().width(),
1582 event->pos().y()/qt_settings->glyphs().height(),
1583 event->button()==LeftButton ? CLICK_1 : CLICK_2
1590 const QPen& nhcolor_to_pen(int c)
1597 pen[2] = QColor(0,191,0);
1598 pen[3] = QColor(127,127,0);
1600 pen[5] = Qt::magenta;
1603 pen[8] = Qt::white; // no color
1604 pen[9] = QColor(255,127,0);
1605 pen[10] = QColor(127,255,127);
1606 pen[11] = Qt::yellow;
1607 pen[12] = QColor(127,127,255);
1608 pen[13] = QColor(255,127,255);
1609 pen[14] = QColor(127,255,255);
1610 pen[15] = Qt::white;
1611 pen[16] = Qt::black;
1618 void NetHackQtMapWindow::paintEvent(QPaintEvent* event)
1620 QRect area=event->rect();
1623 QMAX(0,area.left()/qt_settings->glyphs().width()),
1624 QMAX(0,area.top()/qt_settings->glyphs().height()),
1625 QMIN(COLNO-1,area.right()/qt_settings->glyphs().width()),
1626 QMIN(ROWNO-1,area.bottom()/qt_settings->glyphs().height())
1631 painter.begin(this);
1634 #ifdef REINCARNATION
1635 Is_rogue_level(&u.uz) ||
1640 // You enter a VERY primitive world!
1642 painter.setClipRect( event->rect() ); // (normally we don't clip)
1643 painter.fillRect( event->rect(), black );
1645 if ( !rogue_font ) {
1648 QString fontfamily = iflags.wc_font_map
1649 ? iflags.wc_font_map : "Courier";
1651 if ( fontfamily.right(5).lower() == "-bold" ) {
1652 fontfamily.truncate(fontfamily.length()-5);
1655 while ( pts < 32 ) {
1656 QFont f(fontfamily, pts, bold ? QFont::Bold : QFont::Normal);
1657 painter.setFont(QFont(fontfamily, pts));
1658 QFontMetrics fm = painter.fontMetrics();
1659 if ( fm.width("M") > qt_settings->glyphs().width() )
1661 if ( fm.height() > qt_settings->glyphs().height() )
1665 rogue_font = new QFont(fontfamily,pts-1);
1667 painter.setFont(*rogue_font);
1669 for (int j=garea.top(); j<=garea.bottom(); j++) {
1670 for (int i=garea.left(); i<=garea.right(); i++) {
1671 unsigned short g=Glyph(i,j);
1676 painter.setPen( green );
1677 /* map glyph to character and color */
1678 mapglyph(g, &och, &color, &special, i, j);
1681 painter.setPen( nhcolor_to_pen(color) );
1684 i*qt_settings->glyphs().width(),
1685 j*qt_settings->glyphs().height(),
1686 qt_settings->glyphs().width(),
1687 qt_settings->glyphs().height(),
1693 && ::iflags.hilite_pet
1696 painter.drawPixmap(QPoint(i*qt_settings->glyphs().width(), j*qt_settings->glyphs().height()), pet_annotation);
1701 painter.setFont(font());
1703 for (int j=garea.top(); j<=garea.bottom(); j++) {
1704 for (int i=garea.left(); i<=garea.right(); i++) {
1705 unsigned short g=Glyph(i,j);
1706 qt_settings->glyphs().drawCell(painter, g, i, j);
1709 && ::iflags.hilite_pet
1712 painter.drawPixmap(QPoint(i*qt_settings->glyphs().width(), j*qt_settings->glyphs().height()), pet_annotation);
1718 if (garea.contains(cursor)) {
1719 #ifdef REINCARNATION
1720 if (Is_rogue_level(&u.uz)) {
1722 painter.setPen( white );
1724 painter.setPen( green ); // REALLY primitive
1731 hp100=u.mhmax ? u.mh*100/u.mhmax : 100;
1733 hp100=u.uhpmax ? u.uhp*100/u.uhpmax : 100;
1736 if (hp100 > 75) painter.setPen(white);
1737 else if (hp100 > 50) painter.setPen(yellow);
1738 else if (hp100 > 25) painter.setPen(QColor(0xff,0xbf,0x00)); // orange
1739 else if (hp100 > 10) painter.setPen(red);
1740 else painter.setPen(magenta);
1744 cursor.x()*qt_settings->glyphs().width(),cursor.y()*qt_settings->glyphs().height(),
1745 qt_settings->glyphs().width(),qt_settings->glyphs().height());
1748 if (area.intersects(messages_rect)) {
1749 painter.setPen(black);
1750 painter.drawText(viewport.contentsX()+1,viewport.contentsY()+1,
1751 viewport.width(),0, WordBreak|AlignTop|AlignLeft|DontClip, messages);
1752 painter.setPen(white);
1753 painter.drawText(viewport.contentsX(),viewport.contentsY(),
1754 viewport.width(),0, WordBreak|AlignTop|AlignLeft|DontClip, messages);
1760 void NetHackQtMapWindow::Display(bool block)
1762 for (int i=0; i<change.clusters(); i++) {
1763 const QRect& ch=change[i];
1765 ch.x()*qt_settings->glyphs().width(),
1766 ch.y()*qt_settings->glyphs().height(),
1767 ch.width()*qt_settings->glyphs().width(),
1768 ch.height()*qt_settings->glyphs().height(),
1776 yn_function("Press a key when done viewing",0,'\0');
1780 void NetHackQtMapWindow::CursorTo(int x,int y)
1782 Changed(cursor.x(),cursor.y());
1785 Changed(cursor.x(),cursor.y());
1788 void NetHackQtMapWindow::PutStr(int attr, const char* text)
1790 puts("unexpected PutStr in MapWindow");
1793 void NetHackQtMapWindow::ClipAround(int x,int y)
1795 // Convert to pixel of center of tile
1796 x=x*qt_settings->glyphs().width()+qt_settings->glyphs().width()/2;
1797 y=y*qt_settings->glyphs().height()+qt_settings->glyphs().height()/2;
1799 // Then ensure that pixel is visible
1800 viewport.center(x,y,0.45,0.45);
1803 void NetHackQtMapWindow::PrintGlyph(int x,int y,int glyph)
1809 //void NetHackQtMapWindow::PrintGlyphCompose(int x,int y,int glyph1, int glyph2)
1811 // TODO: composed graphics
1814 void NetHackQtMapWindow::Changed(int x, int y)
1820 class NetHackQtScrollText : public QTableView {
1822 UData() : text(0), attr(0) { }
1823 ~UData() { if (text) free(text); }
1831 NetHackQtScrollText(int maxlength) :
1833 maxitems(maxlength),
1836 item_cycle(maxlength)
1840 setCellHeight(fontMetrics().height());
1841 setBackgroundColor(white);
1842 setTableFlags(Tbl_vScrollBar
1844 |Tbl_clipCellPainting
1845 |Tbl_smoothScrolling);
1848 ~NetHackQtScrollText()
1852 void Scroll(int dx, int dy)
1854 setXOffset(xOffset()+dx*viewWidth());
1855 setYOffset(yOffset()+dy*viewHeight());
1858 void insertItem(int attr, const char* text)
1862 setAutoUpdate(FALSE);
1865 if (count<maxitems) {
1870 first=(first+1)%maxitems;
1873 item(i).text=strdup(text);
1874 int w=datumWidth(item(i));
1876 if (w > cellWidth()) {
1882 setAutoUpdate(TRUE);
1884 if (viewHeight() >= totalHeight()-cellHeight()) {
1887 scroll(0,cellHeight());
1891 virtual void setFont(const QFont& font)
1893 QTableView::setFont(font);
1894 setCellHeight(fontMetrics().height());
1901 return item_cycle[(first+i)%maxitems];
1906 QArray<UData> item_cycle;
1908 int datumWidth(const UData& uitem)
1911 int width=fontMetrics().width(uitem.text)+3;
1913 // XXX Too expensive to do properly, because
1914 // XXX we have to set the font of the widget
1915 // XXX just to get the font metrics information!
1916 // XXX Could hold a fake widget for that
1917 // XXX purpose, but this hack is less ugly.
1926 virtual void setupPainter(QPainter *p)
1928 // XXX This shouldn't be needed - we set the bg in the constructor.
1929 p->setBackgroundColor(white);
1932 virtual void paintCell(QPainter *p, int row, int col)
1935 UData& uitem=item(row);
1937 if (!sel && row < count-uncleared) {
1938 p->setPen(darkGray);
1945 QFont bold(font().family(),font().pointSize(),QFont::Bold);
1949 p->drawText(3, 0, cellWidth(), cellHeight(),
1950 AlignLeft|AlignVCenter, uitem.text);
1958 NetHackQtMessageWindow::NetHackQtMessageWindow() :
1959 list(new NetHackQtScrollText(::iflags.msg_history))
1961 ::iflags.window_inited = 1;
1963 connect(qt_settings,SIGNAL(fontChanged()),this,SLOT(updateFont()));
1967 NetHackQtMessageWindow::~NetHackQtMessageWindow()
1969 ::iflags.window_inited = 0;
1973 QWidget* NetHackQtMessageWindow::Widget() { return list; }
1975 void NetHackQtMessageWindow::setMap(NetHackQtMapWindow* m)
1981 void NetHackQtMessageWindow::updateFont()
1983 list->setFont(qt_settings->normalFont());
1985 map->setFont(qt_settings->normalFont());
1988 void NetHackQtMessageWindow::Scroll(int dx, int dy)
1990 list->Scroll(dx,dy);
1993 void NetHackQtMessageWindow::Clear()
1996 map->clearMessages();
1997 if (list->uncleared) {
2004 void NetHackQtMessageWindow::Display(bool block)
2012 void NetHackQtMessageWindow::PutStr(int attr, const char* text)
2015 play_sound_for_message(text);
2020 list->insertItem(attr,text);
2022 // Force scrollbar to bottom
2023 // XXX list->setTopItem(list->count());
2026 map->putMessage(attr, text);
2031 NetHackQtLabelledIcon::NetHackQtLabelledIcon(QWidget* parent, const char* l) :
2036 label(new QLabel(l,this)),
2041 NetHackQtLabelledIcon::NetHackQtLabelledIcon(QWidget* parent, const char* l, const QPixmap& i) :
2046 label(new QLabel(l,this)),
2047 icon(new QLabel(this))
2052 void NetHackQtLabelledIcon::initHighlight()
2054 const QPalette& pal=palette();
2055 const QColorGroup& pa=pal.normal();
2056 //QColorGroup good(white,darkGreen,pa.light(),pa.dark(),pa.mid(),white,pa.base());
2057 QColorGroup good(black,green,pa.light(),pa.dark(),pa.mid(),black,pa.base());
2058 QColorGroup bad(white,red,pa.light(),pa.dark(),pa.mid(),white,pa.base());
2060 hl_good.setNormal(good);
2061 hl_good.setActive(good);
2063 hl_bad.setNormal(bad);
2064 hl_bad.setActive(bad);
2067 void NetHackQtLabelledIcon::setLabel(const char* t, bool lower)
2070 label=new QLabel(this);
2071 label->setFont(font());
2074 if (0!=strcmp(label->text(),t)) {
2076 highlight(lower==low_is_good ? hl_good : hl_bad);
2079 void NetHackQtLabelledIcon::setLabel(const char* t, long v, long cv, const char* tail)
2083 Sprintf(buf,"%s%s",t,tail);
2085 Sprintf(buf,"%s%ld%s",t,v,tail);
2087 setLabel(buf,cv<prev_value);
2090 void NetHackQtLabelledIcon::setLabel(const char* t, long v, const char* tail)
2092 setLabel(t,v,v,tail);
2094 void NetHackQtLabelledIcon::setIcon(const QPixmap& i)
2096 if (icon) icon->setPixmap(i);
2097 else { icon=new QLabel(this); icon->setPixmap(i); resizeEvent(0); }
2098 icon->resize(i.width(),i.height());
2100 void NetHackQtLabelledIcon::setFont(const QFont& f)
2102 QWidget::setFont(f);
2103 if (label) label->setFont(f);
2105 void NetHackQtLabelledIcon::show()
2107 #if QT_VERSION >= 300
2115 void NetHackQtLabelledIcon::highlightWhenChanging()
2119 void NetHackQtLabelledIcon::lowIsGood()
2123 void NetHackQtLabelledIcon::dissipateHighlight()
2131 void NetHackQtLabelledIcon::highlight(const QPalette& hl)
2133 if (label) { // Surely it is?!
2134 if (turn_count>=0) {
2135 label->setPalette(hl);
2137 // `4' includes this turn, so dissipates after
2138 // 3 more keypresses.
2140 label->setPalette(palette());
2144 void NetHackQtLabelledIcon::unhighlight()
2146 if (label) { // Surely it is?!
2147 label->setPalette(palette());
2150 void NetHackQtLabelledIcon::resizeEvent(QResizeEvent*)
2154 //int labw=label ? label->fontMetrics().width(label->text()) : 0;
2155 int labh=label ? label->fontMetrics().height() : 0;
2156 int icoh=icon ? icon->height() : 0;
2158 int icoy=(h>height() ? height()-labh-icoh : height()/2-h/2);
2161 icon->setGeometry(0,icoy,width(),icoh);
2164 label->setGeometry(0,laby,width(),labh);
2168 void NetHackQtLabelledIcon::setAlignments()
2170 if (label) label->setAlignment(AlignHCenter|AlignVCenter);
2171 if (icon) icon->setAlignment(AlignHCenter|AlignVCenter);
2175 tryload(QPixmap& pm, const char* fn)
2179 msg.sprintf("Cannot load \"%s\"", fn);
2180 QMessageBox::warning(qApp->mainWidget(), "IO Error", msg);
2184 NetHackQtStatusWindow::NetHackQtStatusWindow() :
2186 // Alignment needs -2 init value, because -1 is an alignment.
2187 // Armor Class is an schar, so 256 is out of range.
2188 // Blank value is 0 and should never change.
2189 name(this,"(name)"),
2190 dlevel(this,"(dlevel)"),
2198 hp(this,"Hit Points"),
2199 power(this,"Power"),
2200 ac(this,"Armour Class"),
2201 level(this,"Level"),
2202 exp(this,"Experience"),
2203 align(this,"Alignment"),
2205 score(this,"Score"),
2207 confused(this,"Confused"),
2208 sick_fp(this,"Sick"),
2209 sick_il(this,"Ill"),
2210 blind(this,"Blind"),
2211 stunned(this,"Stunned"),
2212 hallu(this,"Hallu"),
2219 p_str = QPixmap(str_xpm);
2220 p_str = QPixmap(str_xpm);
2221 p_dex = QPixmap(dex_xpm);
2222 p_con = QPixmap(cns_xpm);
2223 p_int = QPixmap(int_xpm);
2224 p_wis = QPixmap(wis_xpm);
2225 p_cha = QPixmap(cha_xpm);
2227 p_chaotic = QPixmap(chaotic_xpm);
2228 p_neutral = QPixmap(neutral_xpm);
2229 p_lawful = QPixmap(lawful_xpm);
2231 p_satiated = QPixmap(satiated_xpm);
2232 p_hungry = QPixmap(hungry_xpm);
2234 p_confused = QPixmap(confused_xpm);
2235 p_sick_fp = QPixmap(sick_fp_xpm);
2236 p_sick_il = QPixmap(sick_il_xpm);
2237 p_blind = QPixmap(blind_xpm);
2238 p_stunned = QPixmap(stunned_xpm);
2239 p_hallu = QPixmap(hallu_xpm);
2241 p_encumber[0] = QPixmap(slt_enc_xpm);
2242 p_encumber[1] = QPixmap(mod_enc_xpm);
2243 p_encumber[2] = QPixmap(hvy_enc_xpm);
2244 p_encumber[3] = QPixmap(ext_enc_xpm);
2245 p_encumber[4] = QPixmap(ovr_enc_xpm);
2250 intel.setIcon(p_int);
2254 align.setIcon(p_neutral);
2255 hunger.setIcon(p_hungry);
2257 confused.setIcon(p_confused);
2258 sick_fp.setIcon(p_sick_fp);
2259 sick_il.setIcon(p_sick_il);
2260 blind.setIcon(p_blind);
2261 stunned.setIcon(p_stunned);
2262 hallu.setIcon(p_hallu);
2264 encumber.setIcon(p_encumber[0]);
2266 hline1.setFrameStyle(QFrame::HLine|QFrame::Sunken);
2267 hline2.setFrameStyle(QFrame::HLine|QFrame::Sunken);
2268 hline3.setFrameStyle(QFrame::HLine|QFrame::Sunken);
2269 hline1.setLineWidth(1);
2270 hline2.setLineWidth(1);
2271 hline3.setLineWidth(1);
2273 connect(qt_settings,SIGNAL(fontChanged()),this,SLOT(doUpdate()));
2277 void NetHackQtStatusWindow::doUpdate()
2279 const QFont& large=qt_settings->largeFont();
2280 name.setFont(large);
2281 dlevel.setFont(large);
2283 const QFont& normal=qt_settings->normalFont();
2284 str.setFont(normal);
2285 dex.setFont(normal);
2286 con.setFont(normal);
2287 intel.setFont(normal);
2288 wis.setFont(normal);
2289 cha.setFont(normal);
2290 gold.setFont(normal);
2292 power.setFont(normal);
2294 level.setFont(normal);
2295 exp.setFont(normal);
2296 align.setFont(normal);
2297 time.setFont(normal);
2298 score.setFont(normal);
2299 hunger.setFont(normal);
2300 confused.setFont(normal);
2301 sick_fp.setFont(normal);
2302 sick_il.setFont(normal);
2303 blind.setFont(normal);
2304 stunned.setFont(normal);
2305 hallu.setFont(normal);
2306 encumber.setFont(normal);
2311 QWidget* NetHackQtStatusWindow::Widget() { return this; }
2313 void NetHackQtStatusWindow::Clear()
2316 void NetHackQtStatusWindow::Display(bool block)
2319 void NetHackQtStatusWindow::CursorTo(int,int y)
2323 void NetHackQtStatusWindow::PutStr(int attr, const char* text)
2325 // do a complete update when line 0 is done (as per X11 fancy status)
2326 if (cursy==0) updateStats();
2329 void NetHackQtStatusWindow::resizeEvent(QResizeEvent*)
2331 const float SP_name=0.13; // <Name> the <Class> (large)
2332 const float SP_dlev=0.13; // Level 3 in The Dungeons of Doom (large)
2333 const float SP_atr1=0.25; // STR DEX CON INT WIS CHA
2334 const float SP_hln1=0.02; // ---
2335 const float SP_atr2=0.09; // Au HP PW AC LVL EXP
2336 const float SP_hln2=0.02; // ---
2337 const float SP_time=0.09; // time score
2338 const float SP_hln3=0.02; // ---
2339 const float SP_stat=0.25; // Alignment, Poisoned, Hungry, Sick, etc.
2344 int iw; // Width of an item across line
2345 int lh; // Height of a line of values
2348 name.setGeometry(0,0,width(),lh); y+=lh;
2350 dlevel.setGeometry(0,y,width(),lh); y+=lh;
2353 hline1.setGeometry(0,y,width(),lh); y+=lh;
2357 str.setGeometry(x,y,iw,lh); x+=iw;
2358 dex.setGeometry(x,y,iw,lh); x+=iw;
2359 con.setGeometry(x,y,iw,lh); x+=iw;
2360 intel.setGeometry(x,y,iw,lh); x+=iw;
2361 wis.setGeometry(x,y,iw,lh); x+=iw;
2362 cha.setGeometry(x,y,iw,lh); x+=iw;
2366 hline2.setGeometry(0,y,width(),lh); y+=lh;
2370 gold.setGeometry(x,y,iw,lh); x+=iw;
2371 hp.setGeometry(x,y,iw,lh); x+=iw;
2372 power.setGeometry(x,y,iw,lh); x+=iw;
2373 ac.setGeometry(x,y,iw,lh); x+=iw;
2374 level.setGeometry(x,y,iw,lh); x+=iw;
2375 exp.setGeometry(x,y,iw,lh); x+=iw;
2379 hline3.setGeometry(0,y,width(),lh); y+=lh;
2382 iw=width()/3; x+=iw/2;
2383 time.setGeometry(x,y,iw,lh); x+=iw;
2384 score.setGeometry(x,y,iw,lh); x+=iw;
2389 align.setGeometry(x,y,iw,lh); x+=iw;
2390 hunger.setGeometry(x,y,iw,lh); x+=iw;
2391 confused.setGeometry(x,y,iw,lh); x+=iw;
2392 sick_fp.setGeometry(x,y,iw,lh); x+=iw;
2393 sick_il.setGeometry(x,y,iw,lh); x+=iw;
2394 blind.setGeometry(x,y,iw,lh); x+=iw;
2395 stunned.setGeometry(x,y,iw,lh); x+=iw;
2396 hallu.setGeometry(x,y,iw,lh); x+=iw;
2397 encumber.setGeometry(x,y,iw,lh); x+=iw;
2403 * Set all widget values to a null string. This is used after all spacings
2404 * have been calculated so that when the window is popped up we don't get all
2405 * kinds of funny values being displayed.
2407 void NetHackQtStatusWindow::nullOut()
2411 void NetHackQtStatusWindow::fadeHighlighting()
2413 name.dissipateHighlight();
2414 dlevel.dissipateHighlight();
2416 str.dissipateHighlight();
2417 dex.dissipateHighlight();
2418 con.dissipateHighlight();
2419 intel.dissipateHighlight();
2420 wis.dissipateHighlight();
2421 cha.dissipateHighlight();
2423 gold.dissipateHighlight();
2424 hp.dissipateHighlight();
2425 power.dissipateHighlight();
2426 ac.dissipateHighlight();
2427 level.dissipateHighlight();
2428 exp.dissipateHighlight();
2429 align.dissipateHighlight();
2431 time.dissipateHighlight();
2432 score.dissipateHighlight();
2434 hunger.dissipateHighlight();
2435 confused.dissipateHighlight();
2436 sick_fp.dissipateHighlight();
2437 sick_il.dissipateHighlight();
2438 blind.dissipateHighlight();
2439 stunned.dissipateHighlight();
2440 hallu.dissipateHighlight();
2441 encumber.dissipateHighlight();
2445 * Update the displayed status. The current code in botl.c updates
2446 * two lines of information. Both lines are always updated one after
2447 * the other. So only do our update when we update the second line.
2449 * Information on the first line:
2450 * name, attributes, alignment, score
2452 * Information on the second line:
2453 * dlvl, gold, hp, power, ac, {level & exp or HD **}
2454 * status (hunger, conf, halu, stun, sick, blind), time, encumbrance
2456 * [**] HD is shown instead of level and exp if mtimedone is non-zero.
2458 void NetHackQtStatusWindow::updateStats()
2460 if (!parentWidget()) return;
2464 if (cursy != 0) return; /* do a complete update when line 0 is done */
2466 if (ACURR(A_STR) > 118) {
2467 Sprintf(buf,"STR:%d",ACURR(A_STR)-100);
2468 } else if (ACURR(A_STR)==118) {
2469 Sprintf(buf,"STR:18/**");
2470 } else if(ACURR(A_STR) > 18) {
2471 Sprintf(buf,"STR:18/%02d",ACURR(A_STR)-18);
2473 Sprintf(buf,"STR:%d",ACURR(A_STR));
2475 str.setLabel(buf,NetHackQtLabelledIcon::NoNum,ACURR(A_STR));
2477 dex.setLabel("DEX:",(long)ACURR(A_DEX));
2478 con.setLabel("CON:",(long)ACURR(A_CON));
2479 intel.setLabel("INT:",(long)ACURR(A_INT));
2480 wis.setLabel("WIS:",(long)ACURR(A_WIS));
2481 cha.setLabel("CHA:",(long)ACURR(A_CHA));
2482 const char* hung=hu_stat[u.uhs];
2486 hunger.setIcon(u.uhs ? p_hungry : p_satiated);
2487 hunger.setLabel(hung);
2490 if (Confusion) confused.show(); else confused.hide();
2492 if (u.usick_type & SICK_VOMITABLE) {
2497 if (u.usick_type & SICK_NONVOMITABLE) {
2506 if (Blind) blind.show(); else blind.hide();
2507 if (Stunned) stunned.show(); else stunned.hide();
2508 if (Hallucination) hallu.show(); else hallu.hide();
2509 const char* enc=enc_stat[near_capacity()];
2510 if (enc[0]==' ' || !enc[0]) {
2513 encumber.setIcon(p_encumber[near_capacity()-1]);
2514 encumber.setLabel(enc);
2517 Strcpy(buf, plname);
2518 if ('a' <= buf[0] && buf[0] <= 'z') buf[0] += 'A'-'a';
2519 Strcat(buf, " the ");
2524 Strcpy(mname, mons[u.umonnum].mname);
2525 while(mname[k] != 0) {
2526 if ((k == 0 || (k > 0 && mname[k-1] == ' '))
2527 && 'a' <= mname[k] && mname[k] <= 'z')
2529 mname[k] += 'A' - 'a';
2535 Strcat(buf, rank_of(u.ulevel, pl_character[0], ::flags.female));
2537 name.setLabel(buf,NetHackQtLabelledIcon::NoNum,u.ulevel);
2539 if (describe_level(buf)) {
2540 dlevel.setLabel(buf,(bool)TRUE);
2542 Sprintf(buf, "%s, level ", dungeons[u.uz.dnum].dname);
2543 dlevel.setLabel(buf,(long)depth(&u.uz));
2547 gold.setLabel("Au:", u.ugold);
2549 gold.setLabel("Au:", money_cnt(invent));
2552 // You're a monster!
2554 Sprintf(buf, "/%d", u.mhmax);
2555 hp.setLabel("HP:",u.mh > 0 ? u.mh : 0,buf);
2556 level.setLabel("HD:",(long)mons[u.umonnum].mlevel);
2560 Sprintf(buf, "/%d", u.uhpmax);
2561 hp.setLabel("HP:",u.uhp > 0 ? u.uhp : 0,buf);
2562 level.setLabel("Level:",(long)u.ulevel);
2564 Sprintf(buf, "/%d", u.uenmax);
2565 power.setLabel("Pow:",u.uen,buf);
2566 ac.setLabel("AC:",(long)u.uac);
2568 if (::flags.showexp) {
2569 exp.setLabel("Exp:",(long)u.uexp);
2575 if (u.ualign.type==A_CHAOTIC) {
2576 align.setIcon(p_chaotic);
2577 align.setLabel("Chaotic");
2578 } else if (u.ualign.type==A_NEUTRAL) {
2579 align.setIcon(p_neutral);
2580 align.setLabel("Neutral");
2582 align.setIcon(p_lawful);
2583 align.setLabel("Lawful");
2586 if (::flags.time) time.setLabel("Time:",(long)moves);
2587 else time.setLabel("");
2588 #ifdef SCORE_ON_BOTL
2589 if (::flags.showscore) {
2590 score.setLabel("Score:",(long)botl_score());
2601 name.highlightWhenChanging();
2602 dlevel.highlightWhenChanging();
2604 str.highlightWhenChanging();
2605 dex.highlightWhenChanging();
2606 con.highlightWhenChanging();
2607 intel.highlightWhenChanging();
2608 wis.highlightWhenChanging();
2609 cha.highlightWhenChanging();
2611 gold.highlightWhenChanging();
2612 hp.highlightWhenChanging();
2613 power.highlightWhenChanging();
2614 ac.highlightWhenChanging(); ac.lowIsGood();
2615 level.highlightWhenChanging();
2616 exp.highlightWhenChanging();
2617 align.highlightWhenChanging();
2619 //time.highlightWhenChanging();
2620 score.highlightWhenChanging();
2622 hunger.highlightWhenChanging();
2623 confused.highlightWhenChanging();
2624 sick_fp.highlightWhenChanging();
2625 sick_il.highlightWhenChanging();
2626 blind.highlightWhenChanging();
2627 stunned.highlightWhenChanging();
2628 hallu.highlightWhenChanging();
2629 encumber.highlightWhenChanging();
2634 * Turn off hilighted status values after a certain amount of turns.
2636 void NetHackQtStatusWindow::checkTurnEvents()
2642 NetHackQtMenuDialog::NetHackQtMenuDialog() :
2643 QDialog(qApp->mainWidget(),0,FALSE)
2647 void NetHackQtMenuDialog::resizeEvent(QResizeEvent*)
2652 void NetHackQtMenuDialog::Accept()
2657 void NetHackQtMenuDialog::Reject()
2662 void NetHackQtMenuDialog::SetResult(int r)
2667 void NetHackQtMenuDialog::done(int i)
2673 // Table view columns:
2675 // [pick-count] [accel] [glyph] [string]
2677 // Maybe accel should be near string. We'll see.
2678 // pick-count normally blank.
2679 // double-clicking or click-on-count gives pop-up entry
2680 // string is green when selected
2682 NetHackQtMenuWindow::NetHackQtMenuWindow(NetHackQtKeyBuffer& ks) :
2685 dialog(new NetHackQtMenuDialog()),
2690 setCellHeight(QMAX(qt_settings->glyphs().height()+1,fontMetrics().height()));
2691 setBackgroundColor(lightGray);
2692 setFrameStyle(Panel|Sunken);
2695 ok=new QPushButton("Ok",dialog);
2696 connect(ok,SIGNAL(clicked()),dialog,SLOT(accept()));
2698 cancel=new QPushButton("Cancel",dialog);
2699 connect(cancel,SIGNAL(clicked()),dialog,SLOT(reject()));
2701 all=new QPushButton("All",dialog);
2702 connect(all,SIGNAL(clicked()),this,SLOT(All()));
2704 none=new QPushButton("None",dialog);
2705 connect(none,SIGNAL(clicked()),this,SLOT(ChooseNone()));
2707 invert=new QPushButton("Invert",dialog);
2708 connect(invert,SIGNAL(clicked()),this,SLOT(Invert()));
2710 search=new QPushButton("Search",dialog);
2711 connect(search,SIGNAL(clicked()),this,SLOT(Search()));
2713 QPoint pos(0,ok->height());
2714 recreate(dialog,0,pos);
2715 prompt.recreate(dialog,0,pos);
2717 setBackgroundColor(lightGray);
2719 connect(dialog,SIGNAL(Resized()),this,SLOT(Layout()));
2721 setTableFlags(Tbl_autoHScrollBar|Tbl_autoVScrollBar
2722 |Tbl_smoothScrolling|Tbl_clipCellPainting);
2723 setFocusPolicy(StrongFocus);
2726 NetHackQtMenuWindow::~NetHackQtMenuWindow()
2728 // Remove from dialog before we destruct it
2729 recreate(0,0,QPoint(0,0));
2733 void NetHackQtMenuWindow::focusInEvent(QFocusEvent *)
2735 // Don't repaint at all, since nothing is using the focus colour
2737 void NetHackQtMenuWindow::focusOutEvent(QFocusEvent *)
2739 // Don't repaint at all, since nothing is using the focus colour
2742 int NetHackQtMenuWindow::cellWidth(int col)
2746 return fontMetrics().width("All ");
2748 return fontMetrics().width(" m ");
2750 return qt_settings->glyphs().width();
2754 impossible("Extra column (#%d) in MenuWindow",col);
2758 QWidget* NetHackQtMenuWindow::Widget() { return dialog; }
2760 void NetHackQtMenuWindow::StartMenu()
2762 setNumRows((itemcount=0));
2769 NetHackQtMenuWindow::MenuItem::MenuItem() :
2774 NetHackQtMenuWindow::MenuItem::~MenuItem()
2776 if (str) free((void*)str);
2779 #define STR_MARGIN 4
2781 void NetHackQtMenuWindow::AddMenu(int glyph, const ANY_P* identifier,
2782 char ch, char gch, int attr, const char* str, bool presel)
2784 if (!ch && identifier->a_void!=0) {
2785 // Supply a keyboard accelerator. Limited supply.
2786 static char accel[]="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
2787 if (accel[next_accel]) {
2788 ch=accel[next_accel++];
2792 if ((int)item.size() < itemcount+1) {
2793 item.resize(itemcount*4+10);
2795 item[itemcount].glyph=glyph;
2796 item[itemcount].identifier=*identifier;
2797 item[itemcount].ch=ch;
2798 item[itemcount].attr=attr;
2799 item[itemcount].str=strdup(str);
2800 item[itemcount].selected=presel;
2801 item[itemcount].count=-1;
2804 str_fixed=str_fixed || strstr(str," ");
2805 if (glyph!=NO_GLYPH) has_glyphs=TRUE;
2807 void NetHackQtMenuWindow::EndMenu(const char* p)
2809 prompt.setText(p ? p : "");
2811 void NetHackQtMenuWindow::Layout()
2813 int butw=totalWidth()/6; // 6 buttons
2814 int buth=fontMetrics().height()+8; // 8 for spacing & mitres
2815 int prompth=(prompt.text().isNull() ? 0 : buth);
2817 prompt.setGeometry(6,buth,dialog->width()-6,prompth);
2818 int h=dialog->height()-buth-prompth;
2819 setGeometry(0,buth+prompth, dialog->width(), h);
2821 // Below, we take care to use up full width
2823 ok->setGeometry(x,0,butw,buth); x+=butw; butw=(dialog->width()-x)/5;
2824 cancel->setGeometry(x,0,butw,buth); x+=butw; butw=(dialog->width()-x)/4;
2825 all->setGeometry(x,0,butw,buth); x+=butw; butw=(dialog->width()-x)/3;
2826 none->setGeometry(x,0,butw,buth); x+=butw; butw=(dialog->width()-x)/2;
2827 invert->setGeometry(x,0,butw,buth); x+=butw; butw=(dialog->width()-x)/1;
2828 search->setGeometry(x,0,butw,buth);
2830 int NetHackQtMenuWindow::SelectMenu(int h, MENU_ITEM_P **menu_list)
2833 qt_settings->normalFixedFont() : qt_settings->normalFont());
2835 for (int i=0; i<itemcount; i++) {
2836 str_width=QMAX(str_width,
2837 STR_MARGIN+fontMetrics().width(item[i].str));
2840 setCellHeight(QMAX(qt_settings->glyphs().height()+1,fontMetrics().height()));
2841 setNumRows(itemcount);
2843 int buth=fontMetrics().height()+8; // 8 for spacing & mitres
2847 ok->setEnabled(how!=PICK_ONE);ok->setDefault(how!=PICK_ONE);
2848 cancel->setEnabled(how!=PICK_NONE);
2849 all->setEnabled(how==PICK_ANY);
2850 none->setEnabled(how==PICK_ANY);
2851 invert->setEnabled(how==PICK_ANY);
2852 search->setEnabled(how!=PICK_NONE);
2854 dialog->SetResult(-1);
2856 // 20 allows for scrollbar or spacing
2857 // 4 for frame borders
2858 int mh = QApplication::desktop()->height()*3/5;
2859 if ( qt_compact_mode && totalHeight() > mh ) {
2860 // big, so make it fill
2861 dialog->showMaximized();
2863 dialog->resize(totalWidth()+20,
2864 QMIN(totalHeight(), mh)+buth+4+(prompt.text().isNull() ? 0 : buth));
2865 if ( dialog->width() > QApplication::desktop()->width() )
2866 dialog->resize(QApplication::desktop()->width(),dialog->height()+16);
2867 centerOnMain(dialog);
2872 while (dialog->result()<0) {
2873 // changed the defaults below to the values in wintype.h 000119 - azy
2874 if (!keysource.Empty()) {
2875 char k=keysource.GetAscii();
2876 k=map_menu_cmd(k); /* added 000119 - azy */
2879 else if (k=='\r' || k=='\n' || k==' ')
2881 else if (k==MENU_SEARCH)
2883 else if (k==MENU_SELECT_ALL)
2885 else if (k==MENU_INVERT_ALL)
2887 else if (k==MENU_UNSELECT_ALL)
2890 for (int i=0; i<itemcount; i++) {
2896 if (dialog->result()<0)
2899 //if ( (nhid != WIN_INVEN || !flags.perm_invent) ) // doesn't work yet
2903 int result=dialog->result();
2905 // Consume ^M (which QDialog steals for default button)
2906 while (!keysource.Empty() &&
2907 (keysource.TopAscii()=='\n' || keysource.TopAscii()=='\r'))
2908 keysource.GetAscii();
2911 if (result>0 && how!=PICK_NONE) {
2912 if (how==PICK_ONE) {
2914 for (i=0; i<itemcount && !item[i].selected; i++)
2917 *menu_list=(MENU_ITEM_P*)malloc(sizeof(MENU_ITEM_P));
2918 (*menu_list)[0].item=item[i].identifier;
2919 (*menu_list)[0].count=item[i].count;
2926 for (int i=0; i<itemcount; i++)
2927 if (item[i].selected) count++;
2929 *menu_list=(MENU_ITEM_P*)malloc(count*sizeof(MENU_ITEM_P));
2931 for (int i=0; i<itemcount; i++) {
2932 if (item[i].selected) {
2933 (*menu_list)[j].item=item[i].identifier;
2934 (*menu_list)[j].count=item[i].count;
2947 void NetHackQtMenuWindow::keyPressEvent(QKeyEvent* event)
2949 if (viewHeight() < totalHeight() && !(event->state()&ShiftButton)) {
2950 if (event->key()==Key_Prior) {
2951 setYOffset(yOffset()-viewHeight());
2952 } else if (event->key()==Key_Next) {
2953 setYOffset(yOffset()+viewHeight());
2962 void NetHackQtMenuWindow::All()
2964 for (int i=0; i<itemcount; i++) {
2965 if (!item[i].selected) ToggleSelect(i);
2968 void NetHackQtMenuWindow::ChooseNone()
2970 for (int i=0; i<itemcount; i++) {
2971 if (item[i].selected) ToggleSelect(i);
2974 void NetHackQtMenuWindow::Invert()
2976 for (int i=0; i<itemcount; i++) {
2980 void NetHackQtMenuWindow::Search()
2982 NetHackQtStringRequestor requestor(keysource,"Search for:");
2984 if (requestor.Get(line)) {
2985 for (int i=0; i<itemcount; i++) {
2986 if (strstr(item[i].str,line))
2991 void NetHackQtMenuWindow::ToggleSelect(int i)
2993 if (item[i].Selectable()) {
2994 item[i].selected = !item[i].selected;
2995 if ( !item[i].selected )
2998 if (how==PICK_ONE) {
3005 void NetHackQtMenuWindow::paintCell(QPainter* painter, int row, int col)
3007 // [pick-count] [accel] [glyph] [string]
3009 MenuItem& i = item[row];
3011 painter->setPen(black);
3012 painter->setFont(font());
3015 painter->setPen(darkGreen);
3020 if ( i.ch || i.attr!=ATR_INVERSE ) {
3022 if ( i.selected && i.count == -1 ) {
3023 if ( i.str[0]>='0' && i.str[0]<='9' )
3027 } else if ( i.count<0 ) {
3030 text.sprintf("%d",i.count);
3032 painter->drawText(0,0,cellWidth(col),cellHeight(),
3033 AlignHCenter|AlignVCenter,text);
3036 if ((signed char)i.ch >= 0) {
3037 char text[2]={i.ch,0};
3038 painter->drawText(0,0,cellWidth(col),cellHeight(),
3039 AlignHCenter|AlignVCenter,text);
3042 if (i.glyph!=NO_GLYPH) {
3043 // Centered in height
3044 int y=(cellHeight()-qt_settings->glyphs().height())/2;
3046 qt_settings->glyphs().drawGlyph(*painter, i.glyph, 0, y);
3049 // XXX should qt_settings have ALL the various fonts
3050 QFont newfont=font();
3055 newfont.setUnderline(TRUE);
3056 break; case ATR_BOLD:
3057 painter->setPen(red);
3058 break; case ATR_BLINK:
3059 newfont.setItalic(TRUE);
3060 break; case ATR_INVERSE:
3061 newfont=qt_settings->largeFont();
3062 newfont.setWeight(QFont::Bold);
3065 painter->setPen(blue);
3067 painter->setPen(darkBlue);
3071 painter->setFont(newfont);
3073 painter->drawText(STR_MARGIN,0,cellWidth(col),cellHeight(),
3074 AlignLeft|AlignVCenter,i.str);
3078 void NetHackQtMenuWindow::mousePressEvent(QMouseEvent* event)
3080 int col=findCol(event->pos().x());
3081 int row=findRow(event->pos().y());
3083 if (col<0 || row<0 || !item[row].Selectable()) return;
3085 if (how!=PICK_NONE) {
3088 NetHackQtStringRequestor requestor(keysource,"Count:");
3091 if (item[row].count>0)
3092 Sprintf(buf,"%d", item[row].count);
3096 requestor.SetDefault(buf);
3097 if (requestor.Get(buf)) {
3098 item[row].count=atoi(buf);
3099 if (item[row].count==0) {
3101 if (item[row].selected) ToggleSelect(row);
3103 if (!item[row].selected) ToggleSelect(row);
3109 was_sel=item[row].selected;
3115 void NetHackQtMenuWindow::mouseReleaseEvent(QMouseEvent* event)
3123 void NetHackQtMenuWindow::mouseMoveEvent(QMouseEvent* event)
3126 int col=findCol(event->pos().x());
3127 int row=findRow(event->pos().y());
3129 if (row>=0 && col>=0) {
3131 // reset to initial state
3132 if (item[pressed].selected!=was_sel)
3133 ToggleSelect(pressed);
3135 // reset to new state
3136 if (item[pressed].selected==was_sel)
3137 ToggleSelect(pressed);
3144 class NetHackQtTextListBox : public QListBox {
3146 NetHackQtTextListBox(QWidget* parent) : QListBox(parent) { }
3151 return contentsWidth();
3156 return contentsHeight();
3159 virtual void setFont(const QFont &font)
3161 QListBox::setFont(font);
3163 void keyPressEvent(QKeyEvent* e)
3165 QListBox::keyPressEvent(e);
3170 QPixmap* NetHackQtRIP::pixmap=0;
3172 NetHackQtRIP::NetHackQtRIP(QWidget* parent) :
3177 tryload(*pixmap, "rip.xpm");
3180 resize(pixmap->width(),pixmap->height());
3181 setFont(QFont("times",12)); // XXX may need to be configurable
3184 void NetHackQtRIP::setLines(char** l, int n)
3190 QSize NetHackQtRIP::sizeHint() const
3192 return pixmap->size();
3195 void NetHackQtRIP::paintEvent(QPaintEvent* event)
3198 int pix_x=(width()-pixmap->width())/2;
3199 int pix_y=(height()-pixmap->height())/2;
3201 // XXX positions based on RIP image
3202 int rip_text_x=pix_x+156;
3203 int rip_text_y=pix_y+67;
3204 int rip_text_h=94/riplines;
3207 painter.begin(this);
3208 painter.drawPixmap(pix_x,pix_y,*pixmap);
3209 for (int i=0; i<riplines; i++) {
3210 painter.drawText(rip_text_x-i/2,rip_text_y+i*rip_text_h,
3211 1,1,DontClip|AlignHCenter,line[i]);
3217 NetHackQtTextWindow::NetHackQtTextWindow(NetHackQtKeyBuffer& ks) :
3218 QDialog(qApp->mainWidget(),0,FALSE),
3223 search("Search",this),
3224 lines(new NetHackQtTextListBox(this)),
3227 ok.setDefault(TRUE);
3228 connect(&ok,SIGNAL(clicked()),this,SLOT(accept()));
3229 connect(&search,SIGNAL(clicked()),this,SLOT(Search()));
3230 connect(qt_settings,SIGNAL(fontChanged()),this,SLOT(doUpdate()));
3232 QVBoxLayout* vb = new QVBoxLayout(this);
3233 vb->addWidget(&rip);
3234 QHBoxLayout* hb = new QHBoxLayout(vb);
3236 hb->addWidget(&search);
3237 vb->addWidget(lines);
3240 void NetHackQtTextWindow::doUpdate()
3246 NetHackQtTextWindow::~NetHackQtTextWindow()
3251 QWidget* NetHackQtTextWindow::Widget()
3256 bool NetHackQtTextWindow::Destroy()
3258 return !isVisible();
3261 void NetHackQtTextWindow::UseRIP(int how)
3263 // Code from X11 windowport
3264 #define STONE_LINE_LEN 16 /* # chars that fit on one line */
3265 #define NAME_LINE 0 /* line # for player name */
3266 #define GOLD_LINE 1 /* line # for amount of gold */
3267 #define DEATH_LINE 2 /* line # for death description */
3268 #define YEAR_LINE 6 /* line # for year */
3270 static char** rip_line=0;
3272 rip_line=new char*[YEAR_LINE+1];
3273 for (int i=0; i<YEAR_LINE+1; i++) {
3274 rip_line[i]=new char[STONE_LINE_LEN+1];
3278 /* Follows same algorithm as genl_outrip() */
3284 /* Put name on stone */
3285 Sprintf(rip_line[NAME_LINE], "%s", plname);
3287 /* Put $ on stone */
3289 Sprintf(rip_line[GOLD_LINE], "%ld Au", u.ugold);
3291 Sprintf(rip_line[GOLD_LINE], "%ld Au", done_money);
3294 /* Put together death description */
3295 switch (killer_format) {
3296 default: impossible("bad killer format?");
3298 Strcpy(buf, killed_by_prefix[how]);
3299 Strcat(buf, an(killer));
3302 Strcpy(buf, killed_by_prefix[how]);
3303 Strcat(buf, killer);
3305 case NO_KILLER_PREFIX:
3306 Strcpy(buf, killer);
3310 /* Put death type on stone */
3311 for (line=DEATH_LINE, dpx = buf; line<YEAR_LINE; line++) {
3315 if ( (i0=strlen(dpx)) > STONE_LINE_LEN) {
3316 for(i = STONE_LINE_LEN;
3317 ((i0 > STONE_LINE_LEN) && i); i--)
3318 if(dpx[i] == ' ') i0 = i;
3319 if(!i) i0 = STONE_LINE_LEN;
3323 strcpy(rip_line[line], dpx);
3324 if (tmpchar != ' ') {
3327 } else dpx= &dpx[i0+1];
3330 /* Put year on stone */
3331 Sprintf(rip_line[YEAR_LINE], "%4d", getyear());
3333 rip.setLines(rip_line,YEAR_LINE+1);
3338 void NetHackQtTextWindow::Clear()
3345 void NetHackQtTextWindow::Display(bool block)
3348 lines->setFont(qt_settings->normalFixedFont());
3350 lines->setFont(qt_settings->normalFont());
3365 int mh = QApplication::desktop()->height()*3/5;
3366 if ( qt_compact_mode && lines->TotalHeight() > mh || use_rip ) {
3367 // big, so make it fill
3370 resize(QMAX(use_rip ? rip.width() : 200,
3371 lines->TotalWidth()+24),
3372 QMIN(mh, lines->TotalHeight()+h));
3378 while (result()==-1) {
3380 if (result()==-1 && !keysource.Empty()) {
3381 char k=keysource.GetAscii();
3382 if (k=='\033' || k==' ' || k=='\r' || k=='\n') {
3384 } else if (k=='/') {
3392 void NetHackQtTextWindow::PutStr(int attr, const char* text)
3394 str_fixed=str_fixed || strstr(text," ");
3395 lines->insertItem(text);
3398 void NetHackQtTextWindow::done(int i)
3405 void NetHackQtTextWindow::keyPressEvent(QKeyEvent* e)
3407 if ( e->ascii() != '\r' && e->ascii() != '\n' && e->ascii() != '\033' )
3408 lines->keyPressEvent(e);
3410 QDialog::keyPressEvent(e);
3413 void NetHackQtTextWindow::Search()
3415 NetHackQtStringRequestor requestor(keysource,"Search for:");
3416 static char line[256]="";
3417 requestor.SetDefault(line);
3418 if (requestor.Get(line)) {
3419 int current=lines->currentItem();
3420 for (uint i=1; i<lines->count(); i++) {
3421 int lnum=(i+current)%lines->count();
3422 QString str=lines->text(lnum);
3423 if (str.contains(line)) {
3424 lines->setCurrentItem(lnum);
3425 lines->centerCurrentItem();
3429 lines->setCurrentItem(-1);
3434 NetHackQtDelay::NetHackQtDelay(int ms) :
3439 void NetHackQtDelay::wait()
3445 void NetHackQtDelay::timerEvent(QTimerEvent* timer)
3451 NetHackQtInvUsageWindow::NetHackQtInvUsageWindow(QWidget* parent) :
3456 void NetHackQtInvUsageWindow::drawWorn(QPainter& painter, obj* nhobj, int x, int y, bool canbe)
3460 glyph=obj_to_glyph(nhobj);
3462 glyph=cmap_to_glyph(S_room);
3464 glyph=cmap_to_glyph(S_stone);
3466 qt_settings->glyphs().drawCell(painter,glyph,x,y);
3469 void NetHackQtInvUsageWindow::paintEvent(QPaintEvent*)
3481 painter.begin(this);
3484 drawWorn(painter,0,0,4,FALSE);
3485 drawWorn(painter,0,0,5,FALSE);
3486 drawWorn(painter,0,2,4,FALSE);
3487 drawWorn(painter,0,2,5,FALSE);
3489 drawWorn(painter,uarm,1,3); // Armour
3490 drawWorn(painter,uarmc,1,2); // Cloak
3491 drawWorn(painter,uarmh,1,0); // Helmet
3492 drawWorn(painter,uarms,0,1); // Shield
3493 drawWorn(painter,uarmg,0,2); // Gloves - repeated
3494 drawWorn(painter,uarmg,2,2); // Gloves - repeated
3496 drawWorn(painter,uarmf,1,5); // Shoes (feet)
3497 drawWorn(painter,uarmu,1,4); // Undershirt
3499 drawWorn(painter,0 ,1,5,FALSE);
3500 drawWorn(painter,uarmf,1,4); // Shoes (feet)
3502 drawWorn(painter,uleft,0,3); // RingL
3503 drawWorn(painter,uright,2,3); // RingR
3505 drawWorn(painter,uwep,2,1); // Weapon
3506 drawWorn(painter,uswapwep,0,0); // Secondary weapon
3507 drawWorn(painter,uamul,1,1); // Amulet
3508 drawWorn(painter,ublindf,2,0); // Blindfold
3513 class SmallToolButton : public QToolButton {
3515 SmallToolButton(const QPixmap & pm, const QString &textLabel,
3516 const QString& grouptext,
3517 QObject * receiver, const char* slot,
3518 QToolBar * parent) :
3519 QToolButton(pm, textLabel,
3520 #if QT_VERSION < 210
3525 receiver, slot, parent)
3529 QSize sizeHint() const
3531 // get just a couple more pixels for the map
3532 return QToolButton::sizeHint()-QSize(0,2);
3537 NetHackQtMainWindow::NetHackQtMainWindow(NetHackQtKeyBuffer& ks) :
3538 message(0), map(0), status(0), invusage(0),
3539 keysink(ks), dirkey(0)
3541 QToolBar* toolbar = new QToolBar(this);
3542 #if QT_VERSION >= 210
3543 setToolBarsMovable(FALSE);
3544 toolbar->setHorizontalStretchable(TRUE);
3545 toolbar->setVerticalStretchable(TRUE);
3547 addToolBar(toolbar);
3548 menubar = menuBar();
3550 setCaption("Qt NetHack");
3551 if ( qt_compact_mode )
3552 setIcon(QPixmap(nh_icon_small));
3554 setIcon(QPixmap(nh_icon));
3556 QPopupMenu* game=new QPopupMenu;
3557 QPopupMenu* apparel=new QPopupMenu;
3558 QPopupMenu* act1=new QPopupMenu;
3559 QPopupMenu* act2 = qt_compact_mode ? new QPopupMenu : act1;
3560 QPopupMenu* magic=new QPopupMenu;
3561 QPopupMenu* info=new QPopupMenu;
3566 help = kapp->getHelpMenu( TRUE, "" );
3567 help->insertSeparator();
3569 help = qt_compact_mode ? info : new QPopupMenu;
3572 enum { OnDesktop=1, OnHandhelds=2 };
3580 { game, "Version\tv", "v", 3},
3581 { game, "Compilation\tAlt+V", "\366", 3},
3582 { game, "History\tShift+V", "V", 3},
3583 { game, "Redraw\tCtrl+R", "\022", 0}, // useless
3584 { game, "Options\tShift+O", "O", 3},
3585 { game, "Explore mode\tShift+X", "X", 3},
3587 { game, "Save\tSy", "Sy", 3},
3588 { game, "Quit\tAlt+Q", "\361", 3},
3590 { apparel, "Apparel off\tShift+A", "A", 2},
3591 { apparel, "Remove many\tShift+A", "A", 1},
3592 { apparel, 0, 0, 3},
3593 { apparel, "Wield weapon\tw", "w", 3},
3594 { apparel, "Exchange weapons\tx", "x", 3},
3595 { apparel, "Two weapon combat\t#two", "#tw", 3},
3596 { apparel, "Load quiver\tShift+Q", "Q", 3},
3597 { apparel, 0, 0, 3},
3598 { apparel, "Wear armour\tShift+W", "W", 3},
3599 { apparel, "Take off armour\tShift+T", "T", 3},
3600 { apparel, 0, 0, 3},
3601 { apparel, "Put on non-armour\tShift+P", "P", 3},
3602 { apparel, "Remove non-armour\tShift+R", "R", 3},
3604 { act1, "Again\tCtrl+A", "\001", 2},
3606 { act1, "Apply\ta?", "a?", 3},
3607 { act1, "Chat\tAlt+C", "\343", 3},
3608 { act1, "Close door\tc", "c", 3},
3609 { act1, "Down\t>", ">", 3},
3610 { act1, "Drop many\tShift+D", "D", 2},
3611 { act1, "Drop\td?", "d?", 2},
3612 { act1, "Eat\te?", "e?", 2},
3613 { act1, "Engrave\tShift+E", "E", 3},
3614 { act1, "Fight\tShift+F", "F", 3},
3615 { act1, "Fire from quiver\tf", "f", 2},
3616 { act1, "Force\tAlt+F", "\346", 3},
3617 { act1, "Get\t,", ",", 2},
3618 { act1, "Jump\tAlt+J", "\352", 3},
3619 { act2, "Kick\tCtrl+D", "\004", 2},
3620 { act2, "Loot\tAlt+L", "\354", 3},
3621 { act2, "Open door\to", "o", 3},
3622 { act2, "Pay\tp", "p", 3},
3623 { act2, "Rest\t.", ".", 2},
3624 { act2, "Ride\t#ri", "#ri", 3},
3625 { act2, "Search\ts", "s", 3},
3626 { act2, "Sit\tAlt+S", "\363", 3},
3627 { act2, "Throw\tt", "t", 2},
3628 { act2, "Untrap\t#u", "#u", 3},
3629 { act2, "Up\t<", "<", 3},
3630 { act2, "Wipe face\tAlt+W", "\367", 3},
3632 { magic, "Quaff potion\tq?", "q?", 3},
3633 { magic, "Read scroll/book\tr?", "r?", 3},
3634 { magic, "Zap wand\tz?", "z?", 3},
3635 { magic, "Zap spell\tShift+Z", "Z", 3},
3636 { magic, "Dip\tAlt+D", "\344", 3},
3637 { magic, "Rub\tAlt+R", "\362", 3},
3638 { magic, "Invoke\tAlt+I", "\351", 3},
3640 { magic, "Offer\tAlt+O", "\357", 3},
3641 { magic, "Pray\tAlt+P", "\360", 3},
3643 { magic, "Teleport\tCtrl+T", "\024", 3},
3644 { magic, "Monster action\tAlt+M", "\355", 3},
3645 { magic, "Turn undead\tAlt+T", "\364", 3},
3647 { help, "Help\t?", "?", 3},
3649 { help, "What is here\t:", ":", 3},
3650 { help, "What is there\t;", ";", 3},
3651 { help, "What is...\t/y", "/y", 2},
3654 { info, "Inventory\ti", "i", 3},
3656 { info, "Angbandish inventory\t*", "*", 3},
3658 { info, "Conduct\t#co", "#co", 3},
3659 { info, "Discoveries\t\\", "\\", 3},
3660 { info, "List/reorder spells\t+", "+", 3},
3661 { info, "Adjust letters\tAlt+A", "\341", 2},
3663 { info, "Name object\tAlt+N", "\356y?", 3},
3664 { info, "Name object type\tAlt+N", "\356n?", 3},
3665 { info, "Name creature\tShift+C", "C", 3},
3667 { info, "Qualifications\tAlt+E", "\345", 3},
3674 for (i=0; item[i].menu; i++)
3675 if (item[i].name) count++;
3677 macro=new const char* [count];
3679 game->insertItem("Qt settings...",1000);
3680 help->insertItem("About Qt NetHack...",2000);
3681 //help->insertItem("NetHack Guidebook...",3000);
3682 help->insertSeparator();
3685 for (i=0; item[i].menu; i++) {
3686 if ( item[i].flags & (qt_compact_mode ? 1 : 2) ) {
3688 QString name = item[i].name;
3689 if ( qt_compact_mode ) // accelerators aren't
3690 name.replace(QRegExp("\t.*"),"");
3691 item[i].menu->insertItem(name,count);
3692 macro[count++]=item[i].action;
3694 item[i].menu->insertSeparator();
3699 menubar->insertItem("Game",game);
3700 menubar->insertItem("Gear",apparel);
3702 if ( qt_compact_mode ) {
3703 menubar->insertItem("A-J",act1);
3704 menubar->insertItem("K-Z",act2);
3705 menubar->insertItem("Magic",magic);
3706 menubar->insertItem(QPixmap(info_xpm),info);
3707 menubar->insertItem(QPixmap(map_xpm), this, SLOT(raiseMap()));
3708 menubar->insertItem(QPixmap(msg_xpm), this, SLOT(raiseMessages()));
3709 menubar->insertItem(QPixmap(stat_xpm), this, SLOT(raiseStatus()));
3711 menubar->insertItem("Action",act1);
3712 menubar->insertItem("Magic",magic);
3713 menubar->insertItem("Info",info);
3714 menubar->insertSeparator();
3715 menubar->insertItem("Help",help);
3718 QSignalMapper* sm = new QSignalMapper(this);
3719 connect(sm, SIGNAL(mapped(const QString&)), this, SLOT(doKeys(const QString&)));
3721 tb = new SmallToolButton( QPixmap(again_xpm),"Again","Action", sm, SLOT(map()), toolbar );
3722 sm->setMapping(tb, "\001" );
3723 tb = new SmallToolButton( QPixmap(get_xpm),"Get","Action", sm, SLOT(map()), toolbar );
3724 sm->setMapping(tb, "," );
3725 tb = new SmallToolButton( QPixmap(kick_xpm),"Kick","Action", sm, SLOT(map()), toolbar );
3726 sm->setMapping(tb, "\004" );
3727 tb = new SmallToolButton( QPixmap(throw_xpm),"Throw","Action", sm, SLOT(map()), toolbar );
3728 sm->setMapping(tb, "t" );
3729 tb = new SmallToolButton( QPixmap(fire_xpm),"Fire","Action", sm, SLOT(map()), toolbar );
3730 sm->setMapping(tb, "f" );
3731 tb = new SmallToolButton( QPixmap(drop_xpm),"Drop","Action", sm, SLOT(map()), toolbar );
3732 sm->setMapping(tb, "D" );
3733 tb = new SmallToolButton( QPixmap(eat_xpm),"Eat","Action", sm, SLOT(map()), toolbar );
3734 sm->setMapping(tb, "e" );
3735 tb = new SmallToolButton( QPixmap(rest_xpm),"Rest","Action", sm, SLOT(map()), toolbar );
3736 sm->setMapping(tb, "." );
3737 tb = new SmallToolButton( QPixmap(cast_a_xpm),"Cast A","Magic", sm, SLOT(map()), toolbar );
3738 sm->setMapping(tb, "Za" );
3739 tb = new SmallToolButton( QPixmap(cast_b_xpm),"Cast B","Magic", sm, SLOT(map()), toolbar );
3740 sm->setMapping(tb, "Zb" );
3741 tb = new SmallToolButton( QPixmap(cast_c_xpm),"Cast C","Magic", sm, SLOT(map()), toolbar );
3742 sm->setMapping(tb, "Zc" );
3743 if ( !qt_compact_mode ) {
3744 QWidget* filler = new QWidget(toolbar);
3745 filler->setBackgroundMode(PaletteButton);
3746 toolbar->setStretchableWidget(filler);
3749 connect(menubar,SIGNAL(activated(int)),this,SLOT(doMenuItem(int)));
3756 int w=QApplication::desktop()->width()-10; // XXX arbitrary extra space for frame
3757 int h=QApplication::desktop()->height()-50;
3761 if (qt_tilewidth != NULL) {
3762 maxwn = atoi(qt_tilewidth) * COLNO + 10;
3766 if (qt_tileheight != NULL) {
3767 maxhn = atoi(qt_tileheight) * ROWNO * 6/4;
3772 // Be exactly the size we want to be - full map...
3775 w=maxwn; // Doesn't need to be any wider
3779 h=maxhn; // Doesn't need to be any taller
3782 setGeometry(x,y,w,h);
3784 if ( qt_compact_mode ) {
3785 stack = new QWidgetStack(this);
3786 setCentralWidget(stack);
3788 setCentralWidget(new QWidget(this));
3789 invusage = new NetHackQtInvUsageWindow(centralWidget());
3793 void NetHackQtMainWindow::zoomMap()
3795 qt_settings->toggleGlyphSize();
3798 void NetHackQtMainWindow::raiseMap()
3800 if ( stack->id(stack->visibleWidget()) == 0 ) {
3803 stack->raiseWidget(0);
3807 void NetHackQtMainWindow::raiseMessages()
3809 stack->raiseWidget(1);
3812 void NetHackQtMainWindow::raiseStatus()
3814 stack->raiseWidget(2);
3817 class NetHackMimeSourceFactory : public QMimeSourceFactory {
3819 const QMimeSource* data(const QString& abs_name) const
3821 const QMimeSource* r = 0;
3822 if ( (NetHackMimeSourceFactory*)this == QMimeSourceFactory::defaultFactory() )
3823 r = QMimeSourceFactory::data(abs_name);
3825 r = QMimeSourceFactory::defaultFactory()->data(abs_name);
3827 int sl = abs_name.length();
3829 sl = abs_name.findRev('/',sl-1);
3830 QString name = sl>=0 ? abs_name.mid(sl+1) : abs_name;
3831 int dot = name.findRev('.');
3833 name = name.left(dot);
3834 if ( name == "map" )
3835 r = new QImageDrag(QImage(map_xpm));
3836 else if ( name == "msg" )
3837 r = new QImageDrag(QImage(msg_xpm));
3838 else if ( name == "stat" )
3839 r = new QImageDrag(QImage(stat_xpm));
3840 } while (!r && sl>0);
3846 void NetHackQtMainWindow::doMenuItem(int id)
3850 centerOnMain(qt_settings);
3851 qt_settings->show();
3854 QMessageBox::about(this, "About Qt NetHack", aboutMsg());
3857 QDialog dlg(this,0,TRUE);
3858 (new QVBoxLayout(&dlg))->setAutoAdd(TRUE);
3859 QTextBrowser browser(&dlg);
3860 NetHackMimeSourceFactory ms;
3861 browser.setMimeSourceFactory(&ms);
3862 browser.setSource(QDir::currentDirPath()+"/Guidebook.html");
3863 if ( qt_compact_mode )
3864 dlg.showMaximized();
3874 void NetHackQtMainWindow::doKeys(const QString& k)
3880 void NetHackQtMainWindow::AddMessageWindow(NetHackQtMessageWindow* window)
3886 void NetHackQtMainWindow::AddMapWindow(NetHackQtMapWindow* window)
3890 connect(map,SIGNAL(resized()),this,SLOT(layout()));
3893 void NetHackQtMainWindow::AddStatusWindow(NetHackQtStatusWindow* window)
3899 void NetHackQtMainWindow::RemoveWindow(NetHackQtWindow* window)
3901 if (window==status) {
3904 } else if (window==map) {
3907 } else if (window==message) {
3913 void NetHackQtMainWindow::updateInventory()
3916 invusage->repaint(FALSE);
3919 void NetHackQtMainWindow::fadeHighlighting()
3922 status->fadeHighlighting();
3926 void NetHackQtMainWindow::layout()
3928 if ( qt_compact_mode )
3930 if (message && map && status) {
3931 QSize maxs=map->Widget()->maximumSize();
3932 int maph=QMIN(height()*2/3,maxs.height());
3934 QWidget* c = centralWidget();
3937 int iuw=3*qt_settings->glyphs().width();
3938 int topw=(c->width()-iuw)/2;
3940 message->Widget()->setGeometry(0,0,topw,toph);
3941 invusage->setGeometry(topw,0,iuw,toph);
3942 status->Widget()->setGeometry(topw+iuw,0,topw,toph);
3943 map->Widget()->setGeometry(QMAX(0,(c->width()-maxs.width())/2),
3944 toph,c->width(),maph);
3948 void NetHackQtMainWindow::resizeEvent(QResizeEvent*)
3956 void NetHackQtMainWindow::keyReleaseEvent(QKeyEvent* event)
3959 doKeys(QString(QChar(dirkey)));
3960 if ( !event->isAutoRepeat() )
3965 void NetHackQtMainWindow::keyPressEvent(QKeyEvent* event)
3967 // Global key controls
3969 // For desktop, arrow keys scroll map, since we don't want players
3970 // to think that's the way to move. For handhelds, the normal way is to
3971 // click-to-travel, so we allow the cursor keys for fine movements.
3977 if ( event->isAutoRepeat() &&
3978 event->key() >= Key_Left && event->key() <= Key_Down )
3981 const char* d = iflags.num_pad ? ndir : sdir;
3982 switch (event->key()) {
3984 if ( dirkey == d[0] )
3986 else if ( dirkey == d[4] )
3990 break; case Key_Down:
3991 if ( dirkey == d[0] )
3993 else if ( dirkey == d[4] )
3997 break; case Key_Left:
3998 if ( dirkey == d[2] )
4000 else if ( dirkey == d[6] )
4004 break; case Key_Right:
4005 if ( dirkey == d[2] )
4007 else if ( dirkey == d[6] )
4011 break; case Key_Prior:
4013 if (message) message->Scroll(0,-1);
4014 break; case Key_Next:
4016 if (message) message->Scroll(0,+1);
4017 break; case Key_Space:
4018 if ( flags.rest_on_space ) {
4031 void NetHackQtMainWindow::closeEvent(QCloseEvent* e)
4033 if ( program_state.something_worth_saving ) {
4034 switch ( QMessageBox::information( this, "NetHack",
4035 "This will end your NetHack session",
4036 "&Save", "&Cancel", 0, 1 ) )
4039 // See dosave() function
4042 NetHackQtBind::qt_exit_nhwindows(0);
4043 terminate(EXIT_SUCCESS);
4047 break; // ignore the event
4054 void NetHackQtMainWindow::ShowIfReady()
4056 if (message && map && status) {
4058 QWidget* p = qt_compact_mode ? stack : centralWidget();
4059 message->Widget()->recreate(p,0,pos);
4060 map->Widget()->recreate(p,0,pos);
4061 status->Widget()->recreate(p,0,pos);
4062 if ( qt_compact_mode ) {
4063 message->setMap(map);
4064 stack->addWidget(map->Widget(), 0);
4065 stack->addWidget(message->Widget(), 1);
4066 stack->addWidget(status->Widget(), 2);
4072 } else if (isVisible()) {
4078 NetHackQtYnDialog::NetHackQtYnDialog(NetHackQtKeyBuffer& keysrc,const char* q,const char* ch,char df) :
4079 QDialog(qApp->mainWidget(),0,FALSE),
4080 question(q), choices(ch), def(df),
4083 setCaption("NetHack: Question");
4086 char NetHackQtYnDialog::Exec()
4088 QString ch(choices);
4092 if ( qt_compact_mode && !choices ) {
4093 // expand choices from prompt
4094 // ##### why isn't choices set properly???
4095 const char* c=question;
4096 while ( *c && *c != '[' )
4098 qlabel = QString(question).left(c-question);
4104 while ( *c && *c != ']' && *c != ' ' ) {
4107 } else if ( from ) {
4108 for (char f=from+1; f<=*c; f++)
4118 while ( *c && *c != ']' ) {
4119 if ( *c == '*' || *c == '?' )
4125 if ( strstr(question, "what direction") ) {
4126 // We replace this regardless, since sometimes you get choices.
4127 const char* d = iflags.num_pad ? ndir : sdir;
4144 // Hmm... they'll have to use a virtual keyboard
4150 QVBoxLayout vb(this);
4151 vb.setAutoAdd(TRUE);
4152 bool bigq = qlabel.length()>40;
4154 QLabel* q = new QLabel(qlabel,this);
4155 q->setAlignment(AlignLeft|WordBreak);
4158 QButtonGroup group(ch_per_line, Horizontal,
4159 bigq ? QString::null : qlabel, this);
4161 int nchoices=ch.length();
4163 bool allow_count=ch.contains('#');
4167 const int extra=fontMetrics().height(); // Extra for group
4168 int x=margin, y=extra+margin;
4169 int butsize=fontMetrics().height()*2+5;
4171 QPushButton* button;
4172 for (int i=0; i<nchoices && ch[i]!='\033'; i++) {
4173 button=new QPushButton(QString(ch[i]),&group);
4174 if ( !enable.isNull() ) {
4175 if ( !enable.contains(ch[i]) )
4176 button->setEnabled(FALSE);
4178 button->setFixedSize(butsize,butsize); // Square
4179 if (ch[i]==def) button->setDefault(TRUE);
4189 connect(&group,SIGNAL(clicked(int)),this,SLOT(doneItem(int)));
4195 QHBox *hb = new QHBox(this);
4196 lb=new QLabel("Count: ",hb);
4197 le=new QLineEdit(hb);
4205 for (uint i=0; i<ch.length(); i++) {
4206 if (ch[i].latin1()=='q') ch_esc='q';
4207 else if (!ch_esc && ch[i].latin1()=='n') ch_esc='n';
4211 if (!keysource.Empty()) {
4212 char k=keysource.GetAscii();
4214 for (uint i=0; i<ch.length(); i++)
4215 if (ch[i].latin1()==k)
4218 if (k=='\033' && ch_esc)
4220 else if (k==' ' || k=='\r' || k=='\n')
4222 // else choice remains 0
4224 } else if ( result() == 0 ) {
4225 choice = ch_esc ? ch_esc : def ? def : ' ';
4226 } else if ( result() == 1 ) {
4227 choice = def ? def : ch_esc ? ch_esc : ' ';
4228 } else if ( result() >= 1000 ) {
4229 choice = ch[result() - 1000].latin1();
4235 if (allow_count && !le->text().isEmpty()) {
4236 yn_number=atoi(le->text());
4241 QLabel label(qlabel,this);
4242 QPushButton cancel("Dismiss",this);
4243 label.setFrameStyle(QFrame::Box|QFrame::Sunken);
4244 label.setAlignment(AlignCenter);
4245 label.resize(fontMetrics().width(qlabel)+60,30+fontMetrics().height());
4246 cancel.move(width()/2-cancel.width()/2,label.geometry().bottom()+8);
4247 connect(&cancel,SIGNAL(clicked()),this,SLOT(reject()));
4251 while (result()<0 && keysource.Empty()) {
4255 if (keysource.Empty()) {
4258 return keysource.GetAscii();
4262 void NetHackQtYnDialog::keyPressEvent(QKeyEvent* event)
4264 // Don't want QDialog's Return/Esc behaviour
4268 void NetHackQtYnDialog::doneItem(int i)
4273 void NetHackQtYnDialog::done(int i)
4279 NetHackQtGlyphs::NetHackQtGlyphs()
4281 const char* tile_file = "nhtiles.bmp";
4282 if ( iflags.wc_tile_file )
4283 tile_file = iflags.wc_tile_file;
4285 if (!img.load(tile_file)) {
4286 tile_file = "x11tiles";
4287 if (!img.load(tile_file)) {
4289 msg.sprintf("Cannot load x11tiles or nhtiles.bmp");
4290 QMessageBox::warning(0, "IO Error", msg);
4292 tiles_per_row = TILES_PER_ROW;
4293 if (img.width()%tiles_per_row) {
4294 impossible("Tile file \"%s\" has %d columns, not multiple of row count (%d)",
4295 tile_file, img.width(), tiles_per_row);
4302 if ( iflags.wc_tile_width )
4303 tilefile_tile_W = iflags.wc_tile_width;
4305 tilefile_tile_W = img.width() / tiles_per_row;
4306 if ( iflags.wc_tile_height )
4307 tilefile_tile_H = iflags.wc_tile_height;
4309 tilefile_tile_H = tilefile_tile_W;
4311 setSize(tilefile_tile_W, tilefile_tile_H);
4314 void NetHackQtGlyphs::drawGlyph(QPainter& painter, int glyph, int x, int y)
4316 int tile = glyph2tile[glyph];
4317 int px = (tile%tiles_per_row)*width();
4318 int py = tile/tiles_per_row*height();
4328 void NetHackQtGlyphs::drawCell(QPainter& painter, int glyph, int cellx, int celly)
4330 drawGlyph(painter,glyph,cellx*width(),celly*height());
4332 void NetHackQtGlyphs::setSize(int w, int h)
4334 if ( size == QSize(w,h) )
4337 bool was1 = size == pm1.size();
4340 return; // Still not decided
4342 if ( size == pm1.size() ) {
4346 if ( size == pm2.size() ) {
4351 if (w==tilefile_tile_W && h==tilefile_tile_H) {
4352 pm.convertFromImage(img);
4354 QApplication::setOverrideCursor( Qt::waitCursor );
4355 QImage scaled = img.smoothScale(
4356 w*img.width()/tilefile_tile_W,
4357 h*img.height()/tilefile_tile_H
4359 pm.convertFromImage(scaled,Qt::ThresholdDither|Qt::PreferDither);
4360 QApplication::restoreOverrideCursor();
4362 (was1 ? pm2 : pm1) = pm;
4366 //////////////////////////////////////////////////////////////
4368 // The ugly C binding classes...
4370 //////////////////////////////////////////////////////////////
4373 NetHackQtMenuOrTextWindow::NetHackQtMenuOrTextWindow(NetHackQtKeyBuffer& ks) :
4379 QWidget* NetHackQtMenuOrTextWindow::Widget()
4381 if (!actual) impossible("Widget called before we know if Menu or Text");
4382 return actual->Widget();
4386 void NetHackQtMenuOrTextWindow::Clear()
4388 if (!actual) impossible("Clear called before we know if Menu or Text");
4391 void NetHackQtMenuOrTextWindow::Display(bool block)
4393 if (!actual) impossible("Display called before we know if Menu or Text");
4394 actual->Display(block);
4396 bool NetHackQtMenuOrTextWindow::Destroy()
4398 if (!actual) impossible("Destroy called before we know if Menu or Text");
4399 return actual->Destroy();
4402 void NetHackQtMenuOrTextWindow::PutStr(int attr, const char* text)
4404 if (!actual) actual=new NetHackQtTextWindow(keysource);
4405 actual->PutStr(attr,text);
4409 void NetHackQtMenuOrTextWindow::StartMenu()
4411 if (!actual) actual=new NetHackQtMenuWindow(keysource);
4412 actual->StartMenu();
4414 void NetHackQtMenuOrTextWindow::AddMenu(int glyph, const ANY_P* identifier, char ch, char gch, int attr,
4415 const char* str, bool presel)
4417 if (!actual) impossible("AddMenu called before we know if Menu or Text");
4418 actual->AddMenu(glyph,identifier,ch,gch,attr,str,presel);
4420 void NetHackQtMenuOrTextWindow::EndMenu(const char* prompt)
4422 if (!actual) impossible("EndMenu called before we know if Menu or Text");
4423 actual->EndMenu(prompt);
4425 int NetHackQtMenuOrTextWindow::SelectMenu(int how, MENU_ITEM_P **menu_list)
4427 if (!actual) impossible("SelectMenu called before we know if Menu or Text");
4428 return actual->SelectMenu(how,menu_list);
4432 // XXX Should be from Options
4434 // XXX Hmm. Tricky part is that perhaps some macros should only be active
4435 // XXX when a key is about to be gotten. For example, the user could
4436 // XXX define "-" to do "E-yyyyyyyy\r", but would still need "-" for
4437 // XXX other purposes. Maybe just too bad.
4444 { Qt::Key_F1, 0, "n100." }, // Rest (x100)
4445 { Qt::Key_F2, 0, "n20s" }, // Search (x20)
4446 { Qt::Key_F3, 0, "o8o4o6o2o8o4o6o2o8o4o6o2" }, // Open all doors (x3)
4447 { Qt::Key_Tab, 0, "\001" },
4452 NetHackQtBind::NetHackQtBind(int& argc, char** argv) :
4454 KApplication(argc,argv)
4455 #elif defined(QWS) // not quite the right condition
4456 QPEApplication(argc,argv)
4458 QApplication(argc,argv)
4461 QPixmap pm("nhsplash.xpm");
4462 if ( iflags.wc_splash_screen && !pm.isNull() ) {
4463 QVBox *vb = new QVBox(0,0,
4464 WStyle_Customize | WStyle_NoBorder | nh_WX11BypassWM | WStyle_StaysOnTop );
4466 QLabel *lsplash = new QLabel(vb);
4467 lsplash->setAlignment(AlignCenter);
4468 lsplash->setPixmap(pm);
4469 QLabel* capt = new QLabel("Loading...",vb);
4470 capt->setAlignment(AlignCenter);
4472 lsplash->setFixedSize(pm.size());
4473 lsplash->setMask(*pm.mask());
4475 splash->move((QApplication::desktop()->width()-pm.width())/2,
4476 (QApplication::desktop()->height()-pm.height())/2);
4477 //splash->setGeometry(0,0,100,100);
4478 if ( qt_compact_mode ) {
4479 splash->showMaximized();
4481 vb->setFrameStyle(QFrame::WinPanel|QFrame::Raised);
4483 splash->adjustSize();
4487 // force content refresh outside event loop
4488 splash->repaint(FALSE);
4489 lsplash->repaint(FALSE);
4490 capt->repaint(FALSE);
4496 main = new NetHackQtMainWindow(keybuffer);
4497 #if defined(QWS) // not quite the right condition
4498 showMainWidget(main);
4500 setMainWidget(main);
4502 qt_settings=new NetHackQtSettings(main->width(),main->height());
4505 void NetHackQtBind::qt_init_nhwindows(int* argc, char** argv)
4510 // Michael Hohmuth <hohmuth@inf.tu-dresden.de>...
4512 // As the game runs setuid games, it must seteuid(getuid()) before
4513 // calling XOpenDisplay(), and reset the euid afterwards.
4514 // Otherwise, it can't read the $HOME/.Xauthority file and whines about
4515 // not being able to open the X display (if a magic-cookie
4516 // authorization mechanism is being used).
4518 uid_t gamesuid=geteuid();
4522 QApplication::setColorSpec(ManyColor);
4523 instance=new NetHackQtBind(*argc,argv);
4530 // This nethack engine feature should be moved into windowport API
4531 nt_kbhit = NetHackQtBind::qt_kbhit;
4535 int NetHackQtBind::qt_kbhit()
4537 return !keybuffer.Empty();
4540 static bool have_asked = FALSE;
4542 void NetHackQtBind::qt_player_selection()
4548 NetHackQtSavedGameSelector::NetHackQtSavedGameSelector(const char** saved) :
4549 QDialog(qApp->mainWidget(),"sgsel",TRUE)
4551 QVBoxLayout *vbl = new QVBoxLayout(this,6);
4554 QLabel* logo = new QLabel(this); vbl->addWidget(logo);
4555 logo->setAlignment(AlignCenter);
4556 logo->setPixmap(QPixmap("nhsplash.xpm"));
4557 QLabel* attr = new QLabel("by the NetHack DevTeam",this);
4558 attr->setAlignment(AlignCenter);
4559 vbl->addWidget(attr);
4562 QLabel* logo = new QLabel(hb);
4563 hb = new QHBox(this);
4564 vbl->addWidget(hb, AlignCenter);
4565 logo->setPixmap(QPixmap(nh_icon));
4566 logo->setAlignment(AlignRight|AlignVCenter);
4567 new QLabel(nh_attribution,hb);
4570 hb = new QHBox(this);
4571 vbl->addWidget(hb, AlignCenter);
4572 QPushButton* q = new QPushButton("Quit",hb);
4573 connect(q, SIGNAL(clicked()), this, SLOT(reject()));
4574 QPushButton* c = new QPushButton("New Game",hb);
4575 connect(c, SIGNAL(clicked()), this, SLOT(accept()));
4576 c->setDefault(TRUE);
4578 QButtonGroup* bg = new QButtonGroup(3, Horizontal, "Saved Characters",this);
4580 connect(bg, SIGNAL(clicked(int)), this, SLOT(done(int)));
4581 for (int i=0; saved[i]; i++) {
4582 QPushButton* b = new QPushButton(saved[i],bg);
4587 int NetHackQtSavedGameSelector::choose()
4589 #if defined(QWS) // probably safe with Qt 3, too (where show!=exec in QDialog).
4590 if ( qt_compact_mode )
4596 void NetHackQtBind::qt_askname()
4600 // We do it all here, and nothing in askname
4602 char** saved = get_saved_games();
4604 if ( saved && *saved ) {
4605 if ( splash ) splash->hide();
4606 NetHackQtSavedGameSelector sgsel((const char**)saved);
4607 ch = sgsel.choose();
4609 strcpy(plname,saved[ch]);
4611 free_saved_games(saved);
4615 if ( splash ) splash->hide();
4616 if (NetHackQtPlayerSelector(keybuffer).Choose())
4626 qt_exit_nhwindows(0);
4630 void NetHackQtBind::qt_get_nh_event()
4635 // Kludge to access lastWindowClosed() signal.
4636 class TApp : public QApplication {
4638 TApp(int& c, char**v) : QApplication(c,v) {}
4639 void lwc() { emit lastWindowClosed(); }
4643 void NetHackQtBind::qt_exit_nhwindows(const char *)
4646 // Avoids bug in SHARP SL5500
4647 ((TApp*)qApp)->lwc();
4651 delete instance; // ie. qApp
4654 void NetHackQtBind::qt_suspend_nhwindows(const char *)
4658 void NetHackQtBind::qt_resume_nhwindows()
4662 static QArray<NetHackQtWindow*> id_to_window;
4664 winid NetHackQtBind::qt_create_nhwindow(int type)
4667 for (id = 0; id < (winid) id_to_window.size(); id++) {
4668 if ( !id_to_window[id] )
4671 if ( id == (winid) id_to_window.size() )
4672 id_to_window.resize(id+1);
4674 NetHackQtWindow* window=0;
4678 NetHackQtMapWindow* w=new NetHackQtMapWindow(clickbuffer);
4679 main->AddMapWindow(w);
4681 } break; case NHW_MESSAGE: {
4682 NetHackQtMessageWindow* w=new NetHackQtMessageWindow;
4683 main->AddMessageWindow(w);
4685 } break; case NHW_STATUS: {
4686 NetHackQtStatusWindow* w=new NetHackQtStatusWindow;
4687 main->AddStatusWindow(w);
4689 } break; case NHW_MENU:
4690 window=new NetHackQtMenuOrTextWindow(keybuffer);
4691 break; case NHW_TEXT:
4692 window=new NetHackQtTextWindow(keybuffer);
4697 // Note: use of isHidden does not work with Qt 2.1
4699 #if QT_VERSION >= 300
4700 && !main->isHidden()
4702 && main->isVisible()
4710 id_to_window[id] = window;
4714 void NetHackQtBind::qt_clear_nhwindow(winid wid)
4716 NetHackQtWindow* window=id_to_window[wid];
4720 void NetHackQtBind::qt_display_nhwindow(winid wid, BOOLEAN_P block)
4722 NetHackQtWindow* window=id_to_window[wid];
4723 window->Display(block);
4726 void NetHackQtBind::qt_destroy_nhwindow(winid wid)
4728 NetHackQtWindow* window=id_to_window[wid];
4729 main->RemoveWindow(window);
4730 if (window->Destroy())
4732 id_to_window[wid] = 0;
4735 void NetHackQtBind::qt_curs(winid wid, int x, int y)
4737 NetHackQtWindow* window=id_to_window[wid];
4738 window->CursorTo(x,y);
4741 void NetHackQtBind::qt_putstr(winid wid, int attr, const char *text)
4743 NetHackQtWindow* window=id_to_window[wid];
4744 window->PutStr(attr,text);
4747 void NetHackQtBind::qt_display_file(const char *filename, BOOLEAN_P must_exist)
4749 NetHackQtTextWindow* window=new NetHackQtTextWindow(keybuffer);
4750 bool complain = FALSE;
4759 f = dlb_fopen(filename, "r");
4761 complain = must_exist;
4763 while (dlb_fgets(buf, BUFSZ, f)) {
4764 if ((cr = index(buf, '\n')) != 0) *cr = 0;
4766 if ((cr = index(buf, '\r')) != 0) *cr = 0;
4768 if (index(buf, '\t') != 0) (void) tabexpand(buf);
4769 window->PutStr(ATR_NONE, buf);
4771 window->Display(FALSE);
4772 (void) dlb_fclose(f);
4776 QFile file(filename);
4778 if (file.open(IO_ReadOnly)) {
4780 while (file.readLine(line,127) >= 0) {
4781 line[strlen(line)-1]=0;// remove newline
4782 window->PutStr(ATR_NONE,line);
4784 window->Display(FALSE);
4786 complain = must_exist;
4792 message.sprintf("File not found: %s\n",filename);
4793 QMessageBox::message("File Error", (const char*)message, "Ignore");
4797 void NetHackQtBind::qt_start_menu(winid wid)
4799 NetHackQtWindow* window=id_to_window[wid];
4800 window->StartMenu();
4803 void NetHackQtBind::qt_add_menu(winid wid, int glyph,
4804 const ANY_P * identifier, CHAR_P ch, CHAR_P gch, int attr,
4805 const char *str, BOOLEAN_P presel)
4807 NetHackQtWindow* window=id_to_window[wid];
4808 window->AddMenu(glyph, identifier, ch, gch, attr, str, presel);
4811 void NetHackQtBind::qt_end_menu(winid wid, const char *prompt)
4813 NetHackQtWindow* window=id_to_window[wid];
4814 window->EndMenu(prompt);
4817 int NetHackQtBind::qt_select_menu(winid wid, int how, MENU_ITEM_P **menu_list)
4819 NetHackQtWindow* window=id_to_window[wid];
4820 return window->SelectMenu(how,menu_list);
4823 void NetHackQtBind::qt_update_inventory()
4826 main->updateInventory();
4828 if (program_state.something_worth_saving && flags.perm_invent)
4829 display_inventory(NULL, FALSE);
4833 void NetHackQtBind::qt_mark_synch()
4837 void NetHackQtBind::qt_wait_synch()
4841 void NetHackQtBind::qt_cliparound(int x, int y)
4843 // XXXNH - winid should be a parameter!
4844 qt_cliparound_window(WIN_MAP,x,y);
4847 void NetHackQtBind::qt_cliparound_window(winid wid, int x, int y)
4849 NetHackQtWindow* window=id_to_window[wid];
4850 window->ClipAround(x,y);
4852 void NetHackQtBind::qt_print_glyph(winid wid,XCHAR_P x,XCHAR_P y,int glyph)
4854 NetHackQtWindow* window=id_to_window[wid];
4855 window->PrintGlyph(x,y,glyph);
4857 //void NetHackQtBind::qt_print_glyph_compose(winid wid,XCHAR_P x,XCHAR_P y,int glyph1, int glyph2)
4859 //NetHackQtWindow* window=id_to_window[wid];
4860 //window->PrintGlyphCompose(x,y,glyph1,glyph2);
4863 void NetHackQtBind::qt_raw_print(const char *str)
4868 void NetHackQtBind::qt_raw_print_bold(const char *str)
4873 int NetHackQtBind::qt_nhgetch()
4876 main->fadeHighlighting();
4878 // Process events until a key arrives.
4880 while (keybuffer.Empty()) {
4884 return keybuffer.GetAscii();
4887 int NetHackQtBind::qt_nh_poskey(int *x, int *y, int *mod)
4890 main->fadeHighlighting();
4892 // Process events until a key or map-click arrives.
4894 while (keybuffer.Empty() && clickbuffer.Empty()) {
4897 if (!keybuffer.Empty()) {
4898 return keybuffer.GetAscii();
4900 *x=clickbuffer.NextX();
4901 *y=clickbuffer.NextY();
4902 *mod=clickbuffer.NextMod();
4908 void NetHackQtBind::qt_nhbell()
4910 QApplication::beep();
4913 int NetHackQtBind::qt_doprev_message()
4915 // Don't need it - uses scrollbar
4916 // XXX but could make this a shortcut
4920 char NetHackQtBind::qt_yn_function(const char *question, const char *choices, CHAR_P def)
4922 if (qt_settings->ynInMessages() && WIN_MESSAGE!=WIN_ERR) {
4923 // Similar to X11 windowport `slow' feature.
4925 char message[BUFSZ];
4926 char yn_esc_map='\033';
4929 char *cb, choicebuf[QBUFSZ];
4930 Strcpy(choicebuf, choices);
4931 if ((cb = index(choicebuf, '\033')) != 0) {
4932 // anything beyond <esc> is hidden
4935 Sprintf(message, "%s [%s] ", question, choicebuf);
4936 if (def) Sprintf(eos(message), "(%c) ", def);
4937 // escape maps to 'q' or 'n' or default, in that order
4938 yn_esc_map = (index(choices, 'q') ? 'q' :
4939 (index(choices, 'n') ? 'n' : def));
4941 Strcpy(message, question);
4945 // Improve some special-cases (DIRKS 08/02/23)
4946 if (strcmp (choices,"ynq") == 0) {
4947 switch (QMessageBox::information (qApp->mainWidget(),"NetHack",question,"&Yes","&No","&Quit",0,2))
4955 if (strcmp (choices,"yn") == 0) {
4956 switch (QMessageBox::information(qApp->mainWidget(),"NetHack",question,"&Yes", "&No",0,1))
4964 NetHackQtBind::qt_putstr(WIN_MESSAGE, ATR_BOLD, message);
4968 char ch=NetHackQtBind::qt_nhgetch();
4971 } else if (choices && !index(choices,ch)) {
4972 if (def && (ch==' ' || ch=='\r' || ch=='\n')) {
4975 NetHackQtBind::qt_nhbell();
4983 NetHackQtBind::qt_clear_nhwindow(WIN_MESSAGE);
4987 NetHackQtYnDialog dialog(keybuffer,question,choices,def);
4988 return dialog.Exec();
4992 void NetHackQtBind::qt_getlin(const char *prompt, char *line)
4994 NetHackQtStringRequestor requestor(keybuffer,prompt);
4995 if (!requestor.Get(line)) {
5000 NetHackQtExtCmdRequestor::NetHackQtExtCmdRequestor(NetHackQtKeyBuffer& ks) :
5001 QDialog(qApp->mainWidget(), "ext-cmd", FALSE),
5005 QVBoxLayout *l = new QVBoxLayout(this,marg,marg);
5007 QPushButton* can = new QPushButton("Cancel", this);
5008 can->setDefault(TRUE);
5009 can->setMinimumSize(can->sizeHint());
5012 QButtonGroup *group=new QButtonGroup("",0);
5013 QGroupBox *grid=new QGroupBox("Extended commands",this);
5018 QFontMetrics fm = fontMetrics();
5019 for (i=0; extcmdlist[i].ef_txt; i++) {
5020 butw = QMAX(butw,30+fm.width(extcmdlist[i].ef_txt));
5023 int nrows=(i+ncols-1)/ncols;
5025 QVBoxLayout* bl = new QVBoxLayout(grid,marg);
5026 bl->addSpacing(fm.height());
5027 QGridLayout* gl = new QGridLayout(nrows,ncols,marg);
5029 for (i=0; extcmdlist[i].ef_txt; i++) {
5030 QPushButton* pb=new QPushButton(extcmdlist[i].ef_txt, grid);
5031 pb->setMinimumSize(butw,pb->sizeHint().height());
5033 gl->addWidget(pb,i/ncols,i%ncols);
5035 connect(group,SIGNAL(clicked(int)),this,SLOT(done(int)));
5041 connect(can,SIGNAL(clicked()),this,SLOT(cancel()));
5044 void NetHackQtExtCmdRequestor::cancel()
5050 void NetHackQtExtCmdRequestor::done(int i)
5056 int NetHackQtExtCmdRequestor::get()
5058 const int none = -10;
5061 resize(1,1); // pack
5065 while (result()==none) {
5066 while (result()==none && !keysource.Empty()) {
5067 char k=keysource.GetAscii();
5068 if (k=='\r' || k=='\n' || k==' ' || k=='\033') {
5073 for (int i=0; extcmdlist[i].ef_txt; i++) {
5074 if (qstrnicmp(str, extcmdlist[i].ef_txt, cursor)==0) {
5081 if ( r == -1 ) { // no match!
5082 QApplication::beep();
5084 } else if ( r != -2 ) { // only one match
5097 int NetHackQtBind::qt_get_ext_cmd()
5099 NetHackQtExtCmdRequestor requestor(keybuffer);
5100 return requestor.get();
5103 void NetHackQtBind::qt_number_pad(int)
5108 void NetHackQtBind::qt_delay_output()
5110 NetHackQtDelay delay(15);
5114 void NetHackQtBind::qt_start_screen()
5119 void NetHackQtBind::qt_end_screen()
5124 void NetHackQtBind::qt_outrip(winid wid, int how)
5126 NetHackQtWindow* window=id_to_window[wid];
5128 window->UseRIP(how);
5131 bool NetHackQtBind::notify(QObject *receiver, QEvent *event)
5133 // Ignore Alt-key navigation to menubar, it's annoying when you
5134 // use Alt-Direction to move around.
5135 if ( main && event->type()==QEvent::KeyRelease && main==receiver
5136 && ((QKeyEvent*)event)->key() == Key_Alt )
5139 bool result=QApplication::notify(receiver,event);
5140 if (event->type()==QEvent::KeyPress) {
5141 QKeyEvent* key_event=(QKeyEvent*)event;
5143 if (!key_event->isAccepted()) {
5144 const int k=key_event->key();
5146 for (int i=0; !macro && key_macro[i].key; i++) {
5147 if (key_macro[i].key==k
5148 && ((key_macro[i].state&key_event->state())==key_macro[i].state))
5150 keybuffer.Put(key_macro[i].macro);
5154 char ch=key_event->ascii();
5155 if ( !ch && (key_event->state() & Qt::ControlButton) ) {
5156 // On Mac, ascii control codes are not sent, force them.
5157 if ( k>=Qt::Key_A && k<=Qt::Key_Z )
5158 ch = k - Qt::Key_A + 1;
5161 bool alt = (key_event->state()&AltButton) ||
5162 (k >= Key_0 && k <= Key_9 && (key_event->state()&ControlButton));
5163 keybuffer.Put(key_event->key(),ch + (alt ? 128 : 0),
5164 key_event->state());
5165 key_event->accept();
5177 NetHackQtBind* NetHackQtBind::instance=0;
5178 NetHackQtKeyBuffer NetHackQtBind::keybuffer;
5179 NetHackQtClickBuffer NetHackQtBind::clickbuffer;
5180 NetHackQtMainWindow* NetHackQtBind::main=0;
5181 QWidget* NetHackQtBind::splash=0;
5184 extern "C" struct window_procs Qt_procs;
5186 struct window_procs Qt_procs = {
5188 WC_COLOR|WC_HILITE_PET|
5189 WC_ASCII_MAP|WC_TILED_MAP|
5190 WC_FONT_MAP|WC_TILE_FILE|WC_TILE_WIDTH|WC_TILE_HEIGHT|
5191 WC_PLAYER_SELECTION|WC_SPLASH_SCREEN,
5193 NetHackQtBind::qt_init_nhwindows,
5194 NetHackQtBind::qt_player_selection,
5195 NetHackQtBind::qt_askname,
5196 NetHackQtBind::qt_get_nh_event,
5197 NetHackQtBind::qt_exit_nhwindows,
5198 NetHackQtBind::qt_suspend_nhwindows,
5199 NetHackQtBind::qt_resume_nhwindows,
5200 NetHackQtBind::qt_create_nhwindow,
5201 NetHackQtBind::qt_clear_nhwindow,
5202 NetHackQtBind::qt_display_nhwindow,
5203 NetHackQtBind::qt_destroy_nhwindow,
5204 NetHackQtBind::qt_curs,
5205 NetHackQtBind::qt_putstr,
5206 NetHackQtBind::qt_display_file,
5207 NetHackQtBind::qt_start_menu,
5208 NetHackQtBind::qt_add_menu,
5209 NetHackQtBind::qt_end_menu,
5210 NetHackQtBind::qt_select_menu,
5211 genl_message_menu, /* no need for X-specific handling */
5212 NetHackQtBind::qt_update_inventory,
5213 NetHackQtBind::qt_mark_synch,
5214 NetHackQtBind::qt_wait_synch,
5216 NetHackQtBind::qt_cliparound,
5221 NetHackQtBind::qt_print_glyph,
5222 //NetHackQtBind::qt_print_glyph_compose,
5223 NetHackQtBind::qt_raw_print,
5224 NetHackQtBind::qt_raw_print_bold,
5225 NetHackQtBind::qt_nhgetch,
5226 NetHackQtBind::qt_nh_poskey,
5227 NetHackQtBind::qt_nhbell,
5228 NetHackQtBind::qt_doprev_message,
5229 NetHackQtBind::qt_yn_function,
5230 NetHackQtBind::qt_getlin,
5231 NetHackQtBind::qt_get_ext_cmd,
5232 NetHackQtBind::qt_number_pad,
5233 NetHackQtBind::qt_delay_output,
5234 #ifdef CHANGE_COLOR /* only a Mac option currently */
5238 /* other defs that really should go away (they're tty specific) */
5239 NetHackQtBind::qt_start_screen,
5240 NetHackQtBind::qt_end_screen,
5241 #ifdef GRAPHIC_TOMBSTONE
5242 NetHackQtBind::qt_outrip,
5246 genl_preference_update,
5249 extern "C" void play_usersound(const char* filename, int volume)
5253 QSound::play(filename);
5258 #include "qt_win.moc"
5260 #include "qt_kde0.moc"
5262 #if QT_VERSION >= 300
5263 #include "qttableview.moc"