From ed335dd0a7470713b91aa299e3667f249543012d Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Sat, 7 Oct 2017 20:26:02 +0300 Subject: [PATCH] Add Qt4 windowport Originally by Ray Chason for 3.4.3, based on the Qt windowport by Warwick Allison. The look and feel is mostly the same. Some improvements over the Qt 3 interface are: * Panes are resizable * Full support for IBMgraphics, and walls and corridors are drawn with graphical primitives for a continuous appearance no matter what the font says * Lots of irritating glitches fixed * Menus support proportional fonts correctly Adding this because the old Qt windowport cannot be compiled on Qt4, even with Qt3 compatibility stuff. TODO: - background map glyphs - status hilites - menucolors --- doc/fixes36.1 | 1 + include/extern.h | 3 + src/cmd.c | 9 +- sys/unix/Makefile.src | 93 +++- sys/unix/hints/linux-qt4 | 47 ++ win/Qt4/qt4bind.cpp | 745 ++++++++++++++++++++++++++ win/Qt4/qt4bind.h | 92 ++++ win/Qt4/qt4click.cpp | 48 ++ win/Qt4/qt4click.h | 37 ++ win/Qt4/qt4clust.cpp | 167 ++++++ win/Qt4/qt4clust.h | 29 ++ win/Qt4/qt4delay.cpp | 42 ++ win/Qt4/qt4delay.h | 26 + win/Qt4/qt4glyph.cpp | 141 +++++ win/Qt4/qt4glyph.h | 34 ++ win/Qt4/qt4icon.cpp | 203 ++++++++ win/Qt4/qt4icon.h | 53 ++ win/Qt4/qt4inv.cpp | 104 ++++ win/Qt4/qt4inv.h | 25 + win/Qt4/qt4kde0.h | 14 + win/Qt4/qt4key.cpp | 91 ++++ win/Qt4/qt4key.h | 40 ++ win/Qt4/qt4line.cpp | 42 ++ win/Qt4/qt4line.h | 22 + win/Qt4/qt4main.cpp | 1063 ++++++++++++++++++++++++++++++++++++++ win/Qt4/qt4main.h | 94 ++++ win/Qt4/qt4map.cpp | 964 ++++++++++++++++++++++++++++++++++ win/Qt4/qt4map.h | 81 +++ win/Qt4/qt4menu.cpp | 840 ++++++++++++++++++++++++++++++ win/Qt4/qt4menu.h | 182 +++++++ win/Qt4/qt4msg.cpp | 134 +++++ win/Qt4/qt4msg.h | 42 ++ win/Qt4/qt4plsel.cpp | 502 ++++++++++++++++++ win/Qt4/qt4plsel.h | 45 ++ win/Qt4/qt4rip.cpp | 94 ++++ win/Qt4/qt4rip.h | 30 ++ win/Qt4/qt4set.cpp | 186 +++++++ win/Qt4/qt4set.h | 57 ++ win/Qt4/qt4stat.cpp | 540 +++++++++++++++++++ win/Qt4/qt4stat.h | 106 ++++ win/Qt4/qt4str.cpp | 83 +++ win/Qt4/qt4str.h | 24 + win/Qt4/qt4streq.cpp | 99 ++++ win/Qt4/qt4streq.h | 30 ++ win/Qt4/qt4svsel.cpp | 80 +++ win/Qt4/qt4svsel.h | 21 + win/Qt4/qt4win.cpp | 136 +++++ win/Qt4/qt4win.h | 49 ++ win/Qt4/qt4xcmd.cpp | 134 +++++ win/Qt4/qt4xcmd.h | 31 ++ win/Qt4/qt4yndlg.cpp | 244 +++++++++ win/Qt4/qt4yndlg.h | 34 ++ 52 files changed, 8024 insertions(+), 9 deletions(-) create mode 100644 sys/unix/hints/linux-qt4 create mode 100644 win/Qt4/qt4bind.cpp create mode 100644 win/Qt4/qt4bind.h create mode 100644 win/Qt4/qt4click.cpp create mode 100644 win/Qt4/qt4click.h create mode 100644 win/Qt4/qt4clust.cpp create mode 100644 win/Qt4/qt4clust.h create mode 100644 win/Qt4/qt4delay.cpp create mode 100644 win/Qt4/qt4delay.h create mode 100644 win/Qt4/qt4glyph.cpp create mode 100644 win/Qt4/qt4glyph.h create mode 100644 win/Qt4/qt4icon.cpp create mode 100644 win/Qt4/qt4icon.h create mode 100644 win/Qt4/qt4inv.cpp create mode 100644 win/Qt4/qt4inv.h create mode 100644 win/Qt4/qt4kde0.h create mode 100644 win/Qt4/qt4key.cpp create mode 100644 win/Qt4/qt4key.h create mode 100644 win/Qt4/qt4line.cpp create mode 100644 win/Qt4/qt4line.h create mode 100644 win/Qt4/qt4main.cpp create mode 100644 win/Qt4/qt4main.h create mode 100644 win/Qt4/qt4map.cpp create mode 100644 win/Qt4/qt4map.h create mode 100644 win/Qt4/qt4menu.cpp create mode 100644 win/Qt4/qt4menu.h create mode 100644 win/Qt4/qt4msg.cpp create mode 100644 win/Qt4/qt4msg.h create mode 100644 win/Qt4/qt4plsel.cpp create mode 100644 win/Qt4/qt4plsel.h create mode 100644 win/Qt4/qt4rip.cpp create mode 100644 win/Qt4/qt4rip.h create mode 100644 win/Qt4/qt4set.cpp create mode 100644 win/Qt4/qt4set.h create mode 100644 win/Qt4/qt4stat.cpp create mode 100644 win/Qt4/qt4stat.h create mode 100644 win/Qt4/qt4str.cpp create mode 100644 win/Qt4/qt4str.h create mode 100644 win/Qt4/qt4streq.cpp create mode 100644 win/Qt4/qt4streq.h create mode 100644 win/Qt4/qt4svsel.cpp create mode 100644 win/Qt4/qt4svsel.h create mode 100644 win/Qt4/qt4win.cpp create mode 100644 win/Qt4/qt4win.h create mode 100644 win/Qt4/qt4xcmd.cpp create mode 100644 win/Qt4/qt4xcmd.h create mode 100644 win/Qt4/qt4yndlg.cpp create mode 100644 win/Qt4/qt4yndlg.h diff --git a/doc/fixes36.1 b/doc/fixes36.1 index 8e2d0f486..e6f3e7c78 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.1 @@ -718,6 +718,7 @@ Ray Chason's proper background tiles for lava and water Ray Chason's MS-DOS port restored to functionality with credit to Reddit user b_helyer for the fix to sys/share/pcmain.c Ray Chason's MSDOS port support for some VESA modes +Ray Chason's Qt4 windowport Darshan Shaligram's pet ranged attack Jason Dorje Short's key rebinding Maxime Bacoux's new DUMPLOG: compile-time option to enable logging of diff --git a/include/extern.h b/include/extern.h index 54ec9440c..018094dd4 100644 --- a/include/extern.h +++ b/include/extern.h @@ -168,6 +168,9 @@ E boolean NDECL(status_hilite_menu); /* ### cmd.c ### */ +E int NDECL(doconduct); +E int NDECL(domonability); +E char FDECL(cmd_from_func, (int NDECL((*)))); E boolean FDECL(redraw_cmd, (CHAR_P)); #ifdef USE_TRAMPOLI E int NDECL(doextcmd); diff --git a/src/cmd.c b/src/cmd.c index 6849f2ca4..add6ebd52 100644 --- a/src/cmd.c +++ b/src/cmd.c @@ -117,7 +117,6 @@ static int NDECL((*timed_occ_fn)); STATIC_PTR int NDECL(doprev_message); STATIC_PTR int NDECL(timed_occupation); STATIC_PTR int NDECL(doextcmd); -STATIC_PTR int NDECL(domonability); STATIC_PTR int NDECL(dotravel); STATIC_PTR int NDECL(doterrain); STATIC_PTR int NDECL(wiz_wish); @@ -164,9 +163,7 @@ STATIC_DCL boolean FDECL(accept_menu_prefix, (int NDECL((*)))); STATIC_DCL int NDECL(wiz_port_debug); #endif STATIC_PTR int NDECL(wiz_rumor_check); -STATIC_DCL char FDECL(cmd_from_func, (int NDECL((*)))); STATIC_PTR int NDECL(doattributes); -STATIC_PTR int NDECL(doconduct); /**/ STATIC_DCL void FDECL(enlght_line, (const char *, const char *, const char *, const char *)); @@ -523,7 +520,7 @@ extcmd_via_menu() #endif /* TTY_GRAPHICS */ /* #monster command - use special monster ability while polymorphed */ -STATIC_PTR int +int domonability(VOID_ARGS) { if (can_breathe(youmonst.data)) @@ -2734,7 +2731,7 @@ int msgflag; /* for variant message phrasing */ /* KMH, #conduct * (shares enlightenment's tense handling) */ -STATIC_PTR int +int doconduct(VOID_ARGS) { show_conduct(0); @@ -3283,7 +3280,7 @@ dokeylist(VOID_ARGS) destroy_nhwindow(datawin); } -STATIC_OVL char +char cmd_from_func(fn) int NDECL((*fn)); { diff --git a/sys/unix/Makefile.src b/sys/unix/Makefile.src index 5488e6106..8c27b612c 100644 --- a/sys/unix/Makefile.src +++ b/sys/unix/Makefile.src @@ -201,6 +201,22 @@ WINX11OBJ = Window.o dialogs.o winX.o winmap.o winmenu.o winmesg.o \ WINQTSRC = ../win/Qt/qt_win.cpp ../win/Qt/qt_clust.cpp ../win/Qt/qttableview.cpp WINQTOBJ = qt_win.o qt_clust.o qttableview.o tile.o # +# Files for a Qt 4 or 5 port +# +WINQT4SRC = ../win/Qt4/qt4bind.cpp ../win/Qt4/qt4click.cpp \ + ../win/Qt4/qt4clust.cpp ../win/Qt4/qt4delay.cpp \ + ../win/Qt4/qt4glyph.cpp ../win/Qt4/qt4icon.cpp ../win/Qt4/qt4inv.cpp \ + ../win/Qt4/qt4key.cpp ../win/Qt4/qt4line.cpp ../win/Qt4/qt4main.cpp \ + ../win/Qt4/qt4map.cpp ../win/Qt4/qt4menu.cpp ../win/Qt4/qt4msg.cpp \ + ../win/Qt4/qt4plsel.cpp ../win/Qt4/qt4rip.cpp ../win/Qt4/qt4set.cpp \ + ../win/Qt4/qt4stat.cpp ../win/Qt4/qt4str.cpp ../win/Qt4/qt4streq.cpp \ + ../win/Qt4/qt4svsel.cpp ../win/Qt4/qt4win.cpp ../win/Qt4/qt4xcmd.cpp \ + ../win/Qt4/qt4yndlg.cpp +WINQT4OBJ = qt4bind.o qt4click.o qt4clust.o qt4delay.o qt4glyph.o qt4icon.o \ + qt4inv.o qt4key.o qt4line.o qt4main.o qt4map.o qt4menu.o qt4msg.o \ + qt4plsel.o qt4rip.o qt4set.o qt4stat.o qt4str.o qt4streq.o qt4svsel.o \ + qt4win.o qt4xcmd.o qt4yndlg.o tile.o +# # Files for a Gnome port # WINGNOMESRC = ../win/gnome/gnaskstr.c ../win/gnome/gnbind.c \ @@ -253,9 +269,12 @@ WINX11LIB = -lXaw -lXmu -lXext -lXt -lX11 # WINX11LIB = -lXaw -lXmu -lXext -lXt -lXpm -lX11 -lm # WINX11LIB = -lXaw -lXmu -lXpm -lXext -lXt -lX11 -lSM -lICE -lm # BSD/OS 2.0 # -# libraries for Qt +# libraries for Qt 3 WINQTLIB = -L$(QTDIR)/lib -lqt # +# libraries for Qt 4 +WINQT4LIB = `pkg-config QtGui --libs` +# # libraries for KDE (with Qt) WINKDELIB = -lkdecore -lkdeui -lXext # @@ -360,7 +379,7 @@ GENCSRC = monstr.c vis_tab.c #tile.c # all windowing-system-dependent .c (for dependencies and such) WINCSRC = $(WINTTYSRC) $(WINX11SRC) $(WINGNOMESRC) $(WINGEMSRC) # all windowing-system-dependent .cpp (for dependencies and such) -WINCXXSRC = $(WINQTSRC) $(WINBESRC) +WINCXXSRC = $(WINQTSRC) $(WINQT4SRC) $(WINBESRC) # Files for window system chaining. Requires SYSCF; include via HINTSRC/HINTOBJ CHAINSRC = ../win/chain/wc_chainin.c ../win/chain/wc_chainout.c \ @@ -486,7 +505,7 @@ objects.o: $(CC) $(CFLAGS) -c objects.c @rm -f $(MAKEDEFS) -# Qt windowport meta-object-compiler output +# Qt 3 windowport meta-object-compiler output qt_kde0.moc: ../include/qt_kde0.h $(QTDIR)/bin/moc -o qt_kde0.moc ../include/qt_kde0.h @@ -496,6 +515,28 @@ qt_win.moc: ../include/qt_win.h qttableview.moc: ../include/qttableview.h $(QTDIR)/bin/moc -o qttableview.moc ../include/qttableview.h +# Qt 4 windowport meta-object-compiler output +qt4kde0.moc : ../win/Qt4/qt4kde0.h + $(QTDIR)/bin/moc -o qt4kde0.moc ../win/Qt4/qt4kde0.h +qt4main.moc : ../win/Qt4/qt4main.h + $(QTDIR)/bin/moc -o qt4main.moc ../win/Qt4/qt4main.h +qt4map.moc : ../win/Qt4/qt4map.h + $(QTDIR)/bin/moc -o qt4map.moc ../win/Qt4/qt4map.h +qt4menu.moc : ../win/Qt4/qt4menu.h + $(QTDIR)/bin/moc -o qt4menu.moc ../win/Qt4/qt4menu.h +qt4msg.moc : ../win/Qt4/qt4msg.h + $(QTDIR)/bin/moc -o qt4msg.moc ../win/Qt4/qt4msg.h +qt4plsel.moc : ../win/Qt4/qt4plsel.h + $(QTDIR)/bin/moc -o qt4plsel.moc ../win/Qt4/qt4plsel.h +qt4set.moc : ../win/Qt4/qt4set.h + $(QTDIR)/bin/moc -o qt4set.moc ../win/Qt4/qt4set.h +qt4stat.moc : ../win/Qt4/qt4stat.h + $(QTDIR)/bin/moc -o qt4stat.moc ../win/Qt4/qt4stat.h +qt4xcmd.moc : ../win/Qt4/qt4xcmd.h + $(QTDIR)/bin/moc -o qt4xcmd.moc ../win/Qt4/qt4xcmd.h +qt4yndlg.moc : ../win/Qt4/qt4yndlg.h + $(QTDIR)/bin/moc -o qt4yndlg.moc ../win/Qt4/qt4yndlg.h + # build monst.o and objects.o before executing '$(MAKE) makedefs' $(MAKEDEFS): $(FIRSTOBJ) \ ../util/makedefs.c $(CONFIG_H) ../include/permonst.h \ @@ -737,6 +778,52 @@ qt_clust.o: ../win/Qt/qt_clust.cpp ../include/qt_clust.h $(CXX) $(CXXFLAGS) -c ../win/Qt/qt_clust.cpp qttableview.o: ../win/Qt/qttableview.cpp ../include/qttableview.h $(CXX) $(CXXFLAGS) -c ../win/Qt/qttableview.cpp +qt4bind.o : ../win/Qt4/qt4bind.cpp $(HACK_H) ../win/Qt4/qt4bind.h + $(CXX) $(CXXFLAGS) -c ../win/Qt4/qt4bind.cpp +qt4click.o : ../win/Qt4/qt4click.cpp $(HACK_H) ../win/Qt4/qt4click.h + $(CXX) $(CXXFLAGS) -c ../win/Qt4/qt4click.cpp +qt4clust.o : ../win/Qt4/qt4clust.cpp $(HACK_H) ../win/Qt4/qt4clust.h + $(CXX) $(CXXFLAGS) -c ../win/Qt4/qt4clust.cpp +qt4delay.o : ../win/Qt4/qt4delay.cpp $(HACK_H) ../win/Qt4/qt4delay.h + $(CXX) $(CXXFLAGS) -c ../win/Qt4/qt4delay.cpp +qt4glyph.o : ../win/Qt4/qt4glyph.cpp $(HACK_H) ../win/Qt4/qt4glyph.h + $(CXX) $(CXXFLAGS) -c ../win/Qt4/qt4glyph.cpp +qt4icon.o : ../win/Qt4/qt4icon.cpp $(HACK_H) ../win/Qt4/qt4icon.h + $(CXX) $(CXXFLAGS) -c ../win/Qt4/qt4icon.cpp +qt4inv.o : ../win/Qt4/qt4inv.cpp $(HACK_H) ../win/Qt4/qt4inv.h + $(CXX) $(CXXFLAGS) -c ../win/Qt4/qt4inv.cpp +qt4key.o : ../win/Qt4/qt4key.cpp $(HACK_H) ../win/Qt4/qt4key.h + $(CXX) $(CXXFLAGS) -c ../win/Qt4/qt4key.cpp +qt4line.o : ../win/Qt4/qt4line.cpp $(HACK_H) ../win/Qt4/qt4line.h + $(CXX) $(CXXFLAGS) -c ../win/Qt4/qt4line.cpp +qt4main.o : ../win/Qt4/qt4main.cpp $(HACK_H) qt4main.moc ../win/Qt4/qt4main.h qt4kde0.moc + $(CXX) $(CXXFLAGS) -c ../win/Qt4/qt4main.cpp +qt4map.o : ../win/Qt4/qt4map.cpp $(HACK_H) qt4map.moc ../win/Qt4/qt4map.h + $(CXX) $(CXXFLAGS) -c ../win/Qt4/qt4map.cpp +qt4menu.o : ../win/Qt4/qt4menu.cpp $(HACK_H) qt4menu.moc ../win/Qt4/qt4menu.h + $(CXX) $(CXXFLAGS) -c ../win/Qt4/qt4menu.cpp +qt4msg.o : ../win/Qt4/qt4msg.cpp $(HACK_H) qt4msg.moc ../win/Qt4/qt4msg.h + $(CXX) $(CXXFLAGS) -c ../win/Qt4/qt4msg.cpp +qt4plsel.o : ../win/Qt4/qt4plsel.cpp $(HACK_H) qt4plsel.moc ../win/Qt4/qt4plsel.h + $(CXX) $(CXXFLAGS) -c ../win/Qt4/qt4plsel.cpp +qt4rip.o : ../win/Qt4/qt4rip.cpp $(HACK_H) ../win/Qt4/qt4rip.h + $(CXX) $(CXXFLAGS) -c ../win/Qt4/qt4rip.cpp +qt4set.o : ../win/Qt4/qt4set.cpp $(HACK_H) qt4set.moc ../win/Qt4/qt4set.h + $(CXX) $(CXXFLAGS) -c ../win/Qt4/qt4set.cpp +qt4stat.o : ../win/Qt4/qt4stat.cpp $(HACK_H) qt4stat.moc ../win/Qt4/qt4stat.h + $(CXX) $(CXXFLAGS) -c ../win/Qt4/qt4stat.cpp +qt4str.o : ../win/Qt4/qt4str.cpp ../win/Qt4/qt4str.h + $(CXX) $(CXXFLAGS) -c ../win/Qt4/qt4str.cpp +qt4streq.o : ../win/Qt4/qt4streq.cpp $(HACK_H) ../win/Qt4/qt4streq.h + $(CXX) $(CXXFLAGS) -c ../win/Qt4/qt4streq.cpp +qt4svsel.o : ../win/Qt4/qt4svsel.cpp $(HACK_H) ../win/Qt4/qt4svsel.h + $(CXX) $(CXXFLAGS) -c ../win/Qt4/qt4svsel.cpp +qt4win.o : ../win/Qt4/qt4win.cpp $(HACK_H) ../win/Qt4/qt4win.h + $(CXX) $(CXXFLAGS) -c ../win/Qt4/qt4win.cpp +qt4xcmd.o : ../win/Qt4/qt4xcmd.cpp $(HACK_H) qt4xcmd.moc ../win/Qt4/qt4xcmd.h + $(CXX) $(CXXFLAGS) -c ../win/Qt4/qt4xcmd.cpp +qt4yndlg.o : ../win/Qt4/qt4yndlg.cpp $(HACK_H) qt4yndlg.moc ../win/Qt4/qt4yndlg.h + $(CXX) $(CXXFLAGS) -c ../win/Qt4/qt4yndlg.cpp wc_chainin.o: ../win/chain/wc_chainin.c $(HACK_H) $(CC) $(CFLAGS) -c ../win/chain/wc_chainin.c wc_chainout.o: ../win/chain/wc_chainout.c $(HACK_H) diff --git a/sys/unix/hints/linux-qt4 b/sys/unix/hints/linux-qt4 new file mode 100644 index 000000000..0feddb9b5 --- /dev/null +++ b/sys/unix/hints/linux-qt4 @@ -0,0 +1,47 @@ +# +# NetHack 3.6 linux-x11 $NHDT-Date: 1432512814 2015/05/25 00:13:34 $ $NHDT-Branch: master $:$NHDT-Revision: 1.12 $ +# Copyright (c) Kenneth Lorber, Kensington, Maryland, 2007. +# NetHack may be freely redistributed. See license for details. +# +#-PRE +# Linux hints file +# This hints file provides a single-user Qt4 build for Linux, specifically +# for Ubuntu dapper. + + +#PREFIX=/usr +PREFIX=$(wildcard ~)/nh/install +HACKDIR=$(PREFIX)/games/lib/$(GAME)dir +SHELLDIR = $(PREFIX)/games +INSTDIR=$(HACKDIR) +VARDIR = $(HACKDIR) + + +POSTINSTALL= cp -n sys/unix/sysconf $(INSTDIR)/sysconf; $(CHOWN) $(GAMEUID) $(INSTDIR)/sysconf; $(CHGRP) $(GAMEGRP) $(INSTDIR)/sysconf; chmod $(VARFILEPERM) $(INSTDIR)/sysconf; +POSTINSTALL+= bdftopcf win/X11/nh10.bdf > $(INSTDIR)/nh10.pcf; (cd $(INSTDIR); mkfontdir); + +CFLAGS=-O -I../include -DNOTPARMDECL +CFLAGS+=-DHACKDIR=\"$(HACKDIR)\" +CFLAGS+=-DSYSCF -DSYSCF_FILE=\"$(HACKDIR)/sysconf\" +CFLAGS+=-DCOMPRESS=\"/bin/gzip\" -DCOMPRESS_EXTENSION=\".gz\" +CFLAGS+=-DQT_GRAPHICS -DDEFAULT_WINDOW_SYS=\"Qt\" -DNOTTYGRAPHICS +CFLAGS+=`pkg-config QtGui --cflags` + +LINK=g++ +CXX=g++ + +WINSRC = $(WINQT4SRC) +WINOBJ = $(WINQT4OBJ) +WINLIB = $(WINQT4LIB) + +VARDATND = nhtiles.bmp rip.xpm nhsplash.xpm pet_mark.xbm pilemark.xbm + +QTDIR=/usr + +CHOWN=true +CHGRP=true +VARDIRPERM = 0755 +VARFILEPERM = 0600 +GAMEPERM = 0755 + +# note: needs libxt-dev libxaw7-dev libx11-dev bdftopcf diff --git a/win/Qt4/qt4bind.cpp b/win/Qt4/qt4bind.cpp new file mode 100644 index 000000000..5dc17de99 --- /dev/null +++ b/win/Qt4/qt4bind.cpp @@ -0,0 +1,745 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4bind.cpp -- bindings between the Qt 4 interface and the main code + +extern "C" { +#include "hack.h" +} +#undef Invisible +#undef Warning +#undef index +#undef msleep +#undef rindex +#undef wizard +#undef yn +#undef min +#undef max + +#include +#if QT_VERSION >= 0x050000 +#include +#include +#else +#include +#endif +#include "qt4bind.h" +#include "qt4click.h" +#include "qt4delay.h" +#include "qt4xcmd.h" +#include "qt4key.h" +#include "qt4map.h" +#include "qt4menu.h" +#include "qt4msg.h" +#include "qt4plsel.h" +#include "qt4svsel.h" +#include "qt4set.h" +#include "qt4stat.h" +#include "qt4streq.h" +#include "qt4yndlg.h" +#include "qt4str.h" + +extern "C" { +#include "dlb.h" +} + +// temporary +extern int qt_compact_mode; +// end temporary + +namespace nethack_qt4 { + +// XXX Should be from Options +// +// XXX Hmm. Tricky part is that perhaps some macros should only be active +// XXX when a key is about to be gotten. For example, the user could +// XXX define "-" to do "E-yyyyyyyy\r", but would still need "-" for +// XXX other purposes. Maybe just too bad. +// +static struct key_macro_rec { + int key; + int state; + const char* macro; +} key_macro[]={ + { Qt::Key_F1, 0, "n100." }, // Rest (x100) + { Qt::Key_F2, 0, "n20s" }, // Search (x20) + { Qt::Key_Tab, 0, "\001" }, + { 0, 0, 0 } +}; + +NetHackQtBind::NetHackQtBind(int& argc, char** argv) : +#ifdef KDE + KApplication(argc,argv) +#elif defined(QWS) // not quite the right condition + QPEApplication(argc,argv) +#else + QApplication(argc,argv) +#endif +{ + QPixmap pm("nhsplash.xpm"); + if ( iflags.wc_splash_screen && !pm.isNull() ) { + splash = new QFrame(NULL, + Qt::FramelessWindowHint | Qt::X11BypassWindowManagerHint | Qt::WindowStaysOnTopHint ); + QVBoxLayout *vb = new QVBoxLayout(splash); + QLabel *lsplash = new QLabel(splash); + vb->addWidget(lsplash); + lsplash->setAlignment(Qt::AlignCenter); + lsplash->setPixmap(pm); + QLabel* capt = new QLabel("Loading...",splash); + vb->addWidget(capt); + capt->setAlignment(Qt::AlignCenter); + if ( !pm.isNull() ) { + lsplash->setFixedSize(pm.size()); + lsplash->setMask(pm); + } + splash->move((QApplication::desktop()->width()-pm.width())/2, + (QApplication::desktop()->height()-pm.height())/2); + //splash->setGeometry(0,0,100,100); + if ( qt_compact_mode ) { + splash->showMaximized(); + } else { + splash->setFrameStyle(QFrame::WinPanel|QFrame::Raised); + splash->setLineWidth(10); + splash->adjustSize(); + splash->show(); + } + + // force content refresh outside event loop + splash->repaint(); + lsplash->repaint(); + capt->repaint(); + qApp->flush(); + + } else { + splash = 0; + } + main = new NetHackQtMainWindow(keybuffer); + connect(qApp, SIGNAL(lastWindowClosed()), qApp, SLOT(quit())); + qt_settings=new NetHackQtSettings(main->width(),main->height()); +} + +void NetHackQtBind::qt_init_nhwindows(int* argc, char** argv) +{ + iflags.menu_tab_sep = true; + +#ifdef UNIX +// Userid control +// +// Michael Hohmuth ... +// +// As the game runs setuid games, it must seteuid(getuid()) before +// calling XOpenDisplay(), and reset the euid afterwards. +// Otherwise, it can't read the $HOME/.Xauthority file and whines about +// not being able to open the X display (if a magic-cookie +// authorization mechanism is being used). + + uid_t gamesuid=geteuid(); + seteuid(getuid()); +#endif + + QApplication::setColorSpec(ManyColor); + instance=new NetHackQtBind(*argc,argv); + +#ifdef UNIX + seteuid(gamesuid); +#endif + +#ifdef _WS_WIN_ + // This nethack engine feature should be moved into windowport API + nt_kbhit = NetHackQtBind::qt_kbhit; +#endif +} + +int NetHackQtBind::qt_kbhit() +{ + return !keybuffer.Empty(); +} + + +static bool have_asked = false; + +void NetHackQtBind::qt_player_selection() +{ + if ( !have_asked ) + qt_askname(); +} + +void NetHackQtBind::qt_askname() +{ + have_asked = true; + + // We do it all here, and nothing in askname + + char** saved = get_saved_games(); + int ch = -1; + if ( saved && *saved ) { + if ( splash ) splash->hide(); + NetHackQtSavedGameSelector sgsel((const char**)saved); + ch = sgsel.choose(); + if ( ch >= 0 ) + str_copy(plname, saved[ch], SIZE(plname)); + } + free_saved_games(saved); + + switch (ch) { + case -1: + if ( splash ) splash->hide(); + if (NetHackQtPlayerSelector(keybuffer).Choose()) + return; + case -2: + break; + default: + return; + } + + // Quit + clearlocks(); + qt_exit_nhwindows(0); + nh_terminate(0); +} + +void NetHackQtBind::qt_get_nh_event() +{ +} + +#if defined(QWS) +// Kludge to access lastWindowClosed() signal. +class TApp : public QApplication { +public: + TApp(int& c, char**v) : QApplication(c,v) {} + void lwc() { emit lastWindowClosed(); } +}; +#endif + +void NetHackQtBind::qt_exit_nhwindows(const char *) +{ +#if defined(QWS) + // Avoids bug in SHARP SL5500 + ((TApp*)qApp)->lwc(); + qApp->quit(); +#endif + + delete instance; // ie. qApp +} + +void NetHackQtBind::qt_suspend_nhwindows(const char *) +{ +} + +void NetHackQtBind::qt_resume_nhwindows() +{ +} + +static QVector id_to_window; + +winid NetHackQtBind::qt_create_nhwindow(int type) +{ + winid id; + for (id = 0; id < (winid) id_to_window.size(); id++) { + if ( !id_to_window[(int)id] ) + break; + } + if ( id == (winid) id_to_window.size() ) + id_to_window.resize(id+1); + + NetHackQtWindow* window=0; + + switch (type) { + case NHW_MAP: { + NetHackQtMapWindow2* w=new NetHackQtMapWindow2(clickbuffer); + main->AddMapWindow(w); + window=w; + } break; case NHW_MESSAGE: { + NetHackQtMessageWindow* w=new NetHackQtMessageWindow; + main->AddMessageWindow(w); + window=w; + } break; case NHW_STATUS: { + NetHackQtStatusWindow* w=new NetHackQtStatusWindow; + main->AddStatusWindow(w); + window=w; + } break; case NHW_MENU: + window=new NetHackQtMenuOrTextWindow(mainWidget()); + break; case NHW_TEXT: + window=new NetHackQtTextWindow(mainWidget()); + } + + window->nhid = id; + + // Note: use of isHidden does not work with Qt 2.1 + if ( splash +#if QT_VERSION >= 300 + && !main->isHidden() +#else + && main->isVisible() +#endif + ) + { + delete splash; + splash = 0; + } + + id_to_window[(int)id] = window; + return id; +} + +void NetHackQtBind::qt_clear_nhwindow(winid wid) +{ + NetHackQtWindow* window=id_to_window[(int)wid]; + window->Clear(); +} + +void NetHackQtBind::qt_display_nhwindow(winid wid, BOOLEAN_P block) +{ + NetHackQtWindow* window=id_to_window[(int)wid]; + window->Display(block); +} + +void NetHackQtBind::qt_destroy_nhwindow(winid wid) +{ + NetHackQtWindow* window=id_to_window[(int)wid]; + main->RemoveWindow(window); + if (window->Destroy()) + delete window; + id_to_window[(int)wid] = 0; +} + +void NetHackQtBind::qt_curs(winid wid, int x, int y) +{ + NetHackQtWindow* window=id_to_window[(int)wid]; + window->CursorTo(x,y); +} + +void NetHackQtBind::qt_putstr(winid wid, int attr, const char *text) +{ + NetHackQtWindow* window=id_to_window[(int)wid]; + window->PutStr(attr,QString::fromLatin1(text)); +} + +void NetHackQtBind::qt_putstr(winid wid, int attr, const std::string& text) +{ + NetHackQtWindow* window=id_to_window[(int)wid]; + window->PutStr(attr,QString::fromLatin1(text.c_str(), text.size())); +} + +void NetHackQtBind::qt_putstr(winid wid, int attr, const QString& text) +{ + NetHackQtWindow* window=id_to_window[(int)wid]; + window->PutStr(attr,text); +} + +void NetHackQtBind::qt_display_file(const char *filename, BOOLEAN_P must_exist) +{ + NetHackQtTextWindow* window=new NetHackQtTextWindow(mainWidget()); + bool complain = false; + + { + dlb *f; + char buf[BUFSZ]; + char *cr; + + window->Clear(); + f = dlb_fopen(filename, "r"); + if (!f) { + complain = must_exist; + } else { + while (dlb_fgets(buf, BUFSZ, f)) { + if ((cr = index(buf, '\n')) != 0) *cr = 0; +#ifdef MSDOS + if ((cr = index(buf, '\r')) != 0) *cr = 0; +#endif + window->PutStr(ATR_NONE, tabexpand(buf)); + } + window->Display(false); + (void) dlb_fclose(f); + } + } + + if (complain) { + QString message; + message.sprintf("File not found: %s\n",filename); + QMessageBox::warning(NULL, "File Error", message, QMessageBox::Ignore); + } +} + +void NetHackQtBind::qt_start_menu(winid wid) +{ + NetHackQtWindow* window=id_to_window[(int)wid]; + window->StartMenu(); +} + +void NetHackQtBind::qt_add_menu(winid wid, int glyph, + const ANY_P * identifier, CHAR_P ch, CHAR_P gch, int attr, + const char *str, BOOLEAN_P presel) +{ + NetHackQtWindow* window=id_to_window[(int)wid]; + window->AddMenu(glyph, identifier, ch, gch, attr, + QString::fromLatin1(str), + presel); +} + +void NetHackQtBind::qt_end_menu(winid wid, const char *prompt) +{ + NetHackQtWindow* window=id_to_window[(int)wid]; + window->EndMenu(prompt); +} + +int NetHackQtBind::qt_select_menu(winid wid, int how, MENU_ITEM_P **menu_list) +{ + NetHackQtWindow* window=id_to_window[(int)wid]; + return window->SelectMenu(how,menu_list); +} + +void NetHackQtBind::qt_update_inventory() +{ + if (main) + main->updateInventory(); + /* doesn't work yet + if (program_state.something_worth_saving && flags.perm_invent) + display_inventory(NULL, false); + */ +} + +void NetHackQtBind::qt_mark_synch() +{ +} + +void NetHackQtBind::qt_wait_synch() +{ +} + +void NetHackQtBind::qt_cliparound(int x, int y) +{ + // XXXNH - winid should be a parameter! + qt_cliparound_window(WIN_MAP,x,y); +} + +void NetHackQtBind::qt_cliparound_window(winid wid, int x, int y) +{ + NetHackQtWindow* window=id_to_window[(int)wid]; + window->ClipAround(x,y); +} +void NetHackQtBind::qt_print_glyph(winid wid,XCHAR_P x,XCHAR_P y,int glyph,int bkglyph) +{ + /* TODO: bkglyph */ + NetHackQtWindow* window=id_to_window[(int)wid]; + window->PrintGlyph(x,y,glyph); +} +//void NetHackQtBind::qt_print_glyph_compose(winid wid,xchar x,xchar y,int glyph1, int glyph2) +//{ + //NetHackQtWindow* window=id_to_window[(int)wid]; + //window->PrintGlyphCompose(x,y,glyph1,glyph2); +//} + +void NetHackQtBind::qt_raw_print(const char *str) +{ + puts(str); +} + +void NetHackQtBind::qt_raw_print_bold(const char *str) +{ + puts(str); +} + +int NetHackQtBind::qt_nhgetch() +{ + if (main) + main->fadeHighlighting(); + + // Process events until a key arrives. + // + while (keybuffer.Empty()) { + qApp->exec(); + } + + return keybuffer.GetAscii(); +} + +int NetHackQtBind::qt_nh_poskey(int *x, int *y, int *mod) +{ + if (main) + main->fadeHighlighting(); + + // Process events until a key or map-click arrives. + // + while (keybuffer.Empty() && clickbuffer.Empty()) { + qApp->exec(); + } + if (!keybuffer.Empty()) { + return keybuffer.GetAscii(); + } else { + *x=clickbuffer.NextX(); + *y=clickbuffer.NextY(); + *mod=clickbuffer.NextMod(); + clickbuffer.Get(); + return 0; + } +} + +void NetHackQtBind::qt_nhbell() +{ + QApplication::beep(); +} + +int NetHackQtBind::qt_doprev_message() +{ + // Don't need it - uses scrollbar + // XXX but could make this a shortcut + return 0; +} + +char NetHackQtBind::qt_yn_function(const char *question_, const char *choices, CHAR_P def) +{ + QString question(QString::fromLatin1(question_)); + + if (qt_settings->ynInMessages() && WIN_MESSAGE!=WIN_ERR) { + // Similar to X11 windowport `slow' feature. + + QString message; + char yn_esc_map='\033'; + + if (choices) { + // anything beyond is hidden> + QString choicebuf = choices; + size_t cb = choicebuf.indexOf('\033'); + choicebuf = choicebuf.mid(0U, cb); + message = QString("%1 [%2] ").arg(question, choicebuf); + if (def) message += QString("(%1) ").arg(QChar(def)); + // escape maps to 'q' or 'n' or default, in that order + yn_esc_map = (index(choices, 'q') ? 'q' : + (index(choices, 'n') ? 'n' : def)); + } else { + message = question; + } + +#ifdef USE_POPUPS + // Improve some special-cases (DIRKS 08/02/23) + if (strcmp (choices,"ynq") == 0) { + switch (QMessageBox::information (NetHackQtBind::mainWidget(),"NetHack",question,"&Yes","&No","&Quit",0,2)) + { + case 0: return 'y'; + case 1: return 'n'; + case 2: return 'q'; + } + } + + if (strcmp (choices,"yn") == 0) { + switch (QMessageBox::information(NetHackQtBind::mainWidget(),"NetHack",question,"&Yes", "&No",0,1)) + { + case 0: return 'y'; + case 1: return 'n'; + } + } +#endif + + NetHackQtBind::qt_putstr(WIN_MESSAGE, ATR_BOLD, message); + + int result=-1; + while (result<0) { + char ch=NetHackQtBind::qt_nhgetch(); + if (ch=='\033') { + result=yn_esc_map; + } else if (choices && !index(choices,ch)) { + if (def && (ch==' ' || ch=='\r' || ch=='\n')) { + result=def; + } else { + NetHackQtBind::qt_nhbell(); + // and try again... + } + } else { + result=ch; + } + } + + NetHackQtBind::qt_clear_nhwindow(WIN_MESSAGE); + + return result; + } else { + NetHackQtYnDialog dialog(mainWidget(),question,choices,def); + return dialog.Exec(); + } +} + +void NetHackQtBind::qt_getlin(const char *prompt, char *line) +{ + NetHackQtStringRequestor requestor(mainWidget(),prompt); + if (!requestor.Get(line)) { + line[0]=0; + } +} + +int NetHackQtBind::qt_get_ext_cmd() +{ + NetHackQtExtCmdRequestor requestor(mainWidget()); + return requestor.get(); +} + +void NetHackQtBind::qt_number_pad(int) +{ + // Ignore. +} + +void NetHackQtBind::qt_delay_output() +{ + NetHackQtDelay delay(15); + delay.wait(); +} + +void NetHackQtBind::qt_start_screen() +{ + // Ignore. +} + +void NetHackQtBind::qt_end_screen() +{ + // Ignore. +} + +void NetHackQtBind::qt_outrip(winid wid, int how, time_t when) +{ + NetHackQtWindow* window=id_to_window[(int)wid]; + window->UseRIP(how, when); +} + +bool NetHackQtBind::notify(QObject *receiver, QEvent *event) +{ + // Ignore Alt-key navigation to menubar, it's annoying when you + // use Alt-Direction to move around. + if ( main && event->type()==QEvent::KeyRelease && main==receiver + && ((QKeyEvent*)event)->key() == Qt::Key_Alt ) + return true; + + bool result=QApplication::notify(receiver,event); + if (event->type()==QEvent::KeyPress) { + QKeyEvent* key_event=(QKeyEvent*)event; + + if (!key_event->isAccepted()) { + const int k=key_event->key(); + bool macro=false; + for (int i=0; !macro && key_macro[i].key; i++) { + if (key_macro[i].key==k + && ((key_macro[i].state&key_event->modifiers())==key_macro[i].state)) + { + keybuffer.Put(key_macro[i].macro); + macro=true; + } + } + QString key=key_event->text(); + QChar ch = !key.isEmpty() ? key.at(0) : 0; + if (ch > 128) ch = 0; + if ( ch == 0 && (key_event->modifiers() & Qt::ControlModifier) ) { + // On Mac, ascii control codes are not sent, force them. + if ( k>=Qt::Key_A && k<=Qt::Key_Z ) + ch = k - Qt::Key_A + 1; + } + if (!macro && ch != 0) { + bool alt = (key_event->modifiers()&Qt::AltModifier) || + (k >= Qt::Key_0 && k <= Qt::Key_9 && (key_event->modifiers()&Qt::ControlModifier)); + keybuffer.Put(key_event->key(),ch.cell() + (alt ? 128 : 0), + key_event->modifiers()); + key_event->accept(); + result=true; + } + + if (ch != 0 || macro) { + qApp->exit(); + } + } + } + return result; +} + +NetHackQtBind* NetHackQtBind::instance=0; +NetHackQtKeyBuffer NetHackQtBind::keybuffer; +NetHackQtClickBuffer NetHackQtBind::clickbuffer; +NetHackQtMainWindow* NetHackQtBind::main=0; +QFrame* NetHackQtBind::splash=0; + +static void Qt_positionbar(char *) {} + +} // namespace nethack_qt4 + +struct window_procs Qt_procs = { + "Qt", + WC_COLOR | WC_HILITE_PET + | WC_ASCII_MAP | WC_TILED_MAP + | WC_FONT_MAP | WC_TILE_FILE | WC_TILE_WIDTH | WC_TILE_HEIGHT + | WC_PLAYER_SELECTION | WC_SPLASH_SCREEN, + 0L, + nethack_qt4::NetHackQtBind::qt_init_nhwindows, + nethack_qt4::NetHackQtBind::qt_player_selection, + nethack_qt4::NetHackQtBind::qt_askname, + nethack_qt4::NetHackQtBind::qt_get_nh_event, + nethack_qt4::NetHackQtBind::qt_exit_nhwindows, + nethack_qt4::NetHackQtBind::qt_suspend_nhwindows, + nethack_qt4::NetHackQtBind::qt_resume_nhwindows, + nethack_qt4::NetHackQtBind::qt_create_nhwindow, + nethack_qt4::NetHackQtBind::qt_clear_nhwindow, + nethack_qt4::NetHackQtBind::qt_display_nhwindow, + nethack_qt4::NetHackQtBind::qt_destroy_nhwindow, + nethack_qt4::NetHackQtBind::qt_curs, + nethack_qt4::NetHackQtBind::qt_putstr, + nethack_qt4::NetHackQtBind::qt_putstr, /* FIXME: should be qt_putmixed() */ + nethack_qt4::NetHackQtBind::qt_display_file, + nethack_qt4::NetHackQtBind::qt_start_menu, + nethack_qt4::NetHackQtBind::qt_add_menu, + nethack_qt4::NetHackQtBind::qt_end_menu, + nethack_qt4::NetHackQtBind::qt_select_menu, + genl_message_menu, /* no need for X-specific handling */ + nethack_qt4::NetHackQtBind::qt_update_inventory, + nethack_qt4::NetHackQtBind::qt_mark_synch, + nethack_qt4::NetHackQtBind::qt_wait_synch, +#ifdef CLIPPING + nethack_qt4::NetHackQtBind::qt_cliparound, +#endif +#ifdef POSITIONBAR + nethack_qt4::Qt_positionbar, +#endif + nethack_qt4::NetHackQtBind::qt_print_glyph, + //NetHackQtBind::qt_print_glyph_compose, + nethack_qt4::NetHackQtBind::qt_raw_print, + nethack_qt4::NetHackQtBind::qt_raw_print_bold, + nethack_qt4::NetHackQtBind::qt_nhgetch, + nethack_qt4::NetHackQtBind::qt_nh_poskey, + nethack_qt4::NetHackQtBind::qt_nhbell, + nethack_qt4::NetHackQtBind::qt_doprev_message, + nethack_qt4::NetHackQtBind::qt_yn_function, + nethack_qt4::NetHackQtBind::qt_getlin, + nethack_qt4::NetHackQtBind::qt_get_ext_cmd, + nethack_qt4::NetHackQtBind::qt_number_pad, + nethack_qt4::NetHackQtBind::qt_delay_output, +#ifdef CHANGE_COLOR /* only a Mac option currently */ + donull, + donull, + donull, + donull, +#endif + /* other defs that really should go away (they're tty specific) */ + nethack_qt4::NetHackQtBind::qt_start_screen, + nethack_qt4::NetHackQtBind::qt_end_screen, +#ifdef GRAPHIC_TOMBSTONE + nethack_qt4::NetHackQtBind::qt_outrip, +#else + genl_outrip, +#endif + genl_preference_update, + + genl_getmsghistory, genl_putmsghistory, + genl_status_init, + genl_status_finish, genl_status_enablefield, +#ifdef STATUS_HILITES + genl_status_update, +#else + genl_status_update, +#endif + genl_can_suspend_yes, +}; + +extern "C" void play_usersound(const char* filename, int volume) +{ +#ifdef USER_SOUNDS +#ifndef QT_NO_SOUND + QSound::play(filename); +#endif +#endif +} diff --git a/win/Qt4/qt4bind.h b/win/Qt4/qt4bind.h new file mode 100644 index 000000000..99a2c0d11 --- /dev/null +++ b/win/Qt4/qt4bind.h @@ -0,0 +1,92 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4bind.h -- bindings between the Qt 4 interface and the main code + +#ifndef QT4BIND_H +#define QT4BIND_H + +#include "qt4main.h" + +namespace nethack_qt4 { + +class NetHackQtClickBuffer; + +#ifdef KDE +#define NetHackQtBindBase KApplication +#elif defined(QWS) +#define NetHackQtBindBase QPEApplication +#else +#define NetHackQtBindBase QApplication +#endif + +class NetHackQtBind : NetHackQtBindBase { +private: + // Single-instance preservation... + NetHackQtBind(int& argc, char** argv); + + static NetHackQtBind* instance; + + static NetHackQtKeyBuffer keybuffer; + static NetHackQtClickBuffer clickbuffer; + + static QFrame* splash; + static NetHackQtMainWindow* main; + +public: + static void qt_init_nhwindows(int* argc, char** argv); + static void qt_player_selection(); + static void qt_askname(); + static void qt_get_nh_event(); + static void qt_exit_nhwindows(const char *); + static void qt_suspend_nhwindows(const char *); + static void qt_resume_nhwindows(); + static winid qt_create_nhwindow(int type); + static void qt_clear_nhwindow(winid wid); + static void qt_display_nhwindow(winid wid, BOOLEAN_P block); + static void qt_destroy_nhwindow(winid wid); + static void qt_curs(winid wid, int x, int y); + static void qt_putstr(winid wid, int attr, const char *text); + static void qt_putstr(winid wid, int attr, const std::string& text); + static void qt_putstr(winid wid, int attr, const QString& text); + static void qt_display_file(const char *filename, BOOLEAN_P must_exist); + static void qt_start_menu(winid wid); + static void qt_add_menu(winid wid, int glyph, + const ANY_P * identifier, CHAR_P ch, CHAR_P gch, int attr, + const char *str, BOOLEAN_P presel); + static void qt_end_menu(winid wid, const char *prompt); + static int qt_select_menu(winid wid, int how, MENU_ITEM_P **menu_list); + static void qt_update_inventory(); + static void qt_mark_synch(); + static void qt_wait_synch(); + + static void qt_cliparound(int x, int y); + static void qt_cliparound_window(winid wid, int x, int y); + static void qt_print_glyph(winid wid,XCHAR_P x,XCHAR_P y,int glyph,int bkglyph); + static void qt_raw_print(const char *str); + static void qt_raw_print_bold(const char *str); + static int qt_nhgetch(); + static int qt_nh_poskey(int *x, int *y, int *mod); + static void qt_nhbell(); + static int qt_doprev_message(); + static char qt_yn_function(const char *question, const char *choices, CHAR_P def); + static void qt_getlin(const char *prompt, char *line); + static int qt_get_ext_cmd(); + static void qt_number_pad(int); + static void qt_delay_output(); + static void qt_start_screen(); + static void qt_end_screen(); + + static void qt_outrip(winid wid, int how, time_t when); + static int qt_kbhit(); + + static QWidget *mainWidget() { return main; } + +private: + virtual bool notify(QObject *receiver, QEvent *event); +}; + +} // namespace nethack_qt4 + +#endif diff --git a/win/Qt4/qt4click.cpp b/win/Qt4/qt4click.cpp new file mode 100644 index 000000000..78177a14f --- /dev/null +++ b/win/Qt4/qt4click.cpp @@ -0,0 +1,48 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4click.cpp -- a mouse click buffer + +#include "hack.h" +#undef Invisible +#undef Warning +#undef index +#undef msleep +#undef rindex +#undef wizard +#undef yn +#undef min +#undef max + +#include +#include "qt4click.h" + +namespace nethack_qt4 { + +NetHackQtClickBuffer::NetHackQtClickBuffer() : + in(0), out(0) +{ +} + +bool NetHackQtClickBuffer::Empty() const { return in==out; } +bool NetHackQtClickBuffer::Full() const { return (in+1)%maxclick==out; } + +void NetHackQtClickBuffer::Put(int x, int y, int mod) +{ + click[in].x=x; + click[in].y=y; + click[in].mod=mod; + in=(in+1)%maxclick; +} + +int NetHackQtClickBuffer::NextX() const { return click[out].x; } +int NetHackQtClickBuffer::NextY() const { return click[out].y; } +int NetHackQtClickBuffer::NextMod() const { return click[out].mod; } + +void NetHackQtClickBuffer::Get() +{ + out=(out+1)%maxclick; +} + +} // namespace nethack_qt4 diff --git a/win/Qt4/qt4click.h b/win/Qt4/qt4click.h new file mode 100644 index 000000000..50fd8b1cc --- /dev/null +++ b/win/Qt4/qt4click.h @@ -0,0 +1,37 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4click.h -- a mouse click buffer + +#ifndef QT4CLICK_H +#define QT4CLICK_H + +namespace nethack_qt4 { + +class NetHackQtClickBuffer { +public: + NetHackQtClickBuffer(); + + bool Empty() const; + bool Full() const; + + void Put(int x, int y, int mod); + + int NextX() const; + int NextY() const; + int NextMod() const; + + void Get(); + +private: + enum { maxclick=64 }; + struct ClickRec { + int x,y,mod; + } click[maxclick]; + int in,out; +}; + +} // namespace nethack_qt4 + +#endif diff --git a/win/Qt4/qt4clust.cpp b/win/Qt4/qt4clust.cpp new file mode 100644 index 000000000..1cd080c11 --- /dev/null +++ b/win/Qt4/qt4clust.cpp @@ -0,0 +1,167 @@ +/* SCCS Id: @(#)qt_clust.cpp 3.4 1999/11/19 */ +/* Copyright (c) Warwick Allison, 1999. */ +/* NetHack may be freely redistributed. See license for details. */ +#include "qt4clust.h" + +static void include(QRect& r, const QRect& rect) +{ + if (rect.left()r.right()) { + r.setRight(rect.right()); + } + if (rect.top()r.bottom()) { + r.setBottom(rect.bottom()); + } +} + +/* +A Clusterizer groups rectangles (QRects) into non-overlapping rectangles +by a merging heuristic. +*/ +Clusterizer::Clusterizer(int maxclusters) : + cluster(new QRect[maxclusters]), + count(0), + max(maxclusters) +{ } + +Clusterizer::~Clusterizer() +{ + delete [] cluster; +} + +void Clusterizer::clear() +{ + count=0; +} + +void Clusterizer::add(int x, int y) +{ + add(QRect(x,y,1,1)); +} + +void Clusterizer::add(int x, int y, int w, int h) +{ + add(QRect(x,y,w,h)); +} + +void Clusterizer::add(const QRect& rect) +{ + QRect biggerrect(rect.x()-1,rect.y()-1,rect.width()+2,rect.height()+2); + + //assert(rect.width()>0 && rect.height()>0); + + int cursor; + + for (cursor=0; cursor=0) { + include(cluster[cheapest],rect); + return; + } + + if (count < max) { + cluster[count++]=rect; + return; + } + + // Do cheapest of: + // add to closest cluster + // do cheapest cluster merge, add to new cluster + + lowestcost=9999999; + cheapest=-1; + for (cursor=0; cursor=0) { + include(cluster[cheapestmerge1],cluster[cheapestmerge2]); + cluster[cheapestmerge2]=cluster[count--]; + } else { + // if (!cheapest) debugRectangles(rect); + include(cluster[cheapest],rect); + } + + // NB: clusters do not intersect (or intersection will + // overwrite). This is a result of the above algorithm, + // given the assumption that (x,y) are ordered topleft + // to bottomright. +} + +const QRect& Clusterizer::operator[](int i) +{ + return cluster[i]; +} diff --git a/win/Qt4/qt4clust.h b/win/Qt4/qt4clust.h new file mode 100644 index 000000000..c7112ea11 --- /dev/null +++ b/win/Qt4/qt4clust.h @@ -0,0 +1,29 @@ +/* SCCS Id: @(#)qt_clust.h 3.4 1999/11/19 */ +/* Copyright (c) Warwick Allison, 1999. */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef clusterizer_H +#define clusterizer_H + +#include + +class Clusterizer { +public: + Clusterizer(int maxclusters); + ~Clusterizer(); + + void add(int x, int y); // 1x1 rectangle (point) + void add(int x, int y, int w, int h); + void add(const QRect& rect); + + void clear(); + int clusters() { return count; } + const QRect& operator[](int i); + +private: + QRect* cluster; + int count; + const int max; +}; + +#endif diff --git a/win/Qt4/qt4delay.cpp b/win/Qt4/qt4delay.cpp new file mode 100644 index 000000000..eadf42a94 --- /dev/null +++ b/win/Qt4/qt4delay.cpp @@ -0,0 +1,42 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4delay.cpp -- implement a delay + +#include "hack.h" +#undef Invisible +#undef Warning +#undef index +#undef msleep +#undef rindex +#undef wizard +#undef yn +#undef min +#undef max + +#include +#include "qt4delay.h" + +namespace nethack_qt4 { + +// RLC Can we use QTimer::single_shot for this? +NetHackQtDelay::NetHackQtDelay(int ms) : + msec(ms), m_timer(0), m_loop(this) +{ +} + +void NetHackQtDelay::wait() +{ + m_timer = startTimer(msec); + m_loop.exec(); +} + +void NetHackQtDelay::timerEvent(QTimerEvent* timer) +{ + m_loop.exit(); + killTimer(m_timer); + m_timer = 0; +} + +} // namespace nethack_qt4 diff --git a/win/Qt4/qt4delay.h b/win/Qt4/qt4delay.h new file mode 100644 index 000000000..2e0085edf --- /dev/null +++ b/win/Qt4/qt4delay.h @@ -0,0 +1,26 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4delay.h -- implement a delay + +#ifndef QT4DELAY_H +#define QT4DELAY_H + +namespace nethack_qt4 { + +class NetHackQtDelay : QObject { +private: + int msec; + int m_timer; + QEventLoop m_loop; + +public: + NetHackQtDelay(int ms); + void wait(); + virtual void timerEvent(QTimerEvent* timer); +}; + +} // namespace nethack_qt4 + +#endif diff --git a/win/Qt4/qt4glyph.cpp b/win/Qt4/qt4glyph.cpp new file mode 100644 index 000000000..3ba56be6f --- /dev/null +++ b/win/Qt4/qt4glyph.cpp @@ -0,0 +1,141 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4glyph.cpp -- class to manage the glyphs in a tile set + +extern "C" { +#include "hack.h" +} +#include "tile2x11.h" +#undef Invisible +#undef Warning +#undef index +#undef msleep +#undef rindex +#undef wizard +#undef yn +#undef min +#undef max + +#include +#if QT_VERSION >= 0x050000 +#include +#endif +#include "qt4glyph.h" +#include "qt4set.h" +#include "qt4str.h" + +extern short glyph2tile[]; // from tile.c + +namespace nethack_qt4 { + +static int tilefile_tile_W=16; +static int tilefile_tile_H=16; + +// Debian uses a separate PIXMAPDIR +#ifndef PIXMAPDIR +# ifdef HACKDIR +# define PIXMAPDIR HACKDIR +# else +# define PIXMAPDIR "." +# endif +#endif + +NetHackQtGlyphs::NetHackQtGlyphs() +{ + const char* tile_file = PIXMAPDIR "/nhtiles.bmp"; + if ( iflags.wc_tile_file ) + tile_file = iflags.wc_tile_file; + + if (!img.load(tile_file)) { + tile_file = PIXMAPDIR "/x11tiles"; + if (!img.load(tile_file)) { + QString msg; + msg.sprintf("Cannot load x11tiles or nhtiles.bmp"); + QMessageBox::warning(0, "IO Error", msg); + } else { + tiles_per_row = TILES_PER_ROW; + if (img.width()%tiles_per_row) { + impossible("Tile file \"%s\" has %d columns, not multiple of row count (%d)", + tile_file, img.width(), tiles_per_row); + } + } + } else { + tiles_per_row = 40; + } + + if ( iflags.wc_tile_width ) + tilefile_tile_W = iflags.wc_tile_width; + else + tilefile_tile_W = img.width() / tiles_per_row; + if ( iflags.wc_tile_height ) + tilefile_tile_H = iflags.wc_tile_height; + else + tilefile_tile_H = tilefile_tile_W; + + setSize(tilefile_tile_W, tilefile_tile_H); +} + +void NetHackQtGlyphs::drawGlyph(QPainter& painter, int glyph, int x, int y) +{ + int tile = glyph2tile[glyph]; + int px = (tile%tiles_per_row)*width(); + int py = tile/tiles_per_row*height(); + + painter.drawPixmap( + x, + y, + pm, + px,py, + width(),height() + ); +} +void NetHackQtGlyphs::drawCell(QPainter& painter, int glyph, int cellx, int celly) +{ + drawGlyph(painter,glyph,cellx*width(),celly*height()); +} +QPixmap NetHackQtGlyphs::glyph(int glyph) +{ + int tile = glyph2tile[glyph]; + int px = (tile%tiles_per_row)*tilefile_tile_W; + int py = tile/tiles_per_row*tilefile_tile_H; + + return QPixmap::fromImage(img.copy(px, py, tilefile_tile_W, tilefile_tile_H)); +} +void NetHackQtGlyphs::setSize(int w, int h) +{ + if ( size == QSize(w,h) ) + return; + + bool was1 = size == pm1.size(); + size = QSize(w,h); + if (!w || !h) + return; // Still not decided + + if ( size == pm1.size() ) { + pm = pm1; + return; + } + if ( size == pm2.size() ) { + pm = pm2; + return; + } + + if (w==tilefile_tile_W && h==tilefile_tile_H) { + pm.convertFromImage(img); + } else { + QApplication::setOverrideCursor( Qt::WaitCursor ); + QImage scaled = img.scaled( + w*img.width()/tilefile_tile_W, + h*img.height()/tilefile_tile_H, + Qt::IgnoreAspectRatio, + Qt::SmoothTransformation + ); + pm.convertFromImage(scaled,Qt::ThresholdDither|Qt::PreferDither); + QApplication::restoreOverrideCursor(); + } + (was1 ? pm2 : pm1) = pm; +} + +} // namespace nethack_qt4 diff --git a/win/Qt4/qt4glyph.h b/win/Qt4/qt4glyph.h new file mode 100644 index 000000000..12fd915fd --- /dev/null +++ b/win/Qt4/qt4glyph.h @@ -0,0 +1,34 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4glyph.h -- class to manage the glyphs in a tile set + +#ifndef QT4GLYPH_H +#define QT4GLYPH_H + +namespace nethack_qt4 { + +class NetHackQtGlyphs { +public: + NetHackQtGlyphs(); + + int width() const { return size.width(); } + int height() const { return size.height(); } + void toggleSize(); + void setSize(int w, int h); + + void drawGlyph(QPainter&, int glyph, int pixelx, int pixely); + void drawCell(QPainter&, int glyph, int cellx, int celly); + QPixmap glyph(int glyph); + +private: + QImage img; + QPixmap pm,pm1, pm2; + QSize size; + int tiles_per_row; +}; + +} // namespace nethack_qt4 + +#endif diff --git a/win/Qt4/qt4icon.cpp b/win/Qt4/qt4icon.cpp new file mode 100644 index 000000000..0876cb59d --- /dev/null +++ b/win/Qt4/qt4icon.cpp @@ -0,0 +1,203 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4icon.cpp -- a labelled icon + +#include "hack.h" +#undef Invisible +#undef Warning +#undef index +#undef msleep +#undef rindex +#undef wizard +#undef yn +#undef min +#undef max + +#include +#if QT_VERSION >= 0x050000 +#include +#endif +#include "qt4icon.h" + +namespace nethack_qt4 { + +NetHackQtLabelledIcon::NetHackQtLabelledIcon(QWidget* parent, const char* l) : + QWidget(parent), + low_is_good(false), + prev_value(-123), + turn_count(-1), + label(new QLabel(l,this)), + icon(0) +{ + initHighlight(); +} + +NetHackQtLabelledIcon::NetHackQtLabelledIcon(QWidget* parent, const char* l, const QPixmap& i) : + QWidget(parent), + low_is_good(false), + prev_value(-123), + turn_count(-1), + label(new QLabel(l,this)), + icon(new QLabel(this)) +{ + setIcon(i); + initHighlight(); +} + +void NetHackQtLabelledIcon::initHighlight() +{ + hl_good = "QLabel { background-color : green; color : white }"; + hl_bad = "QLabel { background-color : red ; color : white }"; +} + +void NetHackQtLabelledIcon::setLabel(const QString& t, bool lower) +{ + if (!label) { + label=new QLabel(this); + label->setFont(font()); + resizeEvent(0); + } + if (label->text() != t) { + label->setText(t); + highlight(lower==low_is_good ? hl_good : hl_bad); + } +} + +void NetHackQtLabelledIcon::setLabel(const QString& t, long v, long cv, const QString& tail) +{ + QString buf; + if (v==NoNum) { + buf = ""; + } else { + buf.sprintf("%ld", v); + } + setLabel(t + buf + tail, cv < prev_value); + prev_value=cv; +} + +void NetHackQtLabelledIcon::setLabel(const QString& t, long v, const QString& tail) +{ + setLabel(t,v,v,tail); +} + +void NetHackQtLabelledIcon::setIcon(const QPixmap& i) +{ + if (icon) icon->setPixmap(i); + else { icon=new QLabel(this); icon->setPixmap(i); resizeEvent(0); } + icon->resize(i.width(),i.height()); +} + +void NetHackQtLabelledIcon::setFont(const QFont& f) +{ + QWidget::setFont(f); + if (label) label->setFont(f); +} + +void NetHackQtLabelledIcon::show() +{ +#if QT_VERSION >= 300 + if (isHidden()) +#else + if (!isVisible()) +#endif + highlight(hl_bad); + QWidget::show(); +} + +QSize NetHackQtLabelledIcon::sizeHint() const +{ + QSize iconsize, textsize; + + if (label && !icon) return label->sizeHint(); + if (icon && !label) return icon->sizeHint(); + if (!label && !icon) return QWidget::sizeHint(); + + iconsize = icon->sizeHint(); + textsize = label->sizeHint(); + return QSize( + std::max(iconsize.width(), textsize.width()), + iconsize.height() + textsize.height()); +} + +QSize NetHackQtLabelledIcon::minimumSizeHint() const +{ + QSize iconsize, textsize; + + if (label && !icon) return label->minimumSizeHint(); + if (icon && !label) return icon->minimumSizeHint(); + if (!label && !icon) return QWidget::minimumSizeHint(); + + iconsize = icon->minimumSizeHint(); + textsize = label->minimumSizeHint(); + return QSize( + std::max(iconsize.width(), textsize.width()), + iconsize.height() + textsize.height()); +} + +void NetHackQtLabelledIcon::highlightWhenChanging() +{ + turn_count=0; +} + +void NetHackQtLabelledIcon::lowIsGood() +{ + low_is_good=true; +} + +void NetHackQtLabelledIcon::dissipateHighlight() +{ + if (turn_count>0) { + turn_count--; + if (!turn_count) + unhighlight(); + } +} + +void NetHackQtLabelledIcon::highlight(const QString& hl) +{ + if (label) { // Surely it is?! + if (turn_count>=0) { + label->setStyleSheet(hl); + turn_count=4; + // `4' includes this turn, so dissipates after + // 3 more keypresses. + } else { + label->setStyleSheet(""); + } + } +} + +void NetHackQtLabelledIcon::unhighlight() +{ + if (label) { // Surely it is?! + label->setStyleSheet(""); + } +} + +void NetHackQtLabelledIcon::resizeEvent(QResizeEvent*) +{ + setAlignments(); + + //int labw=label ? label->fontMetrics().width(label->text()) : 0; + int labh=label ? label->fontMetrics().height() : 0; + int icoh=icon ? icon->height() : 0; + int h=icoh+labh; + int icoy=(h>height() ? height()-labh-icoh : height()/2-h/2); + int laby=icoy+icoh; + if (icon) { + icon->setGeometry(0,icoy,width(),icoh); + } + if (label) { + label->setGeometry(0,laby,width(),labh); + } +} + +void NetHackQtLabelledIcon::setAlignments() +{ + if (label) label->setAlignment(Qt::AlignHCenter|Qt::AlignVCenter); + if (icon) icon->setAlignment(Qt::AlignHCenter|Qt::AlignVCenter); +} + +} // namespace nethack_qt4 diff --git a/win/Qt4/qt4icon.h b/win/Qt4/qt4icon.h new file mode 100644 index 000000000..bdaf8183c --- /dev/null +++ b/win/Qt4/qt4icon.h @@ -0,0 +1,53 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4icon.cpp -- a labelled icon + +#ifndef QT4ICON_H +#define QT4ICON_H + +namespace nethack_qt4 { + +class NetHackQtLabelledIcon : public QWidget { +public: + NetHackQtLabelledIcon(QWidget* parent, const char* label); + NetHackQtLabelledIcon(QWidget* parent, const char* label, const QPixmap& icon); + + enum { NoNum=-99999 }; + void setLabel(const QString&, bool lower=true); // a string + void setLabel(const QString&, long, const QString& tail=""); // a number + void setLabel(const QString&, long show_value, long comparative_value, const QString& tail=""); + void setIcon(const QPixmap&); + virtual void setFont(const QFont&); + + void highlightWhenChanging(); + void lowIsGood(); + void dissipateHighlight(); + + virtual void show(); + virtual QSize sizeHint() const; + virtual QSize minimumSizeHint() const; + +protected: + void resizeEvent(QResizeEvent*); + +private: + void initHighlight(); + void setAlignments(); + void highlight(const QString& highlight); + void unhighlight(); + + bool low_is_good; + int prev_value; + int turn_count; /* last time the value changed */ + QString hl_good; + QString hl_bad; + + QLabel* label; + QLabel* icon; +}; + +} // namespace nethack_qt4 + +#endif diff --git a/win/Qt4/qt4inv.cpp b/win/Qt4/qt4inv.cpp new file mode 100644 index 000000000..9165d58b3 --- /dev/null +++ b/win/Qt4/qt4inv.cpp @@ -0,0 +1,104 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4inv.cpp -- inventory usage window +// This is at the top center of the main window + +extern "C" { +#include "hack.h" +} +#undef Invisible +#undef Warning +#undef index +#undef msleep +#undef rindex +#undef wizard +#undef yn +#undef min +#undef max + +#include +#if QT_VERSION >= 0x050000 +#include +#endif +#include "qt4inv.h" +#include "qt4glyph.h" +#include "qt4set.h" + +namespace nethack_qt4 { + +NetHackQtInvUsageWindow::NetHackQtInvUsageWindow(QWidget* parent) : + QWidget(parent) +{ + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); +} + +void NetHackQtInvUsageWindow::drawWorn(QPainter& painter, obj* nhobj, int x, int y, bool canbe) +{ + short int glyph; + if (nhobj) + glyph=obj_to_glyph(nhobj); + else if (canbe) + glyph=cmap_to_glyph(S_room); + else + glyph=cmap_to_glyph(S_stone); + + qt_settings->glyphs().drawCell(painter,glyph,x,y); +} + +void NetHackQtInvUsageWindow::paintEvent(QPaintEvent*) +{ + // 012 + // + //0 WhB + //1 s"w + //2 gCg + //3 =A= + //4 T + //5 S + + QPainter painter; + painter.begin(this); + + // Blanks + drawWorn(painter,0,0,4,false); + drawWorn(painter,0,0,5,false); + drawWorn(painter,0,2,4,false); + drawWorn(painter,0,2,5,false); + + drawWorn(painter,uarm,1,3); // Armour + drawWorn(painter,uarmc,1,2); // Cloak + drawWorn(painter,uarmh,1,0); // Helmet + drawWorn(painter,uarms,0,1); // Shield + drawWorn(painter,uarmg,0,2); // Gloves - repeated + drawWorn(painter,uarmg,2,2); // Gloves - repeated +#ifdef TOURIST + drawWorn(painter,uarmf,1,5); // Shoes (feet) + drawWorn(painter,uarmu,1,4); // Undershirt +#else + drawWorn(painter,0 ,1,5,false); + drawWorn(painter,uarmf,1,4); // Shoes (feet) +#endif + drawWorn(painter,uleft,0,3); // RingL + drawWorn(painter,uright,2,3); // RingR + + drawWorn(painter,uwep,2,1); // Weapon + drawWorn(painter,uswapwep,0,0); // Secondary weapon + drawWorn(painter,uamul,1,1); // Amulet + drawWorn(painter,ublindf,2,0); // Blindfold + + painter.end(); +} + +QSize NetHackQtInvUsageWindow::sizeHint(void) const +{ + if (qt_settings) { + return QSize(qt_settings->glyphs().width()*3, + qt_settings->glyphs().height()*6); + } else { + return QWidget::sizeHint(); + } +} + +} // namespace nethack_qt4 diff --git a/win/Qt4/qt4inv.h b/win/Qt4/qt4inv.h new file mode 100644 index 000000000..51cdd4d0b --- /dev/null +++ b/win/Qt4/qt4inv.h @@ -0,0 +1,25 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4inv.h -- inventory usage window +// This is at the top center of the main window + +#ifndef QT4INV_H +#define QT4INV_H + +namespace nethack_qt4 { + +class NetHackQtInvUsageWindow : public QWidget { +public: + NetHackQtInvUsageWindow(QWidget* parent); + virtual void paintEvent(QPaintEvent*); + virtual QSize sizeHint(void) const; + +private: + void drawWorn(QPainter& painter, obj*, int x, int y, bool canbe=true); +}; + +} // namespace nethack_qt4 + +#endif diff --git a/win/Qt4/qt4kde0.h b/win/Qt4/qt4kde0.h new file mode 100644 index 000000000..27a678c01 --- /dev/null +++ b/win/Qt4/qt4kde0.h @@ -0,0 +1,14 @@ +/* SCCS Id: @(#)qt_kde0.h 3.4 1999/11/19 */ +/* Copyright (c) Warwick Allison, 1999. */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef QT_DUMMYKDE +#define QT_DUMMYKDE +namespace nethack_qt4 { + +class KTopLevelWidget : public QMainWindow { + Q_OBJECT +}; + +} +#endif diff --git a/win/Qt4/qt4key.cpp b/win/Qt4/qt4key.cpp new file mode 100644 index 000000000..ff16616fe --- /dev/null +++ b/win/Qt4/qt4key.cpp @@ -0,0 +1,91 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4key.cpp -- a key buffer + +#include "hack.h" +#undef Invisible +#undef Warning +#undef index +#undef msleep +#undef rindex +#undef wizard +#undef yn +#undef min +#undef max + +#include +#include "qt4key.h" + +namespace nethack_qt4 { + +NetHackQtKeyBuffer::NetHackQtKeyBuffer() : + in(0), out(0) +{ +} + +bool NetHackQtKeyBuffer::Empty() const { return in==out; } +bool NetHackQtKeyBuffer::Full() const { return (in+1)%maxkey==out; } + +void NetHackQtKeyBuffer::Put(int k, int a, int state) +{ + if ( Full() ) return; // Safety + key[in]=k; + ascii[in]=a; + in=(in+1)%maxkey; +} + +void NetHackQtKeyBuffer::Put(char a) +{ + Put(0,a,0); +} + +void NetHackQtKeyBuffer::Put(const char* str) +{ + while (*str) Put(*str++); +} + +int NetHackQtKeyBuffer::GetKey() +{ + if ( Empty() ) return 0; + int r=TopKey(); + out=(out+1)%maxkey; + return r; +} + +int NetHackQtKeyBuffer::GetAscii() +{ + if ( Empty() ) return 0; // Safety + int r=TopAscii(); + out=(out+1)%maxkey; + return r; +} + +Qt::KeyboardModifiers NetHackQtKeyBuffer::GetState() +{ + if ( Empty() ) return 0; + Qt::KeyboardModifiers r=TopState(); + out=(out+1)%maxkey; + return r; +} + +int NetHackQtKeyBuffer::TopKey() const +{ + if ( Empty() ) return 0; + return key[out]; +} + +int NetHackQtKeyBuffer::TopAscii() const +{ + if ( Empty() ) return 0; + return ascii[out]; +} + +Qt::KeyboardModifiers NetHackQtKeyBuffer::TopState() const +{ + if ( Empty() ) return 0; + return state[out]; +} + +} // namespace nethack_qt4 diff --git a/win/Qt4/qt4key.h b/win/Qt4/qt4key.h new file mode 100644 index 000000000..0333269cd --- /dev/null +++ b/win/Qt4/qt4key.h @@ -0,0 +1,40 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4key.h -- a key buffer + +#ifndef QT4KEY_H +#define QT4KEY_H + +namespace nethack_qt4 { + +class NetHackQtKeyBuffer { +public: + NetHackQtKeyBuffer(); + + bool Empty() const; + bool Full() const; + + void Put(int k, int ascii, int state); + void Put(char a); + void Put(const char* str); + int GetKey(); + int GetAscii(); + Qt::KeyboardModifiers GetState(); + + int TopKey() const; + int TopAscii() const; + Qt::KeyboardModifiers TopState() const; + +private: + enum { maxkey=64 }; + int key[maxkey]; + int ascii[maxkey]; + Qt::KeyboardModifiers state[maxkey]; + int in,out; +}; + +} // namespace nethack_qt4 + +#endif diff --git a/win/Qt4/qt4line.cpp b/win/Qt4/qt4line.cpp new file mode 100644 index 000000000..dd9b18c5f --- /dev/null +++ b/win/Qt4/qt4line.cpp @@ -0,0 +1,42 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4line.cpp -- a one line input window + +#include "hack.h" +#undef Invisible +#undef Warning +#undef index +#undef msleep +#undef rindex +#undef wizard +#undef yn +#undef min +#undef max + +#include +#if QT_VERSION >= 0x050000 +#include +#endif +#include "qt4line.h" + +namespace nethack_qt4 { + +NetHackQtLineEdit::NetHackQtLineEdit() : + QLineEdit(0) +{ +} + +NetHackQtLineEdit::NetHackQtLineEdit(QWidget* parent, const char* name) : + QLineEdit(parent) +{ +} + +void NetHackQtLineEdit::fakeEvent(int key, int ascii, Qt::KeyboardModifiers state) +{ + QKeyEvent fake(QEvent::KeyPress,key,state,QChar(ascii)); + keyPressEvent(&fake); +} + +} // namespace nethack_qt4 diff --git a/win/Qt4/qt4line.h b/win/Qt4/qt4line.h new file mode 100644 index 000000000..bb5067f79 --- /dev/null +++ b/win/Qt4/qt4line.h @@ -0,0 +1,22 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4line.h -- a one line input window + +#ifndef QT4LINE_H +#define QT4LINE_H + +namespace nethack_qt4 { + +class NetHackQtLineEdit : public QLineEdit { +public: + NetHackQtLineEdit(); + NetHackQtLineEdit(QWidget* parent, const char* name); + + void fakeEvent(int key, int ascii, Qt::KeyboardModifiers state); +}; + +} // namespace nethack_qt4 + +#endif diff --git a/win/Qt4/qt4main.cpp b/win/Qt4/qt4main.cpp new file mode 100644 index 000000000..ec7e58324 --- /dev/null +++ b/win/Qt4/qt4main.cpp @@ -0,0 +1,1063 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4main.cpp -- the main window + +extern "C" { +#include "hack.h" +} +#include "patchlevel.h" +#undef Invisible +#undef Warning +#undef index +#undef msleep +#undef rindex +#undef wizard +#undef yn +#undef min +#undef max + +#include +#if QT_VERSION >= 0x050000 +#include +#endif +#include "qt4main.h" +#include "qt4main.moc" +#include "qt4bind.h" +#include "qt4glyph.h" +#include "qt4inv.h" +#include "qt4key.h" +#include "qt4map.h" +#include "qt4msg.h" +#include "qt4set.h" +#include "qt4stat.h" +#include "qt4str.h" + +#ifndef KDE +#include "qt4kde0.moc" +#endif + +// temporary +extern char *qt_tilewidth; +extern char *qt_tileheight; +extern int qt_compact_mode; +// end temporary + +namespace nethack_qt4 { + +// temporary +void centerOnMain( QWidget* w ); +// end temporary + +/* XPM */ +static const char * nh_icon[] = { +"40 40 6 1", +" s None c none", +". c #ffffff", +"X c #dadab6", +"o c #6c91b6", +"O c #476c6c", +"+ c #000000", +" ", +" ", +" ", +" . .X..XX.XX X ", +" .. .....X.XXXXXX XX ", +" ... ....X..XX.XXXXX XXX ", +" .. ..........X.XXXXXXXXXXX XX ", +" .... ........X..XX.XXXXXXXXX XXXX ", +" .... ..........X.XXXXXXXXXXX XXXX ", +" ooOOO..ooooooOooOOoOOOOOOOXX+++OO++ ", +" ooOOO..ooooooooOoOOOOOOOOOXX+++OO++ ", +" ....O..ooooooOooOOoOOOOOOOXX+XXXX++ ", +" ....O..ooooooooOoOOOOOOOOOXX+XXXX++ ", +" ..OOO..ooooooOooOOoOOOOOOOXX+++XX++ ", +" ++++..ooooooooOoOOOOOOOOOXX+++ +++ ", +" +++..ooooooOooOOoOOOOOOOXX+++ + ", +" ++..ooooooooOoOOOOOOOOOXX+++ ", +" ..ooooooOooOOoOOOOOOOXX+++ ", +" ..ooooooooOoOOOOOOOOOXX+++ ", +" ..ooooooOooOOoOOOOOOOXX+++ ", +" ..ooooooooOoOOOOOOOOOXX+++ ", +" ..oooooOooOOoOOOOOOXX+++ ", +" ..oooooooOoOOOOOOOOXX+++ ", +" ..ooooOooOOoOOOOOXX+++ ", +" ..ooooooOoOOOOOOOXX++++ ", +" ..o..oooOooOOoOOOOXX+XX+++ ", +" ...o..oooooOoOOOOOXX++XXX++ ", +" ....OO..ooOooOOoOOXX+++XXXX++ ", +" ...oo..+..oooOoOOOXX++XXooXXX++ ", +" ...ooo..++..OooOOoXX+++XXooOXXX+ ", +" ..oooOOXX+++....XXXX++++XXOOoOOXX+ ", +" ..oooOOXX+++ ...XXX+++++XXOOooOXX++ ", +" ..oooOXXX+++ ..XX+++ +XXOOooOXX++ ", +" .....XXX++++ XXXXXXX++ ", +" ....XX++++ XXXXXXX+ ", +" ...XX+++ XXXXX++ ", +" ", +" ", +" ", +" "}; +/* XPM */ +static const char * nh_icon_small[] = { +/* width height ncolors chars_per_pixel */ +"16 16 16 1", +/* colors */ +" c #587070", +". c #D1D5C9", +"X c #8B8C84", +"o c #2A2A28", +"O c #9AABA9", +"+ c #6A8FB2", +"@ c #C4CAC4", +"# c #B6BEB6", +"$ c None", +"% c #54564E", +"& c #476C6C", +"* c #ADB2AB", +"= c #ABABA2", +"- c #5E8295", +"; c #8B988F", +": c #E8EAE7", +/* pixels */ +"$$$$$$$$$$$$$$$$", +"$$$.$#::.#==*$$$", +"$.*:::::....#*=$", +"$@#:..@#*==#;XX;", +"$@O:+++- &&; X%X", +"$#%.+++- &&;% oX", +"$$o.++-- &&;%%X$", +"$$$:++-- &&;%%$$", +"$$$.O++- &&=o $$", +"$$$=:++- & XoX$$", +"$$*:@O-- ;%Xo$$", +"$*:O#$+--;oOOX $", +"$:+ =o::=oo=-;%X", +"$::.%o$*;X;##@%$", +"$$@# ;$$$$$=*;X$", +"$$$$$$$$$$$$$$$$" +}; + +#if 0 // RLC +/* XPM */ +static const char * map_xpm[] = { +"12 13 4 1", +". c None", +" c #000000000000", +"X c #0000B6DAFFFF", +"o c #69A69248B6DA", +" .", +" XXXXX ooo ", +" XoooX o ", +" XoooX o o ", +" XoooX ooo ", +" XXoXX o ", +" oooooXXX ", +" oo o oooX ", +" o XooX ", +" oooo XooX ", +" o o XXXX ", +" ", +". "}; +/* XPM */ +static const char * msg_xpm[] = { +"12 13 4 1", +". c None", +" c #FFFFFFFFFFFF", +"X c #69A69248B6DA", +"o c #000000000000", +" .", +" XXX XXX X o", +" o", +" XXXXX XX o", +" o", +" XX XXXXX o", +" o", +" XXXXXX o", +" o", +" XX XXX XX o", +" o", +" o", +".ooooooooooo"}; +/* XPM */ +static const char * stat_xpm[] = { +"12 13 5 1", +" c None", +". c #FFFF00000000", +"X c #000000000000", +"o c #FFFFFFFF0000", +"O c #69A6FFFF0000", +" ", +" ", +"... ", +"...X ", +"...X ... ", +"oooX oooX", +"oooXooo oooX", +"OOOXOOOXOOOX", +"OOOXOOOXOOOX", +"OOOXOOOXOOOX", +"OOOXOOOXOOOX", +"OOOXOOOXOOOX", +" XXXXXXXXXXX"}; +#endif +/* XPM */ +static const char * info_xpm[] = { +"12 13 4 1", +" c None", +". c #00000000FFFF", +"X c #FFFFFFFFFFFF", +"o c #000000000000", +" ... ", +" ....... ", +" ...XXX... ", +" .........o ", +"...XXXX.... ", +"....XXX....o", +"....XXX....o", +"....XXX....o", +" ...XXX...oo", +" ..XXXXX..o ", +" .......oo ", +" o...ooo ", +" ooo "}; + + +/* XPM */ +static const char * again_xpm[] = { +"12 13 2 1", +" c None", +". c #000000000000", +" .. ", +" .. ", +" ..... ", +" ....... ", +"... .. .. ", +".. .. .. ", +".. ..", +".. ..", +".. ..", +" .. .. ", +" .......... ", +" ...... ", +" "}; +/* XPM */ +static const char * kick_xpm[] = { +"12 13 3 1", +" c None", +". c #000000000000", +"X c #FFFF6DB60000", +" ", +" ", +" . . . ", +" ... . . ", +" ... . ", +" ... . ", +" ... ", +"XXX ... ", +"XXX. ... ", +"XXX. ... ", +"XXX. .. ", +" ... ", +" "}; +/* XPM */ +static const char * throw_xpm[] = { +"12 13 3 1", +" c None", +". c #FFFF6DB60000", +"X c #000000000000", +" ", +" ", +" ", +" ", +".... X ", +"....X X ", +"....X XXXXXX", +"....X X ", +" XXXX X ", +" ", +" ", +" ", +" "}; +/* XPM */ +static const char * fire_xpm[] = { +"12 13 5 1", +" c None", +". c #B6DA45140000", +"X c #FFFFB6DA9658", +"o c #000000000000", +"O c #FFFF6DB60000", +" . ", +" X. ", +" X . ", +" X .o ", +" X . o ", +" X .o o ", +"OOOOOOOOoooo", +" X .o o ", +" X . o o ", +" X .o ", +" X. o ", +" . o ", +" o "}; +/* XPM */ +static const char * get_xpm[] = { +"12 13 3 1", +" c None", +". c #000000000000", +"X c #FFFF6DB60000", +" ", +" . ", +" ... ", +" . . . ", +" . ", +" . ", +" ", +" XXXXX ", +" XXXXX. ", +" XXXXX. ", +" XXXXX. ", +" ..... ", +" "}; +/* XPM */ +static const char * drop_xpm[] = { +"12 13 3 1", +" c None", +". c #FFFF6DB60000", +"X c #000000000000", +" ", +" ..... ", +" .....X ", +" .....X ", +" .....X ", +" XXXXX ", +" ", +" X ", +" X ", +" X X X ", +" XXX ", +" X ", +" "}; +/* XPM */ +static const char * eat_xpm[] = { +"12 13 4 1", +" c None", +". c #000000000000", +"X c #FFFFB6DA9658", +"o c #FFFF6DB60000", +" .X. .. ", +" .X. .. ", +" .X. .. ", +" .X. .. ", +" ... .. ", +" .. .. ", +" .. .. ", +" oo oo ", +" oo oo ", +" oo oo ", +" oo oo ", +" oo oo ", +" oo oo "}; +/* XPM */ +static const char * rest_xpm[] = { +"12 13 2 1", +" c None", +". c #000000000000", +" ..... ", +" . ", +" . ", +" . ....", +" ..... . ", +" . ", +" ....", +" ", +" .... ", +" . ", +" . ", +" .... ", +" "}; +/* XPM */ +static const char * cast_a_xpm[] = { +"12 13 3 1", +" c None", +". c #FFFF6DB60000", +"X c #000000000000", +" . ", +" . ", +" .. ", +" .. ", +" .. . ", +" .. . ", +" ...... ", +" .. .. XX ", +" .. X X ", +" .. X X ", +" .. XXXX ", +" . X X ", +" . X X "}; +/* XPM */ +static const char * cast_b_xpm[] = { +"12 13 3 1", +" c None", +". c #FFFF6DB60000", +"X c #000000000000", +" . ", +" . ", +" .. ", +" .. ", +" .. . ", +" .. . ", +" ...... ", +" .. .. XXX ", +" .. X X ", +" .. XXX ", +" .. X X ", +" . X X ", +" . XXX "}; +/* XPM */ +static const char * cast_c_xpm[] = { +"12 13 3 1", +" c None", +". c #FFFF6DB60000", +"X c #000000000000", +" . ", +" . ", +" .. ", +" .. ", +" .. . ", +" .. . ", +" ...... ", +" .. .. XX ", +" .. X X ", +" .. X ", +" .. X ", +" . X X ", +" . XX "}; + +static QString +aboutMsg() +{ + QString msg; + msg.sprintf( + "Qt NetHack is a version of NetHack built\n" +#ifdef KDE + "using KDE and the Qt GUI toolkit.\n" +#else + "using the Qt GUI toolkit.\n" +#endif + "This is version %d.%d.%d\n\n" + "Homepage:\n http://trolls.troll.no/warwick/nethack/\n\n" +#ifdef KDE + "KDE:\n http://www.kde.org\n" +#endif + "Qt:\n http://www.troll.no", + VERSION_MAJOR, + VERSION_MINOR, + PATCHLEVEL); + return msg; +} + +class SmallToolButton : public QToolButton { +public: + SmallToolButton(const QPixmap & pm, const QString &textLabel, + const QString& grouptext, + QObject * receiver, const char* slot, + QWidget * parent) : + QToolButton(parent) + { + setIcon(QIcon(pm)); + setToolTip(textLabel); + setStatusTip(grouptext); + connect(this, SIGNAL(clicked(bool)), receiver, slot); + } + + QSize sizeHint() const + { + // get just a couple more pixels for the map + return QToolButton::sizeHint()-QSize(0,2); + } +}; + +NetHackQtMainWindow::NetHackQtMainWindow(NetHackQtKeyBuffer& ks) : + message(0), map(0), status(0), invusage(0), + hsplitter(0), vsplitter(0), + keysink(ks), dirkey(0) +{ + QToolBar* toolbar = new QToolBar(this); + toolbar->setMovable(false); + toolbar->setFocusPolicy(Qt::NoFocus); + addToolBar(toolbar); + menubar = menuBar(); + + setWindowTitle("Qt NetHack"); + if ( qt_compact_mode ) + setWindowIcon(QIcon(QPixmap(nh_icon_small))); + else + setWindowIcon(QIcon(QPixmap(nh_icon))); + + QMenu* game=new QMenu; + QMenu* apparel=new QMenu; + QMenu* act1=new QMenu; + QMenu* act2 = qt_compact_mode ? new QMenu : act1; + QMenu* magic=new QMenu; + QMenu* info=new QMenu; + + QMenu *help; + +#ifdef KDE + help = kapp->getHelpMenu( true, "" ); + help->addSeparator(); +#else + help = qt_compact_mode ? info : new QMenu; +#endif + + enum { OnDesktop=1, OnHandhelds=2 }; + struct Macro { + QMenu* menu; + const char* name; + int flags; + int NDECL((*funct)); + } item[] = { + { game, 0, 3}, + { game, "Version", 3, doversion}, + { game, "Compilation", 3, doextversion}, + { game, "History", 3, dohistory}, + { game, "Redraw", 0, doredraw}, // useless + { game, "Options", 3, doset}, + { game, "Explore mode", 3, enter_explore_mode}, + { game, 0, 3}, + { game, "Save", 3, dosave}, + { game, "Quit", 3, done2}, + + { apparel, "Apparel off", 2, doddoremarm}, + { apparel, "Remove many", 1, doddoremarm}, + { apparel, 0, 3}, + { apparel, "Wield weapon", 3, dowield}, + { apparel, "Exchange weapons", 3, doswapweapon}, + { apparel, "Two weapon combat", 3, dotwoweapon}, + { apparel, "Load quiver", 3, dowieldquiver}, + { apparel, 0, 3}, + { apparel, "Wear armour", 3, dowear}, + { apparel, "Take off armour", 3, dotakeoff}, + { apparel, 0, 3}, + { apparel, "Put on non-armour", 3, doputon}, + { apparel, "Remove non-armour", 3, doremring}, + + /* { act1, "Again\tCtrl+A", "\001", 2}, + { act1, 0, 0, 3}, */ + { act1, "Apply", 3, doapply}, + { act1, "Chat", 3, dotalk}, + { act1, "Close door", 3, doclose}, + { act1, "Down", 3, dodown}, + { act1, "Drop many", 2, doddrop}, + { act1, "Drop", 2, dodrop}, + { act1, "Eat", 2, doeat}, + { act1, "Engrave", 3, doengrave}, + /* { act1, "Fight\tShift+F", "F", 3}, */ + { act1, "Fire from quiver", 2, dofire}, + { act1, "Force", 3, doforce}, + { act1, "Get", 2, dopickup}, + { act1, "Jump", 3, dojump}, + { act2, "Kick", 2, dokick}, + { act2, "Loot", 3, doloot}, + { act2, "Open door", 3, doopen}, + { act2, "Pay", 3, dopay}, + { act2, "Rest", 2, donull}, + { act2, "Ride", 3, doride}, + { act2, "Search", 3, dosearch}, + { act2, "Sit", 3, dosit}, + { act2, "Throw", 2, dothrow}, + { act2, "Untrap", 3, dountrap}, + { act2, "Up", 3, doup}, + { act2, "Wipe face", 3, dowipe}, + + { magic, "Quaff potion", 3, dodrink}, + { magic, "Read scroll/book", 3, doread}, + { magic, "Zap wand", 3, dozap}, + { magic, "Zap spell", 3, docast}, + { magic, "Dip", 3, dodip}, + { magic, "Rub", 3, dorub}, + { magic, "Invoke", 3, doinvoke}, + { magic, 0, 3}, + { magic, "Offer", 3, dosacrifice}, + { magic, "Pray", 3, dopray}, + { magic, 0, 3}, + { magic, "Teleport", 3, dotele}, + { magic, "Monster action", 3, domonability}, + { magic, "Turn undead", 3, doturn}, + + { help, "Help", 3, dohelp}, + { help, 0, 3}, + { help, "What is here", 3, dolook}, + { help, "What is there", 3, doquickwhatis}, + { help, "What is...", 2, dowhatis}, + { help, 0, 1}, + + { info, "Inventory", 3, ddoinv}, + { info, "Conduct", 3, doconduct}, + { info, "Discoveries", 3, dodiscovered}, + { info, "List/reorder spells", 3, dovspell}, + { info, "Adjust letters", 2, doorganize}, + { info, 0, 3}, + { info, "Name object or creature", 3, docallcmd}, + { info, 0, 3}, + { info, "Skills", 3, enhance_weapon_skill}, + + { 0, 0, 0 } + }; + + int i; + + game->addAction("Qt settings...",this,SLOT(doQtSettings(bool))); + help->addAction("About Qt NetHack...",this,SLOT(doAbout(bool))); + //help->addAction("NetHack Guidebook...",this,SLOT(doGuidebook(bool))); + help->addSeparator(); + + for (i=0; item[i].menu; i++) { + if ( item[i].flags & (qt_compact_mode ? 1 : 2) ) { + if (item[i].name) { + char actchar[32]; + char menuitem[BUFSZ]; + actchar[0] = '\0'; + if (item[i].funct) { + actchar[0] = cmd_from_func(item[i].funct); + actchar[1] = '\0'; + } + if (actchar[0] && !qt_compact_mode) + Sprintf(menuitem, "%s\t%s", item[i].name, + visctrl(actchar[0])); + else + Sprintf(menuitem, "%s", item[i].name); + if (actchar[0]) { + QString name = menuitem; + QAction *action = item[i].menu->addAction(name); + action->setData(actchar); + } + } else { + item[i].menu->addSeparator(); + } + } + } + + game->setTitle("Game"); + menubar->addMenu(game); + apparel->setTitle("Gear"); + menubar->addMenu(apparel); + + if ( qt_compact_mode ) { + act1->setTitle("A-J"); + menubar->addMenu(act1); + act2->setTitle("K-Z"); + menubar->addMenu(act2); + magic->setTitle("Magic"); + menubar->addMenu(magic); + info->setIcon(QIcon(QPixmap(info_xpm))); + info->setTitle("Info"); + menubar->addMenu(info); + //menubar->insertItem(QPixmap(map_xpm), this, SLOT(raiseMap())); + //menubar->insertItem(QPixmap(msg_xpm), this, SLOT(raiseMessages())); + //menubar->insertItem(QPixmap(stat_xpm), this, SLOT(raiseStatus())); + info->addSeparator(); + info->addAction("Map", this, SLOT(raiseMap())); + info->addAction("Messages", this, SLOT(raiseMessages())); + info->addAction("Status", this, SLOT(raiseStatus())); + } else { + act1->setTitle("Action"); + menubar->addMenu(act1); + magic->setTitle("Magic"); + menubar->addMenu(magic); + info->setTitle("Info"); + menubar->addMenu(info); + menubar->addSeparator(); + help->setTitle("Help"); + menubar->addMenu(help); + } + + QSignalMapper* sm = new QSignalMapper(this); + connect(sm, SIGNAL(mapped(const QString&)), this, SLOT(doKeys(const QString&))); + QToolButton* tb; + char actchar[32]; + tb = new SmallToolButton( QPixmap(again_xpm),"Again","Action", sm, SLOT(map()), toolbar ); + Sprintf(actchar, "%c", Cmd.spkeys[NHKF_DOAGAIN]); + sm->setMapping(tb, actchar ); + toolbar->addWidget(tb); + tb = new SmallToolButton( QPixmap(get_xpm),"Get","Action", sm, SLOT(map()), toolbar ); + Sprintf(actchar, "%c", cmd_from_func(dopickup)); + sm->setMapping(tb, actchar ); + toolbar->addWidget(tb); + tb = new SmallToolButton( QPixmap(kick_xpm),"Kick","Action", sm, SLOT(map()), toolbar ); + Sprintf(actchar, "%c", cmd_from_func(dokick)); + sm->setMapping(tb, actchar ); + toolbar->addWidget(tb); + tb = new SmallToolButton( QPixmap(throw_xpm),"Throw","Action", sm, SLOT(map()), toolbar ); + Sprintf(actchar, "%c", cmd_from_func(dothrow)); + sm->setMapping(tb, actchar ); + toolbar->addWidget(tb); + tb = new SmallToolButton( QPixmap(fire_xpm),"Fire","Action", sm, SLOT(map()), toolbar ); + Sprintf(actchar, "%c", cmd_from_func(dofire)); + sm->setMapping(tb, actchar ); + toolbar->addWidget(tb); + tb = new SmallToolButton( QPixmap(drop_xpm),"Drop","Action", sm, SLOT(map()), toolbar ); + Sprintf(actchar, "%c", cmd_from_func(doddrop)); + sm->setMapping(tb, actchar ); + toolbar->addWidget(tb); + tb = new SmallToolButton( QPixmap(eat_xpm),"Eat","Action", sm, SLOT(map()), toolbar ); + Sprintf(actchar, "%c", cmd_from_func(doeat)); + sm->setMapping(tb, actchar ); + toolbar->addWidget(tb); + tb = new SmallToolButton( QPixmap(rest_xpm),"Rest","Action", sm, SLOT(map()), toolbar ); + Sprintf(actchar, "%c", cmd_from_func(donull)); + sm->setMapping(tb, actchar ); + toolbar->addWidget(tb); + + connect(game,SIGNAL(triggered(QAction *)),this,SLOT(doMenuItem(QAction *))); + connect(apparel,SIGNAL(triggered(QAction *)),this,SLOT(doMenuItem(QAction *))); + connect(act1,SIGNAL(triggered(QAction *)),this,SLOT(doMenuItem(QAction *))); + if (act2 != act1) + connect(act2,SIGNAL(triggered(QAction *)),this,SLOT(doMenuItem(QAction *))); + connect(magic,SIGNAL(triggered(QAction *)),this,SLOT(doMenuItem(QAction *))); + connect(info,SIGNAL(triggered(QAction *)),this,SLOT(doMenuItem(QAction *))); + connect(help,SIGNAL(triggered(QAction *)),this,SLOT(doMenuItem(QAction *))); + +#ifdef KDE + setMenu (menubar); +#endif + + int x=0,y=0; + int w=QApplication::desktop()->width()-10; // XXX arbitrary extra space for frame + int h=QApplication::desktop()->height()-50; + + int maxwn; + int maxhn; + if (qt_tilewidth != NULL) { + maxwn = atoi(qt_tilewidth) * COLNO + 10; + } else { + maxwn = 1400; + } + if (qt_tileheight != NULL) { + maxhn = atoi(qt_tileheight) * ROWNO * 6/4; + } else { + maxhn = 1024; + } + + // Be exactly the size we want to be - full map... + if (w>maxwn) { + x+=(w-maxwn)/2; + w=maxwn; // Doesn't need to be any wider + } + if (h>maxhn) { + y+=(h-maxhn)/2; + h=maxhn; // Doesn't need to be any taller + } + + setGeometry(x,y,w,h); + + if ( qt_compact_mode ) { + stack = new QStackedWidget(this); + setCentralWidget(stack); + } else { + vsplitter = new QSplitter(Qt::Vertical); + setCentralWidget(vsplitter); + hsplitter = new QSplitter(Qt::Horizontal); + invusage = new NetHackQtInvUsageWindow(hsplitter); + vsplitter->insertWidget(0, hsplitter); + hsplitter->insertWidget(1, invusage); + } +} + +void NetHackQtMainWindow::zoomMap() +{ + qt_settings->toggleGlyphSize(); +} + +void NetHackQtMainWindow::raiseMap() +{ + if ( stack->currentWidget() == map->Widget() ) { + zoomMap(); + } else { + stack->setCurrentWidget(map->Widget()); + } +} + +void NetHackQtMainWindow::raiseMessages() +{ + stack->setCurrentWidget(message->Widget()); +} + +void NetHackQtMainWindow::raiseStatus() +{ + stack->setCurrentWidget(status->Widget()); +} + +#if 0 // RLC this isn't used +class NetHackMimeSourceFactory : public Q3MimeSourceFactory { +public: + const QMimeSource* data(const QString& abs_name) const + { + const QMimeSource* r = 0; + if ( (NetHackMimeSourceFactory*)this == Q3MimeSourceFactory::defaultFactory() ) + r = Q3MimeSourceFactory::data(abs_name); + else + r = Q3MimeSourceFactory::defaultFactory()->data(abs_name); + if ( !r ) { + int sl = abs_name.length(); + do { + sl = abs_name.lastIndexOf('/',sl-1); + QString name = sl>=0 ? abs_name.mid(sl+1) : abs_name; + int dot = name.lastIndexOf('.'); + if ( dot >= 0 ) + name = name.left(dot); + if ( name == "map" ) + r = new Q3ImageDrag(QImage(map_xpm)); + else if ( name == "msg" ) + r = new Q3ImageDrag(QImage(msg_xpm)); + else if ( name == "stat" ) + r = new Q3ImageDrag(QImage(stat_xpm)); + } while (!r && sl>0); + } + return r; + } +}; +#endif + +void NetHackQtMainWindow::doMenuItem(QAction *action) +{ + doKeys(action->data().toString()); +} + +void NetHackQtMainWindow::doQtSettings(bool) +{ + centerOnMain(qt_settings); + qt_settings->show(); +} + +void NetHackQtMainWindow::doAbout(bool) +{ + QMessageBox::about(this, "About Qt NetHack", aboutMsg()); +} + +#if 0 // RLC this isn't used +void NetHackQtMainWindow::doGuidebook(bool) +{ + QDialog dlg(this); + new QVBoxLayout(&dlg); + Q3TextBrowser browser(&dlg); + NetHackMimeSourceFactory ms; + browser.setMimeSourceFactory(&ms); + browser.setSource(QDir::currentPath()+"/Guidebook.html"); + if ( qt_compact_mode ) + dlg.showMaximized(); + dlg.exec(); +} +#endif + +void NetHackQtMainWindow::doKeys(const QString& k) +{ + keysink.Put(k.toLatin1().constData()); + qApp->exit(); +} + +void NetHackQtMainWindow::AddMessageWindow(NetHackQtMessageWindow* window) +{ + message=window; + hsplitter->insertWidget(0, message->Widget()); + ShowIfReady(); +} + +void NetHackQtMainWindow::AddMapWindow(NetHackQtMapWindow2* window) +{ + map=window; + vsplitter->insertWidget(1, map->Widget()); + ShowIfReady(); + connect(map,SIGNAL(resized()),this,SLOT(layout())); +} + +void NetHackQtMainWindow::AddStatusWindow(NetHackQtStatusWindow* window) +{ + status=window; + hsplitter->insertWidget(2, status->Widget()); + ShowIfReady(); +} + +void NetHackQtMainWindow::RemoveWindow(NetHackQtWindow* window) +{ + if (window==status) { + status=0; + ShowIfReady(); + } else if (window==map) { + map=0; + ShowIfReady(); + } else if (window==message) { + message=0; + ShowIfReady(); + } +} + +void NetHackQtMainWindow::updateInventory() +{ + if ( invusage ) + invusage->repaint(); +} + +void NetHackQtMainWindow::fadeHighlighting() +{ + if (status) { + status->fadeHighlighting(); + } +} + +void NetHackQtMainWindow::layout() +{ +#if 0 + if ( qt_compact_mode ) + return; + if (message && map && status) { + QSize maxs=map->Widget()->maximumSize(); + int maph=std::min(height()*2/3,maxs.height()); + + QWidget* c = centralWidget(); + int h=c->height(); + int toph=h-maph; + int iuw=3*qt_settings->glyphs().width(); + int topw=(c->width()-iuw)/2; + + message->Widget()->setGeometry(0,0,topw,toph); + invusage->setGeometry(topw,0,iuw,toph); + status->Widget()->setGeometry(topw+iuw,0,topw,toph); + map->Widget()->setGeometry(std::max(0,(c->width()-maxs.width())/2), + toph,c->width(),maph); + } +#endif +} + +void NetHackQtMainWindow::resizeEvent(QResizeEvent*) +{ + layout(); +#ifdef KDE + updateRects(); +#endif +} + +void NetHackQtMainWindow::keyReleaseEvent(QKeyEvent* event) +{ + if ( dirkey ) { + doKeys(QString(QChar(dirkey))); + if ( !event->isAutoRepeat() ) + dirkey = 0; + } +} + +void NetHackQtMainWindow::keyPressEvent(QKeyEvent* event) +{ + // Global key controls + + // For desktop, arrow keys scroll map, since we don't want players + // to think that's the way to move. For handhelds, the normal way is to + // click-to-travel, so we allow the cursor keys for fine movements. + + // 321 + // 4 0 + // 567 + + if ( event->isAutoRepeat() && + event->key() >= Qt::Key_Left && event->key() <= Qt::Key_Down ) + return; + + const char* d = Cmd.dirchars; + switch (event->key()) { + case Qt::Key_Up: + if ( dirkey == d[0] ) + dirkey = d[1]; + else if ( dirkey == d[4] ) + dirkey = d[3]; + else + dirkey = d[2]; + break; case Qt::Key_Down: + if ( dirkey == d[0] ) + dirkey = d[7]; + else if ( dirkey == d[4] ) + dirkey = d[5]; + else + dirkey = d[6]; + break; case Qt::Key_Left: + if ( dirkey == d[2] ) + dirkey = d[1]; + else if ( dirkey == d[6] ) + dirkey = d[7]; + else + dirkey = d[0]; + break; case Qt::Key_Right: + if ( dirkey == d[2] ) + dirkey = d[3]; + else if ( dirkey == d[6] ) + dirkey = d[5]; + else + dirkey = d[4]; + break; case Qt::Key_PageUp: + dirkey = 0; + if (message) message->Scroll(0,-1); + break; case Qt::Key_PageDown: + dirkey = 0; + if (message) message->Scroll(0,+1); + break; case Qt::Key_Space: + if ( flags.rest_on_space ) { + event->ignore(); + return; + } + case Qt::Key_Enter: + if ( map ) + map->clickCursor(); + break; default: + dirkey = 0; + event->ignore(); + } +} + +void NetHackQtMainWindow::closeEvent(QCloseEvent* e) +{ + if ( program_state.something_worth_saving ) { + switch ( QMessageBox::information( this, "NetHack", + "This will end your NetHack session", + "&Save", "&Cancel", 0, 1 ) ) + { + case 0: + // See dosave() function + if (dosave0()) { + u.uhp = -1; + NetHackQtBind::qt_exit_nhwindows(0); + nh_terminate(EXIT_SUCCESS); + } + break; + case 1: + break; // ignore the event + } + } else { + e->accept(); + } +} + +void NetHackQtMainWindow::ShowIfReady() +{ + if (message && map && status) { + QWidget* hp = qt_compact_mode ? static_cast(stack) : static_cast(hsplitter); + QWidget* vp = qt_compact_mode ? static_cast(stack) : static_cast(vsplitter); + message->Widget()->setParent(hp); + map->Widget()->setParent(vp); + status->Widget()->setParent(hp); + if ( qt_compact_mode ) { + message->setMap(map); + stack->addWidget(map->Widget()); + stack->addWidget(message->Widget()); + stack->addWidget(status->Widget()); + raiseMap(); + } else { + layout(); + } + showMaximized(); + } else if (isVisible()) { + hide(); + } +} + +} // namespace nethack_qt4 diff --git a/win/Qt4/qt4main.h b/win/Qt4/qt4main.h new file mode 100644 index 000000000..33f5b2635 --- /dev/null +++ b/win/Qt4/qt4main.h @@ -0,0 +1,94 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4main.h -- the main window + +#ifndef QT4MAIN_H +#define QT4MAIN_H + +#ifdef KDE +#include +#include +#else +#include "qt4kde0.h" +#endif + +namespace nethack_qt4 { + +class NetHackQtInvUsageWindow; +class NetHackQtKeyBuffer; +class NetHackQtMapWindow2; +class NetHackQtMessageWindow; +class NetHackQtStatusWindow; +class NetHackQtWindow; + +// This class is the main widget for NetHack +// +// It is a collection of Message, Map, and Status windows. In the current +// version of nethack there is only one of each, and this class makes this +// assumption, not showing itself until all are inserted. +// +// This class simply knows how to layout such children sensibly. +// +// Since it is only responsible for layout, the class does not +// note the actual class of the windows. +// + +class NetHackQtMainWindow : public KTopLevelWidget { + Q_OBJECT +public: + NetHackQtMainWindow(NetHackQtKeyBuffer&); + + void AddMessageWindow(NetHackQtMessageWindow* window); + void AddMapWindow(NetHackQtMapWindow2* window); + void AddStatusWindow(NetHackQtStatusWindow* window); + void RemoveWindow(NetHackQtWindow* window); + void updateInventory(); + + void fadeHighlighting(); + +public slots: + void doMenuItem(QAction *); + void doQtSettings(bool); + void doAbout(bool); + //RLC void doGuidebook(bool); + void doKeys(const QString&); + +protected: + virtual void resizeEvent(QResizeEvent*); + virtual void keyPressEvent(QKeyEvent*); + virtual void keyReleaseEvent(QKeyEvent* event); + virtual void closeEvent(QCloseEvent*); + +private slots: + void layout(); + void raiseMap(); + void zoomMap(); + void raiseMessages(); + void raiseStatus(); + +private: + void ShowIfReady(); + +#ifdef KDE + KMenuBar* menubar; +#else + QMenuBar* menubar; +#endif + NetHackQtMessageWindow* message; + NetHackQtMapWindow2* map; + NetHackQtStatusWindow* status; + NetHackQtInvUsageWindow* invusage; + + QSplitter *hsplitter; + QSplitter *vsplitter; + + NetHackQtKeyBuffer& keysink; + QStackedWidget* stack; + int dirkey; +}; + +} // namespace nethack_qt4 + +#endif diff --git a/win/Qt4/qt4map.cpp b/win/Qt4/qt4map.cpp new file mode 100644 index 000000000..39c915ee5 --- /dev/null +++ b/win/Qt4/qt4map.cpp @@ -0,0 +1,964 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4map.cpp -- the map window + +extern "C" { +#include "hack.h" +} +#undef Invisible +#undef Warning +#undef index +#undef msleep +#undef rindex +#undef wizard +#undef yn +#undef min +#undef max + +#include +#if QT_VERSION >= 0x050000 +#include +#endif +#include "qt4map.h" +#include "qt4map.moc" +#include "qt4click.h" +#include "qt4glyph.h" +#include "qt_xpms.h" +#include "qt4set.h" +#include "qt4str.h" + +// temporary +extern int qt_compact_mode; +// end temporary + +namespace nethack_qt4 { + +#ifdef TEXTCOLOR +static const QPen& nhcolor_to_pen(int c) +{ + static QPen* pen=0; + if ( !pen ) { + pen = new QPen[17]; + pen[0] = QColor(64,64,64); + pen[1] = QColor(Qt::red); + pen[2] = QColor(0,191,0); + pen[3] = QColor(127,127,0); + pen[4] = QColor(Qt::blue); + pen[5] = QColor(Qt::magenta); + pen[6] = QColor(Qt::cyan); + pen[7] = QColor(Qt::gray); + pen[8] = QColor(Qt::white); // no color + pen[9] = QColor(255,127,0); + pen[10] = QColor(127,255,127); + pen[11] = QColor(Qt::yellow); + pen[12] = QColor(127,127,255); + pen[13] = QColor(255,127,255); + pen[14] = QColor(127,255,255); + pen[15] = QColor(Qt::white); + pen[16] = QColor(Qt::black); + } + + return pen[c]; +} +#endif + +NetHackQtMapViewport::NetHackQtMapViewport(NetHackQtClickBuffer& click_sink) : + QWidget(NULL), + rogue_font(NULL), + clicksink(click_sink), + change(10) +{ + pet_annotation = QPixmap(qt_compact_mode ? pet_mark_small_xpm : pet_mark_xpm); + + Clear(); + cursor.setX(0); + cursor.setY(0); +} + +NetHackQtMapViewport::~NetHackQtMapViewport(void) +{ + delete rogue_font; +} + +void NetHackQtMapViewport::paintEvent(QPaintEvent* event) +{ + QRect area=event->rect(); + QRect garea; + garea.setCoords( + std::max(0,area.left()/qt_settings->glyphs().width()), + std::max(0,area.top()/qt_settings->glyphs().height()), + std::min(COLNO-1,area.right()/qt_settings->glyphs().width()), + std::min(ROWNO-1,area.bottom()/qt_settings->glyphs().height()) + ); + + QPainter painter; + + painter.begin(this); + + if (Is_rogue_level(&u.uz) || iflags.wc_ascii_map) { + // You enter a VERY primitive world! + + painter.setClipRect( event->rect() ); // (normally we don't clip) + painter.fillRect( event->rect(), Qt::black ); + + if ( !rogue_font ) { + // Find font... + int pts = 5; + QString fontfamily = iflags.wc_font_map + ? iflags.wc_font_map : "Monospace"; + bool bold = false; + if ( fontfamily.right(5).toLower() == "-bold" ) { + fontfamily.truncate(fontfamily.length()-5); + bold = true; + } + while ( pts < 32 ) { + QFont f(fontfamily, pts, bold ? QFont::Bold : QFont::Normal); + painter.setFont(QFont(fontfamily, pts)); + QFontMetrics fm = painter.fontMetrics(); + if ( fm.width("M") > qt_settings->glyphs().width() ) + break; + if ( fm.height() > qt_settings->glyphs().height() ) + break; + pts++; + } + rogue_font = new QFont(fontfamily,pts-1); + } + painter.setFont(*rogue_font); + + for (int j=garea.top(); j<=garea.bottom(); j++) { + for (int i=garea.left(); i<=garea.right(); i++) { + unsigned short g=Glyph(i,j); + int color; + int ch; + unsigned special; + + painter.setPen( Qt::green ); + /* map glyph to character and color */ + mapglyph(g, &ch, &color, &special, i, j); + ch = cp437(ch); +#ifdef TEXTCOLOR + painter.setPen( nhcolor_to_pen(color) ); +#endif + if (!DrawWalls( + painter, + i*qt_settings->glyphs().width(), + j*qt_settings->glyphs().height(), + qt_settings->glyphs().width(), + qt_settings->glyphs().height(), + ch)) { + painter.drawText( + i*qt_settings->glyphs().width(), + j*qt_settings->glyphs().height(), + qt_settings->glyphs().width(), + qt_settings->glyphs().height(), + Qt::AlignCenter, + QString(QChar(ch)).left(1) + ); + } + if (glyph_is_pet(g) +#ifdef TEXTCOLOR + && ::iflags.hilite_pet +#endif + ) { + painter.drawPixmap(QPoint(i*qt_settings->glyphs().width(), j*qt_settings->glyphs().height()), pet_annotation); + } + } + } + + painter.setFont(font()); + } else { + for (int j=garea.top(); j<=garea.bottom(); j++) { + for (int i=garea.left(); i<=garea.right(); i++) { + unsigned short g=Glyph(i,j); + qt_settings->glyphs().drawCell(painter, g, i, j); + if (glyph_is_pet(g) +#ifdef TEXTCOLOR + && ::iflags.hilite_pet +#endif + ) { + painter.drawPixmap(QPoint(i*qt_settings->glyphs().width(), j*qt_settings->glyphs().height()), pet_annotation); + } + } + } + } + + if (garea.contains(cursor)) { + if (Is_rogue_level(&u.uz)) { +#ifdef TEXTCOLOR + painter.setPen( Qt::white ); +#else + painter.setPen( Qt::green ); // REALLY primitive +#endif + } else + { + int hp100; + if (u.mtimedone) { + hp100=u.mhmax ? u.mh*100/u.mhmax : 100; + } else { + hp100=u.uhpmax ? u.uhp*100/u.uhpmax : 100; + } + + if (hp100 > 75) painter.setPen(Qt::white); + else if (hp100 > 50) painter.setPen(Qt::yellow); + else if (hp100 > 25) painter.setPen(QColor(0xff,0xbf,0x00)); // orange + else if (hp100 > 10) painter.setPen(Qt::red); + else painter.setPen(Qt::magenta); + } + + painter.drawRect( + cursor.x()*qt_settings->glyphs().width(),cursor.y()*qt_settings->glyphs().height(), + qt_settings->glyphs().width()-1,qt_settings->glyphs().height()-1); + } + +#if 0 + if (area.intersects(messages_rect)) { + painter.setPen(Qt::black); + painter.drawText(viewport.contentsX()+1,viewport.contentsY()+1, + viewport.width(),0, Qt::TextWordWrap|Qt::AlignTop|Qt::AlignLeft|Qt::TextDontClip, messages); + painter.setPen(Qt::white); + painter.drawText(viewport.contentsX(),viewport.contentsY(), + viewport.width(),0, Qt::TextWordWrap|Qt::AlignTop|Qt::AlignLeft|Qt::TextDontClip, messages); + } +#endif + + painter.end(); +} + +bool NetHackQtMapViewport::DrawWalls( + QPainter& painter, + int x, int y, int w, int h, + unsigned ch) +{ + enum + { + w_left = 0x01, + w_right = 0x02, + w_up = 0x04, + w_down = 0x08, + w_sq_top = 0x10, + w_sq_bottom = 0x20, + w_sq_left = 0x40, + w_sq_right = 0x80 + }; + unsigned linewidth; + unsigned walls; + int x1, y1, x2, y2, x3, y3; + + linewidth = ((w < h) ? w : h)/8; + if (linewidth == 0) linewidth = 1; + + // Single walls + walls = 0; + switch (ch) + { + case 0x2500: // BOX DRAWINGS LIGHT HORIZONTAL + walls = w_left | w_right; + break; + + case 0x2502: // BOX DRAWINGS LIGHT VERTICAL + walls = w_up | w_down; + break; + + case 0x250C: // BOX DRAWINGS LIGHT DOWN AND RIGHT + walls = w_down | w_right; + break; + + case 0x2510: // BOX DRAWINGS LIGHT DOWN AND LEFT + walls = w_down | w_left; + break; + + case 0x2514: // BOX DRAWINGS LIGHT UP AND RIGHT + walls = w_up | w_right; + break; + + case 0x2518: // BOX DRAWINGS LIGHT UP AND LEFT + walls = w_up | w_left; + break; + + case 0x251C: // BOX DRAWINGS LIGHT VERTICAL AND RIGHT + walls = w_up | w_down | w_right; + break; + + case 0x2524: // BOX DRAWINGS LIGHT VERTICAL AND LEFT + walls = w_up | w_down | w_left; + break; + + case 0x252C: // BOX DRAWINGS LIGHT DOWN AND HORIZONTAL + walls = w_down | w_left | w_right; + break; + + case 0x2534: // BOX DRAWINGS LIGHT UP AND HORIZONTAL + walls = w_up | w_left | w_right; + break; + + case 0x253C: // BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL + walls = w_up | w_down | w_left | w_right; + break; + } + + if (walls != 0) + { + x1 = x + w/2; + switch (walls & (w_up | w_down)) + { + case w_up: + painter.drawLine(x1, y, x1, y+h/2); + break; + + case w_down: + painter.drawLine(x1, y+h/2, x1, y+h-1); + break; + + case w_up | w_down: + painter.drawLine(x1, y, x1, y+h-1); + break; + } + + y1 = y + h/2; + switch (walls & (w_left | w_right)) + { + case w_left: + painter.drawLine(x, y1, x+w/2, y1); + break; + + case w_right: + painter.drawLine(x+w/2, y1, x+w-1, y1); + break; + + case w_left | w_right: + painter.drawLine(x, y1, x+w-1, y1); + break; + } + + return true; + } + + // Double walls + walls = 0; + switch (ch) + { + case 0x2550: // BOX DRAWINGS DOUBLE HORIZONTAL + walls = w_left | w_right | w_sq_top | w_sq_bottom; + break; + + case 0x2551: // BOX DRAWINGS DOUBLE VERTICAL + walls = w_up | w_down | w_sq_left | w_sq_right; + break; + + case 0x2554: // BOX DRAWINGS DOUBLE DOWN AND RIGHT + walls = w_down | w_right | w_sq_top | w_sq_left; + break; + + case 0x2557: // BOX DRAWINGS DOUBLE DOWN AND LEFT + walls = w_down | w_left | w_sq_top | w_sq_right; + break; + + case 0x255A: // BOX DRAWINGS DOUBLE UP AND RIGHT + walls = w_up | w_right | w_sq_bottom | w_sq_left; + break; + + case 0x255D: // BOX DRAWINGS DOUBLE UP AND LEFT + walls = w_up | w_left | w_sq_bottom | w_sq_right; + break; + + case 0x2560: // BOX DRAWINGS DOUBLE VERTICAL AND RIGHT + walls = w_up | w_down | w_right | w_sq_left; + break; + + case 0x2563: // BOX DRAWINGS DOUBLE VERTICAL AND LEFT + walls = w_up | w_down | w_left | w_sq_right; + break; + + case 0x2566: // BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL + walls = w_down | w_left | w_right | w_sq_top; + break; + + case 0x2569: // BOX DRAWINGS DOUBLE UP AND HORIZONTAL + walls = w_up | w_left | w_right | w_sq_bottom; + break; + + case 0x256C: // BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL + walls = w_up | w_down | w_left | w_right; + break; + } + if (walls != 0) + { + x1 = x + w/2 - linewidth; + x2 = x + w/2 + linewidth; + x3 = x + w - 1; + y1 = y + h/2 - linewidth; + y2 = y + h/2 + linewidth; + y3 = y + h - 1; + if (walls & w_up) + { + painter.drawLine(x1, y, x1, y1); + painter.drawLine(x2, y, x2, y1); + } + if (walls & w_down) + { + painter.drawLine(x1, y2, x1, y3); + painter.drawLine(x2, y2, x2, y3); + } + if (walls & w_left) + { + painter.drawLine(x, y1, x1, y1); + painter.drawLine(x, y2, x1, y2); + } + if (walls & w_right) + { + painter.drawLine(x2, y1, x3, y1); + painter.drawLine(x2, y2, x3, y2); + } + if (walls & w_sq_top) + { + painter.drawLine(x1, y1, x2, y1); + } + if (walls & w_sq_bottom) + { + painter.drawLine(x1, y2, x2, y2); + } + if (walls & w_sq_left) + { + painter.drawLine(x1, y1, x1, y2); + } + if (walls & w_sq_right) + { + painter.drawLine(x2, y1, x2, y2); + } + return true; + } + + // Solid blocks + if (0x2591 <= ch && ch <= 0x2593) + { + unsigned shade = ch - 0x2590; + QColor rgb(painter.pen().color()); + QColor rgb2( + rgb.red()*shade/4, + rgb.green()*shade/4, + rgb.blue()*shade/4); + painter.fillRect(x, y, w, h, rgb2); + return true; + } + + return false; +} + +void NetHackQtMapViewport::mousePressEvent(QMouseEvent* event) +{ + clicksink.Put( + event->pos().x()/qt_settings->glyphs().width(), + event->pos().y()/qt_settings->glyphs().height(), + event->button()==Qt::LeftButton ? CLICK_1 : CLICK_2 + ); + qApp->exit(); +} + +void NetHackQtMapViewport::updateTiles() +{ + change.clear(); + change.add(0,0,COLNO,ROWNO); + delete rogue_font; rogue_font = NULL; +} + +QSize NetHackQtMapViewport::sizeHint() const +{ + return QSize( + qt_settings->glyphs().width() * COLNO, + qt_settings->glyphs().height() * ROWNO); +} + +QSize NetHackQtMapViewport::minimumSizeHint() const +{ + return sizeHint(); +} + +void NetHackQtMapViewport::clickCursor() +{ + clicksink.Put(cursor.x(),cursor.y(),CLICK_1); + qApp->exit(); +} + +void NetHackQtMapViewport::Clear() +{ + unsigned short stone=cmap_to_glyph(S_stone); + + for (int j=0; jglyphs().width(), + ch.y()*qt_settings->glyphs().height(), + ch.width()*qt_settings->glyphs().width(), + ch.height()*qt_settings->glyphs().height() + ); + } + + change.clear(); + + if (block) { + yn_function("Press a key when done viewing",0,'\0'); + } +} + +void NetHackQtMapViewport::CursorTo(int x,int y) +{ + Changed(cursor.x(),cursor.y()); + cursor.setX(x); + cursor.setY(y); + Changed(cursor.x(),cursor.y()); +} + +void NetHackQtMapViewport::PrintGlyph(int x,int y,int glyph) +{ + Glyph(x,y)=glyph; + Changed(x,y); +} + +void NetHackQtMapViewport::Changed(int x, int y) +{ + change.add(x,y); +} + +NetHackQtMapWindow2::NetHackQtMapWindow2(NetHackQtClickBuffer& click_sink) : + QScrollArea(NULL), + m_viewport(new NetHackQtMapViewport(click_sink)) +{ + QPalette palette; + palette.setColor(backgroundRole(), Qt::black); + setPalette(palette); + + setWidget(m_viewport); + + connect(qt_settings,SIGNAL(tilesChanged()),this,SLOT(updateTiles())); + updateTiles(); +} + +void NetHackQtMapWindow2::updateTiles() +{ + NetHackQtGlyphs& glyphs = qt_settings->glyphs(); + int gw = glyphs.width(); + int gh = glyphs.height(); + // Be exactly the size we want to be - full map... + m_viewport->resize(COLNO*gw,ROWNO*gh); + + verticalScrollBar()->setSingleStep(gh); + verticalScrollBar()->setPageStep(gh); + horizontalScrollBar()->setSingleStep(gw); + horizontalScrollBar()->setPageStep(gw); + + m_viewport->updateTiles(); + Display(false); + + emit resized(); +} + +void NetHackQtMapWindow2::clearMessages() +{ + messages = ""; + update(messages_rect); + messages_rect = QRect(); +} + +void NetHackQtMapWindow2::putMessage(int attr, const QString& text) +{ + if ( !messages.isEmpty() ) + messages += "\n"; + messages += QString(text).replace(QChar(0x200B), ""); + QFontMetrics fm = fontMetrics(); +#if 0 + messages_rect = fm.boundingRect(viewport.contentsX(),viewport.contentsY(),viewport.width(),0, Qt::TextWordWrap|Qt::AlignTop|Qt::AlignLeft|Qt::TextDontClip, messages); + update(messages_rect); +#endif +} + +void NetHackQtMapWindow2::clickCursor() +{ + m_viewport->clickCursor(); +} + +QWidget *NetHackQtMapWindow2::Widget() +{ + return this; +} + +void NetHackQtMapWindow2::Clear() +{ + m_viewport->Clear(); +} + +void NetHackQtMapWindow2::Display(bool block) +{ + m_viewport->Display(block); +} + +void NetHackQtMapWindow2::CursorTo(int x,int y) +{ + m_viewport->CursorTo(x, y); +} + +void NetHackQtMapWindow2::PutStr(int attr, const QString& text) +{ + puts("unexpected PutStr in MapWindow"); +} + +void NetHackQtMapWindow2::ClipAround(int x,int y) +{ + // Convert to pixel of center of tile + x=x*qt_settings->glyphs().width()+qt_settings->glyphs().width()/2; + y=y*qt_settings->glyphs().height()+qt_settings->glyphs().height()/2; + + // Then ensure that pixel is visible + ensureVisible(x,y,width()*0.45,height()*0.45); +} + +void NetHackQtMapWindow2::PrintGlyph(int x,int y,int glyph) +{ + m_viewport->PrintGlyph(x, y, glyph); +} + +#if 0 //RLC +// XXX Hmmm... crash after saving bones file if Map window is +// XXX deleted. Strange bug somewhere. +bool NetHackQtMapWindow::Destroy() { return false; } + +NetHackQtMapWindow::NetHackQtMapWindow(NetHackQtClickBuffer& click_sink) : + clicksink(click_sink), + change(10), + rogue_font(0) +{ + viewport.addChild(this); + + QPalette palette; + palette.setColor(backgroundRole(), Qt::black); + setPalette(palette); + palette.setColor(viewport.backgroundRole(), Qt::black); + viewport.setPalette(palette); + + pet_annotation = QPixmap(qt_compact_mode ? pet_mark_small_xpm : pet_mark_xpm); + + cursor.setX(0); + cursor.setY(0); + Clear(); + + connect(qt_settings,SIGNAL(tilesChanged()),this,SLOT(updateTiles())); + connect(&viewport, SIGNAL(contentsMoving(int,int)), this, + SLOT(moveMessages(int,int))); + + updateTiles(); + //setFocusPolicy(Qt::StrongFocus); +} + +void NetHackQtMapWindow::moveMessages(int x, int y) +{ + QRect u = messages_rect; + messages_rect.moveTopLeft(QPoint(x,y)); + u |= messages_rect; + update(u); +} + +void NetHackQtMapWindow::clearMessages() +{ + messages = ""; + update(messages_rect); + messages_rect = QRect(); +} + +void NetHackQtMapWindow::putMessage(int attr, const QString& text) +{ + if ( !messages.isEmpty() ) + messages += "\n"; + messages += QString(text).replace(QChar(0x200B), ""); + QFontMetrics fm = fontMetrics(); + messages_rect = fm.boundingRect(viewport.contentsX(),viewport.contentsY(),viewport.width(),0, Qt::TextWordWrap|Qt::AlignTop|Qt::AlignLeft|Qt::TextDontClip, messages); + update(messages_rect); +} + +void NetHackQtMapWindow::updateTiles() +{ + NetHackQtGlyphs& glyphs = qt_settings->glyphs(); + int gw = glyphs.width(); + int gh = glyphs.height(); + // Be exactly the size we want to be - full map... + resize(COLNO*gw,ROWNO*gh); + + viewport.verticalScrollBar()->setSingleStep(gh); + viewport.verticalScrollBar()->setPageStep(gh); + viewport.horizontalScrollBar()->setSingleStep(gw); + viewport.horizontalScrollBar()->setPageStep(gw); + /* + viewport.setMaximumSize( + gw*COLNO + viewport.verticalScrollBar()->width(), + gh*ROWNO + viewport.horizontalScrollBar()->height() + ); + */ + viewport.updateScrollBars(); + + change.clear(); + change.add(0,0,COLNO,ROWNO); + delete rogue_font; rogue_font = 0; + Display(false); + + emit resized(); +} + +NetHackQtMapWindow::~NetHackQtMapWindow() +{ + // Remove from viewport porthole, since that is a destructible member. + viewport.removeChild(this); + setParent(0,0); +} + +QWidget* NetHackQtMapWindow::Widget() +{ + return &viewport; +} + +void NetHackQtMapWindow::Scroll(int dx, int dy) +{ + if (viewport.horizontalScrollBar()->isVisible()) { + while (dx<0) { viewport.horizontalScrollBar()->triggerAction(QAbstractSlider::SliderPageStepSub); dx++; } + while (dx>0) { viewport.horizontalScrollBar()->triggerAction(QAbstractSlider::SliderPageStepAdd); dx--; } + } + if (viewport.verticalScrollBar()->isVisible()) { + while (dy<0) { viewport.verticalScrollBar()->triggerAction(QAbstractSlider::SliderPageStepSub); dy++; } + while (dy>0) { viewport.verticalScrollBar()->triggerAction(QAbstractSlider::SliderPageStepAdd); dy--; } + } +} + +void NetHackQtMapWindow::Clear() +{ + unsigned short stone=cmap_to_glyph(S_stone); + + for (int j=0; jexit(); +} + +void NetHackQtMapWindow::mousePressEvent(QMouseEvent* event) +{ + clicksink.Put( + event->pos().x()/qt_settings->glyphs().width(), + event->pos().y()/qt_settings->glyphs().height(), + event->button()==Qt::LeftButton ? CLICK_1 : CLICK_2 + ); + qApp->exit(); +} + +void NetHackQtMapWindow::paintEvent(QPaintEvent* event) +{ + QRect area=event->rect(); + QRect garea; + garea.setCoords( + std::max(0,area.left()/qt_settings->glyphs().width()), + std::max(0,area.top()/qt_settings->glyphs().height()), + std::min(COLNO-1,area.right()/qt_settings->glyphs().width()), + std::min(ROWNO-1,area.bottom()/qt_settings->glyphs().height()) + ); + + QPainter painter; + + painter.begin(this); + + if (is_rogue_level(&u.uz) || iflags.wc_ascii_map) { + // You enter a VERY primitive world! + + painter.setClipRect( event->rect() ); // (normally we don't clip) + painter.fillRect( event->rect(), Qt::black ); + + if ( !rogue_font ) { + // Find font... + int pts = 5; + QString fontfamily = iflags.wc_font_map + ? iflags.wc_font_map : "Courier"; + bool bold = false; + if ( fontfamily.right(5).toLower() == "-bold" ) { + fontfamily.truncate(fontfamily.length()-5); + bold = true; + } + while ( pts < 32 ) { + QFont f(fontfamily, pts, bold ? QFont::Bold : QFont::Normal); + painter.setFont(QFont(fontfamily, pts)); + QFontMetrics fm = painter.fontMetrics(); + if ( fm.width("M") > qt_settings->glyphs().width() ) + break; + if ( fm.height() > qt_settings->glyphs().height() ) + break; + pts++; + } + rogue_font = new QFont(fontfamily,pts-1); + } + painter.setFont(*rogue_font); + + for (int j=garea.top(); j<=garea.bottom(); j++) { + for (int i=garea.left(); i<=garea.right(); i++) { + unsigned short g=Glyph(i,j); + int color; + char32_t ch; + unsigned special; + + painter.setPen( Qt::green ); + /* map glyph to character and color */ + mapglyph(g, &ch, &color, &special, i, j); +#ifdef TEXTCOLOR + painter.setPen( nhcolor_to_pen(color) ); +#endif + painter.drawText( + i*qt_settings->glyphs().width(), + j*qt_settings->glyphs().height(), + qt_settings->glyphs().width(), + qt_settings->glyphs().height(), + Qt::AlignCenter, + QString(QChar(ch)).left(1) + ); + if (glyph_is_pet(g) +#ifdef TEXTCOLOR + && ::iflags.hilite_pet +#endif + ) { + painter.drawPixmap(QPoint(i*qt_settings->glyphs().width(), j*qt_settings->glyphs().height()), pet_annotation); + } + } + } + + painter.setFont(font()); + } else { + for (int j=garea.top(); j<=garea.bottom(); j++) { + for (int i=garea.left(); i<=garea.right(); i++) { + unsigned short g=Glyph(i,j); + qt_settings->glyphs().drawCell(painter, g, i, j); + if (glyph_is_pet(g) +#ifdef TEXTCOLOR + && ::iflags.hilite_pet +#endif + ) { + painter.drawPixmap(QPoint(i*qt_settings->glyphs().width(), j*qt_settings->glyphs().height()), pet_annotation); + } + } + } + } + + if (garea.contains(cursor)) { + if (Is_rogue_level(&u.uz)) { +#ifdef TEXTCOLOR + painter.setPen( Qt::white ); +#else + painter.setPen( Qt::green ); // REALLY primitive +#endif + } else + { + int hp100; + if (u.mtimedone) { + hp100=u.mhmax ? u.mh*100/u.mhmax : 100; + } else { + hp100=u.uhpmax ? u.uhp*100/u.uhpmax : 100; + } + + if (hp100 > 75) painter.setPen(Qt::white); + else if (hp100 > 50) painter.setPen(Qt::yellow); + else if (hp100 > 25) painter.setPen(QColor(0xff,0xbf,0x00)); // orange + else if (hp100 > 10) painter.setPen(Qt::red); + else painter.setPen(Qt::magenta); + } + + painter.drawRect( + cursor.x()*qt_settings->glyphs().width(),cursor.y()*qt_settings->glyphs().height(), + qt_settings->glyphs().width()-1,qt_settings->glyphs().height()-1); + } + + if (area.intersects(messages_rect)) { + painter.setPen(Qt::black); + painter.drawText(viewport.contentsX()+1,viewport.contentsY()+1, + viewport.width(),0, Qt::TextWordWrap|Qt::AlignTop|Qt::AlignLeft|Qt::TextDontClip, messages); + painter.setPen(Qt::white); + painter.drawText(viewport.contentsX(),viewport.contentsY(), + viewport.width(),0, Qt::TextWordWrap|Qt::AlignTop|Qt::AlignLeft|Qt::TextDontClip, messages); + } + + painter.end(); +} + +void NetHackQtMapWindow::Display(bool block) +{ + for (int i=0; iglyphs().width(), + ch.y()*qt_settings->glyphs().height(), + ch.width()*qt_settings->glyphs().width(), + ch.height()*qt_settings->glyphs().height() + ); + } + + change.clear(); + + if (block) { + yn_function("Press a key when done viewing",0,'\0'); + } +} + +void NetHackQtMapWindow::CursorTo(int x,int y) +{ + Changed(cursor.x(),cursor.y()); + cursor.setX(x); + cursor.setY(y); + Changed(cursor.x(),cursor.y()); +} + +void NetHackQtMapWindow::PutStr(int attr, const QString& text) +{ + puts("unexpected PutStr in MapWindow"); +} + +void NetHackQtMapWindow::ClipAround(int x,int y) +{ + // Convert to pixel of center of tile + x=x*qt_settings->glyphs().width()+qt_settings->glyphs().width()/2; + y=y*qt_settings->glyphs().height()+qt_settings->glyphs().height()/2; + + // Then ensure that pixel is visible + viewport.center(x,y,0.45,0.45); +} + +void NetHackQtMapWindow::PrintGlyph(int x,int y,int glyph) +{ + Glyph(x,y)=glyph; + Changed(x,y); +} + +//void NetHackQtMapWindow::PrintGlyphCompose(int x,int y,int glyph1, int glyph2) +//{ + // TODO: composed graphics +//} + +void NetHackQtMapWindow::Changed(int x, int y) +{ + change.add(x,y); +} +#endif + +} // namespace nethack_qt4 diff --git a/win/Qt4/qt4map.h b/win/Qt4/qt4map.h new file mode 100644 index 000000000..da2df88ad --- /dev/null +++ b/win/Qt4/qt4map.h @@ -0,0 +1,81 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4map.h -- the map window + +#ifndef QT4MAP_H +#define QT4MAP_H + +#include "qt4win.h" +#include "qt4clust.h" + +namespace nethack_qt4 { + +class NetHackQtClickBuffer; + +class NetHackQtMapViewport : public QWidget { + Q_OBJECT +public: + NetHackQtMapViewport(NetHackQtClickBuffer& click_sink); + ~NetHackQtMapViewport(void); + +protected: + virtual void paintEvent(QPaintEvent* event); + bool DrawWalls(QPainter& painter, int x, int y, int w, int h, unsigned ch); + virtual QSize sizeHint() const; + virtual QSize minimumSizeHint() const; + virtual void mousePressEvent(QMouseEvent* event); + +private: + QFont *rogue_font; + unsigned short glyph[ROWNO][COLNO]; + unsigned short& Glyph(int x, int y) { return glyph[y][x]; } + QPoint cursor; + QPixmap pet_annotation; + NetHackQtClickBuffer& clicksink; + Clusterizer change; + + void clickCursor(); + void Clear(); + void Display(bool block); + void CursorTo(int x,int y); + void PrintGlyph(int x,int y,int glyph); + void Changed(int x, int y); + void updateTiles(); + + // NetHackQtMapWindow2 passes through many calls to the viewport + friend class NetHackQtMapWindow2; +}; + +class NetHackQtMapWindow2 : public QScrollArea, public NetHackQtWindow { + Q_OBJECT +public: + NetHackQtMapWindow2(NetHackQtClickBuffer& click_sink); + void clearMessages(); + void putMessage(int attr, const QString& text); + void clickCursor(); + virtual QWidget *Widget(); + + virtual void Clear(); + virtual void Display(bool block); + virtual void CursorTo(int x,int y); + virtual void PutStr(int attr, const QString& text); + virtual void ClipAround(int x,int y); + virtual void PrintGlyph(int x,int y,int glyph); + +signals: + void resized(); + +private slots: + void updateTiles(); + +private: + NetHackQtMapViewport *m_viewport; + QRect messages_rect; + QString messages; +}; + +} // namespace nethack_qt4 + +#endif diff --git a/win/Qt4/qt4menu.cpp b/win/Qt4/qt4menu.cpp new file mode 100644 index 000000000..b15aa0ee5 --- /dev/null +++ b/win/Qt4/qt4menu.cpp @@ -0,0 +1,840 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4menu.cpp -- a menu or text-list widget + +extern "C" { +#include "hack.h" +} +#undef Invisible +#undef Warning +#undef index +#undef msleep +#undef rindex +#undef wizard +#undef yn +#undef min +#undef max + +#include +#if QT_VERSION >= 0x050000 +#include +#endif +#include "qt4menu.h" +#include "qt4menu.moc" +#include "qt4glyph.h" +#include "qt4set.h" +#include "qt4streq.h" +#include "qt4str.h" + +// temporary +extern "C" int qt_compact_mode; +// end temporary + +#ifdef MENU_COLOR +extern "C" struct menucoloring *menu_colorings; +#endif + +namespace nethack_qt4 { + +// temporary +void centerOnMain( QWidget* w ); +// end temporary + +static boolean get_menu_coloring(char const *str, int *color, int *attr); + +QSize NetHackQtTextListBox::sizeHint() const +{ + QScrollBar *hscroll = horizontalScrollBar(); + int hsize = hscroll ? hscroll->height() : 0; + return QSize(TotalWidth()+hsize, TotalHeight()+hsize); +} + +int NetHackQtMenuListBox::TotalWidth() const +{ + int width = 0; + + for (int col = 0; col < columnCount(); ++col) { + width += columnWidth(col); + } + return width; +} + +int NetHackQtMenuListBox::TotalHeight() const +{ + int height = 0; + + for (int row = 0; row < rowCount(); ++row) { + height += rowHeight(row); + } + return height; +} + +QSize NetHackQtMenuListBox::sizeHint() const +{ + QScrollBar *hscroll = horizontalScrollBar(); + int hsize = hscroll ? hscroll->height() : 0; + return QSize(TotalWidth()+hsize, TotalHeight()+hsize); +} + +// Table view columns: +// +// [pick-count] [accel] [glyph] [string] +// +// Maybe accel should be near string. We'll see. +// pick-count normally blank. +// double-clicking or click-on-count gives pop-up entry +// string is green when selected +// +NetHackQtMenuWindow::NetHackQtMenuWindow(QWidget *parent) : + QDialog(parent), + table(new NetHackQtMenuListBox()), + prompt(0), + counting(false) +{ + QGridLayout *grid = new QGridLayout(); + table->setColumnCount(5); + table->setFrameStyle(QFrame::Panel|QFrame::Sunken); + table->setLineWidth(2); + table->setShowGrid(false); + table->horizontalHeader()->hide(); + table->verticalHeader()->hide(); + + ok=new QPushButton("Ok"); + connect(ok,SIGNAL(clicked()),this,SLOT(accept())); + + cancel=new QPushButton("Cancel"); + connect(cancel,SIGNAL(clicked()),this,SLOT(reject())); + + all=new QPushButton("All"); + connect(all,SIGNAL(clicked()),this,SLOT(All())); + + none=new QPushButton("None"); + connect(none,SIGNAL(clicked()),this,SLOT(ChooseNone())); + + invert=new QPushButton("Invert"); + connect(invert,SIGNAL(clicked()),this,SLOT(Invert())); + + search=new QPushButton("Search"); + connect(search,SIGNAL(clicked()),this,SLOT(Search())); + + QPoint pos(0,ok->height()); + move(pos); + prompt.setParent(this,0); + prompt.move(pos); + + grid->addWidget(ok, 0, 0); + grid->addWidget(cancel, 0, 1); + grid->addWidget(all, 0, 2); + grid->addWidget(none, 0, 3); + grid->addWidget(invert, 0, 4); + grid->addWidget(search, 0, 5); + grid->addWidget(&prompt, 1, 0, 1, 7); + grid->addWidget(table, 2, 0, 1, 7); + grid->setColumnStretch(6, 1); + grid->setRowStretch(2, 1); + setFocusPolicy(Qt::StrongFocus); + table->setFocusPolicy(Qt::NoFocus); + + setLayout(grid); +} + +NetHackQtMenuWindow::~NetHackQtMenuWindow() +{ +} + +QWidget* NetHackQtMenuWindow::Widget() { return this; } + +void NetHackQtMenuWindow::StartMenu() +{ + table->setRowCount((itemcount=0)); + next_accel=0; + has_glyphs=false; +} + +NetHackQtMenuWindow::MenuItem::MenuItem() : + str("") +{ +} + +NetHackQtMenuWindow::MenuItem::~MenuItem() +{ +} + +void NetHackQtMenuWindow::AddMenu(int glyph, const ANY_P* identifier, + char ch, char gch, int attr, const QString& str, bool presel) +{ + if (!ch && identifier->a_void!=0) { + // Supply a keyboard accelerator. Limited supply. + static char accel[]="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + if (accel[next_accel]) { + ch=accel[next_accel++]; + } + } + + if ((int)itemlist.size() < itemcount+1) { + itemlist.resize(itemcount*4+10); + } + itemlist[itemcount].glyph=glyph; + itemlist[itemcount].identifier=*identifier; + itemlist[itemcount].ch=ch; + itemlist[itemcount].gch=gch; + itemlist[itemcount].attr=attr; + itemlist[itemcount].str=str; + itemlist[itemcount].selected=presel; + itemlist[itemcount].count=-1; + itemlist[itemcount].color = -1; + // Display the boulder symbol correctly + if (str.left(8) == "boulder\t") { + int bracket = str.indexOf('['); + if (bracket != -1) { + itemlist[itemcount].str = str.left(bracket+1) + + QChar(cp437(str.at(bracket+1).unicode())) + + str.mid(bracket+2); + } + } +#ifdef MENU_COLOR + int mcolor, mattr; + if (attr == 0 + && get_menu_coloring(str.toLatin1().constData(), &mcolor, &mattr)) { + itemlist[itemcount].attr = mattr; + itemlist[itemcount].color = mcolor; + } +#endif + ++itemcount; + + if (glyph!=NO_GLYPH) has_glyphs=true; +} + +#ifdef MENU_COLOR +static boolean +get_menu_coloring(char const *str, int *color, int *attr) +{ + struct menucoloring *tmpmc; + if (iflags.use_menu_color) + for (tmpmc = menu_colorings; tmpmc; tmpmc = tmpmc->next) +# ifdef MENU_COLOR_REGEX + if (re_search(&tmpmc->match, str, strlen(str), 0, 9999, 0) >= 0) { +# else + if (pmatch(tmpmc->match, str)) { +# endif + *color = tmpmc->color; + *attr = tmpmc->attr; + return TRUE; + } + return FALSE; +} +#endif /* MENU_COLOR */ + +void NetHackQtMenuWindow::EndMenu(const QString& p) +{ + prompt.setText(p); + promptstr = p; +} + +int NetHackQtMenuWindow::SelectMenu(int h, MENU_ITEM_P **menu_list) +{ + QFont tablefont(qt_settings->normalFont()); + table->setFont(tablefont); + + table->setRowCount(itemcount); + + how=h; + + ok->setEnabled(how!=PICK_ONE);ok->setDefault(how!=PICK_ONE); + cancel->setEnabled(how!=PICK_NONE); + all->setEnabled(how==PICK_ANY); + none->setEnabled(how==PICK_ANY); + invert->setEnabled(how==PICK_ANY); + search->setEnabled(how!=PICK_NONE); + + setResult(-1); + + // Set contents of table + QFontMetrics fm(table->font()); + for (int i = 0; i < 5; i++) { + table->setColumnWidth(i, 0); + } + for (int i = 0; i < itemcount; i++) { + AddRow(i, itemlist[i]); + } + + // Determine column widths + std::vector col_widths; + for (std::size_t i = 0; i < itemlist.size(); ++i) { + QStringList columns = itemlist[i].str.split("\t"); + if (!itemlist[i].Selectable() && columns.size() == 1) + { + // Nonselectable line with no column dividers + // Assume this is a section header + continue; + } + for (std::size_t j = 0U; j < columns.size(); ++j) { + int w = fm.width(columns[j] + " \t"); + if (j >= col_widths.size()) { + col_widths.push_back(w); + } else if (col_widths[j] < w) { + col_widths[j] = w; + } + } + } + + // Pad each column to its column width + for (std::size_t i = 0U; i < itemlist.size(); ++i) { + QTableWidgetItem *twi = table->item(i, 4); + if (twi == NULL) { continue; } + QString text = twi->text(); + QStringList columns = text.split("\t"); + for (std::size_t j = 0U; j+1U < columns.size(); ++j) { + columns[j] += "\t"; + int width = col_widths[j]; + while (fm.width(columns[j]) < width) { + columns[j] += "\t"; + } + } + text = columns.join(""); + twi->setText(text); + WidenColumn(4, fm.width(text)); + } + + // FIXME: size for compact mode + //resize(this->width(), parent()->height()*7/8); + move(0, 0); + adjustSize(); + centerOnMain(this); + exec(); + int result=this->result(); + + *menu_list=0; + if (result>0 && how!=PICK_NONE) { + if (how==PICK_ONE) { + int i; + for (i=0; ifont()); + QTableWidgetItem *twi; + + if (mi.Selectable() && how != PICK_NONE) { + // Count + twi = new QTableWidgetItem(""); + table->setItem(row, 0, twi); + twi->setFlags(Qt::ItemIsEnabled); + WidenColumn(0, fm.width("999999")); + // Check box, set if selected + QCheckBox *cb = new QCheckBox(); + cb->setChecked(mi.selected); + cb->setFocusPolicy(Qt::NoFocus); + if (how == PICK_ONE) + connect(cb, SIGNAL(clicked(bool)), this, SLOT(DoSelection(bool))); + table->setCellWidget(row, 1, cb); + WidenColumn(1, cb->width()); + } + if (mi.glyph != NO_GLYPH) { + // Icon + QPixmap pm(qt_settings->glyphs().glyph(mi.glyph)); + twi = new QTableWidgetItem(QIcon(pm), ""); + table->setItem(row, 2, twi); + twi->setFlags(Qt::ItemIsEnabled); + WidenColumn(2, pm.width()); + } + QString letter, text(mi.str); + if (mi.ch != 0) { + // Letter specified + letter = QString(mi.ch) + " - "; + } + else { + // Letter is left blank, except for skills display when # and * are + // presented + if (text.startsWith(" ")) { + // If mi.str starts with " ", it's meant to line up with lines + // that have a letter; we don't want that here + text = text.mid(4); + } else if (text.startsWith(" #") || text.startsWith(" *")) { + // Put the * or # in the letter column + letter = text.left(4); + text = text.mid(4); + } + } + twi = new QTableWidgetItem(letter); + table->setItem(row, 3, twi); + table->item(row, 3)->setFlags(Qt::ItemIsEnabled); + WidenColumn(3, fm.width(letter)); + twi = new QTableWidgetItem(text); + table->setItem(row, 4, twi); + table->item(row, 4)->setFlags(Qt::ItemIsEnabled); + WidenColumn(4, fm.width(text)); + +#ifdef MENU_COLOR + if (mi.color != -1) { + twi->setForeground(colors[mi.color]); + } +#endif + + QFont itemfont(table->font()); + switch (mi.attr) { + case ATR_BOLD: + itemfont.setWeight(QFont::Bold); + twi->setFont(itemfont); + break; + + case ATR_DIM: + twi->setFlags(Qt::NoItemFlags); + break; + + case ATR_ULINE: + itemfont.setUnderline(true); + twi->setFont(itemfont); + break; + + case ATR_INVERSE: + { + QBrush fg = twi->foreground(); + QBrush bg = twi->background(); + if (fg == bg) { + // default foreground and background come up the same for + // some unknown reason + twi->setForeground(Qt::white); + twi->setBackground(Qt::black); + } else { + twi->setForeground(bg); + twi->setBackground(fg); + } + } + break; + } +} + +void NetHackQtMenuWindow::WidenColumn(int column, int width) +{ + // need to add a bit so the whole column displays + width += 7; + if (table->columnWidth(column) < width) { + table->setColumnWidth(column, width); + } +} + +void NetHackQtMenuWindow::InputCount(char key) +{ + if (key == '\b') + { + if (counting) + { + if (countstr.isEmpty()) + ClearCount(); + else + countstr = countstr.mid(0, countstr.size() - 1); + } + } + else + { + counting = true; + countstr += QChar(key); + } + if (counting) + prompt.setText("Count: " + countstr); +} + +void NetHackQtMenuWindow::ClearCount(void) +{ + counting = false; + prompt.setText(promptstr); + countstr = ""; +} + +void NetHackQtMenuWindow::keyPressEvent(QKeyEvent* event) +{ + QString text = event->text(); + + const QChar *uni = text.unicode(); + for (unsigned k = 0; uni[k] != 0; k++) { + unsigned key = uni[k].unicode(); + if (key=='\033') { + if (counting) + ClearCount(); + else + reject(); + } else if (key=='\r' || key=='\n' || key==' ') + accept(); + else if (key==MENU_SEARCH) + Search(); + else if (key==MENU_SELECT_ALL) + All(); + else if (key==MENU_INVERT_ALL) + Invert(); + else if (key==MENU_UNSELECT_ALL) + ChooseNone(); + else if (('0' <= key && key <= '9') || key == '\b') + InputCount(key); + else { + for (int i=0; iitem(i, 0); + if (count != NULL) count->setText(""); + + QCheckBox *cb = dynamic_cast(table->cellWidget(i, 1)); + if (cb != NULL) cb->setChecked(true); + } +} +void NetHackQtMenuWindow::ChooseNone() +{ + for (int i=0; iitem(i, 0); + if (count != NULL) count->setText(""); + + QCheckBox *cb = dynamic_cast(table->cellWidget(i, 1)); + if (cb != NULL) cb->setChecked(false); + } +} +void NetHackQtMenuWindow::Invert() +{ + for (int i=0; iitem(i, 0); + if (count != NULL) count->setText(""); + + QCheckBox *cb = dynamic_cast(table->cellWidget(i, 1)); + if (cb != NULL) cb->setChecked(cb->checkState() == Qt::Unchecked); + } +} +void NetHackQtMenuWindow::Search() +{ + NetHackQtStringRequestor requestor(this, "Search for:"); + char line[256]; + if (requestor.Get(line)) { + for (int i=0; i(table->cellWidget(i, 1)); + if (cb == NULL) return; + + cb->setChecked((counting && !countstr.isEmpty()) + || cb->checkState() == Qt::Unchecked); + + QTableWidgetItem *count = table->item(i, 0); + if (count != NULL) count->setText(countstr); + + ClearCount(); + + if (how==PICK_ONE) { + accept(); + } + } +} + +void NetHackQtMenuWindow::DoSelection(bool) +{ + if (how == PICK_ONE) { + accept(); + } +} + +bool NetHackQtMenuWindow::isSelected(int row) +{ + QCheckBox *cb = dynamic_cast(table->cellWidget(row, 1)); + return cb != NULL && cb->checkState() != Qt::Unchecked; +} + +int NetHackQtMenuWindow::count(int row) +{ + QTableWidgetItem *count = table->item(row, 0); + if (count == NULL) return -1; + QString cstr = count->text(); + return cstr.isEmpty() ? -1 : cstr.toInt(); +} + +NetHackQtTextWindow::NetHackQtTextWindow(QWidget *parent) : + QDialog(parent), + use_rip(false), + str_fixed(false), + ok("Dismiss",this), + search("Search",this), + lines(new NetHackQtTextListBox(this)), + rip(this) +{ + ok.setDefault(true); + connect(&ok,SIGNAL(clicked()),this,SLOT(accept())); + connect(&search,SIGNAL(clicked()),this,SLOT(Search())); + connect(qt_settings,SIGNAL(fontChanged()),this,SLOT(doUpdate())); + + QVBoxLayout* vb = new QVBoxLayout(this); + vb->addWidget(&rip); + QHBoxLayout* hb = new QHBoxLayout(); + vb->addLayout(hb); + hb->addWidget(&ok); + hb->addWidget(&search); + vb->addWidget(lines); +} + +void NetHackQtTextWindow::doUpdate() +{ + update(); +} + + +NetHackQtTextWindow::~NetHackQtTextWindow() +{ + +} + +QWidget* NetHackQtTextWindow::Widget() +{ + return this; +} + +bool NetHackQtTextWindow::Destroy() +{ + return !isVisible(); +} + +void NetHackQtTextWindow::UseRIP(int how, time_t when) +{ +// Code from X11 windowport +#define STONE_LINE_LEN 16 /* # chars that fit on one line */ +#define NAME_LINE 0 /* line # for player name */ +#define GOLD_LINE 1 /* line # for amount of gold */ +#define DEATH_LINE 2 /* line # for death description */ +#define YEAR_LINE 6 /* line # for year */ + +static char** rip_line=0; + if (!rip_line) { + rip_line=new char*[YEAR_LINE+1]; + for (int i=0; i STONE_LINE_LEN) { + for(i = STONE_LINE_LEN; + ((i0 > STONE_LINE_LEN) && i); i--) + if(dpx[i] == ' ') i0 = i; + if(!i) i0 = STONE_LINE_LEN; + } + tmpchar = dpx[i0]; + dpx[i0] = 0; + str_copy(rip_line[line], dpx, STONE_LINE_LEN+1); + if (tmpchar != ' ') { + dpx[i0] = tmpchar; + dpx= &dpx[i0]; + } else dpx= &dpx[i0+1]; + } + + /* Put year on stone */ + snprintf(rip_line[YEAR_LINE], STONE_LINE_LEN+1, "%4d", getyear()); + + rip.setLines(rip_line,YEAR_LINE+1); + + use_rip=true; +} + +void NetHackQtTextWindow::Clear() +{ + lines->clear(); + use_rip=false; + str_fixed=false; +} + +void NetHackQtTextWindow::Display(bool block) +{ + if (str_fixed) { + lines->setFont(qt_settings->normalFixedFont()); + } else { + lines->setFont(qt_settings->normalFont()); + } + + int h=0; + if (use_rip) { + h+=rip.height(); + ok.hide(); + search.hide(); + rip.show(); + } else { + h+=ok.height()*2 + 7; + ok.show(); + search.show(); + rip.hide(); + } + int mh = QApplication::desktop()->height()*3/5; + if ( (qt_compact_mode && lines->TotalHeight() > mh) || use_rip ) { + // big, so make it fill + showMaximized(); + } else { + move(0, 0); + adjustSize(); + centerOnMain(this); + show(); + } + exec(); +} + +void NetHackQtTextWindow::PutStr(int attr, const QString& text) +{ + str_fixed=str_fixed || text.contains(" "); + lines->addItem(text); +} + +void NetHackQtTextWindow::Search() +{ + NetHackQtStringRequestor requestor(this, "Search for:"); + static char line[256]=""; + requestor.SetDefault(line); + if (requestor.Get(line)) { + int current=lines->currentRow(); + for (int i=1; icount(); i++) { + int lnum=(i+current)%lines->count(); + QString str=lines->item(lnum)->text(); + if (str.contains(line)) { + lines->setCurrentRow(lnum); + return; + } + } + lines->setCurrentItem(NULL); + } +} + +NetHackQtMenuOrTextWindow::NetHackQtMenuOrTextWindow(QWidget *parent_) : + actual(0), + parent(parent_) +{ +} + +QWidget* NetHackQtMenuOrTextWindow::Widget() +{ + if (!actual) impossible("Widget called before we know if Menu or Text"); + return actual->Widget(); +} + +// Text +void NetHackQtMenuOrTextWindow::Clear() +{ + if (!actual) impossible("Clear called before we know if Menu or Text"); + actual->Clear(); +} +void NetHackQtMenuOrTextWindow::Display(bool block) +{ + if (!actual) impossible("Display called before we know if Menu or Text"); + actual->Display(block); +} +bool NetHackQtMenuOrTextWindow::Destroy() +{ + if (!actual) impossible("Destroy called before we know if Menu or Text"); + return actual->Destroy(); +} + +void NetHackQtMenuOrTextWindow::PutStr(int attr, const QString& text) +{ + if (!actual) actual=new NetHackQtTextWindow(parent); + actual->PutStr(attr,text); +} + +// Menu +void NetHackQtMenuOrTextWindow::StartMenu() +{ + if (!actual) actual=new NetHackQtMenuWindow(parent); + actual->StartMenu(); +} +void NetHackQtMenuOrTextWindow::AddMenu(int glyph, const ANY_P* identifier, char ch, char gch, int attr, + const QString& str, bool presel) +{ + if (!actual) impossible("AddMenu called before we know if Menu or Text"); + actual->AddMenu(glyph,identifier,ch,gch,attr,str,presel); +} +void NetHackQtMenuOrTextWindow::EndMenu(const QString& prompt) +{ + if (!actual) impossible("EndMenu called before we know if Menu or Text"); + actual->EndMenu(prompt); +} +int NetHackQtMenuOrTextWindow::SelectMenu(int how, MENU_ITEM_P **menu_list) +{ + if (!actual) impossible("SelectMenu called before we know if Menu or Text"); + return actual->SelectMenu(how,menu_list); +} + +} // namespace nethack_qt4 diff --git a/win/Qt4/qt4menu.h b/win/Qt4/qt4menu.h new file mode 100644 index 000000000..ad15841bc --- /dev/null +++ b/win/Qt4/qt4menu.h @@ -0,0 +1,182 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4menu.cpp -- a menu or text-list widget + +#ifndef QT4MENU_H +#define QT4MENU_H + +#include "qt4win.h" +#include "qt4rip.h" + +namespace nethack_qt4 { + +class NetHackQtTextListBox : public QListWidget { +public: + NetHackQtTextListBox(QWidget* parent = NULL) : QListWidget(parent) { } + + int TotalWidth() const + { + int width = 0; + QFontMetrics fm(font()); + for (int i = 0; i < count(); i++) { + int lwidth = fm.width(item(i)->text()); + width = std::max(width, lwidth); + } + return width; + } + int TotalHeight() const + { + QFontMetrics fm(font()); + return fm.height() * count(); + } + + virtual QSize sizeHint() const; +}; + +class NetHackQtMenuListBox : public QTableWidget { +public: + NetHackQtMenuListBox(QWidget* parent = NULL) : QTableWidget(parent) { } + + int TotalWidth() const; + int TotalHeight() const; + + virtual QSize sizeHint() const; +}; + +class NetHackQtMenuWindow : public QDialog, public NetHackQtWindow { + Q_OBJECT +public: + NetHackQtMenuWindow(QWidget *parent = NULL); + ~NetHackQtMenuWindow(); + + virtual QWidget* Widget(); + + virtual void StartMenu(); + virtual void AddMenu(int glyph, const ANY_P* identifier, char ch, char gch, int attr, + const QString& str, bool presel); + virtual void EndMenu(const QString& prompt); + virtual int SelectMenu(int how, MENU_ITEM_P **menu_list); + +public slots: + void All(); + void ChooseNone(); + void Invert(); + void Search(); + + void ToggleSelect(int); + void DoSelection(bool); + +protected: + virtual void keyPressEvent(QKeyEvent*); + +private: + struct MenuItem { + MenuItem(); + ~MenuItem(); + + int glyph; + ANY_P identifier; + int attr; + QString str; + int count; + char ch; + char gch; + bool selected; + unsigned color; + + bool Selectable() const { return identifier.a_void!=0; } + }; + + QVector itemlist; + + int itemcount; + int next_accel; + + QTableWidget* table; + QPushButton* ok; + QPushButton* cancel; + QPushButton* all; + QPushButton* none; + QPushButton* invert; + QPushButton* search; + QLabel prompt; + + // Count replaces prompt while it is being input + QString promptstr; + QString countstr; + bool counting; + void InputCount(char key); + void ClearCount(void); + + int how; + + bool has_glyphs; + + bool isSelected(int row); + int count(int row); + + void AddRow(int row, const MenuItem& mi); + void WidenColumn(int column, int width); +}; + +class NetHackQtTextWindow : public QDialog, public NetHackQtWindow { + Q_OBJECT +public: + NetHackQtTextWindow(QWidget *parent = NULL); + ~NetHackQtTextWindow(); + + virtual QWidget* Widget(); + + virtual void Clear(); + virtual bool Destroy(); + virtual void Display(bool block); + virtual void PutStr(int attr, const QString& text); + virtual void UseRIP(int how, time_t when); + +public slots: + void Search(); + +private slots: + void doUpdate(); + +private: + bool use_rip; + bool str_fixed; + + QPushButton ok; + QPushButton search; + NetHackQtTextListBox* lines; + + NetHackQtRIP rip; +}; + +class NetHackQtMenuOrTextWindow : public NetHackQtWindow { +private: + NetHackQtWindow* actual; + QWidget *parent; + +public: + NetHackQtMenuOrTextWindow(QWidget *parent = NULL); + + virtual QWidget* Widget(); + + // Text + virtual void Clear(); + virtual bool Destroy(); + virtual void Display(bool block); + virtual void PutStr(int attr, const QString& text); + + // Menu + virtual void StartMenu(); + virtual void AddMenu(int glyph, const ANY_P* identifier, char ch, char gch, int attr, + const QString& str, bool presel); + virtual void EndMenu(const QString& prompt); + virtual int SelectMenu(int how, MENU_ITEM_P **menu_list); + +}; + +} // namespace nethack_qt4 + +#endif diff --git a/win/Qt4/qt4msg.cpp b/win/Qt4/qt4msg.cpp new file mode 100644 index 000000000..e1194795d --- /dev/null +++ b/win/Qt4/qt4msg.cpp @@ -0,0 +1,134 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4msg.cpp -- a message window + +extern "C" { +#include "hack.h" +} +#undef Invisible +#undef Warning +#undef index +#undef msleep +#undef rindex +#undef wizard +#undef yn +#undef min +#undef max + +#include +#if QT_VERSION >= 0x050000 +#include +#endif +#include "qt4msg.h" +#include "qt4msg.moc" +#include "qt4map.h" +#include "qt4set.h" +#include "qt4str.h" + +namespace nethack_qt4 { + +NetHackQtMessageWindow::NetHackQtMessageWindow() : + list(new QListWidget()) +{ + list->setFocusPolicy(Qt::NoFocus); + ::iflags.window_inited = 1; + map = 0; + connect(qt_settings,SIGNAL(fontChanged()),this,SLOT(updateFont())); + updateFont(); +} + +NetHackQtMessageWindow::~NetHackQtMessageWindow() +{ + ::iflags.window_inited = 0; + delete list; +} + +QWidget* NetHackQtMessageWindow::Widget() { return list; } + +void NetHackQtMessageWindow::setMap(NetHackQtMapWindow2* m) +{ + map = m; + updateFont(); +} + +void NetHackQtMessageWindow::updateFont() +{ + list->setFont(qt_settings->normalFont()); + if ( map ) + map->setFont(qt_settings->normalFont()); +} + +void NetHackQtMessageWindow::Scroll(int dx, int dy) +{ + //RLC Is this necessary? + //RLC list->Scroll(dx,dy); +} + +void NetHackQtMessageWindow::Clear() +{ + if ( map ) + map->clearMessages(); +} + +void NetHackQtMessageWindow::Display(bool block) +{ + if (changed) { + list->repaint(); + changed=false; + } +} + +void NetHackQtMessageWindow::PutStr(int attr, const QString& text) +{ +#ifdef USER_SOUNDS + play_sound_for_message(text.toLatin1().constData()); +#endif + + changed=true; + + // If the line is output from the "/" command, map the first character + // as a symbol + QString text2; + if (text.mid(1, 3) == " ") { + text2 = QChar(cp437(text.at(0).unicode())) + text.mid(1); + } else { + text2 = text; + } +#if 0 + QListWidgetItem *item = new QListWidgetItem(text2); + + QFont font = item->font(); + font.setUnderline(attr == ATR_ULINE); + font.setWeight((attr == ATR_BOLD) ? QFont::Bold : QFont::Normal); + item->setFont(font); + + QColor fg = item->foreground().color(); + QColor bg = item->background().color(); + if (attr == ATR_DIM) + { + fg.setAlpha(fg.alpha() / 2); + } + if (attr == ATR_INVERSE) + { + QColor swap; + swap = fg; fg = bg; bg = swap; + } + item->setForeground(fg); + item->setBackground(bg); +#endif + + // ATR_BLINK not supported + if (list->count() >= ::iflags.msg_history) + delete list->item(0); + list->addItem(text2); + + // Force scrollbar to bottom + list->setCurrentRow(list->count()-1); + + if ( map ) + map->putMessage(attr, text2); +} + +} // namespace nethack_qt4 diff --git a/win/Qt4/qt4msg.h b/win/Qt4/qt4msg.h new file mode 100644 index 000000000..5c391cc4f --- /dev/null +++ b/win/Qt4/qt4msg.h @@ -0,0 +1,42 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4msg.h -- a message window + +#ifndef QT4MSG_H +#define QT4MSG_H + +#include "qt4win.h" + +namespace nethack_qt4 { + +class NetHackQtMapWindow2; + +class NetHackQtMessageWindow : QObject, public NetHackQtWindow { + Q_OBJECT +public: + NetHackQtMessageWindow(); + ~NetHackQtMessageWindow(); + + virtual QWidget* Widget(); + virtual void Clear(); + virtual void Display(bool block); + virtual void PutStr(int attr, const QString& text); + + void Scroll(int dx, int dy); + + void setMap(NetHackQtMapWindow2*); + +private: + QListWidget* list; + bool changed; + NetHackQtMapWindow2* map; + +private slots: + void updateFont(); +}; + +} // namespace nethack_qt4 + +#endif diff --git a/win/Qt4/qt4plsel.cpp b/win/Qt4/qt4plsel.cpp new file mode 100644 index 000000000..56b8849bd --- /dev/null +++ b/win/Qt4/qt4plsel.cpp @@ -0,0 +1,502 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4plsel.cpp -- player selector dialog + +extern "C" { +#include "hack.h" +} +#undef Invisible +#undef Warning +#undef index +#undef msleep +#undef rindex +#undef wizard +#undef yn +#undef min +#undef max + +#include +#if QT_VERSION >= 0x050000 +#include +#endif +#include "qt4plsel.h" +#include "qt4plsel.moc" +#include "qt4bind.h" +#include "qt4glyph.h" +#include "qt4set.h" +#include "qt4str.h" + +// Warwick prefers it this way... +#define QT_CHOOSE_RACE_FIRST + +namespace nethack_qt4 { + +// temporary +void centerOnMain( QWidget* w ); +// end temporary + +static const char nh_attribution[] = "
NetHack" + "
by the NetHack DevTeam
"; + +class NhPSListViewItem : public QTableWidgetItem { +public: + NhPSListViewItem( QTableWidget* parent, const QString& name ) : + QTableWidgetItem(name) + { + } + + void setGlyph(int g) + { + NetHackQtGlyphs& glyphs = qt_settings->glyphs(); + int gw = glyphs.width(); + int gh = glyphs.height(); + QPixmap pm(gw,gh); + QPainter p(&pm); + glyphs.drawGlyph(p, g, 0, 0); + p.end(); + setIcon(QIcon(pm)); + //RLC setHeight(std::max(pm.height()+1,height())); + } + +#if 0 //RLC + void paintCell( QPainter *p, const QColorGroup &cg, + int column, int width, int alignment ) + { + if ( isSelectable() ) { + QTableWidgetItem::paintCell( p, cg, column, width, alignment ); + } else { + QColorGroup disabled( + cg.foreground().light(), + cg.button().light(), + cg.light(), cg.dark(), cg.mid(), + Qt::gray, cg.base() ); + QTableWidgetItem::paintCell( p, disabled, column, width, alignment ); + } + } +#endif +}; + +class NhPSListViewRole : public NhPSListViewItem { +public: + NhPSListViewRole( QTableWidget* parent, int id ) : + NhPSListViewItem(parent, +#ifdef QT_CHOOSE_RACE_FIRST // Lowerize - looks better + QString(roles[id].name.m).toLower() +#else + roles[id].name.m +#endif + ) + { + setGlyph(monnum_to_glyph(roles[id].malenum)); + } +}; + +class NhPSListViewRace : public NhPSListViewItem { +public: + NhPSListViewRace( QTableWidget* parent, int id ) : + NhPSListViewItem(parent, +#ifdef QT_CHOOSE_RACE_FIRST // Capitalize - looks better + str_titlecase(races[id].noun) +#else + races[id].noun +#endif + ) + { + setGlyph(monnum_to_glyph(races[id].malenum)); + } +}; + +class NhPSListView : public QTableWidget { +public: + NhPSListView( QWidget* parent ) : + QTableWidget(parent) + { + setColumnCount(1); + verticalHeader()->hide(); +#if QT_VERSION >= 0x050000 + horizontalHeader()->setSectionsClickable(false); +#else + horizontalHeader()->setClickable(false); +#endif + } + + QSizePolicy sizePolicy() const + { + return QSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding); + } + + QSize minimumSizeHint() const + { + return sizeHint(); + } + + QSize sizeHint() const + { + return QSize(columnWidth(0), QTableWidget::sizeHint().height()); + } +}; + +NetHackQtPlayerSelector::NetHackQtPlayerSelector(NetHackQtKeyBuffer& ks) : + QDialog(NetHackQtBind::mainWidget()), + fully_specified_role(true) +{ + /* + 0 1 2 + + Name ------------------------------------+ + 0 | | + + ---- ------------------------------------+ + + Role ---+ + Race ---+ + Gender ------+ + | | | | | * Male | + 1 | | | | | * Female | + | | | | +--------------+ + | | | | + | | | | + Alignment ---+ + 2 | | | | | * Male | + | | | | | * Female | + | | | | +--------------+ + 3 | | | | ...stretch... + | | | | + 4 | | | | [ Play ] + 5 | | | | [ Quit ] + +---------+ +---------+ + */ + + QGridLayout *l = new QGridLayout(this); + l->setColumnStretch(2, 1); + sizePolicy().setHorizontalPolicy(QSizePolicy::Minimum); + + QGroupBox* namebox = new QGroupBox("Name", this); + QVBoxLayout *namelayout = new QVBoxLayout(namebox); + QLineEdit* name = new QLineEdit(namebox); + namelayout->addWidget(name); + name->setMaxLength(sizeof(plname)-1); + if ( strncmp(plname,"player",6) && strncmp(plname,"games",5) ) + name->setText(plname); + connect(name, SIGNAL(textChanged(const QString&)), + this, SLOT(selectName(const QString&)) ); + name->setFocus(); + QGroupBox* genderbox = new QGroupBox("Sex",this); + QButtonGroup *gendergroup = new QButtonGroup(this); + QGroupBox* alignbox = new QGroupBox("Alignment",this); + QButtonGroup *aligngroup = new QButtonGroup(this); + QVBoxLayout* vbgb = new QVBoxLayout(genderbox); + vbgb->addSpacing(fontMetrics().height()*3/4); + QVBoxLayout* vbab = new QVBoxLayout(alignbox); + vbab->addSpacing(fontMetrics().height()); + QLabel* logo = new QLabel(nh_attribution, this); + + l->addWidget( namebox, 0,0,1,3 ); +#ifdef QT_CHOOSE_RACE_FIRST + race = new NhPSListView(this); + role = new NhPSListView(this); + l->addWidget( race, 1,0,5,1 ); + l->addWidget( role, 1,1,5,1 ); +#else + role = new NhPSListView(this); + race = new NhPSListView(this); + l->addWidget( role, 1,0,5,1 ); + l->addWidget( race, 1,1,5,1 ); +#endif + + l->addWidget( genderbox, 1, 2 ); + l->addWidget( alignbox, 2, 2 ); + l->addWidget( logo, 3, 2, Qt::AlignCenter ); + l->setRowStretch( 3, 5 ); + + int i; + int nrole; + + // XXX QListView unsorted goes in rev. + for (nrole=0; roles[nrole].name.m; nrole++) + ; + role->setRowCount(nrole); + for (i=0; roles[i].name.m; i++) { + QTableWidgetItem *item = new QTableWidgetItem( + QIcon(qt_settings->glyphs().glyph(roles[i].malenum)), + roles[i].name.m); + item->setFlags(Qt::ItemIsEnabled|Qt::ItemIsSelectable); + role->setItem(i, 0, item); + } + connect( role, SIGNAL(currentCellChanged(int, int, int, int)), this, SLOT(selectRole(int, int, int, int)) ); + role->setHorizontalHeaderLabels(QStringList("Role")); + role->resizeColumnToContents(0); + + int nrace; + for (nrace=0; races[nrace].noun; nrace++) + ; + race->setRowCount(nrace); + for (i=0; races[i].noun; i++) { + QTableWidgetItem *item = new QTableWidgetItem( + QIcon(qt_settings->glyphs().glyph(races[i].malenum)), + races[i].noun); + item->setFlags(Qt::ItemIsEnabled|Qt::ItemIsSelectable); + race->setItem(i, 0, item); + } + connect( race, SIGNAL(currentCellChanged(int, int, int, int)), this, SLOT(selectRace(int, int, int, int)) ); + race->setHorizontalHeaderLabels(QStringList("Race")); + race->resizeColumnToContents(0); + + gender = new QRadioButton*[ROLE_GENDERS]; + for (i=0; ilayout()->addWidget(gender[i]); + gendergroup->addButton(gender[i], i); + } + connect( gendergroup, SIGNAL(buttonPressed(int)), this, SLOT(selectGender(int)) ); + + alignment = new QRadioButton*[ROLE_ALIGNS]; + for (i=0; ilayout()->addWidget(alignment[i]); + aligngroup->addButton(alignment[i], i); + } + connect( aligngroup, SIGNAL(buttonPressed(int)), this, SLOT(selectAlignment(int)) ); + + QPushButton* ok = new QPushButton("Play",this); + l->addWidget( ok, 4, 2 ); + ok->setDefault(true); + connect( ok, SIGNAL(clicked()), this, SLOT(accept()) ); + + QPushButton* cancel = new QPushButton("Quit",this); + l->addWidget( cancel, 5, 2 ); + connect( cancel, SIGNAL(clicked()), this, SLOT(reject()) ); + + // Randomize race and role, unless specified in config + int ro = flags.initrole; + if (ro == ROLE_NONE || ro == ROLE_RANDOM) { + ro = rn2(nrole); + if (flags.initrole != ROLE_RANDOM) { + fully_specified_role = false; + } + } + int ra = flags.initrace; + if (ra == ROLE_NONE || ra == ROLE_RANDOM) { + ra = rn2(nrace); + if (flags.initrace != ROLE_RANDOM) { + fully_specified_role = false; + } + } + + // make sure we have a valid combination, honoring + // the users request if possible. + bool choose_race_first; +#ifdef QT_CHOOSE_RACE_FIRST + choose_race_first = true; + if (flags.initrole >= 0 && flags.initrace < 0) { + choose_race_first = false; + } +#else + choose_race_first = false; + if (flags.initrace >= 0 && flags.initrole < 0) { + choose_race_first = true; + } +#endif + while (!validrace(ro,ra)) { + if (choose_race_first) { + ro = rn2(nrole); + if (flags.initrole != ROLE_RANDOM) { + fully_specified_role = false; + } + } else { + ra = rn2(nrace); + if (flags.initrace != ROLE_RANDOM) { + fully_specified_role = false; + } + } + } + + int g = flags.initgend; + if (g == -1) { + g = rn2(ROLE_GENDERS); + fully_specified_role = false; + } + while (!validgend(ro,ra,g)) { + g = rn2(ROLE_GENDERS); + } + gender[g]->setChecked(true); + selectGender(g); + + int a = flags.initalign; + if (a == -1) { + a = rn2(ROLE_ALIGNS); + fully_specified_role = false; + } + while (!validalign(ro,ra,a)) { + a = rn2(ROLE_ALIGNS); + } + alignment[a]->setChecked(true); + selectAlignment(a); + + role->setCurrentCell(ro, 0); + + race->setCurrentCell(ra, 0); + + flags.initrace = race->currentRow(); + flags.initrole = role->currentRow(); +} + + +void NetHackQtPlayerSelector::selectName(const QString& n) +{ + str_copy(plname,n.toLatin1().constData(),SIZE(plname)); +} + +void NetHackQtPlayerSelector::selectRole(int crow, int ccol, int prow, int pcol) +{ + int ra = race->currentRow(); + int ro = role->currentRow(); + if (ra == -1 || ro == -1) return; + QTableWidgetItem* item; + item = role->item(prow, 0); + if (item != NULL) + item->setSelected(false); + +#ifdef QT_CHOOSE_RACE_FIRST + selectRace(crow, ccol, prow, pcol); +#else + QTableWidgetItem* i=role->currentItem(); + QTableWidgetItem* valid=0; + int j; + for (j=0; roles[j].name.m; j++) { + bool v = validrace(j,ra); + item = role->item(j, 0); + item->setFlags(Qt::ItemIsEnabled|Qt::ItemIsSelectable); + if ( !valid && v ) valid = item; + } + if ( !validrace(role->currentRow(),ra) ) + i = valid; + role->setCurrentItem(i, 0); + for (j=0; roles[j].name.m; j++) { + item = role->item(j, 0); + item->setSelected(item == i); + bool v = validrace(j,ra); + item->setFlags( + v ? Qt::ItemIsEnabled|Qt::ItemIsSelectable + : Qt::NoItemFlags); + } +#endif + + flags.initrole = role->currentRow(); + setupOthers(); +} + +void NetHackQtPlayerSelector::selectRace(int crow, int ccol, int prow, int pcol) +{ + int ra = race->currentRow(); + int ro = role->currentRow(); + if (ra == -1 || ro == -1) return; + QTableWidgetItem* item; + item = race->item(prow, 0); + if (item != NULL) + item->setSelected(false); + +#ifndef QT_CHOOSE_RACE_FIRST + selectRole(crow, ccol, prow, pcol); +#else + QTableWidgetItem* i=race->currentItem(); + QTableWidgetItem* valid=0; + int j; + for (j=0; races[j].noun; j++) { + bool v = validrace(ro,j); + item = race->item(j, 0); + item->setFlags(Qt::ItemIsEnabled|Qt::ItemIsSelectable); + if ( !valid && v ) valid = item; + } + if ( !validrace(ro,race->currentRow()) ) + i = valid; + for (j=0; races[j].noun; j++) { + item = race->item(j, 0); + item->setSelected(item == i); + bool v = validrace(ro,j); + item->setFlags( + v ? Qt::ItemIsEnabled|Qt::ItemIsSelectable + : Qt::NoItemFlags); + } +#endif + + flags.initrace = race->currentRow(); + setupOthers(); +} + +void NetHackQtPlayerSelector::setupOthers() +{ + int ro = role->currentRow(); + int ra = race->currentRow(); + int valid=-1; + int c=0; + int j; + for (j=0; jisChecked() ) + c = j; + gender[j]->setEnabled(v); + if ( valid<0 && v ) valid = j; + } + if ( !validgend(ro,ra,c) ) + c = valid; + int k; + for (k=0; ksetChecked(c==k); + } + selectGender(c); + + valid=-1; + for (j=0; jisChecked() ) + c = j; + alignment[j]->setEnabled(v); + if ( valid<0 && v ) valid = j; + } + if ( !validalign(ro,ra,c) ) + c = valid; + for (k=0; ksetChecked(c==k); + } + selectAlignment(c); +} + +void NetHackQtPlayerSelector::selectGender(int i) +{ + flags.initgend = i; +} + +void NetHackQtPlayerSelector::selectAlignment(int i) +{ + flags.initalign = i; +} + +void NetHackQtPlayerSelector::Quit() +{ + done(R_Quit); +} + +void NetHackQtPlayerSelector::Random() +{ + done(R_Rand); +} + +bool NetHackQtPlayerSelector::Choose() +{ + if (fully_specified_role) return true; + +#if defined(QWS) // probably safe with Qt 3, too (where show!=exec in QDialog). + if ( qt_compact_mode ) { + showMaximized(); + } else +#endif + { + adjustSize(); + centerOnMain(this); + } + + if ( exec() ) { + return true; + } else { + return false; + } +} + +} // namespace nethack_qt4 diff --git a/win/Qt4/qt4plsel.h b/win/Qt4/qt4plsel.h new file mode 100644 index 000000000..b82341270 --- /dev/null +++ b/win/Qt4/qt4plsel.h @@ -0,0 +1,45 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4plsel.h -- player selector dialog + +#ifndef QT4PLSEL_H +#define QT4PLSEL_H + +namespace nethack_qt4 { + +class NetHackQtKeyBuffer; + +class NetHackQtPlayerSelector : private QDialog { + Q_OBJECT +public: + enum { R_None=-1, R_Quit=-2, R_Rand=-3 }; + + NetHackQtPlayerSelector(NetHackQtKeyBuffer&); + +public slots: + void Quit(); + void Random(); + + void selectName(const QString& n); + void selectRole(int current, int, int previous, int); + void selectRace(int current, int, int previous, int); + void setupOthers(); + void selectGender(int); + void selectAlignment(int); + +public: + bool Choose(); + +private: + QTableWidget* role; + QTableWidget* race; + QRadioButton **gender; + QRadioButton **alignment; + bool fully_specified_role; +}; + +} // namespace nethack_qt4 + +#endif diff --git a/win/Qt4/qt4rip.cpp b/win/Qt4/qt4rip.cpp new file mode 100644 index 000000000..d509f8895 --- /dev/null +++ b/win/Qt4/qt4rip.cpp @@ -0,0 +1,94 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4rip.cpp -- tombstone window + +#include "hack.h" +#undef Invisible +#undef Warning +#undef index +#undef msleep +#undef rindex +#undef wizard +#undef yn +#undef min +#undef max + +#include +#if QT_VERSION >= 0x050000 +#include +#endif +#include "qt4rip.h" +#include "qt4bind.h" +#include "qt4str.h" + +namespace nethack_qt4 { + +QPixmap* NetHackQtRIP::pixmap=0; + +// Debian uses a separate PIXMAPDIR +#ifndef PIXMAPDIR +# ifdef HACKDIR +# define PIXMAPDIR HACKDIR +# else +# define PIXMAPDIR "." +# endif +#endif + +static void +tryload(QPixmap& pm, const char* fn) +{ + if (!pm.load(fn)) { + QString msg; + msg.sprintf("Cannot load \"%s\"", fn); + QMessageBox::warning(NetHackQtBind::mainWidget(), "IO Error", msg); + } +} + +NetHackQtRIP::NetHackQtRIP(QWidget* parent) : + QWidget(parent) +{ + if (!pixmap) { + pixmap=new QPixmap; + tryload(*pixmap, PIXMAPDIR "/rip.xpm"); + } + riplines=0; + resize(pixmap->width(),pixmap->height()); + setFont(QFont("times",12)); // XXX may need to be configurable +} + +void NetHackQtRIP::setLines(char** l, int n) +{ + line=l; + riplines=n; +} + +QSize NetHackQtRIP::sizeHint() const +{ + return pixmap->size(); +} + +void NetHackQtRIP::paintEvent(QPaintEvent* event) +{ + if ( riplines ) { + int pix_x=(width()-pixmap->width())/2; + int pix_y=(height()-pixmap->height())/2; + + // XXX positions based on RIP image + int rip_text_x=pix_x+156; + int rip_text_y=pix_y+67; + int rip_text_h=94/riplines; + + QPainter painter; + painter.begin(this); + painter.drawPixmap(pix_x,pix_y,*pixmap); + for (int i=0; i +#if QT_VERSION >= 0x050000 +#include +#endif +#include "qt4set.h" +#include "qt4set.moc" +#include "qt4glyph.h" +#include "qt4str.h" + +/* Used by tile/font-size patch below and in ../../src/files.c */ +char *qt_tilewidth=NULL; +char *qt_tileheight=NULL; +char *qt_fontsize=NULL; +#if defined(QWS) +int qt_compact_mode = 1; +#else +int qt_compact_mode = 0; +#endif + +namespace nethack_qt4 { + +#define TILEWMIN 1 +#define TILEHMIN 1 + +NetHackQtSettings::NetHackQtSettings(int w, int h) : + tilewidth(this), + tileheight(this), + widthlbl("&Width:",this), + heightlbl("&Height:",this), + whichsize("&Zoomed",this), + fontsize(this), + normal("times"), +#ifdef WS_WIN + normalfixed("courier new"), +#else + normalfixed("courier"), +#endif + large("times"), + theglyphs(0) + +{ + int default_fontsize; + + widthlbl.setBuddy(&tilewidth); + tilewidth.setRange(TILEWMIN, 128); + heightlbl.setBuddy(&tileheight); + tileheight.setRange(TILEHMIN, 128); + + default_fontsize=2; + tilewidth.setValue(16); + tileheight.setValue(16); + + // Tile/font sizes read from .nethackrc + if (qt_tilewidth != NULL) { + tilewidth.setValue(atoi(qt_tilewidth)); + delete[] qt_tilewidth; + } + if (qt_tileheight != NULL) { + tileheight.setValue(atoi(qt_tileheight)); + delete[] qt_tileheight; + } + if (qt_fontsize != NULL) { + switch (tolower(qt_fontsize[0])) { + case 'h': default_fontsize = 0; break; + case 'l': default_fontsize = 1; break; + case 'm': default_fontsize = 2; break; + case 's': default_fontsize = 3; break; + case 't': default_fontsize = 4; break; + } + delete[] qt_fontsize; + } + + theglyphs=new NetHackQtGlyphs(); + resizeTiles(); + + connect(&tilewidth,SIGNAL(valueChanged(int)),this,SLOT(resizeTiles())); + connect(&tileheight,SIGNAL(valueChanged(int)),this,SLOT(resizeTiles())); + connect(&whichsize,SIGNAL(toggled(bool)),this,SLOT(setGlyphSize(bool))); + + fontsize.addItem("Huge"); + fontsize.addItem("Large"); + fontsize.addItem("Medium"); + fontsize.addItem("Small"); + fontsize.addItem("Tiny"); + fontsize.setCurrentIndex(default_fontsize); + connect(&fontsize,SIGNAL(activated(int)),this,SIGNAL(fontChanged())); + + QGridLayout* grid = new QGridLayout(this); + grid->addWidget(&whichsize, 0, 0, 1, 2); + grid->addWidget(&tilewidth, 1, 1); grid->addWidget(&widthlbl, 1, 0); + grid->addWidget(&tileheight, 2, 1); grid->addWidget(&heightlbl, 2, 0); + QLabel* flabel=new QLabel("&Font:",this); + flabel->setBuddy(&fontsize); + grid->addWidget(flabel, 3, 0); grid->addWidget(&fontsize, 3, 1); + QPushButton* dismiss=new QPushButton("Dismiss",this); + dismiss->setDefault(true); + grid->addWidget(dismiss, 4, 0, 1, 2); + grid->setRowStretch(4,0); + grid->setColumnStretch(1,1); + grid->setColumnStretch(2,2); + grid->activate(); + + connect(dismiss,SIGNAL(clicked()),this,SLOT(accept())); + resize(150,140); +} + +NetHackQtGlyphs& NetHackQtSettings::glyphs() +{ + return *theglyphs; +} + +void NetHackQtSettings::resizeTiles() +{ + int w = tilewidth.value(); + int h = tileheight.value(); + + theglyphs->setSize(w,h); + emit tilesChanged(); +} + +void NetHackQtSettings::toggleGlyphSize() +{ + whichsize.toggle(); +} + +void NetHackQtSettings::setGlyphSize(bool which) +{ + QSize n = QSize(tilewidth.value(),tileheight.value()); + if ( othersize.isValid() ) { + tilewidth.blockSignals(true); + tileheight.blockSignals(true); + tilewidth.setValue(othersize.width()); + tileheight.setValue(othersize.height()); + tileheight.blockSignals(false); + tilewidth.blockSignals(false); + resizeTiles(); + } + othersize = n; +} + +const QFont& NetHackQtSettings::normalFont() +{ + static int size[]={ 18, 14, 12, 10, 8 }; + normal.setPointSize(size[fontsize.currentIndex()]); + return normal; +} + +const QFont& NetHackQtSettings::normalFixedFont() +{ + static int size[]={ 18, 14, 13, 10, 8 }; + normalfixed.setPointSize(size[fontsize.currentIndex()]); + return normalfixed; +} + +const QFont& NetHackQtSettings::largeFont() +{ + static int size[]={ 24, 18, 14, 12, 10 }; + large.setPointSize(size[fontsize.currentIndex()]); + return large; +} + +bool NetHackQtSettings::ynInMessages() +{ + return !qt_compact_mode && !iflags.wc_popup_dialog; +} + +NetHackQtSettings* qt_settings; + +} // namespace nethack_qt4 diff --git a/win/Qt4/qt4set.h b/win/Qt4/qt4set.h new file mode 100644 index 000000000..e2253a8d6 --- /dev/null +++ b/win/Qt4/qt4set.h @@ -0,0 +1,57 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4set.h -- the Qt settings + +#ifndef QT4SET_H +#define QT4SET_H + +namespace nethack_qt4 { + +class NetHackQtGlyphs; + +class NetHackQtSettings : public QDialog { + Q_OBJECT +public: + // Size of window - used to decide default sizes + NetHackQtSettings(int width, int height); + + NetHackQtGlyphs& glyphs(); + const QFont& normalFont(); + const QFont& normalFixedFont(); + const QFont& largeFont(); + + bool ynInMessages(); + +signals: + void fontChanged(); + void tilesChanged(); + +public slots: + void toggleGlyphSize(); + void setGlyphSize(bool); + +private: + QSpinBox tilewidth; + QSpinBox tileheight; + QLabel widthlbl; + QLabel heightlbl; + QCheckBox whichsize; + QSize othersize; + + QComboBox fontsize; + + QFont normal, normalfixed, large; + + NetHackQtGlyphs* theglyphs; + +private slots: + void resizeTiles(); +}; + +extern NetHackQtSettings* qt_settings; + +} // namespace nethack_qt4 + +#endif diff --git a/win/Qt4/qt4stat.cpp b/win/Qt4/qt4stat.cpp new file mode 100644 index 000000000..3a15604e5 --- /dev/null +++ b/win/Qt4/qt4stat.cpp @@ -0,0 +1,540 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4stat.cpp -- bindings between the Qt 4 interface and the main code + +extern "C" { +#include "hack.h" +} +#undef Invisible +#undef Warning +#undef index +#undef msleep +#undef rindex +#undef wizard +#undef yn +#undef min +#undef max + +#include +#if QT_VERSION >= 0x050000 +#include +#endif +#include "qt4stat.h" +#include "qt4stat.moc" +#include "qt4set.h" +#include "qt4str.h" +#include "qt_xpms.h" + +extern const char *enc_stat[]; /* from botl.c */ +extern const char *hu_stat[]; /* from eat.c */ + +namespace nethack_qt4 { + +NetHackQtStatusWindow::NetHackQtStatusWindow() : + // Notes: + // Alignment needs -2 init value, because -1 is an alignment. + // Armor Class is an schar, so 256 is out of range. + // Blank value is 0 and should never change. + name(this,"(name)"), + dlevel(this,"(dlevel)"), + str(this,"STR"), + dex(this,"DEX"), + con(this,"CON"), + intel(this,"INT"), + wis(this,"WIS"), + cha(this,"CHA"), + gold(this,"Gold"), + hp(this,"Hit Points"), + power(this,"Power"), + ac(this,"Armour Class"), + level(this,"Level"), + exp(this,"Experience"), + align(this,"Alignment"), + time(this,"Time"), + score(this,"Score"), + hunger(this,""), + confused(this,"Confused"), + sick_fp(this,"Sick"), + sick_il(this,"Ill"), + blind(this,""), + stunned(this,"Stunned"), + hallu(this,"Hallu"), + encumber(this,""), + hline1(this), + hline2(this), + hline3(this), + first_set(true) +{ + p_str = QPixmap(str_xpm); + p_str = QPixmap(str_xpm); + p_dex = QPixmap(dex_xpm); + p_con = QPixmap(cns_xpm); + p_int = QPixmap(int_xpm); + p_wis = QPixmap(wis_xpm); + p_cha = QPixmap(cha_xpm); + + p_chaotic = QPixmap(chaotic_xpm); + p_neutral = QPixmap(neutral_xpm); + p_lawful = QPixmap(lawful_xpm); + + p_satiated = QPixmap(satiated_xpm); + p_hungry = QPixmap(hungry_xpm); + + p_confused = QPixmap(confused_xpm); + p_sick_fp = QPixmap(sick_fp_xpm); + p_sick_il = QPixmap(sick_il_xpm); + p_blind = QPixmap(blind_xpm); + p_stunned = QPixmap(stunned_xpm); + p_hallu = QPixmap(hallu_xpm); + + p_encumber[0] = QPixmap(slt_enc_xpm); + p_encumber[1] = QPixmap(mod_enc_xpm); + p_encumber[2] = QPixmap(hvy_enc_xpm); + p_encumber[3] = QPixmap(ext_enc_xpm); + p_encumber[4] = QPixmap(ovr_enc_xpm); + + str.setIcon(p_str); + dex.setIcon(p_dex); + con.setIcon(p_con); + intel.setIcon(p_int); + wis.setIcon(p_wis); + cha.setIcon(p_cha); + + align.setIcon(p_neutral); + hunger.setIcon(p_hungry); + + confused.setIcon(p_confused); + sick_fp.setIcon(p_sick_fp); + sick_il.setIcon(p_sick_il); + blind.setIcon(p_blind); + stunned.setIcon(p_stunned); + hallu.setIcon(p_hallu); + + encumber.setIcon(p_encumber[0]); + + hline1.setFrameStyle(QFrame::HLine|QFrame::Sunken); + hline2.setFrameStyle(QFrame::HLine|QFrame::Sunken); + hline3.setFrameStyle(QFrame::HLine|QFrame::Sunken); + hline1.setLineWidth(1); + hline2.setLineWidth(1); + hline3.setLineWidth(1); + +#if 1 //RLC + name.setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); + dlevel.setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); + QVBoxLayout *vbox = new QVBoxLayout(); + vbox->setSpacing(0); + vbox->addWidget(&name); + vbox->addWidget(&dlevel); + vbox->addWidget(&hline1); + QHBoxLayout *atr1box = new QHBoxLayout(); + atr1box->addWidget(&str); + atr1box->addWidget(&dex); + atr1box->addWidget(&con); + atr1box->addWidget(&intel); + atr1box->addWidget(&wis); + atr1box->addWidget(&cha); + vbox->addLayout(atr1box); + vbox->addWidget(&hline2); + QHBoxLayout *atr2box = new QHBoxLayout(); + atr2box->addWidget(&gold); + atr2box->addWidget(&hp); + atr2box->addWidget(&power); + atr2box->addWidget(&ac); + atr2box->addWidget(&level); + atr2box->addWidget(&exp); + vbox->addLayout(atr2box); + vbox->addWidget(&hline3); + QHBoxLayout *timebox = new QHBoxLayout(); + timebox->addWidget(&time); + timebox->addWidget(&score); + vbox->addLayout(timebox); + QHBoxLayout *statbox = new QHBoxLayout(); + statbox->addWidget(&align); + statbox->addWidget(&hunger); + statbox->addWidget(&confused); + statbox->addWidget(&sick_fp); + statbox->addWidget(&sick_il); + statbox->addWidget(&blind); + statbox->addWidget(&stunned); + statbox->addWidget(&hallu); + statbox->addWidget(&encumber); + statbox->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); + vbox->addLayout(statbox); + setLayout(vbox); +#endif + + connect(qt_settings,SIGNAL(fontChanged()),this,SLOT(doUpdate())); + doUpdate(); +} + +void NetHackQtStatusWindow::doUpdate() +{ + const QFont& large=qt_settings->largeFont(); + name.setFont(large); + dlevel.setFont(large); + + const QFont& normal=qt_settings->normalFont(); + str.setFont(normal); + dex.setFont(normal); + con.setFont(normal); + intel.setFont(normal); + wis.setFont(normal); + cha.setFont(normal); + gold.setFont(normal); + hp.setFont(normal); + power.setFont(normal); + ac.setFont(normal); + level.setFont(normal); + exp.setFont(normal); + align.setFont(normal); + time.setFont(normal); + score.setFont(normal); + hunger.setFont(normal); + confused.setFont(normal); + sick_fp.setFont(normal); + sick_il.setFont(normal); + blind.setFont(normal); + stunned.setFont(normal); + hallu.setFont(normal); + encumber.setFont(normal); + + updateStats(); +} + +QWidget* NetHackQtStatusWindow::Widget() { return this; } + +void NetHackQtStatusWindow::Clear() +{ +} +void NetHackQtStatusWindow::Display(bool block) +{ +} +void NetHackQtStatusWindow::CursorTo(int,int y) +{ + cursy=y; +} +void NetHackQtStatusWindow::PutStr(int attr, const QString& text) +{ + // do a complete update when line 0 is done (as per X11 fancy status) + if (cursy==0) updateStats(); +} + +#if 0 // RLC +void NetHackQtStatusWindow::resizeEvent(QResizeEvent*) +{ +#if 0 + const float SP_name=0.13; // the (large) + const float SP_dlev=0.13; // Level 3 in The Dungeons of Doom (large) + const float SP_atr1=0.25; // STR DEX CON INT WIS CHA + const float SP_hln1=0.02; // --- + const float SP_atr2=0.09; // Au HP PW AC LVL EXP + const float SP_hln2=0.02; // --- + const float SP_time=0.09; // time score + const float SP_hln3=0.02; // --- + const float SP_stat=0.25; // Alignment, Poisoned, Hungry, Sick, etc. + + int h=height(); + int x=0,y=0; + + int iw; // Width of an item across line + int lh; // Height of a line of values + + lh=int(h*SP_name); + name.setGeometry(0,0,width(),lh); y+=lh; + lh=int(h*SP_dlev); + dlevel.setGeometry(0,y,width(),lh); y+=lh; + + lh=int(h*SP_hln1); + hline1.setGeometry(0,y,width(),lh); y+=lh; + + lh=int(h*SP_atr1); + iw=width()/6; + str.setGeometry(x,y,iw,lh); x+=iw; + dex.setGeometry(x,y,iw,lh); x+=iw; + con.setGeometry(x,y,iw,lh); x+=iw; + intel.setGeometry(x,y,iw,lh); x+=iw; + wis.setGeometry(x,y,iw,lh); x+=iw; + cha.setGeometry(x,y,iw,lh); x+=iw; + x=0; y+=lh; + + lh=int(h*SP_hln2); + hline2.setGeometry(0,y,width(),lh); y+=lh; + + lh=int(h*SP_atr2); + iw=width()/6; + gold.setGeometry(x,y,iw,lh); x+=iw; + hp.setGeometry(x,y,iw,lh); x+=iw; + power.setGeometry(x,y,iw,lh); x+=iw; + ac.setGeometry(x,y,iw,lh); x+=iw; + level.setGeometry(x,y,iw,lh); x+=iw; + exp.setGeometry(x,y,iw,lh); x+=iw; + x=0; y+=lh; + + lh=int(h*SP_hln3); + hline3.setGeometry(0,y,width(),lh); y+=lh; + + lh=int(h*SP_time); + iw=width()/3; x+=iw/2; + time.setGeometry(x,y,iw,lh); x+=iw; + score.setGeometry(x,y,iw,lh); x+=iw; + x=0; y+=lh; + + lh=int(h*SP_stat); + iw=width()/9; + align.setGeometry(x,y,iw,lh); x+=iw; + hunger.setGeometry(x,y,iw,lh); x+=iw; + confused.setGeometry(x,y,iw,lh); x+=iw; + sick_fp.setGeometry(x,y,iw,lh); x+=iw; + sick_il.setGeometry(x,y,iw,lh); x+=iw; + blind.setGeometry(x,y,iw,lh); x+=iw; + stunned.setGeometry(x,y,iw,lh); x+=iw; + hallu.setGeometry(x,y,iw,lh); x+=iw; + encumber.setGeometry(x,y,iw,lh); x+=iw; + x=0; y+=lh; +#else + // This is clumsy. But QLayout objects are proving balky. + + int row[10]; + + row[0] = name.sizeHint().height(); + row[1] = dlevel.sizeHint().height(); + row[2] = h.sizeHint().height(); +#endif +} +#endif + + +/* + * Set all widget values to a null string. This is used after all spacings + * have been calculated so that when the window is popped up we don't get all + * kinds of funny values being displayed. + */ +void NetHackQtStatusWindow::nullOut() +{ +} + +void NetHackQtStatusWindow::fadeHighlighting() +{ + name.dissipateHighlight(); + dlevel.dissipateHighlight(); + + str.dissipateHighlight(); + dex.dissipateHighlight(); + con.dissipateHighlight(); + intel.dissipateHighlight(); + wis.dissipateHighlight(); + cha.dissipateHighlight(); + + gold.dissipateHighlight(); + hp.dissipateHighlight(); + power.dissipateHighlight(); + ac.dissipateHighlight(); + level.dissipateHighlight(); + exp.dissipateHighlight(); + align.dissipateHighlight(); + + time.dissipateHighlight(); + score.dissipateHighlight(); + + hunger.dissipateHighlight(); + confused.dissipateHighlight(); + sick_fp.dissipateHighlight(); + sick_il.dissipateHighlight(); + blind.dissipateHighlight(); + stunned.dissipateHighlight(); + hallu.dissipateHighlight(); + encumber.dissipateHighlight(); +} + +/* + * Update the displayed status. The current code in botl.c updates + * two lines of information. Both lines are always updated one after + * the other. So only do our update when we update the second line. + * + * Information on the first line: + * name, attributes, alignment, score + * + * Information on the second line: + * dlvl, gold, hp, power, ac, {level & exp or HD **} + * status (hunger, conf, halu, stun, sick, blind), time, encumbrance + * + * [**] HD is shown instead of level and exp if mtimedone is non-zero. + */ +void NetHackQtStatusWindow::updateStats() +{ + if (!parentWidget()) return; + + QString buf; + const char *text; + + if (cursy != 0) return; /* do a complete update when line 0 is done */ + + if (ACURR(A_STR) > 118) { + buf.sprintf("STR:%d",ACURR(A_STR)-100); + } else if (ACURR(A_STR)==118) { + buf.sprintf("STR:18/**"); + } else if(ACURR(A_STR) > 18) { + buf.sprintf("STR:18/%02d",ACURR(A_STR)-18); + } else { + buf.sprintf("STR:%d",ACURR(A_STR)); + } + str.setLabel(buf,NetHackQtLabelledIcon::NoNum,ACURR(A_STR)); + + dex.setLabel("DEX:",(long)ACURR(A_DEX)); + con.setLabel("CON:",(long)ACURR(A_CON)); + intel.setLabel("INT:",(long)ACURR(A_INT)); + wis.setLabel("WIS:",(long)ACURR(A_WIS)); + cha.setLabel("CHA:",(long)ACURR(A_CHA)); + const char* hung=hu_stat[u.uhs]; + if (hung[0]==' ') { + hunger.hide(); + } else { + hunger.setIcon(u.uhs ? p_hungry : p_satiated); + hunger.setLabel(hung); + hunger.show(); + } + if (Confusion) confused.show(); else confused.hide(); + if (Sick) { + if (u.usick_type & SICK_VOMITABLE) { + sick_fp.show(); + } else { + sick_fp.hide(); + } + if (u.usick_type & SICK_NONVOMITABLE) { + sick_il.show(); + } else { + sick_il.hide(); + } + } else { + sick_fp.hide(); + sick_il.hide(); + } + if (Blind) { + blind.setLabel("Blind"); + blind.show(); + } else { + blind.hide(); + } + if (Stunned) stunned.show(); else stunned.hide(); + if (Hallucination) hallu.show(); else hallu.hide(); + const char* enc=enc_stat[near_capacity()]; + if (enc[0]==' ' || !enc[0]) { + encumber.hide(); + } else { + encumber.setIcon(p_encumber[near_capacity()-1]); + encumber.setLabel(enc); + encumber.show(); + } + if (u.mtimedone) { + buf = nh_capitalize_words(mons[u.umonnum].mname); + } else { + buf = rank_of(u.ulevel, pl_character[0], ::flags.female); + } + QString buf2; + buf2.sprintf("%s the %s", plname, buf.toLatin1().constData()); + name.setLabel(buf2, NetHackQtLabelledIcon::NoNum, u.ulevel); + + char buf3[BUFSZ]; + if (describe_level(buf3)) { + dlevel.setLabel(buf3,true); + } else { + buf.sprintf("%s, level ", dungeons[u.uz.dnum].dname); + dlevel.setLabel(buf,(long)::depth(&u.uz)); + } + + gold.setLabel("Au:", money_cnt(invent)); + + if (u.mtimedone) { + // You're a monster! + + buf.sprintf("/%d", u.mhmax); + hp.setLabel("HP:", u.mh > 0 ? u.mh : 0, buf); + level.setLabel("HD:",(long)mons[u.umonnum].mlevel); + } else { + // You're normal. + + buf.sprintf("/%d", u.uhpmax); + hp.setLabel("HP:", u.uhp > 0 ? u.uhp : 0, buf); + level.setLabel("Level:",(long)u.ulevel); + } + buf.sprintf("/%d", u.uenmax); + power.setLabel("Pow:", u.uen, buf); + ac.setLabel("AC:",(long)u.uac); +#ifdef EXP_ON_BOTL + if (::flags.showexp) { + exp.setLabel("Exp:",(long)u.uexp); + } else +#endif + { + exp.setLabel(""); + } + if (u.ualign.type==A_CHAOTIC) { + align.setIcon(p_chaotic); + text = "Chaotic"; + } else if (u.ualign.type==A_NEUTRAL) { + align.setIcon(p_neutral); + text = "Neutral"; + } else { + align.setIcon(p_lawful); + text = "Lawful"; + } + align.setLabel(text); + + if (::flags.time) time.setLabel("Time:",(long)moves); + else time.setLabel(""); +#ifdef SCORE_ON_BOTL + if (::flags.showscore) { + score.setLabel("Score:",(long)botl_score()); + } else +#endif + { + score.setLabel(""); + } + + if (first_set) + { + first_set=false; + + name.highlightWhenChanging(); + dlevel.highlightWhenChanging(); + + str.highlightWhenChanging(); + dex.highlightWhenChanging(); + con.highlightWhenChanging(); + intel.highlightWhenChanging(); + wis.highlightWhenChanging(); + cha.highlightWhenChanging(); + + gold.highlightWhenChanging(); + hp.highlightWhenChanging(); + power.highlightWhenChanging(); + ac.highlightWhenChanging(); ac.lowIsGood(); + level.highlightWhenChanging(); + exp.highlightWhenChanging(); + align.highlightWhenChanging(); + + //time.highlightWhenChanging(); + score.highlightWhenChanging(); + + hunger.highlightWhenChanging(); + confused.highlightWhenChanging(); + sick_fp.highlightWhenChanging(); + sick_il.highlightWhenChanging(); + blind.highlightWhenChanging(); + stunned.highlightWhenChanging(); + hallu.highlightWhenChanging(); + encumber.highlightWhenChanging(); + } +} + +/* + * Turn off hilighted status values after a certain amount of turns. + */ +void NetHackQtStatusWindow::checkTurnEvents() +{ +} + +} // namespace nethack_qt4 diff --git a/win/Qt4/qt4stat.h b/win/Qt4/qt4stat.h new file mode 100644 index 000000000..a0a00a46f --- /dev/null +++ b/win/Qt4/qt4stat.h @@ -0,0 +1,106 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4stat.h -- bindings between the Qt 4 interface and the main code + +#ifndef QT4STAT_H +#define QT4STAT_H + +#include "qt4win.h" +#include "qt4icon.h" + +namespace nethack_qt4 { + +class NetHackQtStatusWindow : QWidget, public NetHackQtWindow { + Q_OBJECT +public: + NetHackQtStatusWindow(); + + virtual QWidget* Widget(); + + virtual void Clear(); + virtual void Display(bool block); + virtual void CursorTo(int x,int y); + virtual void PutStr(int attr, const QString& text); + + void fadeHighlighting(); + +protected: + //RLC void resizeEvent(QResizeEvent*); + +private slots: + void doUpdate(); + +private: + enum { hilight_time=1 }; + + QPixmap p_str; + QPixmap p_dex; + QPixmap p_con; + QPixmap p_int; + QPixmap p_wis; + QPixmap p_cha; + + QPixmap p_chaotic; + QPixmap p_neutral; + QPixmap p_lawful; + + QPixmap p_satiated; + QPixmap p_hungry; + + QPixmap p_confused; + QPixmap p_sick_fp; + QPixmap p_sick_il; + QPixmap p_blind; + QPixmap p_stunned; + QPixmap p_hallu; + + QPixmap p_encumber[5]; + + NetHackQtLabelledIcon name; + NetHackQtLabelledIcon dlevel; + + NetHackQtLabelledIcon str; + NetHackQtLabelledIcon dex; + NetHackQtLabelledIcon con; + NetHackQtLabelledIcon intel; + NetHackQtLabelledIcon wis; + NetHackQtLabelledIcon cha; + + NetHackQtLabelledIcon gold; + NetHackQtLabelledIcon hp; + NetHackQtLabelledIcon power; + NetHackQtLabelledIcon ac; + NetHackQtLabelledIcon level; + NetHackQtLabelledIcon exp; + NetHackQtLabelledIcon align; + + NetHackQtLabelledIcon time; + NetHackQtLabelledIcon score; + + NetHackQtLabelledIcon hunger; + NetHackQtLabelledIcon confused; + NetHackQtLabelledIcon sick_fp; + NetHackQtLabelledIcon sick_il; + NetHackQtLabelledIcon blind; + NetHackQtLabelledIcon stunned; + NetHackQtLabelledIcon hallu; + NetHackQtLabelledIcon encumber; + + QFrame hline1; + QFrame hline2; + QFrame hline3; + + int cursy; + + bool first_set; + + void nullOut(); + void updateStats(); + void checkTurnEvents(); +}; + +} // namespace nethack_qt4 + +#endif diff --git a/win/Qt4/qt4str.cpp b/win/Qt4/qt4str.cpp new file mode 100644 index 000000000..b6b4440eb --- /dev/null +++ b/win/Qt4/qt4str.cpp @@ -0,0 +1,83 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4str.cpp -- some string functions + +#include +#include +#include "qt4str.h" + +namespace nethack_qt4 { + +// Bounded string copy +size_t str_copy(char *dest, const char *src, size_t max) +{ + size_t len = strlen(src); + if (max != 0) { + size_t csize = len; + if (len > max - 1) { + len = max - 1; + } + memcpy(dest, src, csize); + dest[csize] = '\0'; + } + return len; +} + +QString str_titlecase(const QString& str) +{ + if (str == "") { return str; } + + return str.left(1).toUpper() + str.mid(1).toLower(); +} + +QString nh_capitalize_words(const QString& str) +{ + QStringList words = str.split(" "); + for (size_t i = 0; i < words.size(); ++i) { + words[i] = str_titlecase(words[i]); + } + return words.join(" "); +} + +int cp437(int ch) +{ + static const unsigned short cp437table[] = { + 0x0000, 0x263A, 0x263B, 0x2665, 0x2666, 0x2663, 0x2660, 0x2022, + 0x25D8, 0x25CB, 0x25D9, 0x2642, 0x2640, 0x266A, 0x266B, 0x263C, + 0x25BA, 0x25C4, 0x2195, 0x203C, 0x00B6, 0x00A7, 0x25AC, 0x21A8, + 0x2191, 0x2193, 0x2192, 0x2190, 0x221F, 0x2194, 0x25B2, 0x25BC, + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, + 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, + 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, + 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, + 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, + 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, + 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, + 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, + 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, + 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x2302, + 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, + 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5, + 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, + 0x00FF, 0x00D6, 0x00DC, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192, + 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, + 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, + 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, + 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, + 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, + 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, + 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, + 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229, + 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, + 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0, + }; + return cp437table[(unsigned char)ch]; +} + +} // namespace nethack_qt4 diff --git a/win/Qt4/qt4str.h b/win/Qt4/qt4str.h new file mode 100644 index 000000000..05f25f4c4 --- /dev/null +++ b/win/Qt4/qt4str.h @@ -0,0 +1,24 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4str.h -- various string functions + +#ifndef QT4STR_H +#define QT4STR_H + +namespace nethack_qt4 { + +// Bounded string copy +extern size_t str_copy(char *dest, const char *src, size_t max); + +// Case mappings +extern QString str_titlecase(const QString& str); +extern QString nh_capitalize_words(const QString& str); + +// Map symbol conversion +extern int cp437(int ch); + +} // namespace nethack_qt4 + +#endif diff --git a/win/Qt4/qt4streq.cpp b/win/Qt4/qt4streq.cpp new file mode 100644 index 000000000..5d03bb996 --- /dev/null +++ b/win/Qt4/qt4streq.cpp @@ -0,0 +1,99 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4streq.cpp -- string requestor + +#include "hack.h" +#undef Invisible +#undef Warning +#undef index +#undef msleep +#undef rindex +#undef wizard +#undef yn +#undef min +#undef max + +#include +#if QT_VERSION >= 0x050000 +#include +#endif +#include "qt4streq.h" +#include "qt4str.h" + +namespace nethack_qt4 { + +// temporary +void centerOnMain(QWidget *); +// end temporary + +NetHackQtStringRequestor::NetHackQtStringRequestor(QWidget *parent, const char* p, const char* cancelstr) : + QDialog(parent), + prompt(QString::fromLatin1(p),this), + input(this,"input") +{ + cancel=new QPushButton(cancelstr,this); + connect(cancel,SIGNAL(clicked()),this,SLOT(reject())); + + okay=new QPushButton("Okay",this); + connect(okay,SIGNAL(clicked()),this,SLOT(accept())); + connect(&input,SIGNAL(returnPressed()),this,SLOT(accept())); + okay->setDefault(true); + + setFocusPolicy(Qt::StrongFocus); +} + +void NetHackQtStringRequestor::resizeEvent(QResizeEvent*) +{ + const int margin=5; + const int gutter=5; + + int h=(height()-margin*2-gutter); + + if (prompt.text().size() > 16) { + h/=3; + prompt.setGeometry(margin,margin,width()-margin*2,h); + input.setGeometry(width()*1/5,margin+h+gutter, + (width()-margin-2-gutter)*4/5,h); + } else { + h/=2; + prompt.setGeometry(margin,margin,(width()-margin*2-gutter)*2/5,h); + input.setGeometry(prompt.geometry().right()+gutter,margin, + (width()-margin-2-gutter)*3/5,h); + } + + cancel->setGeometry(margin,input.geometry().bottom()+gutter, + (width()-margin*2-gutter)/2,h); + okay->setGeometry(cancel->geometry().right()+gutter,cancel->geometry().y(), + cancel->width(),h); +} + +void NetHackQtStringRequestor::SetDefault(const char* d) +{ + input.setText(d); +} + +bool NetHackQtStringRequestor::Get(char* buffer, int maxchar) +{ + input.setMaxLength(maxchar); + if (prompt.text().size() > 16) { + resize(fontMetrics().width(prompt.text())+50,fontMetrics().height()*6); + } else { + resize(fontMetrics().width(prompt.text())*2+50,fontMetrics().height()*4); + } + + centerOnMain(this); + show(); + input.setFocus(); + exec(); + + if (result()) { + str_copy(buffer,input.text().toLatin1().constData(),maxchar); + return true; + } else { + return false; + } +} + +} // namespace nethack_qt4 diff --git a/win/Qt4/qt4streq.h b/win/Qt4/qt4streq.h new file mode 100644 index 000000000..a5f05d769 --- /dev/null +++ b/win/Qt4/qt4streq.h @@ -0,0 +1,30 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4streq.h -- string requestor + +#ifndef QT4STREQ_H +#define QT4STREQ_H + +#include "qt4line.h" + +namespace nethack_qt4 { + +class NetHackQtStringRequestor : QDialog { +private: + QLabel prompt; + NetHackQtLineEdit input; + QPushButton* okay; + QPushButton* cancel; + +public: + NetHackQtStringRequestor(QWidget *parent, const char* p,const char* cancelstr="Cancel"); + void SetDefault(const char*); + bool Get(char* buffer, int maxchar=80); + virtual void resizeEvent(QResizeEvent*); +}; + +} // namespace nethack_qt4 + +#endif diff --git a/win/Qt4/qt4svsel.cpp b/win/Qt4/qt4svsel.cpp new file mode 100644 index 000000000..0b5271588 --- /dev/null +++ b/win/Qt4/qt4svsel.cpp @@ -0,0 +1,80 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4svsel.cpp -- saved game selector + +#include "hack.h" +#undef Invisible +#undef Warning +#undef index +#undef msleep +#undef rindex +#undef wizard +#undef yn +#undef min +#undef max + +#include +#if QT_VERSION >= 0x050000 +#include +#endif +#include "qt4svsel.h" +#include "qt4bind.h" +#include "qt4str.h" + +namespace nethack_qt4 { + +NetHackQtSavedGameSelector::NetHackQtSavedGameSelector(const char** saved) : + QDialog(NetHackQtBind::mainWidget()) +{ + QVBoxLayout *vbl = new QVBoxLayout(this); + QHBoxLayout* hb; + + QLabel* logo = new QLabel(this); vbl->addWidget(logo); + logo->setAlignment(Qt::AlignCenter); + logo->setPixmap(QPixmap("nhsplash.xpm")); + QLabel* attr = new QLabel("by the NetHack DevTeam",this); + attr->setAlignment(Qt::AlignCenter); + vbl->addWidget(attr); + vbl->addStretch(2); + /* + QLabel* logo = new QLabel(hb); + hb = new QHBox(this); + vbl->addWidget(hb, Qt::AlignCenter); + logo->setPixmap(QPixmap(nh_icon)); + logo->setAlignment(AlignRight|Qt::AlignVCenter); + new QLabel(nh_attribution,hb); + */ + + hb = new QHBoxLayout(this); + vbl->addLayout(hb, Qt::AlignCenter); + QPushButton* q = new QPushButton("Quit",this); + hb->addWidget(q); + connect(q, SIGNAL(clicked()), this, SLOT(reject())); + QPushButton* c = new QPushButton("New Game",this); + hb->addWidget(c); + connect(c, SIGNAL(clicked()), this, SLOT(accept())); + c->setDefault(true); + + QGroupBox* box = new QGroupBox("Saved Characters",this); + QButtonGroup *bg = new QButtonGroup(this); + vbl->addWidget(box); + QVBoxLayout *bgl = new QVBoxLayout(box); + connect(bg, SIGNAL(buttonPressed(int)), this, SLOT(done(int))); + for (int i=0; saved[i]; i++) { + QPushButton* b = new QPushButton(saved[i],box); + bg->addButton(b, i+2); + } +} + +int NetHackQtSavedGameSelector::choose() +{ +#if defined(QWS) // probably safe with Qt 3, too (where show!=exec in QDialog). + if ( qt_compact_mode ) + showMaximized(); +#endif + return exec()-2; +} + +} // namespace nethack_qt4 diff --git a/win/Qt4/qt4svsel.h b/win/Qt4/qt4svsel.h new file mode 100644 index 000000000..918e6f7e3 --- /dev/null +++ b/win/Qt4/qt4svsel.h @@ -0,0 +1,21 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4svsel.h -- saved game selector + +#ifndef QT4SVSEL_H +#define QT4SVSEL_H + +namespace nethack_qt4 { + +class NetHackQtSavedGameSelector : public QDialog { +public: + NetHackQtSavedGameSelector(const char** saved); + + int choose(); +}; + +} // namespace nethack_qt4 + +#endif diff --git a/win/Qt4/qt4win.cpp b/win/Qt4/qt4win.cpp new file mode 100644 index 000000000..ca0abe57b --- /dev/null +++ b/win/Qt4/qt4win.cpp @@ -0,0 +1,136 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// Qt Binding for NetHack 3.4 +// +// Copyright (C) 1996-2001 by Warwick W. Allison (warwick@troll.no) +// +// Contributors: +// Michael Hohmuth +// - Userid control +// Svante Gerhard +// - .nethackrc tile and font size settings +// Dirk Schoenberger +// - KDE support +// - SlashEm support +// and many others for bug reports. +// +// Unfortunately, this doesn't use Qt as well as I would like, +// primarily because NetHack is fundamentally a getkey-type program +// rather than being event driven (hence the ugly key and click buffer) +// and also because this is my first major application of Qt. +// +// The problem of NetHack's getkey requirement is solved by intercepting +// key events by overiding QApplicion::notify(...), and putting them in +// a buffer. Mouse clicks on the map window are treated with a similar +// buffer. When the NetHack engine calls for a key, one is taken from +// the buffer, or if that is empty, QApplication::exec() is called. +// Whenever keys or clicks go into the buffer, QApplication::exit() +// is called. +// +// Another problem is that some NetHack players are decade-long players who +// demand complete keyboard control (while Qt and X11 conspire to make this +// difficult by having widget-based focus rather than application based - +// a good thing in general). This problem is solved by again using the key +// event buffer. +// +// Out of all this hackery comes a silver lining however, as macros for +// the super-expert and menus for the ultra-newbie are also made possible +// by the key event buffer. +// + +// This includes all the definitions we need from the NetHack main +// engine. We pretend MSC is a STDC compiler, because C++ is close +// enough, and we undefine NetHack macros which conflict with Qt +// identifiers. + +#define QT_DEPRECATED_WARNINGS +#include "hack.h" +#undef Invisible +#undef Warning +#undef index +#undef msleep +#undef rindex +#undef wizard +#undef yn +#undef min +#undef max + +#include +#if QT_VERSION >= 0x050000 +#include +#endif +#include "qt4win.h" +#include "qt4bind.h" +#include "qt4click.h" +#include "qt4glyph.h" +#include "qt4inv.h" +#include "qt4key.h" +#include "qt4icon.h" +#include "qt4map.h" +#include "qt4menu.h" +#include "qt4msg.h" +#include "qt4set.h" + +#include + +#include "qt4clust.h" + +#include + +#ifdef _WS_X11_ +// For userid control +#include +#endif + +#ifdef USER_SOUNDS +#if QT_VERSION >= 0x050000 +# include +# else +# include +# endif +#endif + + +#ifdef USER_SOUNDS +extern void play_sound_for_message(const std::string& str); +#endif + +namespace nethack_qt4 { + +void +centerOnMain( QWidget* w ) +{ + QWidget* m = NetHackQtBind::mainWidget(); + if (!m) m = qApp->desktop(); + QPoint p = m->mapToGlobal(QPoint(0,0)); + w->move( p.x() + m->width()/2 - w->width()/2, + p.y() + m->height()/2 - w->height()/2 ); +} + +NetHackQtWindow::NetHackQtWindow() +{ +} +NetHackQtWindow::~NetHackQtWindow() +{ +} + +// XXX Use "expected ..." for now, abort or default later. +// +void NetHackQtWindow::Clear() { puts("unexpected Clear"); } +void NetHackQtWindow::Display(bool block) { puts("unexpected Display"); } +bool NetHackQtWindow::Destroy() { return true; } +void NetHackQtWindow::CursorTo(int x,int y) { puts("unexpected CursorTo"); } +void NetHackQtWindow::PutStr(int attr, const QString& text) { puts("unexpected PutStr"); } +void NetHackQtWindow::StartMenu() { puts("unexpected StartMenu"); } +void NetHackQtWindow::AddMenu(int glyph, const ANY_P* identifier, char ch, char gch, int attr, + const QString& str, bool presel) { puts("unexpected AddMenu"); } +void NetHackQtWindow::EndMenu(const QString& prompt) { puts("unexpected EndMenu"); } +int NetHackQtWindow::SelectMenu(int how, MENU_ITEM_P **menu_list) { puts("unexpected SelectMenu"); return 0; } +void NetHackQtWindow::ClipAround(int x,int y) { puts("unexpected ClipAround"); } +void NetHackQtWindow::PrintGlyph(int x,int y,int glyph) { puts("unexpected PrintGlyph"); } +//void NetHackQtWindow::PrintGlyphCompose(int x,int y,int,int) { puts("unexpected PrintGlyphCompose"); } +void NetHackQtWindow::UseRIP(int how, time_t when) { puts("unexpected UseRIP"); } + +} // namespace nethack_qt4 diff --git a/win/Qt4/qt4win.h b/win/Qt4/qt4win.h new file mode 100644 index 000000000..02e96cd31 --- /dev/null +++ b/win/Qt4/qt4win.h @@ -0,0 +1,49 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// Qt Binding for NetHack 3.4 +// +// Unfortunately, this doesn't use Qt as well as I would like, +// primarily because NetHack is fundamentally a getkey-type +// program rather than being event driven (hence the ugly key +// and click buffer rather), but also because this is my first +// major application of Qt. +// + +#ifndef qt4win_h +#define qt4win_h + +namespace nethack_qt4 { + +class NetHackQtWindow { +public: + NetHackQtWindow(); + virtual ~NetHackQtWindow(); + + virtual QWidget* Widget() =0; + + virtual void Clear(); + virtual void Display(bool block); + virtual bool Destroy(); + virtual void CursorTo(int x,int y); + virtual void PutStr(int attr, const QString& text); + void PutStr(int attr, const char *text) + { + PutStr(attr, QString::fromUtf8(text).replace(QChar(0x200B), "")); + } + virtual void StartMenu(); + virtual void AddMenu(int glyph, const ANY_P* identifier, char ch, char gch, int attr, + const QString& str, bool presel); + virtual void EndMenu(const QString& prompt); + virtual int SelectMenu(int how, MENU_ITEM_P **menu_list); + virtual void ClipAround(int x,int y); + virtual void PrintGlyph(int x,int y,int glyph); + virtual void UseRIP(int how, time_t when); + + int nhid; +}; + +} // namespace nethack_qt4 + +#endif diff --git a/win/Qt4/qt4xcmd.cpp b/win/Qt4/qt4xcmd.cpp new file mode 100644 index 000000000..3e8703e49 --- /dev/null +++ b/win/Qt4/qt4xcmd.cpp @@ -0,0 +1,134 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4xcmd.cpp -- extended command widget + +#include "hack.h" +#include "func_tab.h" +#undef Invisible +#undef Warning +#undef index +#undef msleep +#undef rindex +#undef wizard +#undef yn +#undef min +#undef max + +#include +#if QT_VERSION >= 0x050000 +#include +#endif +#include "qt4xcmd.h" +#include "qt4xcmd.moc" +#include "qt4bind.h" +#include "qt4set.h" +#include "qt4str.h" + +namespace nethack_qt4 { + +// temporary +void centerOnMain(QWidget *); +// end temporary + +NetHackQtExtCmdRequestor::NetHackQtExtCmdRequestor(QWidget *parent) : + QDialog(parent) +{ + QVBoxLayout *l = new QVBoxLayout(this); + + QPushButton* can = new QPushButton("Cancel", this); + can->setDefault(true); + can->setMinimumSize(can->sizeHint()); + l->addWidget(can); + + prompt = new QLabel("#", this); + l->addWidget(prompt); + + QButtonGroup *group=new QButtonGroup(this); + QGroupBox *grid=new QGroupBox("Extended commands",this); + l->addWidget(grid); + + int i; + int butw=50; + QFontMetrics fm = fontMetrics(); + for (i=0; extcmdlist[i].ef_txt; i++) { + butw = std::max(butw,30+fm.width(extcmdlist[i].ef_txt)); + } + int ncols=4; + + QVBoxLayout* bl = new QVBoxLayout(grid); + bl->addSpacing(fm.height()); + QGridLayout* gl = new QGridLayout(); + bl->addLayout(gl); + for (i=0; extcmdlist[i].ef_txt; i++) { + QPushButton* pb=new QPushButton(extcmdlist[i].ef_txt, grid); + pb->setMinimumSize(butw,pb->sizeHint().height()); + group->addButton(pb, i+1); + gl->addWidget(pb,i/ncols,i%ncols); + } + group->addButton(can, 0); + connect(group,SIGNAL(buttonPressed(int)),this,SLOT(done(int))); + + bl->activate(); + l->activate(); + resize(1,1); +} + +void NetHackQtExtCmdRequestor::cancel() +{ + reject(); +} + +void NetHackQtExtCmdRequestor::keyPressEvent(QKeyEvent *event) +{ + QString text = event->text(); + if (text == "\r" || text == "\n" || text == " " || text == "\033") + { + reject(); + } + else if (text == "\b") + { + QString promptstr = prompt->text(); + if (promptstr != "#") + prompt->setText(promptstr.left(promptstr.size()-1)); + } + else + { + QString promptstr = prompt->text() + text; + QString typedstr = promptstr.mid(1); // skip the '#' + unsigned matches = 0; + unsigned match = 0; + for (unsigned i=0; extcmdlist[i].ef_txt; i++) { + if (QString(extcmdlist[i].ef_txt).startsWith(typedstr)) { + ++matches; + if (matches >= 2) + break; + match = i; + } + } + if (matches == 1) + done(match+1); + else if (matches >= 2) + prompt->setText(promptstr); + } +} + +int NetHackQtExtCmdRequestor::get() +{ + const int none = -10; + resize(1,1); // pack + centerOnMain(this); + // Add any keys presently buffered to the prompt + setResult(none); + while (NetHackQtBind::qt_kbhit() && result() == none) { + int ch = NetHackQtBind::qt_nhgetch(); + QKeyEvent event(QEvent::KeyPress, 0, Qt::NoModifier, QChar(ch)); + keyPressEvent(&event); + } + if (result() == none) + exec(); + return result()-1; +} + +} // namespace nethack_qt4 diff --git a/win/Qt4/qt4xcmd.h b/win/Qt4/qt4xcmd.h new file mode 100644 index 000000000..29ba23f0d --- /dev/null +++ b/win/Qt4/qt4xcmd.h @@ -0,0 +1,31 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4xcmd.h -- extended command widget + +#ifndef QT4XCMD_H +#define QT4XCMD_H + +namespace nethack_qt4 { + +class NetHackQtExtCmdRequestor : public QDialog { + Q_OBJECT + +protected: + virtual void keyPressEvent(QKeyEvent *event); + +public: + NetHackQtExtCmdRequestor(QWidget *parent); + int get(); + +private: + QLabel *prompt; + +private slots: + void cancel(); +}; + +} // namespace nethack_qt4 + +#endif diff --git a/win/Qt4/qt4yndlg.cpp b/win/Qt4/qt4yndlg.cpp new file mode 100644 index 000000000..e315189de --- /dev/null +++ b/win/Qt4/qt4yndlg.cpp @@ -0,0 +1,244 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4yndlg.cpp -- yes/no dialog + +#include "hack.h" +#undef Invisible +#undef Warning +#undef index +#undef msleep +#undef rindex +#undef wizard +#undef yn +#undef min +#undef max + +#include +#if QT_VERSION >= 0x050000 +#include +#endif +#include "qt4yndlg.h" +#include "qt4yndlg.moc" +#include "qt4str.h" + +// temporary +extern int qt_compact_mode; +// end temporary + +namespace nethack_qt4 { + +// temporary +void centerOnMain(QWidget *); +// end temporary + +NetHackQtYnDialog::NetHackQtYnDialog(QWidget *parent,const QString& q,const char* ch,char df) : + QDialog(parent), + question(q), choices(ch), def(df), + keypress('\033') +{ + setWindowTitle("NetHack: Question"); +} + +char NetHackQtYnDialog::Exec() +{ + QString ch(QString::fromLatin1(choices)); + int ch_per_line=6; + QString qlabel; + QString enable; + if ( qt_compact_mode && !choices ) { + ch = ""; + // expand choices from prompt + // ##### why isn't choices set properly??? + int c = question.indexOf(QChar('[')); + qlabel = QString(question).left(c); + if ( c >= 0 ) { + c++; + if ( question[c] == '-' ) + ch.append(question[c++]); + unsigned from=0; + while ( c < question.size() && question[c] != ']' && question[c] != ' ' ) { + if ( question[c] == '-' ) { + from = question[c-1].unicode(); + } else if ( from != 0 ) { + for (unsigned f=from+1; f<=question[c]; f++) + ch.append(QChar(f)); + from = 0; + } else { + ch.append(question[c]); + from = 0; + } + c++; + } + if ( question[c] == ' ' ) { + while ( c < question.size() && question[c] != ']' ) { + if ( question[c] == '*' || question[c] == '?' ) + ch.append(question[c]); + c++; + } + } + } + if ( question.indexOf("what direction") >= 0 ) { + // We replace this regardless, since sometimes you get choices. + const char* d = Cmd.dirchars; + enable=ch; + ch=""; + ch.append(d[1]); + ch.append(d[2]); + ch.append(d[3]); + ch.append(d[0]); + ch.append('.'); + ch.append(d[4]); + ch.append(d[7]); + ch.append(d[6]); + ch.append(d[5]); + ch.append(d[8]); + ch.append(d[9]); + ch_per_line = 3; + def = ' '; + } else { + // Hmm... they'll have to use a virtual keyboard + } + } else { + ch = QString::fromLatin1(choices); + qlabel = question.replace(QChar(0x200B), QString("")); + } + if (!ch.isNull()) { + QVBoxLayout *vb = new QVBoxLayout; + bool bigq = qlabel.length()>40; + if ( bigq ) { + QLabel* q = new QLabel(qlabel,this); + q->setAlignment(Qt::AlignLeft); + q->setWordWrap(true); + q->setMargin(4); + vb->addWidget(q); + } + QGroupBox *group = new QGroupBox(bigq ? QString::null : qlabel, this); + vb->addWidget(group); + QHBoxLayout *groupbox = new QHBoxLayout(); + group->setLayout(groupbox); + QButtonGroup *bgroup = new QButtonGroup(group); + + int nchoices=ch.length(); + + bool allow_count=ch.contains('#'); + QString yn = "yn", ynq = "ynq"; + bool is_ynq = ch == yn || ch == ynq; + + const int margin=8; + const int gutter=8; + const int extra=fontMetrics().height(); // Extra for group + int x=margin, y=extra+margin; + int butsize=fontMetrics().height()*2+5; + + QPushButton* button; + for (int i=0; isetEnabled(false); + } + button->setFixedSize(butsize,butsize); // Square + if (ch[i]==def) button->setDefault(true); + if (i%10==9) { + // last in row + x=margin; + y+=butsize+gutter; + } else { + x+=butsize+gutter; + } + groupbox->addWidget(button); + bgroup->addButton(button, i); + } + + connect(bgroup,SIGNAL(buttonClicked(int)),this,SLOT(doneItem(int))); + + QLabel* lb=0; + QLineEdit* le=0; + + if (allow_count) { + QHBoxLayout *hb = new QHBoxLayout(this); + lb=new QLabel("Count: "); + hb->addWidget(lb); + le=new QLineEdit(); + hb->addWidget(le); + vb->addLayout(hb); + } + + setLayout(vb); + adjustSize(); + centerOnMain(this); + show(); + char choice=0; + char ch_esc=0; + for (uint i=0; i= 1000 ) { + choice = ch[result() - 1000].unicode(); + } + if (allow_count && !le->text().isEmpty()) { + yn_number=le->text().toInt(); + choice='#'; + } + return choice; + } else { + QLabel label(qlabel,this); + QPushButton cancel("Dismiss",this); + label.setFrameStyle(QFrame::Box|QFrame::Sunken); + label.setAlignment(Qt::AlignCenter); + label.resize(fontMetrics().width(qlabel)+60,30+fontMetrics().height()); + cancel.move(width()/2-cancel.width()/2,label.geometry().bottom()+8); + connect(&cancel,SIGNAL(clicked()),this,SLOT(reject())); + centerOnMain(this); + setResult(-1); + show(); + keypress = '\033'; + exec(); + return keypress; + } +} + +void NetHackQtYnDialog::keyPressEvent(QKeyEvent* event) +{ + // Don't want QDialog's Return/Esc behaviour + //RLC ...or do we? + QString text(event->text()); + if (choices == NULL || choices[0] == 0) { + if (text != "") { + keypress = text.toUcs4()[0]; + done(1); + } + } else { + int where = QString::fromLatin1(choices).indexOf(text); + if (where != -1 && text != "#") { + done(where+1000); + } else { + QDialog::keyPressEvent(event); + } + } +} + +void NetHackQtYnDialog::doneItem(int i) +{ + done(i+1000); +} + +} // namespace nethack_qt4 diff --git a/win/Qt4/qt4yndlg.h b/win/Qt4/qt4yndlg.h new file mode 100644 index 000000000..d1474f541 --- /dev/null +++ b/win/Qt4/qt4yndlg.h @@ -0,0 +1,34 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4yndlg.h -- yes/no dialog + +#ifndef QT4YNDLG_H +#define QT4YNDLG_H + +namespace nethack_qt4 { + +class NetHackQtYnDialog : QDialog { + Q_OBJECT +private: + QString question; + const char* choices; + char def; + char keypress; + +protected: + virtual void keyPressEvent(QKeyEvent*); + +private slots: + void doneItem(int); + +public: + NetHackQtYnDialog(QWidget *,const QString&,const char*,char); + + char Exec(); +}; + +} // namespace nethack_qt4 + +#endif