1 // NetHack 3.6 qt_win.cpp $NHDT-Date$ $NHDT-Branch$:$NHDT-Revision$
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
);
140 // Warwick prefers it this way...
141 #define QT_CHOOSE_RACE_FIRST
143 static const char nh_attribution
[] = "<center><big>NetHack</big>"
144 "<br><small>by the NetHack DevTeam</small></center>";
151 "Qt NetHack is a version of NetHack built\n"
153 "using KDE and the Qt GUI toolkit.\n"
155 "using the Qt GUI toolkit.\n"
157 "This is version %d.%d.%d\n\n"
158 "Homepage:\n http://trolls.troll.no/warwick/nethack/\n\n"
160 "KDE:\n http://www.kde.org\n"
162 "Qt:\n http://www.troll.no",
170 centerOnMain( QWidget
* w
)
172 QWidget
* m
= qApp
->mainWidget();
173 if (!m
) m
= qApp
->desktop();
174 QPoint p
= m
->mapToGlobal(QPoint(0,0));
175 w
->move( p
.x() + m
->width()/2 - w
->width()/2,
176 p
.y() + m
->height()/2 - w
->height()/2 );
179 NetHackQtLineEdit::NetHackQtLineEdit() :
184 NetHackQtLineEdit::NetHackQtLineEdit(QWidget
* parent
, const char* name
) :
185 QLineEdit(parent
,name
)
189 void NetHackQtLineEdit::fakeEvent(int key
, int ascii
, int state
)
191 QKeyEvent
fake(QEvent::KeyPress
,key
,ascii
,state
);
192 keyPressEvent(&fake
);
196 /* Used by tile/font-size patch below and in ../../src/files.c */
197 char *qt_tilewidth
=NULL
;
198 char *qt_tileheight
=NULL
;
199 char *qt_fontsize
=NULL
;
201 int qt_compact_mode
= 1;
203 int qt_compact_mode
= 0;
205 extern const char *enc_stat
[]; /* from botl.c */
206 extern const char *hu_stat
[]; /* from eat.c */
207 extern int total_tiles_used
; // from tile.c
208 extern short glyph2tile
[]; // from tile.c
211 static int tilefile_tile_W
=16;
212 static int tilefile_tile_H
=16;
219 static const char * nh_icon
[] = {
231 " .. .....X.XXXXXX XX ",
232 " ... ....X..XX.XXXXX XXX ",
233 " .. ..........X.XXXXXXXXXXX XX ",
234 " .... ........X..XX.XXXXXXXXX XXXX ",
235 " .... ..........X.XXXXXXXXXXX XXXX ",
236 " ooOOO..ooooooOooOOoOOOOOOOXX+++OO++ ",
237 " ooOOO..ooooooooOoOOOOOOOOOXX+++OO++ ",
238 " ....O..ooooooOooOOoOOOOOOOXX+XXXX++ ",
239 " ....O..ooooooooOoOOOOOOOOOXX+XXXX++ ",
240 " ..OOO..ooooooOooOOoOOOOOOOXX+++XX++ ",
241 " ++++..ooooooooOoOOOOOOOOOXX+++ +++ ",
242 " +++..ooooooOooOOoOOOOOOOXX+++ + ",
243 " ++..ooooooooOoOOOOOOOOOXX+++ ",
244 " ..ooooooOooOOoOOOOOOOXX+++ ",
245 " ..ooooooooOoOOOOOOOOOXX+++ ",
246 " ..ooooooOooOOoOOOOOOOXX+++ ",
247 " ..ooooooooOoOOOOOOOOOXX+++ ",
248 " ..oooooOooOOoOOOOOOXX+++ ",
249 " ..oooooooOoOOOOOOOOXX+++ ",
250 " ..ooooOooOOoOOOOOXX+++ ",
251 " ..ooooooOoOOOOOOOXX++++ ",
252 " ..o..oooOooOOoOOOOXX+XX+++ ",
253 " ...o..oooooOoOOOOOXX++XXX++ ",
254 " ....OO..ooOooOOoOOXX+++XXXX++ ",
255 " ...oo..+..oooOoOOOXX++XXooXXX++ ",
256 " ...ooo..++..OooOOoXX+++XXooOXXX+ ",
257 " ..oooOOXX+++....XXXX++++XXOOoOOXX+ ",
258 " ..oooOOXX+++ ...XXX+++++XXOOooOXX++ ",
259 " ..oooOXXX+++ ..XX+++ +XXOOooOXX++ ",
260 " .....XXX++++ XXXXXXX++ ",
261 " ....XX++++ XXXXXXX+ ",
262 " ...XX+++ XXXXX++ ",
268 static const char * nh_icon_small
[] = {
269 /* width height ncolors chars_per_pixel */
308 static const char * map_xpm
[] = {
328 static const char * msg_xpm
[] = {
348 static const char * stat_xpm
[] = {
369 static const char * info_xpm
[] = {
391 static const char * again_xpm
[] = {
409 static const char * kick_xpm
[] = {
428 static const char * throw_xpm
[] = {
447 static const char * fire_xpm
[] = {
468 static const char * get_xpm
[] = {
487 static const char * drop_xpm
[] = {
506 static const char * eat_xpm
[] = {
526 static const char * rest_xpm
[] = {
544 static const char * cast_a_xpm
[] = {
563 static const char * cast_b_xpm
[] = {
582 static const char * cast_c_xpm
[] = {
601 NetHackQtSettings::NetHackQtSettings(int w
, int h
) :
602 tilewidth(TILEWMIN
,64,1,this),
603 tileheight(TILEHMIN
,64,1,this),
604 widthlbl(&tilewidth
,"&Width:",this),
605 heightlbl(&tileheight
,"&Height:",this),
606 whichsize("&Zoomed",this),
610 normalfixed("courier new"),
612 normalfixed("fixed"),
618 int default_fontsize
;
623 tilewidth
.setValue(8);
624 tileheight
.setValue(12);
628 tilewidth
.setValue(8);
629 tileheight
.setValue(14);
633 tilewidth
.setValue(10);
634 tileheight
.setValue(17);
635 } else if (w
<=1100) {
638 tilewidth
.setValue(12);
639 tileheight
.setValue(22);
640 } else if (w
<=1200) {
643 tilewidth
.setValue(14);
644 tileheight
.setValue(26);
646 // ~1280x1024 and larger
648 tilewidth
.setValue(16);
649 tileheight
.setValue(30);
652 // Tile/font sizes read from .nethackrc
653 if (qt_tilewidth
!= NULL
) {
654 tilewidth
.setValue(atoi(qt_tilewidth
));
657 if (qt_tileheight
!= NULL
) {
658 tileheight
.setValue(atoi(qt_tileheight
));
661 if (qt_fontsize
!= NULL
) {
662 switch (tolower(qt_fontsize
[0])) {
663 case 'h': default_fontsize
= 0; break;
664 case 'l': default_fontsize
= 1; break;
665 case 'm': default_fontsize
= 2; break;
666 case 's': default_fontsize
= 3; break;
667 case 't': default_fontsize
= 4; break;
672 theglyphs
=new NetHackQtGlyphs();
675 connect(&tilewidth
,SIGNAL(valueChanged(int)),this,SLOT(resizeTiles()));
676 connect(&tileheight
,SIGNAL(valueChanged(int)),this,SLOT(resizeTiles()));
677 connect(&whichsize
,SIGNAL(toggled(bool)),this,SLOT(setGlyphSize(bool)));
679 fontsize
.insertItem("Huge");
680 fontsize
.insertItem("Large");
681 fontsize
.insertItem("Medium");
682 fontsize
.insertItem("Small");
683 fontsize
.insertItem("Tiny");
684 fontsize
.setCurrentItem(default_fontsize
);
685 connect(&fontsize
,SIGNAL(activated(int)),this,SIGNAL(fontChanged()));
687 QGridLayout
* grid
= new QGridLayout(this, 5, 2, 8);
688 grid
->addMultiCellWidget(&whichsize
, 0, 0, 0, 1);
689 grid
->addWidget(&tilewidth
, 1, 1); grid
->addWidget(&widthlbl
, 1, 0);
690 grid
->addWidget(&tileheight
, 2, 1); grid
->addWidget(&heightlbl
, 2, 0);
691 QLabel
* flabel
=new QLabel(&fontsize
, "&Font:",this);
692 grid
->addWidget(flabel
, 3, 0); grid
->addWidget(&fontsize
, 3, 1);
693 QPushButton
* dismiss
=new QPushButton("Dismiss",this);
694 dismiss
->setDefault(TRUE
);
695 grid
->addMultiCellWidget(dismiss
, 4, 4, 0, 1);
696 grid
->setRowStretch(4,0);
697 grid
->setColStretch(1,1);
698 grid
->setColStretch(2,2);
701 connect(dismiss
,SIGNAL(clicked()),this,SLOT(accept()));
705 NetHackQtGlyphs
& NetHackQtSettings::glyphs()
710 void NetHackQtSettings::resizeTiles()
712 int w
= tilewidth
.value();
713 int h
= tileheight
.value();
715 theglyphs
->setSize(w
,h
);
719 void NetHackQtSettings::toggleGlyphSize()
724 void NetHackQtSettings::setGlyphSize(bool which
)
726 QSize n
= QSize(tilewidth
.value(),tileheight
.value());
727 if ( othersize
.isValid() ) {
728 tilewidth
.blockSignals(TRUE
);
729 tileheight
.blockSignals(TRUE
);
730 tilewidth
.setValue(othersize
.width());
731 tileheight
.setValue(othersize
.height());
732 tileheight
.blockSignals(FALSE
);
733 tilewidth
.blockSignals(FALSE
);
739 const QFont
& NetHackQtSettings::normalFont()
741 static int size
[]={ 18, 14, 12, 10, 8 };
742 normal
.setPointSize(size
[fontsize
.currentItem()]);
746 const QFont
& NetHackQtSettings::normalFixedFont()
748 static int size
[]={ 18, 14, 13, 10, 8 };
749 normalfixed
.setPointSize(size
[fontsize
.currentItem()]);
753 const QFont
& NetHackQtSettings::largeFont()
755 static int size
[]={ 24, 18, 14, 12, 10 };
756 large
.setPointSize(size
[fontsize
.currentItem()]);
760 bool NetHackQtSettings::ynInMessages()
762 return !qt_compact_mode
;
766 NetHackQtSettings
* qt_settings
;
770 NetHackQtKeyBuffer::NetHackQtKeyBuffer() :
775 bool NetHackQtKeyBuffer::Empty() const { return in
==out
; }
776 bool NetHackQtKeyBuffer::Full() const { return (in
+1)%maxkey
==out
; }
778 void NetHackQtKeyBuffer::Put(int k
, int a
, int state
)
780 if ( Full() ) return; // Safety
786 void NetHackQtKeyBuffer::Put(char a
)
791 void NetHackQtKeyBuffer::Put(const char* str
)
793 while (*str
) Put(*str
++);
796 int NetHackQtKeyBuffer::GetKey()
798 if ( Empty() ) return 0;
804 int NetHackQtKeyBuffer::GetAscii()
806 if ( Empty() ) return 0; // Safety
812 int NetHackQtKeyBuffer::GetState()
814 if ( Empty() ) return 0;
820 int NetHackQtKeyBuffer::TopKey() const
822 if ( Empty() ) return 0;
826 int NetHackQtKeyBuffer::TopAscii() const
828 if ( Empty() ) return 0;
832 int NetHackQtKeyBuffer::TopState() const
834 if ( Empty() ) return 0;
839 NetHackQtClickBuffer::NetHackQtClickBuffer() :
844 bool NetHackQtClickBuffer::Empty() const { return in
==out
; }
845 bool NetHackQtClickBuffer::Full() const { return (in
+1)%maxclick
==out
; }
847 void NetHackQtClickBuffer::Put(int x
, int y
, int mod
)
855 int NetHackQtClickBuffer::NextX() const { return click
[out
].x
; }
856 int NetHackQtClickBuffer::NextY() const { return click
[out
].y
; }
857 int NetHackQtClickBuffer::NextMod() const { return click
[out
].mod
; }
859 void NetHackQtClickBuffer::Get()
861 out
=(out
+1)%maxclick
;
864 class NhPSListViewItem
: public QListViewItem
{
866 NhPSListViewItem( QListView
* parent
, const QString
& name
) :
867 QListViewItem(parent
, name
)
873 NetHackQtGlyphs
& glyphs
= qt_settings
->glyphs();
874 int gw
= glyphs
.width();
875 int gh
= glyphs
.height();
878 glyphs
.drawGlyph(p
, g
, 0, 0);
881 setHeight(QMAX(pm
.height()+1,height()));
884 void paintCell( QPainter
*p
, const QColorGroup
&cg
,
885 int column
, int width
, int alignment
)
887 if ( isSelectable() ) {
888 QListViewItem::paintCell( p
, cg
, column
, width
, alignment
);
890 QColorGroup
disabled(
891 cg
.foreground().light(),
893 cg
.light(), cg
.dark(), cg
.mid(),
895 QListViewItem::paintCell( p
, disabled
, column
, width
, alignment
);
900 class NhPSListViewRole
: public NhPSListViewItem
{
902 NhPSListViewRole( QListView
* parent
, int id
) :
903 NhPSListViewItem(parent
,
904 #ifdef QT_CHOOSE_RACE_FIRST // Lowerize - looks better
905 QString(QChar(roles
[id
].name
.m
[0])).lower()+QString(roles
[id
].name
.m
+1)
911 setGlyph(monnum_to_glyph(roles
[id
].malenum
));
915 class NhPSListViewRace
: public NhPSListViewItem
{
917 NhPSListViewRace( QListView
* parent
, int id
) :
918 NhPSListViewItem(parent
,
919 #ifdef QT_CHOOSE_RACE_FIRST // Capitalize - looks better
920 QString(QChar(races
[id
].noun
[0])).upper()+QString(races
[id
].noun
+1)
922 QString(QChar(races
[id
].noun
[0])+QString(races
[id
].noun
+1))
926 setGlyph(monnum_to_glyph(races
[id
].malenum
));
930 class NhPSListView
: public QListView
{
932 NhPSListView( QWidget
* parent
) :
935 setSorting(-1); // order is identity
936 header()->setClickEnabled(FALSE
);
939 QSizePolicy
sizePolicy() const
941 return QSizePolicy(QSizePolicy::Expanding
,QSizePolicy::Expanding
);
944 QSize
minimumSizeHint() const
949 QSize
sizeHint() const
951 QListView::sizeHint();
952 QSize sz
= header()->sizeHint();
954 QListViewItem
* c
=firstChild();
955 while (c
) h
+=c
->height(),c
= c
->nextSibling();
956 sz
+= QSize(frameWidth()*2, h
+frameWidth()*2);
960 int selectedItemNumber() const
963 QListViewItem
* c
= firstChild();
965 if (c
== selectedItem()) {
969 c
= c
->nextSibling();
974 void setSelectedItemNumber(int i
)
976 QListViewItem
* c
=firstChild();
978 c
= c
->nextSibling();
979 c
->setSelected(TRUE
);
983 NetHackQtPlayerSelector::NetHackQtPlayerSelector(NetHackQtKeyBuffer
& ks
) :
984 QDialog(qApp
->mainWidget(),"plsel",TRUE
),
986 fully_specified_role(TRUE
)
990 + Name ------------------------------------+
992 + ---- ------------------------------------+
993 + Role ---+ + Race ---+ + Gender ------+
995 1 | | | | | * Female |
996 | | | | +--------------+
998 | | | | + Alignment ---+
1000 | | | | | * Female |
1001 | | | | +--------------+
1002 3 | | | | ...stretch...
1006 +---------+ +---------+
1010 QGridLayout
*l
= new QGridLayout(this,6,3,marg
,marg
);
1012 QButtonGroup
* namebox
= new QButtonGroup(1,Horizontal
,"Name",this);
1013 QLineEdit
* name
= new QLineEdit(namebox
);
1014 name
->setMaxLength(sizeof(plname
)-1);
1015 if ( strncmp(plname
,"player",6) && strncmp(plname
,"games",5) )
1016 name
->setText(plname
);
1017 connect(name
, SIGNAL(textChanged(const QString
&)),
1018 this, SLOT(selectName(const QString
&)) );
1020 QButtonGroup
* genderbox
= new QButtonGroup("Sex",this);
1021 QButtonGroup
* alignbox
= new QButtonGroup("Alignment",this);
1022 QVBoxLayout
* vbgb
= new QVBoxLayout(genderbox
,3,1);
1023 vbgb
->setAutoAdd(TRUE
);
1024 vbgb
->addSpacing(fontMetrics().height()*3/4);
1025 QVBoxLayout
* vbab
= new QVBoxLayout(alignbox
,3,1);
1026 vbab
->setAutoAdd(TRUE
);
1027 vbab
->addSpacing(fontMetrics().height());
1028 QLabel
* logo
= new QLabel(nh_attribution
, this);
1030 l
->addMultiCellWidget( namebox
, 0,0,0,2 );
1031 #ifdef QT_CHOOSE_RACE_FIRST
1032 race
= new NhPSListView(this);
1033 role
= new NhPSListView(this);
1034 l
->addMultiCellWidget( race
, 1,5,0,0 );
1035 l
->addMultiCellWidget( role
, 1,5,1,1 );
1037 role
= new NhPSListView(this);
1038 race
= new NhPSListView(this);
1039 l
->addMultiCellWidget( role
, 1,5,0,0 );
1040 l
->addMultiCellWidget( race
, 1,5,1,1 );
1042 role
->addColumn("Role");
1043 race
->addColumn("Race");
1045 l
->addWidget( genderbox
, 1, 2 );
1046 l
->addWidget( alignbox
, 2, 2 );
1047 l
->addWidget( logo
, 3, 2, AlignCenter
);
1048 l
->setRowStretch( 3, 5 );
1053 for (nrole
=0; roles
[nrole
].name
.m
; nrole
++)
1055 for (i
=nrole
-1; i
>=0; i
--) { // XXX QListView unsorted goes in rev.
1056 new NhPSListViewRole( role
, i
);
1058 connect( role
, SIGNAL(selectionChanged()), this, SLOT(selectRole()) );
1061 for (nrace
=0; races
[nrace
].noun
; nrace
++)
1063 for (i
=nrace
-1; i
>=0; i
--) {
1064 new NhPSListViewRace( race
, i
);
1066 connect( race
, SIGNAL(selectionChanged()), this, SLOT(selectRace()) );
1068 gender
= new QRadioButton
*[ROLE_GENDERS
];
1069 for (i
=0; i
<ROLE_GENDERS
; i
++) {
1070 gender
[i
] = new QRadioButton( genders
[i
].adj
, genderbox
);
1072 connect( genderbox
, SIGNAL(clicked(int)), this, SLOT(selectGender(int)) );
1074 alignment
= new QRadioButton
*[ROLE_ALIGNS
];
1075 for (i
=0; i
<ROLE_ALIGNS
; i
++) {
1076 alignment
[i
] = new QRadioButton( aligns
[i
].adj
, alignbox
);
1078 connect( alignbox
, SIGNAL(clicked(int)), this, SLOT(selectAlignment(int)) );
1080 QPushButton
* ok
= new QPushButton("Play",this);
1081 l
->addWidget( ok
, 4, 2 );
1082 ok
->setDefault(TRUE
);
1083 connect( ok
, SIGNAL(clicked()), this, SLOT(accept()) );
1085 QPushButton
* cancel
= new QPushButton("Quit",this);
1086 l
->addWidget( cancel
, 5, 2 );
1087 connect( cancel
, SIGNAL(clicked()), this, SLOT(reject()) );
1089 // Randomize race and role, unless specified in config
1090 int ro
= flags
.initrole
;
1091 if (ro
== ROLE_NONE
|| ro
== ROLE_RANDOM
) {
1093 if (flags
.initrole
!= ROLE_RANDOM
) {
1094 fully_specified_role
= FALSE
;
1097 int ra
= flags
.initrace
;
1098 if (ra
== ROLE_NONE
|| ra
== ROLE_RANDOM
) {
1100 if (flags
.initrace
!= ROLE_RANDOM
) {
1101 fully_specified_role
= FALSE
;
1105 // make sure we have a valid combination, honoring
1106 // the users request if possible.
1107 bool choose_race_first
;
1108 #ifdef QT_CHOOSE_RACE_FIRST
1109 choose_race_first
= TRUE
;
1110 if (flags
.initrole
>= 0 && flags
.initrace
< 0) {
1111 choose_race_first
= FALSE
;
1114 choose_race_first
= FALSE
;
1115 if (flags
.initrace
>= 0 && flags
.initrole
< 0) {
1116 choose_race_first
= TRUE
;
1119 while (!validrace(ro
,ra
)) {
1120 if (choose_race_first
) {
1122 if (flags
.initrole
!= ROLE_RANDOM
) {
1123 fully_specified_role
= FALSE
;
1127 if (flags
.initrace
!= ROLE_RANDOM
) {
1128 fully_specified_role
= FALSE
;
1133 int g
= flags
.initgend
;
1135 g
= rn2(ROLE_GENDERS
);
1136 fully_specified_role
= FALSE
;
1138 while (!validgend(ro
,ra
,g
)) {
1139 g
= rn2(ROLE_GENDERS
);
1141 gender
[g
]->setChecked(TRUE
);
1144 int a
= flags
.initalign
;
1146 a
= rn2(ROLE_ALIGNS
);
1147 fully_specified_role
= FALSE
;
1149 while (!validalign(ro
,ra
,a
)) {
1150 a
= rn2(ROLE_ALIGNS
);
1152 alignment
[a
]->setChecked(TRUE
);
1157 li
= role
->firstChild();
1158 while (ro
--) li
=li
->nextSibling();
1159 role
->setSelected(li
,TRUE
);
1161 li
= race
->firstChild();
1162 while (ra
--) li
=li
->nextSibling();
1163 race
->setSelected(li
,TRUE
);
1165 flags
.initrace
= race
->selectedItemNumber();
1166 flags
.initrole
= role
->selectedItemNumber();
1170 void NetHackQtPlayerSelector::selectName(const QString
& n
)
1172 strncpy(plname
,n
.latin1(),sizeof(plname
)-1);
1175 void NetHackQtPlayerSelector::selectRole()
1177 int ra
= race
->selectedItemNumber();
1178 int ro
= role
->selectedItemNumber();
1179 if (ra
== -1 || ro
== -1) return;
1181 #ifdef QT_CHOOSE_RACE_FIRST
1184 QListViewItem
* i
=role
->currentItem();
1185 QListViewItem
* valid
=0;
1187 NhPSListViewItem
* item
;
1188 item
= (NhPSListViewItem
*)role
->firstChild();
1189 for (j
=0; roles
[j
].name
.m
; j
++) {
1190 bool v
= validrace(j
,ra
);
1191 item
->setSelectable(TRUE
);
1192 if ( !valid
&& v
) valid
= item
;
1193 item
=(NhPSListViewItem
*)item
->nextSibling();
1195 if ( !validrace(role
->selectedItemNumber(),ra
) )
1197 role
->setSelected(i
,TRUE
);
1198 item
= (NhPSListViewItem
*)role
->firstChild();
1199 for (j
=0; roles
[j
].name
.m
; j
++) {
1200 bool v
= validrace(j
,ra
);
1201 item
->setSelectable(v
);
1203 item
=(NhPSListViewItem
*)item
->nextSibling();
1207 flags
.initrole
= role
->selectedItemNumber();
1211 void NetHackQtPlayerSelector::selectRace()
1213 int ra
= race
->selectedItemNumber();
1214 int ro
= role
->selectedItemNumber();
1215 if (ra
== -1 || ro
== -1) return;
1217 #ifndef QT_CHOOSE_RACE_FIRST
1220 QListViewItem
* i
=race
->currentItem();
1221 QListViewItem
* valid
=0;
1223 NhPSListViewItem
* item
;
1224 item
= (NhPSListViewItem
*)race
->firstChild();
1225 for (j
=0; races
[j
].noun
; j
++) {
1226 bool v
= validrace(ro
,j
);
1227 item
->setSelectable(TRUE
);
1228 if ( !valid
&& v
) valid
= item
;
1229 item
=(NhPSListViewItem
*)item
->nextSibling();
1231 if ( !validrace(ro
,race
->selectedItemNumber()) )
1233 race
->setSelected(i
,TRUE
);
1234 item
= (NhPSListViewItem
*)race
->firstChild();
1235 for (j
=0; races
[j
].noun
; j
++) {
1236 bool v
= validrace(ro
,j
);
1237 item
->setSelectable(v
);
1239 item
=(NhPSListViewItem
*)item
->nextSibling();
1243 flags
.initrace
= race
->selectedItemNumber();
1247 void NetHackQtPlayerSelector::setupOthers()
1249 int ro
= role
->selectedItemNumber();
1250 int ra
= race
->selectedItemNumber();
1254 for (j
=0; j
<ROLE_GENDERS
; j
++) {
1255 bool v
= validgend(ro
,ra
,j
);
1256 if ( gender
[j
]->isChecked() )
1258 gender
[j
]->setEnabled(v
);
1259 if ( valid
<0 && v
) valid
= j
;
1261 if ( !validgend(ro
,ra
,c
) )
1264 for (k
=0; k
<ROLE_GENDERS
; k
++) {
1265 gender
[k
]->setChecked(c
==k
);
1270 for (j
=0; j
<ROLE_ALIGNS
; j
++) {
1271 bool v
= validalign(ro
,ra
,j
);
1272 if ( alignment
[j
]->isChecked() )
1274 alignment
[j
]->setEnabled(v
);
1275 if ( valid
<0 && v
) valid
= j
;
1277 if ( !validalign(ro
,ra
,c
) )
1279 for (k
=0; k
<ROLE_ALIGNS
; k
++) {
1280 alignment
[k
]->setChecked(c
==k
);
1285 void NetHackQtPlayerSelector::selectGender(int i
)
1290 void NetHackQtPlayerSelector::selectAlignment(int i
)
1292 flags
.initalign
= i
;
1296 void NetHackQtPlayerSelector::done(int i
)
1302 void NetHackQtPlayerSelector::Quit()
1308 void NetHackQtPlayerSelector::Random()
1314 bool NetHackQtPlayerSelector::Choose()
1316 if (fully_specified_role
) return TRUE
;
1318 #if defined(QWS) // probably safe with Qt 3, too (where show!=exec in QDialog).
1319 if ( qt_compact_mode
) {
1336 NetHackQtStringRequestor::NetHackQtStringRequestor(NetHackQtKeyBuffer
& ks
, const char* p
, const char* cancelstr
) :
1337 QDialog(qApp
->mainWidget(),"string",FALSE
),
1338 prompt(p
,this,"prompt"),
1339 input(this,"input"),
1342 cancel
=new QPushButton(cancelstr
,this);
1343 connect(cancel
,SIGNAL(clicked()),this,SLOT(reject()));
1345 okay
=new QPushButton("Okay",this);
1346 connect(okay
,SIGNAL(clicked()),this,SLOT(accept()));
1347 connect(&input
,SIGNAL(returnPressed()),this,SLOT(accept()));
1348 okay
->setDefault(TRUE
);
1350 setFocusPolicy(StrongFocus
);
1353 void NetHackQtStringRequestor::resizeEvent(QResizeEvent
*)
1358 int h
=(height()-margin
*2-gutter
);
1360 if (strlen(prompt
.text()) > 16) {
1362 prompt
.setGeometry(margin
,margin
,width()-margin
*2,h
);
1363 input
.setGeometry(width()*1/5,margin
+h
+gutter
,
1364 (width()-margin
-2-gutter
)*4/5,h
);
1367 prompt
.setGeometry(margin
,margin
,(width()-margin
*2-gutter
)*2/5,h
);
1368 input
.setGeometry(prompt
.geometry().right()+gutter
,margin
,
1369 (width()-margin
-2-gutter
)*3/5,h
);
1372 cancel
->setGeometry(margin
,input
.geometry().bottom()+gutter
,
1373 (width()-margin
*2-gutter
)/2,h
);
1374 okay
->setGeometry(cancel
->geometry().right()+gutter
,cancel
->geometry().y(),
1378 void NetHackQtStringRequestor::SetDefault(const char* d
)
1383 bool NetHackQtStringRequestor::Get(char* buffer
, int maxchar
)
1385 input
.setMaxLength(maxchar
);
1386 if (strlen(prompt
.text()) > 16) {
1387 resize(fontMetrics().width(prompt
.text())+50,fontMetrics().height()*6);
1389 resize(fontMetrics().width(prompt
.text())*2+50,fontMetrics().height()*4);
1396 while (result()==-1) {
1397 // Put keys in buffer (eg. from macros, from out-of-focus input)
1398 if (!keysource
.Empty()) {
1399 while (!keysource
.Empty()) {
1400 int key
=keysource
.TopKey();
1401 int ascii
=keysource
.TopAscii();
1402 int state
=keysource
.GetState();
1403 if (ascii
=='\r' || ascii
=='\n') {
1404 // CR or LF in buffer causes confirmation
1405 strcpy(buffer
,input
.text());
1407 } else if (ascii
=='\033') {
1410 input
.fakeEvent(key
,ascii
,state
);
1416 // XXX Get rid of extra keys, since we couldn't get focus!
1417 while (!keysource
.Empty()) keysource
.GetKey();
1420 strcpy(buffer
,input
.text());
1426 void NetHackQtStringRequestor::done(int i
)
1433 NetHackQtWindow::NetHackQtWindow()
1437 NetHackQtWindow::~NetHackQtWindow()
1441 // XXX Use "expected ..." for now, abort or default later.
1443 void NetHackQtWindow::Clear() { puts("unexpected Clear"); }
1444 void NetHackQtWindow::Display(bool block
) { puts("unexpected Display"); }
1445 bool NetHackQtWindow::Destroy() { return TRUE
; }
1446 void NetHackQtWindow::CursorTo(int x
,int y
) { puts("unexpected CursorTo"); }
1447 void NetHackQtWindow::PutStr(int attr
, const char* text
) { puts("unexpected PutStr"); }
1448 void NetHackQtWindow::StartMenu() { puts("unexpected StartMenu"); }
1449 void NetHackQtWindow::AddMenu(int glyph
, const ANY_P
* identifier
, char ch
, char gch
, int attr
,
1450 const char* str
, bool presel
) { puts("unexpected AddMenu"); }
1451 void NetHackQtWindow::EndMenu(const char* prompt
) { puts("unexpected EndMenu"); }
1452 int NetHackQtWindow::SelectMenu(int how
, MENU_ITEM_P
**menu_list
) { puts("unexpected SelectMenu"); return 0; }
1453 void NetHackQtWindow::ClipAround(int x
,int y
) { puts("unexpected ClipAround"); }
1454 void NetHackQtWindow::PrintGlyph(int x
,int y
,int glyph
) { puts("unexpected PrintGlyph"); }
1455 //void NetHackQtWindow::PrintGlyphCompose(int x,int y,int,int) { puts("unexpected PrintGlyphCompose"); }
1456 void NetHackQtWindow::UseRIP(int how
, time_t when
) { puts("unexpected UseRIP"); }
1460 // XXX Hmmm... crash after saving bones file if Map window is
1461 // XXX deleted. Strange bug somewhere.
1462 bool NetHackQtMapWindow::Destroy() { return FALSE
; }
1464 NetHackQtMapWindow::NetHackQtMapWindow(NetHackQtClickBuffer
& click_sink
) :
1465 clicksink(click_sink
),
1469 viewport
.addChild(this);
1471 setBackgroundColor(black
);
1472 viewport
.setBackgroundColor(black
);
1474 pet_annotation
= QPixmap(qt_compact_mode
? pet_mark_small_xpm
: pet_mark_xpm
);
1480 connect(qt_settings
,SIGNAL(tilesChanged()),this,SLOT(updateTiles()));
1481 connect(&viewport
, SIGNAL(contentsMoving(int,int)), this,
1482 SLOT(moveMessages(int,int)));
1485 //setFocusPolicy(StrongFocus);
1487 QTimer
* deadman
= new QTimer(this);
1488 connect(deadman
, SIGNAL(timeout()), SLOT(timeout()));
1489 deadman
->start(2000); // deadman timer every 2 seconds
1494 // The "deadman" timer is received by this slot
1495 void NetHackQtMapWindow::timeout() {}
1498 void NetHackQtMapWindow::moveMessages(int x
, int y
)
1500 QRect u
= messages_rect
;
1501 messages_rect
.moveTopLeft(QPoint(x
,y
));
1506 void NetHackQtMapWindow::clearMessages()
1509 update(messages_rect
);
1510 messages_rect
= QRect();
1513 void NetHackQtMapWindow::putMessage(int attr
, const char* text
)
1515 if ( !messages
.isEmpty() )
1518 QFontMetrics fm
= fontMetrics();
1519 messages_rect
= fm
.boundingRect(viewport
.contentsX(), viewport
.contentsY(),
1520 viewport
.width(), 0,
1521 WordBreak
|AlignTop
|AlignLeft
|DontClip
,
1523 update(messages_rect
);
1526 void NetHackQtMapWindow::updateTiles()
1528 NetHackQtGlyphs
& glyphs
= qt_settings
->glyphs();
1529 int gw
= glyphs
.width();
1530 int gh
= glyphs
.height();
1531 // Be exactly the size we want to be - full map...
1532 resize(COLNO
*gw
,ROWNO
*gh
);
1534 viewport
.verticalScrollBar()->setSteps(gh
,gh
);
1535 viewport
.horizontalScrollBar()->setSteps(gw
,gw
);
1537 viewport.setMaximumSize(
1538 gw*COLNO + viewport.verticalScrollBar()->width(),
1539 gh*ROWNO + viewport.horizontalScrollBar()->height()
1542 viewport
.updateScrollBars();
1545 change
.add(0,0,COLNO
,ROWNO
);
1546 delete rogue_font
; rogue_font
= 0;
1552 NetHackQtMapWindow::~NetHackQtMapWindow()
1554 // Remove from viewport porthole, since that is a destructible member.
1555 viewport
.removeChild(this);
1556 recreate(0,0,QPoint(0,0));
1559 QWidget
* NetHackQtMapWindow::Widget()
1564 void NetHackQtMapWindow::Scroll(int dx
, int dy
)
1566 if (viewport
.horizontalScrollBar()->isVisible()) {
1567 while (dx
<0) { viewport
.horizontalScrollBar()->subtractPage(); dx
++; }
1568 while (dx
>0) { viewport
.horizontalScrollBar()->addPage(); dx
--; }
1570 if (viewport
.verticalScrollBar()->isVisible()) {
1571 while (dy
<0) { viewport
.verticalScrollBar()->subtractPage(); dy
++; }
1572 while (dy
>0) { viewport
.verticalScrollBar()->addPage(); dy
--; }
1576 void NetHackQtMapWindow::Clear()
1578 unsigned short stone
=cmap_to_glyph(S_stone
);
1580 for (int j
=0; j
<ROWNO
; j
++) {
1581 for (int i
=0; i
<COLNO
; i
++) {
1587 change
.add(0,0,COLNO
,ROWNO
);
1590 void NetHackQtMapWindow::clickCursor()
1592 clicksink
.Put(cursor
.x(),cursor
.y(),CLICK_1
);
1596 void NetHackQtMapWindow::mousePressEvent(QMouseEvent
* event
)
1599 event
->pos().x()/qt_settings
->glyphs().width(),
1600 event
->pos().y()/qt_settings
->glyphs().height(),
1601 event
->button()==LeftButton
? CLICK_1
: CLICK_2
1608 const QPen
& nhcolor_to_pen(int c
)
1613 pen
[0] = QColor(24,24,24); // "black" on black
1615 pen
[2] = QColor(0,191,0);
1616 pen
[3] = QColor(127,127,0);
1618 pen
[5] = Qt::magenta
;
1621 pen
[8] = Qt::white
; // no color
1622 pen
[9] = QColor(255,127,0);
1623 pen
[10] = QColor(127,255,127);
1624 pen
[11] = Qt::yellow
;
1625 pen
[12] = QColor(127,127,255);
1626 pen
[13] = QColor(255,127,255);
1627 pen
[14] = QColor(127,255,255);
1628 pen
[15] = Qt::white
;
1629 pen
[16] = QColor(24,24,24); // "black" on black
1636 void NetHackQtMapWindow::paintEvent(QPaintEvent
* event
)
1638 QRect area
=event
->rect();
1641 QMAX(0,area
.left()/qt_settings
->glyphs().width()),
1642 QMAX(0,area
.top()/qt_settings
->glyphs().height()),
1643 QMIN(COLNO
-1,area
.right()/qt_settings
->glyphs().width()),
1644 QMIN(ROWNO
-1,area
.bottom()/qt_settings
->glyphs().height())
1649 painter
.begin(this);
1651 if (Is_rogue_level(&u
.uz
) || iflags
.wc_ascii_map
)
1653 // You enter a VERY primitive world!
1655 painter
.setClipRect( event
->rect() ); // (normally we don't clip)
1656 painter
.fillRect( event
->rect(), black
);
1658 if ( !rogue_font
) {
1661 QString fontfamily
= iflags
.wc_font_map
1662 ? iflags
.wc_font_map
: "Courier";
1664 if ( fontfamily
.right(5).lower() == "-bold" ) {
1665 fontfamily
.truncate(fontfamily
.length()-5);
1668 while ( pts
< 32 ) {
1669 QFont
f(fontfamily
, pts
, bold
? QFont::Bold
: QFont::Normal
);
1670 painter
.setFont(QFont(fontfamily
, pts
));
1671 QFontMetrics fm
= painter
.fontMetrics();
1672 if ( fm
.width("M") > qt_settings
->glyphs().width() )
1674 if ( fm
.height() > qt_settings
->glyphs().height() )
1678 rogue_font
= new QFont(fontfamily
,pts
-1);
1680 painter
.setFont(*rogue_font
);
1682 for (int j
=garea
.top(); j
<=garea
.bottom(); j
++) {
1683 for (int i
=garea
.left(); i
<=garea
.right(); i
++) {
1684 unsigned short g
=Glyph(i
,j
);
1689 painter
.setPen( green
);
1690 /* map glyph to character and color */
1691 (void)mapglyph(g
, &och
, &color
, &special
, i
, j
);
1694 painter
.setPen( nhcolor_to_pen(color
) );
1697 i
*qt_settings
->glyphs().width(),
1698 j
*qt_settings
->glyphs().height(),
1699 qt_settings
->glyphs().width(),
1700 qt_settings
->glyphs().height(),
1706 && ::iflags
.hilite_pet
1709 painter
.drawPixmap(QPoint(i
*qt_settings
->glyphs().width(),
1710 j
*qt_settings
->glyphs().height()),
1716 painter
.setFont(font());
1718 for (int j
=garea
.top(); j
<=garea
.bottom(); j
++) {
1719 for (int i
=garea
.left(); i
<=garea
.right(); i
++) {
1720 unsigned short g
=Glyph(i
,j
);
1721 qt_settings
->glyphs().drawCell(painter
, g
, i
, j
);
1724 && ::iflags
.hilite_pet
1727 painter
.drawPixmap(QPoint(i
*qt_settings
->glyphs().width(),
1728 j
*qt_settings
->glyphs().height()),
1735 if (garea
.contains(cursor
)) {
1736 if (Is_rogue_level(&u
.uz
)) {
1738 painter
.setPen( white
);
1740 painter
.setPen( green
); // REALLY primitive
1746 hp100
=u
.mhmax
? u
.mh
*100/u
.mhmax
: 100;
1748 hp100
=u
.uhpmax
? u
.uhp
*100/u
.uhpmax
: 100;
1751 if (hp100
> 75) painter
.setPen(white
);
1752 else if (hp100
> 50) painter
.setPen(yellow
);
1753 else if (hp100
> 25) painter
.setPen(QColor(0xff,0xbf,0x00)); // orange
1754 else if (hp100
> 10) painter
.setPen(red
);
1755 else painter
.setPen(magenta
);
1759 cursor
.x()*qt_settings
->glyphs().width(),cursor
.y()*qt_settings
->glyphs().height(),
1760 qt_settings
->glyphs().width(),qt_settings
->glyphs().height());
1763 if (area
.intersects(messages_rect
)) {
1764 painter
.setPen(black
);
1765 painter
.drawText(viewport
.contentsX()+1,viewport
.contentsY()+1,
1766 viewport
.width(),0, WordBreak
|AlignTop
|AlignLeft
|DontClip
, messages
);
1767 painter
.setPen(white
);
1768 painter
.drawText(viewport
.contentsX(),viewport
.contentsY(),
1769 viewport
.width(),0, WordBreak
|AlignTop
|AlignLeft
|DontClip
, messages
);
1775 void NetHackQtMapWindow::Display(bool block
)
1777 for (int i
=0; i
<change
.clusters(); i
++) {
1778 const QRect
& ch
=change
[i
];
1780 ch
.x()*qt_settings
->glyphs().width(),
1781 ch
.y()*qt_settings
->glyphs().height(),
1782 ch
.width()*qt_settings
->glyphs().width(),
1783 ch
.height()*qt_settings
->glyphs().height(),
1791 yn_function("Press a key when done viewing",0,'\0');
1795 void NetHackQtMapWindow::CursorTo(int x
,int y
)
1797 Changed(cursor
.x(),cursor
.y());
1800 Changed(cursor
.x(),cursor
.y());
1803 void NetHackQtMapWindow::PutStr(int attr
, const char* text
)
1805 puts("unexpected PutStr in MapWindow");
1808 void NetHackQtMapWindow::ClipAround(int x
,int y
)
1810 // Convert to pixel of center of tile
1811 x
=x
*qt_settings
->glyphs().width()+qt_settings
->glyphs().width()/2;
1812 y
=y
*qt_settings
->glyphs().height()+qt_settings
->glyphs().height()/2;
1814 // Then ensure that pixel is visible
1815 viewport
.center(x
,y
,0.45,0.45);
1818 void NetHackQtMapWindow::PrintGlyph(int x
,int y
,int glyph
)
1824 //void NetHackQtMapWindow::PrintGlyphCompose(int x,int y,int glyph1, int glyph2)
1826 // TODO: composed graphics
1829 void NetHackQtMapWindow::Changed(int x
, int y
)
1835 class NetHackQtScrollText
: public QTableView
{
1837 UData() : text(0), attr(0) { }
1838 ~UData() { if (text
) free(text
); }
1846 NetHackQtScrollText(int maxlength
) :
1848 maxitems(maxlength
),
1851 item_cycle(maxlength
)
1855 setCellHeight(fontMetrics().height());
1856 setBackgroundColor(white
);
1857 setTableFlags(Tbl_vScrollBar
1859 |Tbl_clipCellPainting
1860 |Tbl_smoothScrolling
);
1863 ~NetHackQtScrollText()
1867 void Scroll(int dx
, int dy
)
1869 setXOffset(xOffset()+dx
*viewWidth());
1870 setYOffset(yOffset()+dy
*viewHeight());
1873 void insertItem(int attr
, const char* text
)
1877 setAutoUpdate(FALSE
);
1880 if (count
<maxitems
) {
1885 first
=(first
+1)%maxitems
;
1888 item(i
).text
=strdup(text
);
1889 int w
=datumWidth(item(i
));
1891 if (w
> cellWidth()) {
1897 setAutoUpdate(TRUE
);
1899 if (viewHeight() >= totalHeight()-cellHeight()) {
1902 scroll(0,cellHeight());
1906 virtual void setFont(const QFont
& font
)
1908 QTableView::setFont(font
);
1909 setCellHeight(fontMetrics().height());
1916 return item_cycle
[(first
+i
)%maxitems
];
1921 QArray
<UData
> item_cycle
;
1923 int datumWidth(const UData
& uitem
)
1926 int width
=fontMetrics().width(uitem
.text
)+3;
1928 // XXX Too expensive to do properly, because
1929 // XXX we have to set the font of the widget
1930 // XXX just to get the font metrics information!
1931 // XXX Could hold a fake widget for that
1932 // XXX purpose, but this hack is less ugly.
1941 virtual void setupPainter(QPainter
*p
)
1943 // XXX This shouldn't be needed - we set the bg in the constructor.
1944 p
->setBackgroundColor(white
);
1947 virtual void paintCell(QPainter
*p
, int row
, int col
)
1950 UData
& uitem
=item(row
);
1952 if (!sel
&& row
< count
-uncleared
) {
1953 p
->setPen(darkGray
);
1960 QFont
bold(font().family(),font().pointSize(),QFont::Bold
);
1964 p
->drawText(3, 0, cellWidth(), cellHeight(),
1965 AlignLeft
|AlignVCenter
, uitem
.text
);
1973 NetHackQtMessageWindow::NetHackQtMessageWindow() :
1974 list(new NetHackQtScrollText(::iflags
.msg_history
))
1976 ::iflags
.window_inited
= 1;
1978 connect(qt_settings
,SIGNAL(fontChanged()),this,SLOT(updateFont()));
1982 NetHackQtMessageWindow::~NetHackQtMessageWindow()
1984 ::iflags
.window_inited
= 0;
1988 QWidget
* NetHackQtMessageWindow::Widget() { return list
; }
1990 void NetHackQtMessageWindow::setMap(NetHackQtMapWindow
* m
)
1996 void NetHackQtMessageWindow::updateFont()
1998 list
->setFont(qt_settings
->normalFont());
2000 map
->setFont(qt_settings
->normalFont());
2003 void NetHackQtMessageWindow::Scroll(int dx
, int dy
)
2005 list
->Scroll(dx
,dy
);
2008 void NetHackQtMessageWindow::Clear()
2011 map
->clearMessages();
2012 if (list
->uncleared
) {
2019 void NetHackQtMessageWindow::Display(bool block
)
2027 void NetHackQtMessageWindow::PutStr(int attr
, const char* text
)
2030 play_sound_for_message(text
);
2035 list
->insertItem(attr
,text
);
2037 // Force scrollbar to bottom
2038 // XXX list->setTopItem(list->count());
2041 map
->putMessage(attr
, text
);
2046 NetHackQtLabelledIcon::NetHackQtLabelledIcon(QWidget
* parent
, const char* l
) :
2051 label(new QLabel(l
,this)),
2056 NetHackQtLabelledIcon::NetHackQtLabelledIcon(QWidget
* parent
, const char* l
, const QPixmap
& i
) :
2061 label(new QLabel(l
,this)),
2062 icon(new QLabel(this))
2067 void NetHackQtLabelledIcon::initHighlight()
2069 const QPalette
& pal
=palette();
2070 const QColorGroup
& pa
=pal
.normal();
2071 //QColorGroup good(white,darkGreen,pa.light(),pa.dark(),pa.mid(),white,pa.base());
2072 QColorGroup
good(black
,green
,pa
.light(),pa
.dark(),pa
.mid(),black
,pa
.base());
2073 QColorGroup
bad(white
,red
,pa
.light(),pa
.dark(),pa
.mid(),white
,pa
.base());
2075 hl_good
.setNormal(good
);
2076 hl_good
.setActive(good
);
2078 hl_bad
.setNormal(bad
);
2079 hl_bad
.setActive(bad
);
2082 void NetHackQtLabelledIcon::setLabel(const char* t
, bool lower
)
2085 label
=new QLabel(this);
2086 label
->setFont(font());
2089 if (0!=strcmp(label
->text(),t
)) {
2091 highlight(lower
==low_is_good
? hl_good
: hl_bad
);
2094 void NetHackQtLabelledIcon::setLabel(const char* t
, long v
, long cv
, const char* tail
)
2098 Sprintf(buf
,"%s%s",t
,tail
);
2100 Sprintf(buf
,"%s%ld%s",t
,v
,tail
);
2102 setLabel(buf
,cv
<prev_value
);
2105 void NetHackQtLabelledIcon::setLabel(const char* t
, long v
, const char* tail
)
2107 setLabel(t
,v
,v
,tail
);
2109 void NetHackQtLabelledIcon::setIcon(const QPixmap
& i
)
2111 if (icon
) icon
->setPixmap(i
);
2112 else { icon
=new QLabel(this); icon
->setPixmap(i
); resizeEvent(0); }
2113 icon
->resize(i
.width(),i
.height());
2115 void NetHackQtLabelledIcon::setFont(const QFont
& f
)
2117 QWidget::setFont(f
);
2118 if (label
) label
->setFont(f
);
2120 void NetHackQtLabelledIcon::show()
2122 #if QT_VERSION >= 300
2130 void NetHackQtLabelledIcon::highlightWhenChanging()
2134 void NetHackQtLabelledIcon::lowIsGood()
2138 void NetHackQtLabelledIcon::dissipateHighlight()
2146 void NetHackQtLabelledIcon::highlight(const QPalette
& hl
)
2148 if (label
) { // Surely it is?!
2149 if (turn_count
>=0) {
2150 label
->setPalette(hl
);
2152 // `4' includes this turn, so dissipates after
2153 // 3 more keypresses.
2155 label
->setPalette(palette());
2159 void NetHackQtLabelledIcon::unhighlight()
2161 if (label
) { // Surely it is?!
2162 label
->setPalette(palette());
2165 void NetHackQtLabelledIcon::resizeEvent(QResizeEvent
*)
2169 //int labw=label ? label->fontMetrics().width(label->text()) : 0;
2170 int labh
=label
? label
->fontMetrics().height() : 0;
2171 int icoh
=icon
? icon
->height() : 0;
2173 int icoy
=(h
>height() ? height()-labh
-icoh
: height()/2-h
/2);
2176 icon
->setGeometry(0,icoy
,width(),icoh
);
2179 label
->setGeometry(0,laby
,width(),labh
);
2183 void NetHackQtLabelledIcon::setAlignments()
2185 if (label
) label
->setAlignment(AlignHCenter
|AlignVCenter
);
2186 if (icon
) icon
->setAlignment(AlignHCenter
|AlignVCenter
);
2190 tryload(QPixmap
& pm
, const char* fn
)
2194 msg
.sprintf("Cannot load \"%s\"", fn
);
2195 QMessageBox::warning(0, "IO Error", msg
);
2199 NetHackQtStatusWindow::NetHackQtStatusWindow() :
2201 // Alignment needs -2 init value, because -1 is an alignment.
2202 // Armor Class is an schar, so 256 is out of range.
2203 // Blank value is 0 and should never change.
2204 name(this,"(name)"),
2205 dlevel(this,"(dlevel)"),
2213 hp(this,"Hit Points"),
2214 power(this,"Power"),
2215 ac(this,"Armour Class"),
2216 level(this,"Level"),
2217 exp(this,"Experience"),
2218 align(this,"Alignment"),
2220 score(this,"Score"),
2222 confused(this,"Confused"),
2223 sick_fp(this,"Sick"),
2224 sick_il(this,"Ill"),
2225 blind(this,"Blind"),
2226 stunned(this,"Stunned"),
2227 hallu(this,"Hallu"),
2234 p_str
= QPixmap(str_xpm
);
2235 p_str
= QPixmap(str_xpm
);
2236 p_dex
= QPixmap(dex_xpm
);
2237 p_con
= QPixmap(cns_xpm
);
2238 p_int
= QPixmap(int_xpm
);
2239 p_wis
= QPixmap(wis_xpm
);
2240 p_cha
= QPixmap(cha_xpm
);
2242 p_chaotic
= QPixmap(chaotic_xpm
);
2243 p_neutral
= QPixmap(neutral_xpm
);
2244 p_lawful
= QPixmap(lawful_xpm
);
2246 p_satiated
= QPixmap(satiated_xpm
);
2247 p_hungry
= QPixmap(hungry_xpm
);
2249 p_confused
= QPixmap(confused_xpm
);
2250 p_sick_fp
= QPixmap(sick_fp_xpm
);
2251 p_sick_il
= QPixmap(sick_il_xpm
);
2252 p_blind
= QPixmap(blind_xpm
);
2253 p_stunned
= QPixmap(stunned_xpm
);
2254 p_hallu
= QPixmap(hallu_xpm
);
2256 p_encumber
[0] = QPixmap(slt_enc_xpm
);
2257 p_encumber
[1] = QPixmap(mod_enc_xpm
);
2258 p_encumber
[2] = QPixmap(hvy_enc_xpm
);
2259 p_encumber
[3] = QPixmap(ext_enc_xpm
);
2260 p_encumber
[4] = QPixmap(ovr_enc_xpm
);
2265 intel
.setIcon(p_int
);
2269 align
.setIcon(p_neutral
);
2270 hunger
.setIcon(p_hungry
);
2272 confused
.setIcon(p_confused
);
2273 sick_fp
.setIcon(p_sick_fp
);
2274 sick_il
.setIcon(p_sick_il
);
2275 blind
.setIcon(p_blind
);
2276 stunned
.setIcon(p_stunned
);
2277 hallu
.setIcon(p_hallu
);
2279 encumber
.setIcon(p_encumber
[0]);
2281 hline1
.setFrameStyle(QFrame::HLine
|QFrame::Sunken
);
2282 hline2
.setFrameStyle(QFrame::HLine
|QFrame::Sunken
);
2283 hline3
.setFrameStyle(QFrame::HLine
|QFrame::Sunken
);
2284 hline1
.setLineWidth(1);
2285 hline2
.setLineWidth(1);
2286 hline3
.setLineWidth(1);
2288 connect(qt_settings
,SIGNAL(fontChanged()),this,SLOT(doUpdate()));
2292 void NetHackQtStatusWindow::doUpdate()
2294 const QFont
& large
=qt_settings
->largeFont();
2295 name
.setFont(large
);
2296 dlevel
.setFont(large
);
2298 const QFont
& normal
=qt_settings
->normalFont();
2299 str
.setFont(normal
);
2300 dex
.setFont(normal
);
2301 con
.setFont(normal
);
2302 intel
.setFont(normal
);
2303 wis
.setFont(normal
);
2304 cha
.setFont(normal
);
2305 gold
.setFont(normal
);
2307 power
.setFont(normal
);
2309 level
.setFont(normal
);
2310 exp
.setFont(normal
);
2311 align
.setFont(normal
);
2312 time
.setFont(normal
);
2313 score
.setFont(normal
);
2314 hunger
.setFont(normal
);
2315 confused
.setFont(normal
);
2316 sick_fp
.setFont(normal
);
2317 sick_il
.setFont(normal
);
2318 blind
.setFont(normal
);
2319 stunned
.setFont(normal
);
2320 hallu
.setFont(normal
);
2321 encumber
.setFont(normal
);
2326 QWidget
* NetHackQtStatusWindow::Widget() { return this; }
2328 void NetHackQtStatusWindow::Clear()
2331 void NetHackQtStatusWindow::Display(bool block
)
2334 void NetHackQtStatusWindow::CursorTo(int,int y
)
2338 void NetHackQtStatusWindow::PutStr(int attr
, const char* text
)
2340 // do a complete update when line 0 is done (as per X11 fancy status)
2341 if (cursy
==0) updateStats();
2344 void NetHackQtStatusWindow::resizeEvent(QResizeEvent
*)
2346 const float SP_name
=0.13; // <Name> the <Class> (large)
2347 const float SP_dlev
=0.13; // Level 3 in The Dungeons of Doom (large)
2348 const float SP_atr1
=0.25; // STR DEX CON INT WIS CHA
2349 const float SP_hln1
=0.02; // ---
2350 const float SP_atr2
=0.09; // Au HP PW AC LVL EXP
2351 const float SP_hln2
=0.02; // ---
2352 const float SP_time
=0.09; // time score
2353 const float SP_hln3
=0.02; // ---
2354 const float SP_stat
=0.25; // Alignment, Poisoned, Hungry, Sick, etc.
2359 int iw
; // Width of an item across line
2360 int lh
; // Height of a line of values
2363 name
.setGeometry(0,0,width(),lh
); y
+=lh
;
2365 dlevel
.setGeometry(0,y
,width(),lh
); y
+=lh
;
2368 hline1
.setGeometry(0,y
,width(),lh
); y
+=lh
;
2372 str
.setGeometry(x
,y
,iw
,lh
); x
+=iw
;
2373 dex
.setGeometry(x
,y
,iw
,lh
); x
+=iw
;
2374 con
.setGeometry(x
,y
,iw
,lh
); x
+=iw
;
2375 intel
.setGeometry(x
,y
,iw
,lh
); x
+=iw
;
2376 wis
.setGeometry(x
,y
,iw
,lh
); x
+=iw
;
2377 cha
.setGeometry(x
,y
,iw
,lh
); x
+=iw
;
2381 hline2
.setGeometry(0,y
,width(),lh
); y
+=lh
;
2385 gold
.setGeometry(x
,y
,iw
,lh
); x
+=iw
;
2386 hp
.setGeometry(x
,y
,iw
,lh
); x
+=iw
;
2387 power
.setGeometry(x
,y
,iw
,lh
); x
+=iw
;
2388 ac
.setGeometry(x
,y
,iw
,lh
); x
+=iw
;
2389 level
.setGeometry(x
,y
,iw
,lh
); x
+=iw
;
2390 exp
.setGeometry(x
,y
,iw
,lh
); x
+=iw
;
2394 hline3
.setGeometry(0,y
,width(),lh
); y
+=lh
;
2397 iw
=width()/3; x
+=iw
/2;
2398 time
.setGeometry(x
,y
,iw
,lh
); x
+=iw
;
2399 score
.setGeometry(x
,y
,iw
,lh
); x
+=iw
;
2404 align
.setGeometry(x
,y
,iw
,lh
); x
+=iw
;
2405 hunger
.setGeometry(x
,y
,iw
,lh
); x
+=iw
;
2406 confused
.setGeometry(x
,y
,iw
,lh
); x
+=iw
;
2407 sick_fp
.setGeometry(x
,y
,iw
,lh
); x
+=iw
;
2408 sick_il
.setGeometry(x
,y
,iw
,lh
); x
+=iw
;
2409 blind
.setGeometry(x
,y
,iw
,lh
); x
+=iw
;
2410 stunned
.setGeometry(x
,y
,iw
,lh
); x
+=iw
;
2411 hallu
.setGeometry(x
,y
,iw
,lh
); x
+=iw
;
2412 encumber
.setGeometry(x
,y
,iw
,lh
); x
+=iw
;
2418 * Set all widget values to a null string. This is used after all spacings
2419 * have been calculated so that when the window is popped up we don't get all
2420 * kinds of funny values being displayed.
2422 void NetHackQtStatusWindow::nullOut()
2426 void NetHackQtStatusWindow::fadeHighlighting()
2428 name
.dissipateHighlight();
2429 dlevel
.dissipateHighlight();
2431 str
.dissipateHighlight();
2432 dex
.dissipateHighlight();
2433 con
.dissipateHighlight();
2434 intel
.dissipateHighlight();
2435 wis
.dissipateHighlight();
2436 cha
.dissipateHighlight();
2438 gold
.dissipateHighlight();
2439 hp
.dissipateHighlight();
2440 power
.dissipateHighlight();
2441 ac
.dissipateHighlight();
2442 level
.dissipateHighlight();
2443 exp
.dissipateHighlight();
2444 align
.dissipateHighlight();
2446 time
.dissipateHighlight();
2447 score
.dissipateHighlight();
2449 hunger
.dissipateHighlight();
2450 confused
.dissipateHighlight();
2451 sick_fp
.dissipateHighlight();
2452 sick_il
.dissipateHighlight();
2453 blind
.dissipateHighlight();
2454 stunned
.dissipateHighlight();
2455 hallu
.dissipateHighlight();
2456 encumber
.dissipateHighlight();
2460 * Update the displayed status. The current code in botl.c updates
2461 * two lines of information. Both lines are always updated one after
2462 * the other. So only do our update when we update the second line.
2464 * Information on the first line:
2465 * name, attributes, alignment, score
2467 * Information on the second line:
2468 * dlvl, gold, hp, power, ac, {level & exp or HD **}
2469 * status (hunger, conf, halu, stun, sick, blind), time, encumbrance
2471 * [**] HD is shown instead of level and exp if mtimedone is non-zero.
2473 void NetHackQtStatusWindow::updateStats()
2475 if (!parentWidget()) return;
2479 if (cursy
!= 0) return; /* do a complete update when line 0 is done */
2481 if (ACURR(A_STR
) > 118) {
2482 Sprintf(buf
,"STR:%d",ACURR(A_STR
)-100);
2483 } else if (ACURR(A_STR
)==118) {
2484 Sprintf(buf
,"STR:18/**");
2485 } else if(ACURR(A_STR
) > 18) {
2486 Sprintf(buf
,"STR:18/%02d",ACURR(A_STR
)-18);
2488 Sprintf(buf
,"STR:%d",ACURR(A_STR
));
2490 str
.setLabel(buf
,NetHackQtLabelledIcon::NoNum
,ACURR(A_STR
));
2492 dex
.setLabel("DEX:",(long)ACURR(A_DEX
));
2493 con
.setLabel("CON:",(long)ACURR(A_CON
));
2494 intel
.setLabel("INT:",(long)ACURR(A_INT
));
2495 wis
.setLabel("WIS:",(long)ACURR(A_WIS
));
2496 cha
.setLabel("CHA:",(long)ACURR(A_CHA
));
2497 const char* hung
=hu_stat
[u
.uhs
];
2501 hunger
.setIcon(u
.uhs
? p_hungry
: p_satiated
);
2502 hunger
.setLabel(hung
);
2505 if (Confusion
) confused
.show(); else confused
.hide();
2507 if (u
.usick_type
& SICK_VOMITABLE
) {
2512 if (u
.usick_type
& SICK_NONVOMITABLE
) {
2521 if (Blind
) blind
.show(); else blind
.hide();
2522 if (Stunned
) stunned
.show(); else stunned
.hide();
2523 if (Hallucination
) hallu
.show(); else hallu
.hide();
2524 const char* enc
=enc_stat
[near_capacity()];
2525 if (enc
[0]==' ' || !enc
[0]) {
2528 encumber
.setIcon(p_encumber
[near_capacity()-1]);
2529 encumber
.setLabel(enc
);
2532 Strcpy(buf
, plname
);
2533 if ('a' <= buf
[0] && buf
[0] <= 'z') buf
[0] += 'A'-'a';
2534 Strcat(buf
, " the ");
2539 Strcpy(mname
, mons
[u
.umonnum
].mname
);
2540 while(mname
[k
] != 0) {
2541 if ((k
== 0 || (k
> 0 && mname
[k
-1] == ' '))
2542 && 'a' <= mname
[k
] && mname
[k
] <= 'z')
2544 mname
[k
] += 'A' - 'a';
2550 Strcat(buf
, rank_of(u
.ulevel
, pl_character
[0], ::flags
.female
));
2552 name
.setLabel(buf
,NetHackQtLabelledIcon::NoNum
,u
.ulevel
);
2554 if (describe_level(buf
)) {
2555 dlevel
.setLabel(buf
,(bool)TRUE
);
2557 Sprintf(buf
, "%s, level ", dungeons
[u
.uz
.dnum
].dname
);
2558 dlevel
.setLabel(buf
,(long)depth(&u
.uz
));
2561 gold
.setLabel("Au:", money_cnt(invent
));
2563 // You're a monster!
2565 Sprintf(buf
, "/%d", u
.mhmax
);
2566 hp
.setLabel("HP:",u
.mh
> 0 ? u
.mh
: 0,buf
);
2567 level
.setLabel("HD:",(long)mons
[u
.umonnum
].mlevel
);
2571 Sprintf(buf
, "/%d", u
.uhpmax
);
2572 hp
.setLabel("HP:",u
.uhp
> 0 ? u
.uhp
: 0,buf
);
2573 level
.setLabel("Level:",(long)u
.ulevel
);
2575 Sprintf(buf
, "/%d", u
.uenmax
);
2576 power
.setLabel("Pow:",u
.uen
,buf
);
2577 ac
.setLabel("AC:",(long)u
.uac
);
2578 if (::flags
.showexp
) {
2579 exp
.setLabel("Exp:",(long)u
.uexp
);
2584 if (u
.ualign
.type
==A_CHAOTIC
) {
2585 align
.setIcon(p_chaotic
);
2586 align
.setLabel("Chaotic");
2587 } else if (u
.ualign
.type
==A_NEUTRAL
) {
2588 align
.setIcon(p_neutral
);
2589 align
.setLabel("Neutral");
2591 align
.setIcon(p_lawful
);
2592 align
.setLabel("Lawful");
2595 if (::flags
.time
) time
.setLabel("Time:",(long)moves
);
2596 else time
.setLabel("");
2597 #ifdef SCORE_ON_BOTL
2598 if (::flags
.showscore
) {
2599 score
.setLabel("Score:",(long)botl_score());
2610 name
.highlightWhenChanging();
2611 dlevel
.highlightWhenChanging();
2613 str
.highlightWhenChanging();
2614 dex
.highlightWhenChanging();
2615 con
.highlightWhenChanging();
2616 intel
.highlightWhenChanging();
2617 wis
.highlightWhenChanging();
2618 cha
.highlightWhenChanging();
2620 gold
.highlightWhenChanging();
2621 hp
.highlightWhenChanging();
2622 power
.highlightWhenChanging();
2623 ac
.highlightWhenChanging(); ac
.lowIsGood();
2624 level
.highlightWhenChanging();
2625 exp
.highlightWhenChanging();
2626 align
.highlightWhenChanging();
2628 //time.highlightWhenChanging();
2629 score
.highlightWhenChanging();
2631 hunger
.highlightWhenChanging();
2632 confused
.highlightWhenChanging();
2633 sick_fp
.highlightWhenChanging();
2634 sick_il
.highlightWhenChanging();
2635 blind
.highlightWhenChanging();
2636 stunned
.highlightWhenChanging();
2637 hallu
.highlightWhenChanging();
2638 encumber
.highlightWhenChanging();
2643 * Turn off hilighted status values after a certain amount of turns.
2645 void NetHackQtStatusWindow::checkTurnEvents()
2651 NetHackQtMenuDialog::NetHackQtMenuDialog() :
2652 QDialog(qApp
->mainWidget(),0,FALSE
)
2656 void NetHackQtMenuDialog::resizeEvent(QResizeEvent
*)
2661 void NetHackQtMenuDialog::Accept()
2666 void NetHackQtMenuDialog::Reject()
2671 void NetHackQtMenuDialog::SetResult(int r
)
2676 void NetHackQtMenuDialog::done(int i
)
2682 // Table view columns:
2684 // [pick-count] [accel] [glyph] [string]
2686 // Maybe accel should be near string. We'll see.
2687 // pick-count normally blank.
2688 // double-clicking or click-on-count gives pop-up entry
2689 // string is green when selected
2691 NetHackQtMenuWindow::NetHackQtMenuWindow(NetHackQtKeyBuffer
& ks
) :
2694 dialog(new NetHackQtMenuDialog()),
2699 setCellHeight(QMAX(qt_settings
->glyphs().height()+1,fontMetrics().height()));
2700 setBackgroundColor(lightGray
);
2701 setFrameStyle(Panel
|Sunken
);
2704 ok
=new QPushButton("Ok",dialog
);
2705 connect(ok
,SIGNAL(clicked()),dialog
,SLOT(accept()));
2707 cancel
=new QPushButton("Cancel",dialog
);
2708 connect(cancel
,SIGNAL(clicked()),dialog
,SLOT(reject()));
2710 all
=new QPushButton("All",dialog
);
2711 connect(all
,SIGNAL(clicked()),this,SLOT(All()));
2713 none
=new QPushButton("None",dialog
);
2714 connect(none
,SIGNAL(clicked()),this,SLOT(ChooseNone()));
2716 invert
=new QPushButton("Invert",dialog
);
2717 connect(invert
,SIGNAL(clicked()),this,SLOT(Invert()));
2719 search
=new QPushButton("Search",dialog
);
2720 connect(search
,SIGNAL(clicked()),this,SLOT(Search()));
2722 QPoint
pos(0,ok
->height());
2723 recreate(dialog
,0,pos
);
2724 prompt
.recreate(dialog
,0,pos
);
2726 setBackgroundColor(lightGray
);
2728 connect(dialog
,SIGNAL(Resized()),this,SLOT(Layout()));
2730 setTableFlags(Tbl_autoHScrollBar
|Tbl_autoVScrollBar
2731 |Tbl_smoothScrolling
|Tbl_clipCellPainting
);
2732 setFocusPolicy(StrongFocus
);
2735 NetHackQtMenuWindow::~NetHackQtMenuWindow()
2737 // Remove from dialog before we destruct it
2738 recreate(0,0,QPoint(0,0));
2742 void NetHackQtMenuWindow::focusInEvent(QFocusEvent
*)
2744 // Don't repaint at all, since nothing is using the focus colour
2746 void NetHackQtMenuWindow::focusOutEvent(QFocusEvent
*)
2748 // Don't repaint at all, since nothing is using the focus colour
2751 int NetHackQtMenuWindow::cellWidth(int col
)
2755 return fontMetrics().width("All ");
2757 return fontMetrics().width(" m ");
2759 return qt_settings
->glyphs().width();
2763 impossible("Extra column (#%d) in MenuWindow",col
);
2767 QWidget
* NetHackQtMenuWindow::Widget() { return dialog
; }
2769 void NetHackQtMenuWindow::StartMenu()
2771 setNumRows((itemcount
=0));
2778 NetHackQtMenuWindow::MenuItem::MenuItem() :
2783 NetHackQtMenuWindow::MenuItem::~MenuItem()
2785 if (str
) free((void*)str
);
2788 #define STR_MARGIN 4
2790 void NetHackQtMenuWindow::AddMenu(int glyph
, const ANY_P
* identifier
,
2791 char ch
, char gch
, int attr
, const char* str
, bool presel
)
2793 if (!ch
&& identifier
->a_void
!=0) {
2794 // Supply a keyboard accelerator. Limited supply.
2795 static char accel
[]="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
2796 if (accel
[next_accel
]) {
2797 ch
=accel
[next_accel
++];
2801 if ((int)item
.size() < itemcount
+1) {
2802 item
.resize(itemcount
*4+10);
2804 item
[itemcount
].glyph
=glyph
;
2805 item
[itemcount
].identifier
=*identifier
;
2806 item
[itemcount
].ch
=ch
;
2807 item
[itemcount
].attr
=attr
;
2808 item
[itemcount
].str
=strdup(str
);
2809 item
[itemcount
].selected
=presel
;
2810 item
[itemcount
].count
=-1;
2813 str_fixed
=str_fixed
|| strstr(str
," ");
2814 if (glyph
!=NO_GLYPH
) has_glyphs
=TRUE
;
2816 void NetHackQtMenuWindow::EndMenu(const char* p
)
2818 prompt
.setText(p
? p
: "");
2820 void NetHackQtMenuWindow::Layout()
2822 int butw
=totalWidth()/6; // 6 buttons
2823 int buth
=fontMetrics().height()+8; // 8 for spacing & mitres
2824 int prompth
=(prompt
.text().isNull() ? 0 : buth
);
2826 prompt
.setGeometry(6,buth
,dialog
->width()-6,prompth
);
2827 int h
=dialog
->height()-buth
-prompth
;
2828 setGeometry(0,buth
+prompth
, dialog
->width(), h
);
2830 // Below, we take care to use up full width
2832 ok
->setGeometry(x
,0,butw
,buth
); x
+=butw
; butw
=(dialog
->width()-x
)/5;
2833 cancel
->setGeometry(x
,0,butw
,buth
); x
+=butw
; butw
=(dialog
->width()-x
)/4;
2834 all
->setGeometry(x
,0,butw
,buth
); x
+=butw
; butw
=(dialog
->width()-x
)/3;
2835 none
->setGeometry(x
,0,butw
,buth
); x
+=butw
; butw
=(dialog
->width()-x
)/2;
2836 invert
->setGeometry(x
,0,butw
,buth
); x
+=butw
; butw
=(dialog
->width()-x
)/1;
2837 search
->setGeometry(x
,0,butw
,buth
);
2840 int NetHackQtMenuWindow::SelectMenu(int h
, MENU_ITEM_P
**menu_list
)
2843 qt_settings
->normalFixedFont() : qt_settings
->normalFont());
2845 for (int i
=0; i
<itemcount
; i
++) {
2846 str_width
=QMAX(str_width
,
2847 STR_MARGIN
+fontMetrics().width(item
[i
].str
));
2850 setCellHeight(QMAX(qt_settings
->glyphs().height()+1,fontMetrics().height()));
2851 setNumRows(itemcount
);
2853 int buth
=fontMetrics().height()+8; // 8 for spacing & mitres
2857 ok
->setEnabled(how
!=PICK_ONE
);ok
->setDefault(how
!=PICK_ONE
);
2858 cancel
->setEnabled(TRUE
);
2859 all
->setEnabled(how
==PICK_ANY
);
2860 none
->setEnabled(how
==PICK_ANY
);
2861 invert
->setEnabled(how
==PICK_ANY
);
2862 search
->setEnabled(how
!=PICK_NONE
);
2864 dialog
->SetResult(-1);
2866 // 20 allows for scrollbar or spacing
2867 // 4 for frame borders
2868 int mh
= QApplication::desktop()->height()*3/5;
2869 if ( qt_compact_mode
&& totalHeight() > mh
) {
2870 // big, so make it fill
2871 dialog
->showMaximized();
2873 dialog
->resize(totalWidth()+20,
2874 QMIN(totalHeight(), mh
)+buth
+4+(prompt
.text().isNull() ? 0 : buth
));
2875 if ( dialog
->width() > QApplication::desktop()->width() )
2876 dialog
->resize(QApplication::desktop()->width(),dialog
->height()+16);
2877 centerOnMain(dialog
);
2882 while (dialog
->result()<0) {
2883 // changed the defaults below to the values in wintype.h 000119 - azy
2884 if (!keysource
.Empty()) {
2885 char k
=keysource
.GetAscii();
2886 k
=map_menu_cmd(k
); /* added 000119 - azy */
2889 else if (k
=='\r' || k
=='\n' || k
==' ')
2891 else if (k
==MENU_SEARCH
)
2893 else if (k
==MENU_SELECT_ALL
)
2895 else if (k
==MENU_INVERT_ALL
)
2897 else if (k
==MENU_UNSELECT_ALL
)
2900 for (int i
=0; i
<itemcount
; i
++) {
2906 if (dialog
->result()<0)
2909 //if ( (nhid != WIN_INVEN || !flags.perm_invent) ) // doesn't work yet
2913 int result
=dialog
->result();
2915 // Consume ^M (which QDialog steals for default button)
2916 while (!keysource
.Empty() &&
2917 (keysource
.TopAscii()=='\n' || keysource
.TopAscii()=='\r'))
2918 keysource
.GetAscii();
2922 return result
==0 ? -1 : 0;
2925 if (how
==PICK_ONE
) {
2927 for (i
=0; i
<itemcount
&& !item
[i
].selected
; i
++)
2930 *menu_list
=(MENU_ITEM_P
*)malloc(sizeof(MENU_ITEM_P
));
2931 (*menu_list
)[0].item
=item
[i
].identifier
;
2932 (*menu_list
)[0].count
=item
[i
].count
;
2939 for (int i
=0; i
<itemcount
; i
++)
2940 if (item
[i
].selected
) count
++;
2942 *menu_list
=(MENU_ITEM_P
*)malloc(count
*sizeof(MENU_ITEM_P
));
2944 for (int i
=0; i
<itemcount
; i
++) {
2945 if (item
[i
].selected
) {
2946 (*menu_list
)[j
].item
=item
[i
].identifier
;
2947 (*menu_list
)[j
].count
=item
[i
].count
;
2961 void NetHackQtMenuWindow::keyPressEvent(QKeyEvent
* event
)
2963 if (viewHeight() < totalHeight() && !(event
->state()&ShiftButton
)) {
2964 if (event
->key()==Key_Prior
) {
2965 setYOffset(yOffset()-viewHeight());
2966 } else if (event
->key()==Key_Next
) {
2967 setYOffset(yOffset()+viewHeight());
2976 void NetHackQtMenuWindow::All()
2978 for (int i
=0; i
<itemcount
; i
++) {
2979 if (!item
[i
].selected
) ToggleSelect(i
);
2982 void NetHackQtMenuWindow::ChooseNone()
2984 for (int i
=0; i
<itemcount
; i
++) {
2985 if (item
[i
].selected
) ToggleSelect(i
);
2989 void NetHackQtMenuWindow::Invert()
2991 for (int i
=0; i
<itemcount
; i
++) {
2996 void NetHackQtMenuWindow::Search()
2998 NetHackQtStringRequestor
requestor(keysource
,"Search for:");
3000 if (requestor
.Get(line
)) {
3001 for (int i
=0; i
<itemcount
; i
++) {
3002 if (strstr(item
[i
].str
,line
))
3008 void NetHackQtMenuWindow::ToggleSelect(int i
)
3010 if (item
[i
].Selectable()) {
3011 item
[i
].selected
= !item
[i
].selected
;
3012 if ( !item
[i
].selected
)
3015 if (how
==PICK_ONE
) {
3022 void NetHackQtMenuWindow::paintCell(QPainter
* painter
, int row
, int col
)
3024 // [pick-count] [accel] [glyph] [string]
3026 MenuItem
& i
= item
[row
];
3028 painter
->setPen(black
);
3029 painter
->setFont(font());
3032 painter
->setPen(darkGreen
);
3037 if ( i
.ch
|| i
.attr
!=ATR_INVERSE
) {
3039 if ( i
.selected
&& i
.count
== -1 ) {
3040 if ( i
.str
[0]>='0' && i
.str
[0]<='9' )
3044 } else if ( i
.count
<0 ) {
3047 text
.sprintf("%d",i
.count
);
3049 painter
->drawText(0,0,cellWidth(col
),cellHeight(),
3050 AlignHCenter
|AlignVCenter
,text
);
3053 if ((signed char)i
.ch
>= 0) {
3054 char text
[2]={i
.ch
,0};
3055 painter
->drawText(0,0,cellWidth(col
),cellHeight(),
3056 AlignHCenter
|AlignVCenter
,text
);
3059 if (i
.glyph
!=NO_GLYPH
) {
3060 // Centered in height
3061 int y
=(cellHeight()-qt_settings
->glyphs().height())/2;
3063 qt_settings
->glyphs().drawGlyph(*painter
, i
.glyph
, 0, y
);
3066 // XXX should qt_settings have ALL the various fonts
3067 QFont newfont
=font();
3072 newfont
.setUnderline(TRUE
);
3073 break; case ATR_BOLD
:
3074 painter
->setPen(red
);
3075 break; case ATR_BLINK
:
3076 newfont
.setItalic(TRUE
);
3077 break; case ATR_INVERSE
:
3078 newfont
=qt_settings
->largeFont();
3079 newfont
.setWeight(QFont::Bold
);
3082 painter
->setPen(blue
);
3084 painter
->setPen(darkBlue
);
3088 painter
->setFont(newfont
);
3090 painter
->drawText(STR_MARGIN
,0,cellWidth(col
),cellHeight(),
3091 AlignLeft
|AlignVCenter
,i
.str
);
3095 void NetHackQtMenuWindow::mousePressEvent(QMouseEvent
* event
)
3097 int col
=findCol(event
->pos().x());
3098 int row
=findRow(event
->pos().y());
3100 if (col
<0 || row
<0 || !item
[row
].Selectable()) return;
3102 if (how
!=PICK_NONE
) {
3105 NetHackQtStringRequestor
requestor(keysource
,"Count:");
3108 if (item
[row
].count
>0)
3109 Sprintf(buf
,"%d", item
[row
].count
);
3113 requestor
.SetDefault(buf
);
3114 if (requestor
.Get(buf
)) {
3115 item
[row
].count
=atoi(buf
);
3116 if (item
[row
].count
==0) {
3118 if (item
[row
].selected
) ToggleSelect(row
);
3120 if (!item
[row
].selected
) ToggleSelect(row
);
3126 was_sel
=item
[row
].selected
;
3133 void NetHackQtMenuWindow::mouseReleaseEvent(QMouseEvent
* event
)
3142 void NetHackQtMenuWindow::mouseMoveEvent(QMouseEvent
* event
)
3145 int col
=findCol(event
->pos().x());
3146 int row
=findRow(event
->pos().y());
3148 if (row
>=0 && col
>=0) {
3150 // reset to initial state
3151 if (item
[pressed
].selected
!=was_sel
)
3152 ToggleSelect(pressed
);
3154 // reset to new state
3155 if (item
[pressed
].selected
==was_sel
)
3156 ToggleSelect(pressed
);
3163 class NetHackQtTextListBox
: public QListBox
{
3165 NetHackQtTextListBox(QWidget
* parent
) : QListBox(parent
) { }
3170 return contentsWidth();
3175 return contentsHeight();
3178 virtual void setFont(const QFont
&font
)
3180 QListBox::setFont(font
);
3182 void keyPressEvent(QKeyEvent
* e
)
3184 QListBox::keyPressEvent(e
);
3189 QPixmap
* NetHackQtRIP::pixmap
=0;
3191 NetHackQtRIP::NetHackQtRIP(QWidget
* parent
) :
3196 tryload(*pixmap
, "rip.xpm");
3199 resize(pixmap
->width(),pixmap
->height());
3200 setFont(QFont("times",12)); // XXX may need to be configurable
3203 void NetHackQtRIP::setLines(char** l
, int n
)
3209 QSize
NetHackQtRIP::sizeHint() const
3211 return pixmap
->size();
3214 void NetHackQtRIP::paintEvent(QPaintEvent
* event
)
3217 int pix_x
=(width()-pixmap
->width())/2;
3218 int pix_y
=(height()-pixmap
->height())/2;
3220 // XXX positions based on RIP image
3221 int rip_text_x
=pix_x
+156;
3222 int rip_text_y
=pix_y
+67;
3223 int rip_text_h
=94/riplines
;
3226 painter
.begin(this);
3227 painter
.drawPixmap(pix_x
,pix_y
,*pixmap
);
3228 for (int i
=0; i
<riplines
; i
++) {
3229 painter
.drawText(rip_text_x
-i
/2,rip_text_y
+i
*rip_text_h
,
3230 1,1,DontClip
|AlignHCenter
,line
[i
]);
3236 NetHackQtTextWindow::NetHackQtTextWindow(NetHackQtKeyBuffer
& ks
) :
3237 QDialog(qApp
->mainWidget(),0,FALSE
),
3242 search("Search",this),
3243 lines(new NetHackQtTextListBox(this)),
3246 ok
.setDefault(TRUE
);
3247 connect(&ok
,SIGNAL(clicked()),this,SLOT(accept()));
3248 connect(&search
,SIGNAL(clicked()),this,SLOT(Search()));
3249 connect(qt_settings
,SIGNAL(fontChanged()),this,SLOT(doUpdate()));
3251 QVBoxLayout
* vb
= new QVBoxLayout(this);
3252 vb
->addWidget(&rip
);
3253 QHBoxLayout
* hb
= new QHBoxLayout(vb
);
3255 hb
->addWidget(&search
);
3256 vb
->addWidget(lines
);
3259 void NetHackQtTextWindow::doUpdate()
3265 NetHackQtTextWindow::~NetHackQtTextWindow()
3270 QWidget
* NetHackQtTextWindow::Widget()
3275 bool NetHackQtTextWindow::Destroy()
3277 return !isVisible();
3280 void NetHackQtTextWindow::UseRIP(int how
, time_t when
)
3282 // Code from X11 windowport
3283 #define STONE_LINE_LEN 16 /* # chars that fit on one line */
3284 #define NAME_LINE 0 /* line # for player name */
3285 #define GOLD_LINE 1 /* line # for amount of gold */
3286 #define DEATH_LINE 2 /* line # for death description */
3287 #define YEAR_LINE 6 /* line # for year */
3289 static char** rip_line
=0;
3291 rip_line
=new char*[YEAR_LINE
+1];
3292 for (int i
=0; i
<YEAR_LINE
+1; i
++) {
3293 rip_line
[i
]=new char[STONE_LINE_LEN
+1];
3297 /* Follows same algorithm as genl_outrip() */
3304 /* Put name on stone */
3305 Sprintf(rip_line
[NAME_LINE
], "%s", plname
);
3307 /* Put $ on stone */
3308 Sprintf(rip_line
[GOLD_LINE
], "%ld Au", done_money
);
3310 /* Put together death description */
3311 formatkiller(buf
, sizeof buf
, how
, FALSE
);
3313 /* Put death type on stone */
3314 for (line
=DEATH_LINE
, dpx
= buf
; line
<YEAR_LINE
; line
++) {
3318 if ( (i0
=strlen(dpx
)) > STONE_LINE_LEN
) {
3319 for(i
= STONE_LINE_LEN
;
3320 ((i0
> STONE_LINE_LEN
) && i
); i
--)
3321 if(dpx
[i
] == ' ') i0
= i
;
3322 if(!i
) i0
= STONE_LINE_LEN
;
3326 strcpy(rip_line
[line
], dpx
);
3327 if (tmpchar
!= ' ') {
3330 } else dpx
= &dpx
[i0
+1];
3333 /* Put year on stone */
3334 year
= yyyymmdd(when
) / 10000L;
3335 Sprintf(rip_line
[YEAR_LINE
], "%4ld", year
);
3337 rip
.setLines(rip_line
,YEAR_LINE
+1);
3342 void NetHackQtTextWindow::Clear()
3349 void NetHackQtTextWindow::Display(bool block
)
3352 lines
->setFont(qt_settings
->normalFixedFont());
3354 lines
->setFont(qt_settings
->normalFont());
3369 int mh
= QApplication::desktop()->height()*3/5;
3370 if ( qt_compact_mode
&& (lines
->TotalHeight() > mh
|| use_rip
) ) {
3371 // big, so make it fill
3374 resize(QMAX(use_rip
? rip
.width() : 200,
3375 lines
->TotalWidth()+24),
3376 QMIN(mh
, lines
->TotalHeight()+h
));
3382 while (result()==-1) {
3384 if (result()==-1 && !keysource
.Empty()) {
3385 char k
=keysource
.GetAscii();
3386 if (k
=='\033' || k
==' ' || k
=='\r' || k
=='\n') {
3388 } else if (k
=='/') {
3396 void NetHackQtTextWindow::PutStr(int attr
, const char* text
)
3398 str_fixed
=str_fixed
|| strstr(text
," ");
3399 lines
->insertItem(text
);
3402 void NetHackQtTextWindow::done(int i
)
3409 void NetHackQtTextWindow::keyPressEvent(QKeyEvent
* e
)
3411 if ( e
->ascii() != '\r' && e
->ascii() != '\n' && e
->ascii() != '\033' )
3412 lines
->keyPressEvent(e
);
3414 QDialog::keyPressEvent(e
);
3417 void NetHackQtTextWindow::Search()
3419 NetHackQtStringRequestor
requestor(keysource
,"Search for:");
3420 static char line
[256]="";
3421 requestor
.SetDefault(line
);
3422 if (requestor
.Get(line
)) {
3423 int current
=lines
->currentItem();
3424 for (uint i
=1; i
<lines
->count(); i
++) {
3425 int lnum
=(i
+current
)%lines
->count();
3426 QString str
=lines
->text(lnum
);
3427 if (str
.contains(line
)) {
3428 lines
->setCurrentItem(lnum
);
3429 lines
->centerCurrentItem();
3433 lines
->setCurrentItem(-1);
3438 NetHackQtDelay::NetHackQtDelay(int ms
) :
3443 void NetHackQtDelay::wait()
3449 void NetHackQtDelay::timerEvent(QTimerEvent
* timer
)
3455 NetHackQtInvUsageWindow::NetHackQtInvUsageWindow(QWidget
* parent
) :
3460 void NetHackQtInvUsageWindow::drawWorn(QPainter
& painter
, obj
* nhobj
, int x
, int y
, bool canbe
)
3464 glyph
=obj_to_glyph(nhobj
);
3466 glyph
=cmap_to_glyph(S_room
);
3468 glyph
=cmap_to_glyph(S_stone
);
3470 qt_settings
->glyphs().drawCell(painter
,glyph
,x
,y
);
3473 void NetHackQtInvUsageWindow::paintEvent(QPaintEvent
*)
3485 painter
.begin(this);
3488 drawWorn(painter
,0,0,4,FALSE
);
3489 drawWorn(painter
,0,0,5,FALSE
);
3490 drawWorn(painter
,0,2,4,FALSE
);
3491 drawWorn(painter
,0,2,5,FALSE
);
3493 drawWorn(painter
,uarm
,1,3); // Armour
3494 drawWorn(painter
,uarmc
,1,2); // Cloak
3495 drawWorn(painter
,uarmh
,1,0); // Helmet
3496 drawWorn(painter
,uarms
,0,1); // Shield
3497 drawWorn(painter
,uarmg
,0,2); // Gloves - repeated
3498 drawWorn(painter
,uarmg
,2,2); // Gloves - repeated
3499 drawWorn(painter
,uarmf
,1,5); // Shoes (feet)
3500 drawWorn(painter
,uarmu
,1,4); // Undershirt
3501 drawWorn(painter
,uleft
,0,3); // RingL
3502 drawWorn(painter
,uright
,2,3); // RingR
3504 drawWorn(painter
,uwep
,2,1); // Weapon
3505 drawWorn(painter
,uswapwep
,0,0); // Secondary weapon
3506 drawWorn(painter
,uamul
,1,1); // Amulet
3507 drawWorn(painter
,ublindf
,2,0); // Blindfold
3512 class SmallToolButton
: public QToolButton
{
3514 SmallToolButton(const QPixmap
& pm
, const QString
&textLabel
,
3515 const QString
& grouptext
,
3516 QObject
* receiver
, const char* slot
,
3517 QToolBar
* parent
) :
3518 QToolButton(pm
, textLabel
,
3519 #if QT_VERSION < 210
3524 receiver
, slot
, parent
)
3528 QSize
sizeHint() const
3530 // get just a couple more pixels for the map
3531 return QToolButton::sizeHint()-QSize(0,2);
3536 NetHackQtMainWindow::NetHackQtMainWindow(NetHackQtKeyBuffer
& ks
) :
3537 message(0), map(0), status(0), invusage(0),
3538 keysink(ks
), dirkey(0)
3540 QToolBar
* toolbar
= new QToolBar(this);
3541 #if QT_VERSION >= 210
3542 setToolBarsMovable(FALSE
);
3543 toolbar
->setHorizontalStretchable(TRUE
);
3544 toolbar
->setVerticalStretchable(TRUE
);
3546 addToolBar(toolbar
);
3547 menubar
= menuBar();
3549 setCaption("Qt NetHack");
3550 if ( qt_compact_mode
)
3551 setIcon(QPixmap(nh_icon_small
));
3553 setIcon(QPixmap(nh_icon
));
3555 QPopupMenu
* game
=new QPopupMenu
;
3556 QPopupMenu
* apparel
=new QPopupMenu
;
3557 QPopupMenu
* act1
=new QPopupMenu
;
3558 QPopupMenu
* act2
= qt_compact_mode
? new QPopupMenu
: act1
;
3559 QPopupMenu
* magic
=new QPopupMenu
;
3560 QPopupMenu
* info
=new QPopupMenu
;
3565 help
= kapp
->getHelpMenu( TRUE
, "" );
3566 help
->insertSeparator();
3568 help
= qt_compact_mode
? info
: new QPopupMenu
;
3571 enum { OnDesktop
=1, OnHandhelds
=2 };
3579 { game
, "Version\tv", "v", 3},
3580 { game
, "Compilation\tAlt+V", "\366", 3},
3581 { game
, "History\tShift+V", "V", 3},
3582 { game
, "Redraw\tCtrl+R", "\022", 0}, // useless
3583 { game
, "Options\tShift+O", "O", 3},
3584 { game
, "Explore mode\tShift+X", "X", 3},
3586 { game
, "Save\tSy", "Sy", 3},
3587 { game
, "Quit\tAlt+Q", "\361", 3},
3589 { apparel
, "Apparel off\tShift+A", "A", 2},
3590 { apparel
, "Remove many\tShift+A", "A", 1},
3591 { apparel
, 0, 0, 3},
3592 { apparel
, "Wield weapon\tw", "w", 3},
3593 { apparel
, "Exchange weapons\tx", "x", 3},
3594 { apparel
, "Two weapon combat\t#two", "#tw", 3},
3595 { apparel
, "Load quiver\tShift+Q", "Q", 3},
3596 { apparel
, 0, 0, 3},
3597 { apparel
, "Wear armour\tShift+W", "W", 3},
3598 { apparel
, "Take off armour\tShift+T", "T", 3},
3599 { apparel
, 0, 0, 3},
3600 { apparel
, "Put on non-armour\tShift+P", "P", 3},
3601 { apparel
, "Remove non-armour\tShift+R", "R", 3},
3603 { act1
, "Again\tCtrl+A", "\001", 2},
3605 { act1
, "Apply\ta?", "a?", 3},
3606 { act1
, "Chat\tAlt+C", "\343", 3},
3607 { act1
, "Close door\tc", "c", 3},
3608 { act1
, "Down\t>", ">", 3},
3609 { act1
, "Drop many\tShift+D", "D", 2},
3610 { act1
, "Drop\td?", "d?", 2},
3611 { act1
, "Eat\te?", "e?", 2},
3612 { act1
, "Engrave\tShift+E", "E", 3},
3613 { act1
, "Fight\tShift+F", "F", 3},
3614 { act1
, "Fire from quiver\tf", "f", 2},
3615 { act1
, "Force\tAlt+F", "\346", 3},
3616 { act1
, "Get\t,", ",", 2},
3617 { act1
, "Jump\tAlt+J", "\352", 3},
3618 { act2
, "Kick\tCtrl+D", "\004", 2},
3619 { act2
, "Loot\tAlt+L", "\354", 3},
3620 { act2
, "Open door\to", "o", 3},
3621 { act2
, "Pay\tp", "p", 3},
3622 { act2
, "Rest\t.", ".", 2},
3623 { act2
, "Ride\t#ri", "#ri", 3},
3624 { act2
, "Search\ts", "s", 3},
3625 { act2
, "Sit\tAlt+S", "\363", 3},
3626 { act2
, "Throw\tt", "t", 2},
3627 { act2
, "Untrap\t#u", "#u", 3},
3628 { act2
, "Up\t<", "<", 3},
3629 { act2
, "Wipe face\tAlt+W", "\367", 3},
3631 { magic
, "Quaff potion\tq?", "q?", 3},
3632 { magic
, "Read scroll/book\tr?", "r?", 3},
3633 { magic
, "Zap wand\tz?", "z?", 3},
3634 { magic
, "Zap spell\tShift+Z", "Z", 3},
3635 { magic
, "Dip\tAlt+D", "\344", 3},
3636 { magic
, "Rub\tAlt+R", "\362", 3},
3637 { magic
, "Invoke\tAlt+I", "\351", 3},
3639 { magic
, "Offer\tAlt+O", "\357", 3},
3640 { magic
, "Pray\tAlt+P", "\360", 3},
3642 { magic
, "Teleport\tCtrl+T", "\024", 3},
3643 { magic
, "Monster action\tAlt+M", "\355", 3},
3644 { magic
, "Turn undead\tAlt+T", "\364", 3},
3646 { help
, "Help\t?", "?", 3},
3648 { help
, "What is here\t:", ":", 3},
3649 { help
, "What is there\t;", ";", 3},
3650 { help
, "What is...\t/y", "/y", 2},
3653 { info
, "Inventory\ti", "i", 3},
3655 { info
, "Angbandish inventory\t*", "*", 3},
3657 { info
, "Conduct\t#co", "#co", 3},
3658 { info
, "Discoveries\t\\", "\\", 3},
3659 { info
, "List/reorder spells\t+", "+", 3},
3660 { info
, "Adjust letters\tAlt+A", "\341", 2},
3662 { info
, "Name object\tAlt+N", "\356y?", 3},
3663 { info
, "Name object type\tAlt+N", "\356n?", 3},
3664 { info
, "Name creature\tShift+C", "C", 3},
3666 { info
, "Qualifications\tAlt+E", "\345", 3},
3673 for (i
=0; item
[i
].menu
; i
++)
3674 if (item
[i
].name
) count
++;
3676 macro
=new const char* [count
];
3678 game
->insertItem("Qt settings...",1000);
3679 help
->insertItem("About Qt NetHack...",2000);
3680 //help->insertItem("NetHack Guidebook...",3000);
3681 help
->insertSeparator();
3684 for (i
=0; item
[i
].menu
; i
++) {
3685 if ( item
[i
].flags
& (qt_compact_mode
? 1 : 2) ) {
3687 QString name
= item
[i
].name
;
3688 if ( qt_compact_mode
) // accelerators aren't
3689 name
.replace(QRegExp("\t.*"),"");
3690 item
[i
].menu
->insertItem(name
,count
);
3691 macro
[count
++]=item
[i
].action
;
3693 item
[i
].menu
->insertSeparator();
3698 menubar
->insertItem("Game",game
);
3699 menubar
->insertItem("Gear",apparel
);
3701 if ( qt_compact_mode
) {
3702 menubar
->insertItem("A-J",act1
);
3703 menubar
->insertItem("K-Z",act2
);
3704 menubar
->insertItem("Magic",magic
);
3705 menubar
->insertItem(QPixmap(info_xpm
),info
);
3706 menubar
->insertItem(QPixmap(map_xpm
), this, SLOT(raiseMap()));
3707 menubar
->insertItem(QPixmap(msg_xpm
), this, SLOT(raiseMessages()));
3708 menubar
->insertItem(QPixmap(stat_xpm
), this, SLOT(raiseStatus()));
3710 menubar
->insertItem("Action",act1
);
3711 menubar
->insertItem("Magic",magic
);
3712 menubar
->insertItem("Info",info
);
3713 menubar
->insertSeparator();
3714 menubar
->insertItem("Help",help
);
3717 QSignalMapper
* sm
= new QSignalMapper(this);
3718 connect(sm
, SIGNAL(mapped(const QString
&)), this, SLOT(doKeys(const QString
&)));
3720 tb
= new SmallToolButton( QPixmap(again_xpm
),"Again","Action", sm
, SLOT(map()), toolbar
);
3721 sm
->setMapping(tb
, "\001" );
3722 tb
= new SmallToolButton( QPixmap(get_xpm
),"Get","Action", sm
, SLOT(map()), toolbar
);
3723 sm
->setMapping(tb
, "," );
3724 tb
= new SmallToolButton( QPixmap(kick_xpm
),"Kick","Action", sm
, SLOT(map()), toolbar
);
3725 sm
->setMapping(tb
, "\004" );
3726 tb
= new SmallToolButton( QPixmap(throw_xpm
),"Throw","Action", sm
, SLOT(map()), toolbar
);
3727 sm
->setMapping(tb
, "t" );
3728 tb
= new SmallToolButton( QPixmap(fire_xpm
),"Fire","Action", sm
, SLOT(map()), toolbar
);
3729 sm
->setMapping(tb
, "f" );
3730 tb
= new SmallToolButton( QPixmap(drop_xpm
),"Drop","Action", sm
, SLOT(map()), toolbar
);
3731 sm
->setMapping(tb
, "D" );
3732 tb
= new SmallToolButton( QPixmap(eat_xpm
),"Eat","Action", sm
, SLOT(map()), toolbar
);
3733 sm
->setMapping(tb
, "e" );
3734 tb
= new SmallToolButton( QPixmap(rest_xpm
),"Rest","Action", sm
, SLOT(map()), toolbar
);
3735 sm
->setMapping(tb
, "." );
3736 tb
= new SmallToolButton( QPixmap(cast_a_xpm
),"Cast A","Magic", sm
, SLOT(map()), toolbar
);
3737 sm
->setMapping(tb
, "Za" );
3738 tb
= new SmallToolButton( QPixmap(cast_b_xpm
),"Cast B","Magic", sm
, SLOT(map()), toolbar
);
3739 sm
->setMapping(tb
, "Zb" );
3740 tb
= new SmallToolButton( QPixmap(cast_c_xpm
),"Cast C","Magic", sm
, SLOT(map()), toolbar
);
3741 sm
->setMapping(tb
, "Zc" );
3742 if ( !qt_compact_mode
) {
3743 QWidget
* filler
= new QWidget(toolbar
);
3744 filler
->setBackgroundMode(PaletteButton
);
3745 toolbar
->setStretchableWidget(filler
);
3748 connect(menubar
,SIGNAL(activated(int)),this,SLOT(doMenuItem(int)));
3755 int w
=QApplication::desktop()->width()-10; // XXX arbitrary extra space for frame
3756 int h
=QApplication::desktop()->height()-50;
3760 if (qt_tilewidth
!= NULL
) {
3761 maxwn
= atoi(qt_tilewidth
) * COLNO
+ 10;
3765 if (qt_tileheight
!= NULL
) {
3766 maxhn
= atoi(qt_tileheight
) * ROWNO
* 6/4;
3771 // Be exactly the size we want to be - full map...
3774 w
=maxwn
; // Doesn't need to be any wider
3778 h
=maxhn
; // Doesn't need to be any taller
3781 setGeometry(x
,y
,w
,h
);
3783 if ( qt_compact_mode
) {
3784 stack
= new QWidgetStack(this);
3785 setCentralWidget(stack
);
3787 setCentralWidget(new QWidget(this));
3788 invusage
= new NetHackQtInvUsageWindow(centralWidget());
3792 void NetHackQtMainWindow::zoomMap()
3794 qt_settings
->toggleGlyphSize();
3797 void NetHackQtMainWindow::raiseMap()
3799 if ( stack
->id(stack
->visibleWidget()) == 0 ) {
3802 stack
->raiseWidget(0);
3806 void NetHackQtMainWindow::raiseMessages()
3808 stack
->raiseWidget(1);
3811 void NetHackQtMainWindow::raiseStatus()
3813 stack
->raiseWidget(2);
3816 class NetHackMimeSourceFactory
: public QMimeSourceFactory
{
3818 const QMimeSource
* data(const QString
& abs_name
) const
3820 const QMimeSource
* r
= 0;
3821 if ( (NetHackMimeSourceFactory
*)this == QMimeSourceFactory::defaultFactory() )
3822 r
= QMimeSourceFactory::data(abs_name
);
3824 r
= QMimeSourceFactory::defaultFactory()->data(abs_name
);
3826 int sl
= abs_name
.length();
3828 sl
= abs_name
.findRev('/',sl
-1);
3829 QString name
= sl
>=0 ? abs_name
.mid(sl
+1) : abs_name
;
3830 int dot
= name
.findRev('.');
3832 name
= name
.left(dot
);
3833 if ( name
== "map" )
3834 r
= new QImageDrag(QImage(map_xpm
));
3835 else if ( name
== "msg" )
3836 r
= new QImageDrag(QImage(msg_xpm
));
3837 else if ( name
== "stat" )
3838 r
= new QImageDrag(QImage(stat_xpm
));
3839 } while (!r
&& sl
>0);
3845 void NetHackQtMainWindow::doMenuItem(int id
)
3849 centerOnMain(qt_settings
);
3850 qt_settings
->show();
3853 QMessageBox::about(this, "About Qt NetHack", aboutMsg());
3856 QDialog
dlg(this,0,TRUE
);
3857 (new QVBoxLayout(&dlg
))->setAutoAdd(TRUE
);
3858 QTextBrowser
browser(&dlg
);
3859 NetHackMimeSourceFactory ms
;
3860 browser
.setMimeSourceFactory(&ms
);
3861 browser
.setSource(QDir::currentDirPath()+"/Guidebook.html");
3862 if ( qt_compact_mode
)
3863 dlg
.showMaximized();
3873 void NetHackQtMainWindow::doKeys(const QString
& k
)
3879 void NetHackQtMainWindow::AddMessageWindow(NetHackQtMessageWindow
* window
)
3885 void NetHackQtMainWindow::AddMapWindow(NetHackQtMapWindow
* window
)
3889 connect(map
,SIGNAL(resized()),this,SLOT(layout()));
3892 void NetHackQtMainWindow::AddStatusWindow(NetHackQtStatusWindow
* window
)
3898 void NetHackQtMainWindow::RemoveWindow(NetHackQtWindow
* window
)
3900 if (window
==status
) {
3903 } else if (window
==map
) {
3906 } else if (window
==message
) {
3912 void NetHackQtMainWindow::updateInventory()
3915 invusage
->repaint(FALSE
);
3918 void NetHackQtMainWindow::fadeHighlighting()
3921 status
->fadeHighlighting();
3925 void NetHackQtMainWindow::layout()
3927 if ( qt_compact_mode
)
3929 if (message
&& map
&& status
) {
3930 QSize maxs
=map
->Widget()->maximumSize();
3931 int maph
=QMIN(height()*2/3,maxs
.height());
3933 QWidget
* c
= centralWidget();
3936 int iuw
=3*qt_settings
->glyphs().width();
3937 int topw
=(c
->width()-iuw
)/2;
3939 message
->Widget()->setGeometry(0,0,topw
,toph
);
3940 invusage
->setGeometry(topw
,0,iuw
,toph
);
3941 status
->Widget()->setGeometry(topw
+iuw
,0,topw
,toph
);
3942 map
->Widget()->setGeometry(QMAX(0,(c
->width()-maxs
.width())/2),
3943 toph
,c
->width(),maph
);
3947 void NetHackQtMainWindow::resizeEvent(QResizeEvent
*)
3955 void NetHackQtMainWindow::keyReleaseEvent(QKeyEvent
* event
)
3958 doKeys(QString(QChar(dirkey
)));
3959 if ( !event
->isAutoRepeat() )
3964 void NetHackQtMainWindow::keyPressEvent(QKeyEvent
* event
)
3966 // Global key controls
3968 // For desktop, arrow keys scroll map, since we don't want players
3969 // to think that's the way to move. For handhelds, the normal way is to
3970 // click-to-travel, so we allow the cursor keys for fine movements.
3976 if ( event
->isAutoRepeat() &&
3977 event
->key() >= Key_Left
&& event
->key() <= Key_Down
)
3980 const char* d
= Cmd
.dirchars
;
3981 switch (event
->key()) {
3983 if ( dirkey
== d
[0] )
3985 else if ( dirkey
== d
[4] )
3989 break; case Key_Down
:
3990 if ( dirkey
== d
[0] )
3992 else if ( dirkey
== d
[4] )
3996 break; case Key_Left
:
3997 if ( dirkey
== d
[2] )
3999 else if ( dirkey
== d
[6] )
4003 break; case Key_Right
:
4004 if ( dirkey
== d
[2] )
4006 else if ( dirkey
== d
[6] )
4010 break; case Key_Prior
:
4012 if (message
) message
->Scroll(0,-1);
4013 break; case Key_Next
:
4015 if (message
) message
->Scroll(0,+1);
4016 break; case Key_Space
:
4017 if ( flags
.rest_on_space
) {
4030 void NetHackQtMainWindow::closeEvent(QCloseEvent
* e
)
4032 if ( program_state
.something_worth_saving
) {
4033 switch ( QMessageBox::information( this, "NetHack",
4034 "This will end your NetHack session",
4035 "&Save", "&Cancel", 0, 1 ) )
4038 // See dosave() function
4041 NetHackQtBind::qt_exit_nhwindows(0);
4042 terminate(EXIT_SUCCESS
);
4046 break; // ignore the event
4053 void NetHackQtMainWindow::ShowIfReady()
4055 if (message
&& map
&& status
) {
4057 QWidget
* p
= qt_compact_mode
? stack
: centralWidget();
4058 message
->Widget()->recreate(p
,0,pos
);
4059 map
->Widget()->recreate(p
,0,pos
);
4060 status
->Widget()->recreate(p
,0,pos
);
4061 if ( qt_compact_mode
) {
4062 message
->setMap(map
);
4063 stack
->addWidget(map
->Widget(), 0);
4064 stack
->addWidget(message
->Widget(), 1);
4065 stack
->addWidget(status
->Widget(), 2);
4071 } else if (isVisible()) {
4077 NetHackQtYnDialog::NetHackQtYnDialog(NetHackQtKeyBuffer
& keysrc
,const char* q
,const char* ch
,char df
) :
4078 QDialog(qApp
->mainWidget(),0,FALSE
),
4079 question(q
), choices(ch
), def(df
),
4082 setCaption("NetHack: Question");
4085 char NetHackQtYnDialog::Exec()
4087 QString
ch(choices
);
4091 if ( qt_compact_mode
&& !choices
) {
4092 // expand choices from prompt
4093 // ##### why isn't choices set properly???
4094 const char* c
=question
;
4095 while ( *c
&& *c
!= '[' )
4097 qlabel
= QString(question
).left(c
-question
);
4103 while ( *c
&& *c
!= ']' && *c
!= ' ' ) {
4106 } else if ( from
) {
4107 for (char f
=from
+1; f
<=*c
; f
++)
4117 while ( *c
&& *c
!= ']' ) {
4118 if ( *c
== '*' || *c
== '?' )
4124 if ( strstr(question
, "what direction") ) {
4125 // We replace this regardless, since sometimes you get choices.
4126 const char* d
= Cmd
.dirchars
;
4143 // Hmm... they'll have to use a virtual keyboard
4149 QVBoxLayout
vb(this);
4150 vb
.setAutoAdd(TRUE
);
4151 bool bigq
= qlabel
.length()>40;
4153 QLabel
* q
= new QLabel(qlabel
,this);
4154 q
->setAlignment(AlignLeft
|WordBreak
);
4157 QButtonGroup
group(ch_per_line
, Horizontal
,
4158 bigq
? QString::null
: qlabel
, this);
4160 int nchoices
=ch
.length();
4162 bool allow_count
=ch
.contains('#');
4166 const int extra
=fontMetrics().height(); // Extra for group
4167 int x
=margin
, y
=extra
+margin
;
4168 int butsize
=fontMetrics().height()*2+5;
4170 QPushButton
* button
;
4171 for (int i
=0; i
<nchoices
&& ch
[i
]!='\033'; i
++) {
4172 button
=new QPushButton(QString(ch
[i
]),&group
);
4173 if ( !enable
.isNull() ) {
4174 if ( !enable
.contains(ch
[i
]) )
4175 button
->setEnabled(FALSE
);
4177 button
->setFixedSize(butsize
,butsize
); // Square
4178 if (ch
[i
]==def
) button
->setDefault(TRUE
);
4188 connect(&group
,SIGNAL(clicked(int)),this,SLOT(doneItem(int)));
4194 QHBox
*hb
= new QHBox(this);
4195 lb
=new QLabel("Count: ",hb
);
4196 le
=new QLineEdit(hb
);
4204 for (uint i
=0; i
<ch
.length(); i
++) {
4205 if (ch
[i
].latin1()=='q') ch_esc
='q';
4206 else if (!ch_esc
&& ch
[i
].latin1()=='n') ch_esc
='n';
4210 if (!keysource
.Empty()) {
4211 char k
=keysource
.GetAscii();
4213 for (uint i
=0; i
<ch
.length(); i
++)
4214 if (ch
[i
].latin1()==k
)
4217 if (k
=='\033' && ch_esc
)
4219 else if (k
==' ' || k
=='\r' || k
=='\n')
4221 // else choice remains 0
4223 } else if ( result() == 0 ) {
4224 choice
= ch_esc
? ch_esc
: def
? def
: ' ';
4225 } else if ( result() == 1 ) {
4226 choice
= def
? def
: ch_esc
? ch_esc
: ' ';
4227 } else if ( result() >= 1000 ) {
4228 choice
= ch
[result() - 1000].latin1();
4234 if (allow_count
&& !le
->text().isEmpty()) {
4235 yn_number
=atoi(le
->text());
4240 QLabel
label(qlabel
,this);
4241 QPushButton
cancel("Dismiss",this);
4242 label
.setFrameStyle(QFrame::Box
|QFrame::Sunken
);
4243 label
.setAlignment(AlignCenter
);
4244 label
.resize(fontMetrics().width(qlabel
)+60,30+fontMetrics().height());
4245 cancel
.move(width()/2-cancel
.width()/2,label
.geometry().bottom()+8);
4246 connect(&cancel
,SIGNAL(clicked()),this,SLOT(reject()));
4250 while (result()<0 && keysource
.Empty()) {
4254 if (keysource
.Empty()) {
4257 return keysource
.GetAscii();
4261 void NetHackQtYnDialog::keyPressEvent(QKeyEvent
* event
)
4263 // Don't want QDialog's Return/Esc behaviour
4267 void NetHackQtYnDialog::doneItem(int i
)
4272 void NetHackQtYnDialog::done(int i
)
4278 NetHackQtGlyphs::NetHackQtGlyphs()
4280 const char* tile_file
= "nhtiles.bmp";
4281 if ( iflags
.wc_tile_file
)
4282 tile_file
= iflags
.wc_tile_file
;
4284 if (!img
.load(tile_file
)) {
4285 tile_file
= "x11tiles";
4286 if (!img
.load(tile_file
)) {
4288 msg
.sprintf("Cannot load x11tiles or nhtiles.bmp");
4289 QMessageBox::warning(0, "IO Error", msg
);
4291 tiles_per_row
= TILES_PER_ROW
;
4292 if (img
.width()%tiles_per_row
) {
4294 "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()
4389 impossible("Clear called before we know if Menu or Text");
4394 void NetHackQtMenuOrTextWindow::Display(bool block
)
4397 impossible("Display called before we know if Menu or Text");
4399 actual
->Display(block
);
4402 bool NetHackQtMenuOrTextWindow::Destroy()
4407 impossible("Destroy called before we know if Menu or Text");
4409 res
= actual
->Destroy();
4414 void NetHackQtMenuOrTextWindow::PutStr(int attr
, const char* text
)
4416 if (!actual
) actual
=new NetHackQtTextWindow(keysource
);
4417 actual
->PutStr(attr
,text
);
4421 void NetHackQtMenuOrTextWindow::StartMenu()
4423 if (!actual
) actual
=new NetHackQtMenuWindow(keysource
);
4424 actual
->StartMenu();
4427 void NetHackQtMenuOrTextWindow::AddMenu(int glyph
, const ANY_P
* identifier
, char ch
, char gch
, int attr
,
4428 const char* str
, bool presel
)
4430 if (!actual
) impossible("AddMenu called before we know if Menu or Text");
4431 actual
->AddMenu(glyph
,identifier
,ch
,gch
,attr
,str
,presel
);
4434 void NetHackQtMenuOrTextWindow::EndMenu(const char* prompt
)
4436 if (!actual
) impossible("EndMenu called before we know if Menu or Text");
4437 actual
->EndMenu(prompt
);
4440 int NetHackQtMenuOrTextWindow::SelectMenu(int how
, MENU_ITEM_P
**menu_list
)
4442 if (!actual
) impossible("SelectMenu called before we know if Menu or Text");
4443 return actual
->SelectMenu(how
,menu_list
);
4447 // XXX Should be from Options
4449 // XXX Hmm. Tricky part is that perhaps some macros should only be active
4450 // XXX when a key is about to be gotten. For example, the user could
4451 // XXX define "-" to do "E-yyyyyyyy\r", but would still need "-" for
4452 // XXX other purposes. Maybe just too bad.
4459 { Qt::Key_F1
, 0, "n100." }, // Rest (x100)
4460 { Qt::Key_F2
, 0, "n20s" }, // Search (x20)
4461 { Qt::Key_F3
, 0, "o8o4o6o2o8o4o6o2o8o4o6o2" }, // Open all doors (x3)
4462 { Qt::Key_Tab
, 0, "\001" },
4467 NetHackQtBind::NetHackQtBind(int& argc
, char** argv
) :
4469 KApplication(argc
,argv
)
4470 #elif defined(QWS) // not quite the right condition
4471 QPEApplication(argc
,argv
)
4473 QApplication(argc
,argv
)
4476 QPixmap
pm("nhsplash.xpm");
4477 if ( iflags
.wc_splash_screen
&& !pm
.isNull() ) {
4478 QVBox
*vb
= new QVBox(0,0,
4479 WStyle_Customize
| WStyle_NoBorder
| nh_WX11BypassWM
| WStyle_StaysOnTop
);
4481 QLabel
*lsplash
= new QLabel(vb
);
4482 lsplash
->setAlignment(AlignCenter
);
4483 lsplash
->setPixmap(pm
);
4484 QLabel
* capt
= new QLabel("Loading...",vb
);
4485 capt
->setAlignment(AlignCenter
);
4487 lsplash
->setFixedSize(pm
.size());
4488 lsplash
->setMask(*pm
.mask());
4490 splash
->move((QApplication::desktop()->width()-pm
.width())/2,
4491 (QApplication::desktop()->height()-pm
.height())/2);
4492 //splash->setGeometry(0,0,100,100);
4493 if ( qt_compact_mode
) {
4494 splash
->showMaximized();
4496 vb
->setFrameStyle(QFrame::WinPanel
|QFrame::Raised
);
4498 splash
->adjustSize();
4502 // force content refresh outside event loop
4503 splash
->repaint(FALSE
);
4504 lsplash
->repaint(FALSE
);
4505 capt
->repaint(FALSE
);
4511 main
= new NetHackQtMainWindow(keybuffer
);
4512 #if defined(QWS) // not quite the right condition
4513 showMainWidget(main
);
4515 setMainWidget(main
);
4517 qt_settings
=new NetHackQtSettings(main
->width(),main
->height());
4520 void NetHackQtBind::qt_init_nhwindows(int* argc
, char** argv
)
4525 // Michael Hohmuth <hohmuth@inf.tu-dresden.de>...
4527 // As the game runs setuid games, it must seteuid(getuid()) before
4528 // calling XOpenDisplay(), and reset the euid afterwards.
4529 // Otherwise, it can't read the $HOME/.Xauthority file and whines about
4530 // not being able to open the X display (if a magic-cookie
4531 // authorization mechanism is being used).
4533 uid_t gamesuid
=geteuid();
4537 QApplication::setColorSpec(ManyColor
);
4538 instance
=new NetHackQtBind(*argc
,argv
);
4545 // This nethack engine feature should be moved into windowport API
4546 nt_kbhit
= NetHackQtBind::qt_kbhit
;
4550 int NetHackQtBind::qt_kbhit()
4552 return !keybuffer
.Empty();
4555 static bool have_asked
= FALSE
;
4557 void NetHackQtBind::qt_player_selection()
4563 NetHackQtSavedGameSelector::NetHackQtSavedGameSelector(const char** saved
) :
4564 QDialog(qApp
->mainWidget(),"sgsel",TRUE
)
4566 QVBoxLayout
*vbl
= new QVBoxLayout(this,6);
4569 QLabel
* logo
= new QLabel(this); vbl
->addWidget(logo
);
4570 logo
->setAlignment(AlignCenter
);
4571 logo
->setPixmap(QPixmap("nhsplash.xpm"));
4572 QLabel
* attr
= new QLabel("by the NetHack DevTeam",this);
4573 attr
->setAlignment(AlignCenter
);
4574 vbl
->addWidget(attr
);
4577 QLabel* logo = new QLabel(hb);
4578 hb = new QHBox(this);
4579 vbl->addWidget(hb, AlignCenter);
4580 logo->setPixmap(QPixmap(nh_icon));
4581 logo->setAlignment(AlignRight|AlignVCenter);
4582 new QLabel(nh_attribution,hb);
4585 hb
= new QHBox(this);
4586 vbl
->addWidget(hb
, AlignCenter
);
4587 QPushButton
* q
= new QPushButton("Quit",hb
);
4588 connect(q
, SIGNAL(clicked()), this, SLOT(reject()));
4589 QPushButton
* c
= new QPushButton("New Game",hb
);
4590 connect(c
, SIGNAL(clicked()), this, SLOT(accept()));
4591 c
->setDefault(TRUE
);
4593 QButtonGroup
* bg
= new QButtonGroup(3, Horizontal
, "Saved Characters",this);
4595 connect(bg
, SIGNAL(clicked(int)), this, SLOT(done(int)));
4596 for (int i
=0; saved
[i
]; i
++) {
4597 QPushButton
* b
= new QPushButton(saved
[i
],bg
);
4602 int NetHackQtSavedGameSelector::choose()
4604 #if defined(QWS) // probably safe with Qt 3, too (where show!=exec in QDialog).
4605 if ( qt_compact_mode
)
4611 void NetHackQtBind::qt_askname()
4615 // We do it all here, and nothing in askname
4617 char** saved
= get_saved_games();
4619 if ( saved
&& *saved
) {
4620 if ( splash
) splash
->hide();
4621 NetHackQtSavedGameSelector
sgsel((const char**)saved
);
4622 ch
= sgsel
.choose();
4624 strcpy(plname
,saved
[ch
]);
4626 free_saved_games(saved
);
4630 if ( splash
) splash
->hide();
4631 if (NetHackQtPlayerSelector(keybuffer
).Choose())
4641 qt_exit_nhwindows(0);
4645 void NetHackQtBind::qt_get_nh_event()
4650 // Kludge to access lastWindowClosed() signal.
4651 class TApp
: public QApplication
{
4653 TApp(int& c
, char**v
) : QApplication(c
,v
) {}
4654 void lwc() { emit
lastWindowClosed(); }
4658 void NetHackQtBind::qt_exit_nhwindows(const char *)
4661 // Avoids bug in SHARP SL5500
4662 ((TApp
*)qApp
)->lwc();
4666 delete instance
; // ie. qApp
4669 void NetHackQtBind::qt_suspend_nhwindows(const char *)
4673 void NetHackQtBind::qt_resume_nhwindows()
4677 static QArray
<NetHackQtWindow
*> id_to_window
;
4679 winid
NetHackQtBind::qt_create_nhwindow(int type
)
4682 for (id
= 0; id
< (winid
) id_to_window
.size(); id
++) {
4683 if ( !id_to_window
[id
] )
4686 if ( id
== (winid
) id_to_window
.size() )
4687 id_to_window
.resize(id
+1);
4689 NetHackQtWindow
* window
=0;
4693 NetHackQtMapWindow
* w
=new NetHackQtMapWindow(clickbuffer
);
4694 main
->AddMapWindow(w
);
4696 } break; case NHW_MESSAGE
: {
4697 NetHackQtMessageWindow
* w
=new NetHackQtMessageWindow
;
4698 main
->AddMessageWindow(w
);
4700 } break; case NHW_STATUS
: {
4701 NetHackQtStatusWindow
* w
=new NetHackQtStatusWindow
;
4702 main
->AddStatusWindow(w
);
4704 } break; case NHW_MENU
:
4705 window
=new NetHackQtMenuOrTextWindow(keybuffer
);
4706 break; case NHW_TEXT
:
4707 window
=new NetHackQtTextWindow(keybuffer
);
4712 // Note: use of isHidden does not work with Qt 2.1
4714 #if QT_VERSION >= 300
4715 && !main
->isHidden()
4717 && main
->isVisible()
4725 id_to_window
[id
] = window
;
4729 void NetHackQtBind::qt_clear_nhwindow(winid wid
)
4731 NetHackQtWindow
* window
=id_to_window
[wid
];
4735 void NetHackQtBind::qt_display_nhwindow(winid wid
, BOOLEAN_P block
)
4737 NetHackQtWindow
* window
=id_to_window
[wid
];
4738 window
->Display(block
);
4741 void NetHackQtBind::qt_destroy_nhwindow(winid wid
)
4743 NetHackQtWindow
* window
=id_to_window
[wid
];
4744 main
->RemoveWindow(window
);
4745 if (window
->Destroy())
4747 id_to_window
[wid
] = 0;
4750 void NetHackQtBind::qt_curs(winid wid
, int x
, int y
)
4752 NetHackQtWindow
* window
=id_to_window
[wid
];
4753 window
->CursorTo(x
,y
);
4756 void NetHackQtBind::qt_putstr(winid wid
, int attr
, const char *text
)
4758 NetHackQtWindow
* window
=id_to_window
[wid
];
4759 window
->PutStr(attr
,text
);
4762 void NetHackQtBind::qt_display_file(const char *filename
, BOOLEAN_P must_exist
)
4764 NetHackQtTextWindow
* window
=new NetHackQtTextWindow(keybuffer
);
4765 bool complain
= FALSE
;
4774 f
= dlb_fopen(filename
, "r");
4776 complain
= must_exist
;
4778 while (dlb_fgets(buf
, BUFSZ
, f
)) {
4779 if ((cr
= index(buf
, '\n')) != 0) *cr
= 0;
4781 if ((cr
= index(buf
, '\r')) != 0) *cr
= 0;
4783 if (index(buf
, '\t') != 0) (void) tabexpand(buf
);
4784 window
->PutStr(ATR_NONE
, buf
);
4786 window
->Display(FALSE
);
4787 (void) dlb_fclose(f
);
4791 QFile
file(filename
);
4793 if (file
.open(IO_ReadOnly
)) {
4795 while (file
.readLine(line
,127) >= 0) {
4796 line
[strlen(line
)-1]=0;// remove newline
4797 window
->PutStr(ATR_NONE
,line
);
4799 window
->Display(FALSE
);
4801 complain
= must_exist
;
4807 message
.sprintf("File not found: %s\n",filename
);
4808 QMessageBox::message("File Error", (const char*)message
, "Ignore");
4812 void NetHackQtBind::qt_start_menu(winid wid
)
4814 NetHackQtWindow
* window
=id_to_window
[wid
];
4815 window
->StartMenu();
4818 void NetHackQtBind::qt_add_menu(winid wid
, int glyph
,
4819 const ANY_P
* identifier
, CHAR_P ch
, CHAR_P gch
, int attr
,
4820 const char *str
, BOOLEAN_P presel
)
4822 NetHackQtWindow
* window
=id_to_window
[wid
];
4823 window
->AddMenu(glyph
, identifier
, ch
, gch
, attr
, str
, presel
);
4826 void NetHackQtBind::qt_end_menu(winid wid
, const char *prompt
)
4828 NetHackQtWindow
* window
=id_to_window
[wid
];
4829 window
->EndMenu(prompt
);
4832 int NetHackQtBind::qt_select_menu(winid wid
, int how
, MENU_ITEM_P
**menu_list
)
4834 NetHackQtWindow
* window
=id_to_window
[wid
];
4835 return window
->SelectMenu(how
,menu_list
);
4838 void NetHackQtBind::qt_update_inventory()
4841 main
->updateInventory();
4843 if (program_state.something_worth_saving && flags.perm_invent)
4844 display_inventory(NULL, FALSE);
4848 void NetHackQtBind::qt_mark_synch()
4852 void NetHackQtBind::qt_wait_synch()
4856 void NetHackQtBind::qt_cliparound(int x
, int y
)
4858 // XXXNH - winid should be a parameter!
4859 qt_cliparound_window(WIN_MAP
,x
,y
);
4862 void NetHackQtBind::qt_cliparound_window(winid wid
, int x
, int y
)
4864 NetHackQtWindow
* window
=id_to_window
[wid
];
4865 window
->ClipAround(x
,y
);
4867 void NetHackQtBind::qt_print_glyph(winid wid
,XCHAR_P x
,XCHAR_P y
,int glyph
, int bkglyph
)
4869 NetHackQtWindow
* window
=id_to_window
[wid
];
4870 window
->PrintGlyph(x
,y
,glyph
);
4872 //void NetHackQtBind::qt_print_glyph_compose(winid wid,XCHAR_P x,XCHAR_P y,int glyph1, int glyph2)
4874 //NetHackQtWindow* window=id_to_window[wid];
4875 //window->PrintGlyphCompose(x,y,glyph1,glyph2);
4878 void NetHackQtBind::qt_raw_print(const char *str
)
4883 void NetHackQtBind::qt_raw_print_bold(const char *str
)
4888 int NetHackQtBind::qt_nhgetch()
4891 main
->fadeHighlighting();
4893 // Process events until a key arrives.
4895 while (keybuffer
.Empty()
4897 && !program_state
.done_hup
4904 if (program_state
.done_hup
&& keybuffer
.Empty()) return '\033';
4906 return keybuffer
.GetAscii();
4909 int NetHackQtBind::qt_nh_poskey(int *x
, int *y
, int *mod
)
4912 main
->fadeHighlighting();
4914 // Process events until a key or map-click arrives.
4916 while (keybuffer
.Empty() && clickbuffer
.Empty()
4918 && !program_state
.done_hup
4924 if (program_state
.done_hup
&& keybuffer
.Empty()) return '\033';
4926 if (!keybuffer
.Empty()) {
4927 return keybuffer
.GetAscii();
4929 *x
=clickbuffer
.NextX();
4930 *y
=clickbuffer
.NextY();
4931 *mod
=clickbuffer
.NextMod();
4937 void NetHackQtBind::qt_nhbell()
4939 QApplication::beep();
4942 int NetHackQtBind::qt_doprev_message()
4944 // Don't need it - uses scrollbar
4945 // XXX but could make this a shortcut
4949 char NetHackQtBind::qt_yn_function(const char *question
, const char *choices
, CHAR_P def
)
4951 if (qt_settings
->ynInMessages() && WIN_MESSAGE
!=WIN_ERR
) {
4952 // Similar to X11 windowport `slow' feature.
4954 char message
[BUFSZ
];
4955 char yn_esc_map
='\033';
4958 char *cb
, choicebuf
[QBUFSZ
];
4959 Strcpy(choicebuf
, choices
);
4960 if ((cb
= index(choicebuf
, '\033')) != 0) {
4961 // anything beyond <esc> is hidden
4964 (void)strncpy(message
, question
, QBUFSZ
-1);
4965 message
[QBUFSZ
-1] = '\0';
4966 Sprintf(eos(message
), " [%s]", choicebuf
);
4967 if (def
) Sprintf(eos(message
), " (%c)", def
);
4968 Strcat(message
, " ");
4969 // escape maps to 'q' or 'n' or default, in that order
4970 yn_esc_map
= (index(choices
, 'q') ? 'q' :
4971 (index(choices
, 'n') ? 'n' : def
));
4973 Strcpy(message
, question
);
4977 // Improve some special-cases (DIRKS 08/02/23)
4978 if (strcmp (choices
,"ynq") == 0) {
4979 switch (QMessageBox::information (qApp
->mainWidget(),"NetHack",question
,"&Yes","&No","&Quit",0,2))
4987 if (strcmp (choices
,"yn") == 0) {
4988 switch (QMessageBox::information(qApp
->mainWidget(),"NetHack",question
,"&Yes", "&No",0,1))
4996 NetHackQtBind::qt_putstr(WIN_MESSAGE
, ATR_BOLD
, message
);
5000 char ch
=NetHackQtBind::qt_nhgetch();
5003 } else if (choices
&& !index(choices
,ch
)) {
5004 if (def
&& (ch
==' ' || ch
=='\r' || ch
=='\n')) {
5007 NetHackQtBind::qt_nhbell();
5015 NetHackQtBind::qt_clear_nhwindow(WIN_MESSAGE
);
5019 NetHackQtYnDialog
dialog(keybuffer
,question
,choices
,def
);
5020 return dialog
.Exec();
5024 void NetHackQtBind::qt_getlin(const char *prompt
, char *line
)
5026 NetHackQtStringRequestor
requestor(keybuffer
,prompt
);
5027 if (!requestor
.Get(line
)) {
5032 NetHackQtExtCmdRequestor::NetHackQtExtCmdRequestor(NetHackQtKeyBuffer
& ks
) :
5033 QDialog(qApp
->mainWidget(), "ext-cmd", FALSE
),
5037 QVBoxLayout
*l
= new QVBoxLayout(this,marg
,marg
);
5039 QPushButton
* can
= new QPushButton("Cancel", this);
5040 can
->setDefault(TRUE
);
5041 can
->setMinimumSize(can
->sizeHint());
5044 QButtonGroup
*group
=new QButtonGroup("",0);
5045 QGroupBox
*grid
=new QGroupBox("Extended commands",this);
5050 QFontMetrics fm
= fontMetrics();
5051 for (i
=0; extcmdlist
[i
].ef_txt
; i
++) {
5052 butw
= QMAX(butw
,30+fm
.width(extcmdlist
[i
].ef_txt
));
5055 int nrows
=(i
+ncols
-1)/ncols
;
5057 QVBoxLayout
* bl
= new QVBoxLayout(grid
,marg
);
5058 bl
->addSpacing(fm
.height());
5059 QGridLayout
* gl
= new QGridLayout(nrows
,ncols
,marg
);
5061 for (i
=0; extcmdlist
[i
].ef_txt
; i
++) {
5062 QPushButton
* pb
=new QPushButton(extcmdlist
[i
].ef_txt
, grid
);
5063 pb
->setMinimumSize(butw
,pb
->sizeHint().height());
5065 gl
->addWidget(pb
,i
/ncols
,i
%ncols
);
5067 connect(group
,SIGNAL(clicked(int)),this,SLOT(done(int)));
5073 connect(can
,SIGNAL(clicked()),this,SLOT(cancel()));
5076 void NetHackQtExtCmdRequestor::cancel()
5082 void NetHackQtExtCmdRequestor::done(int i
)
5088 int NetHackQtExtCmdRequestor::get()
5090 const int none
= -10;
5093 resize(1,1); // pack
5097 while (result()==none
) {
5098 while (result()==none
&& !keysource
.Empty()) {
5099 char k
=keysource
.GetAscii();
5100 if (k
=='\r' || k
=='\n' || k
==' ' || k
=='\033') {
5105 for (int i
=0; extcmdlist
[i
].ef_txt
; i
++) {
5106 if (qstrnicmp(str
, extcmdlist
[i
].ef_txt
, cursor
)==0) {
5113 if ( r
== -1 ) { // no match!
5114 QApplication::beep();
5116 } else if ( r
!= -2 ) { // only one match
5129 int NetHackQtBind::qt_get_ext_cmd()
5131 NetHackQtExtCmdRequestor
requestor(keybuffer
);
5132 return requestor
.get();
5135 void NetHackQtBind::qt_number_pad(int)
5140 void NetHackQtBind::qt_delay_output()
5142 NetHackQtDelay
delay(15);
5146 void NetHackQtBind::qt_start_screen()
5151 void NetHackQtBind::qt_end_screen()
5156 void NetHackQtBind::qt_outrip(winid wid
, int how
, time_t when
)
5158 NetHackQtWindow
* window
=id_to_window
[wid
];
5160 window
->UseRIP(how
, when
);
5163 bool NetHackQtBind::notify(QObject
*receiver
, QEvent
*event
)
5165 // Ignore Alt-key navigation to menubar, it's annoying when you
5166 // use Alt-Direction to move around.
5167 if ( main
&& event
->type()==QEvent::KeyRelease
&& main
==receiver
5168 && ((QKeyEvent
*)event
)->key() == Key_Alt
)
5171 bool result
=QApplication::notify(receiver
,event
);
5173 if (program_state
.done_hup
) {
5174 keybuffer
.Put('\033');
5179 if (event
->type()==QEvent::KeyPress
) {
5180 QKeyEvent
* key_event
=(QKeyEvent
*)event
;
5182 if (!key_event
->isAccepted()) {
5183 const int k
=key_event
->key();
5185 for (int i
=0; !macro
&& key_macro
[i
].key
; i
++) {
5186 if (key_macro
[i
].key
==k
5187 && ((key_macro
[i
].state
&key_event
->state())==key_macro
[i
].state
))
5189 keybuffer
.Put(key_macro
[i
].macro
);
5193 char ch
=key_event
->ascii();
5194 if ( !ch
&& (key_event
->state() & Qt::ControlButton
) ) {
5195 // On Mac, it aint-ncessarily-control
5196 if ( k
>=Qt::Key_A
&& k
<=Qt::Key_Z
)
5197 ch
= k
- Qt::Key_A
+ 1;
5200 bool alt
= (key_event
->state()&AltButton
) ||
5201 (k
>= Key_0
&& k
<= Key_9
&& (key_event
->state()&ControlButton
));
5202 keybuffer
.Put(key_event
->key(),ch
+ (alt
? 128 : 0),
5203 key_event
->state());
5204 key_event
->accept();
5216 NetHackQtBind
* NetHackQtBind::instance
=0;
5217 NetHackQtKeyBuffer
NetHackQtBind::keybuffer
;
5218 NetHackQtClickBuffer
NetHackQtBind::clickbuffer
;
5219 NetHackQtMainWindow
* NetHackQtBind::main
=0;
5220 QWidget
* NetHackQtBind::splash
=0;
5223 extern "C" struct window_procs Qt_procs
;
5225 struct window_procs Qt_procs
= {
5227 WC_COLOR
|WC_HILITE_PET
|
5228 WC_ASCII_MAP
|WC_TILED_MAP
|
5229 WC_FONT_MAP
|WC_TILE_FILE
|WC_TILE_WIDTH
|WC_TILE_HEIGHT
|
5230 WC_PLAYER_SELECTION
|WC_SPLASH_SCREEN
,
5232 NetHackQtBind::qt_init_nhwindows
,
5233 NetHackQtBind::qt_player_selection
,
5234 NetHackQtBind::qt_askname
,
5235 NetHackQtBind::qt_get_nh_event
,
5236 NetHackQtBind::qt_exit_nhwindows
,
5237 NetHackQtBind::qt_suspend_nhwindows
,
5238 NetHackQtBind::qt_resume_nhwindows
,
5239 NetHackQtBind::qt_create_nhwindow
,
5240 NetHackQtBind::qt_clear_nhwindow
,
5241 NetHackQtBind::qt_display_nhwindow
,
5242 NetHackQtBind::qt_destroy_nhwindow
,
5243 NetHackQtBind::qt_curs
,
5244 NetHackQtBind::qt_putstr
,
5246 NetHackQtBind::qt_display_file
,
5247 NetHackQtBind::qt_start_menu
,
5248 NetHackQtBind::qt_add_menu
,
5249 NetHackQtBind::qt_end_menu
,
5250 NetHackQtBind::qt_select_menu
,
5251 genl_message_menu
, /* no need for X-specific handling */
5252 NetHackQtBind::qt_update_inventory
,
5253 NetHackQtBind::qt_mark_synch
,
5254 NetHackQtBind::qt_wait_synch
,
5256 NetHackQtBind::qt_cliparound
,
5261 NetHackQtBind::qt_print_glyph
,
5262 //NetHackQtBind::qt_print_glyph_compose,
5263 NetHackQtBind::qt_raw_print
,
5264 NetHackQtBind::qt_raw_print_bold
,
5265 NetHackQtBind::qt_nhgetch
,
5266 NetHackQtBind::qt_nh_poskey
,
5267 NetHackQtBind::qt_nhbell
,
5268 NetHackQtBind::qt_doprev_message
,
5269 NetHackQtBind::qt_yn_function
,
5270 NetHackQtBind::qt_getlin
,
5271 NetHackQtBind::qt_get_ext_cmd
,
5272 NetHackQtBind::qt_number_pad
,
5273 NetHackQtBind::qt_delay_output
,
5274 #ifdef CHANGE_COLOR /* only a Mac option currently */
5278 /* other defs that really should go away (they're tty specific) */
5279 NetHackQtBind::qt_start_screen
,
5280 NetHackQtBind::qt_end_screen
,
5281 #ifdef GRAPHIC_TOMBSTONE
5282 NetHackQtBind::qt_outrip
,
5286 genl_preference_update
,
5289 #ifdef STATUS_VIA_WINDOWPORT
5292 genl_status_enablefield
,
5294 # ifdef STATUS_HILITES
5295 genl_status_threshold
,
5300 extern "C" void play_usersound(const char* filename
, int volume
)
5304 QSound::play(filename
);
5309 #include "qt_win.moc"
5311 #include "qt_kde0.moc"
5313 #if QT_VERSION >= 300
5314 #include "qttableview.moc"