]> granicus.if.org Git - nethack/commitdiff
Add Qt4 windowport
authorPasi Kallinen <paxed@alt.org>
Sat, 7 Oct 2017 17:26:02 +0000 (20:26 +0300)
committerPasi Kallinen <paxed@alt.org>
Sat, 7 Oct 2017 22:15:02 +0000 (01:15 +0300)
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

52 files changed:
doc/fixes36.1
include/extern.h
src/cmd.c
sys/unix/Makefile.src
sys/unix/hints/linux-qt4 [new file with mode: 0644]
win/Qt4/qt4bind.cpp [new file with mode: 0644]
win/Qt4/qt4bind.h [new file with mode: 0644]
win/Qt4/qt4click.cpp [new file with mode: 0644]
win/Qt4/qt4click.h [new file with mode: 0644]
win/Qt4/qt4clust.cpp [new file with mode: 0644]
win/Qt4/qt4clust.h [new file with mode: 0644]
win/Qt4/qt4delay.cpp [new file with mode: 0644]
win/Qt4/qt4delay.h [new file with mode: 0644]
win/Qt4/qt4glyph.cpp [new file with mode: 0644]
win/Qt4/qt4glyph.h [new file with mode: 0644]
win/Qt4/qt4icon.cpp [new file with mode: 0644]
win/Qt4/qt4icon.h [new file with mode: 0644]
win/Qt4/qt4inv.cpp [new file with mode: 0644]
win/Qt4/qt4inv.h [new file with mode: 0644]
win/Qt4/qt4kde0.h [new file with mode: 0644]
win/Qt4/qt4key.cpp [new file with mode: 0644]
win/Qt4/qt4key.h [new file with mode: 0644]
win/Qt4/qt4line.cpp [new file with mode: 0644]
win/Qt4/qt4line.h [new file with mode: 0644]
win/Qt4/qt4main.cpp [new file with mode: 0644]
win/Qt4/qt4main.h [new file with mode: 0644]
win/Qt4/qt4map.cpp [new file with mode: 0644]
win/Qt4/qt4map.h [new file with mode: 0644]
win/Qt4/qt4menu.cpp [new file with mode: 0644]
win/Qt4/qt4menu.h [new file with mode: 0644]
win/Qt4/qt4msg.cpp [new file with mode: 0644]
win/Qt4/qt4msg.h [new file with mode: 0644]
win/Qt4/qt4plsel.cpp [new file with mode: 0644]
win/Qt4/qt4plsel.h [new file with mode: 0644]
win/Qt4/qt4rip.cpp [new file with mode: 0644]
win/Qt4/qt4rip.h [new file with mode: 0644]
win/Qt4/qt4set.cpp [new file with mode: 0644]
win/Qt4/qt4set.h [new file with mode: 0644]
win/Qt4/qt4stat.cpp [new file with mode: 0644]
win/Qt4/qt4stat.h [new file with mode: 0644]
win/Qt4/qt4str.cpp [new file with mode: 0644]
win/Qt4/qt4str.h [new file with mode: 0644]
win/Qt4/qt4streq.cpp [new file with mode: 0644]
win/Qt4/qt4streq.h [new file with mode: 0644]
win/Qt4/qt4svsel.cpp [new file with mode: 0644]
win/Qt4/qt4svsel.h [new file with mode: 0644]
win/Qt4/qt4win.cpp [new file with mode: 0644]
win/Qt4/qt4win.h [new file with mode: 0644]
win/Qt4/qt4xcmd.cpp [new file with mode: 0644]
win/Qt4/qt4xcmd.h [new file with mode: 0644]
win/Qt4/qt4yndlg.cpp [new file with mode: 0644]
win/Qt4/qt4yndlg.h [new file with mode: 0644]

index 8e2d0f4863f958c4f56332cab5a38d5f4213d3f1..e6f3e7c783f0e7614f7fa8229bc661f2ada35c4c 100644 (file)
@@ -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
index 54ec9440cf7e31a3c99ef5959cccbf956341a40b..018094dd4e5eed39573ab5fc1197e987d84b2de7 100644 (file)
@@ -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);
index 6849f2ca470f61cbc100fecb06488bd63658d7e9..add6ebd52922dc7508e9c37fd332977a93633514 100644 (file)
--- 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));
 {
index 5488e61062f5393146fec3678e56f742b384175d..8c27b612cd61eac4f43000f702916c22a010b7e1 100644 (file)
@@ -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 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 (file)
index 0000000..0feddb9
--- /dev/null
@@ -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 (file)
index 0000000..5dc17de
--- /dev/null
@@ -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 <QtGui/QtGui>
+#if QT_VERSION >= 0x050000
+#include <QtWidgets/QtWidgets>
+#include <QtMultimedia/QSound>
+#else
+#include <QtGui/QSound>
+#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 <hohmuth@inf.tu-dresden.de>...
+//
+// 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<NetHackQtWindow*> 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 <esc> 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 (file)
index 0000000..99a2c0d
--- /dev/null
@@ -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 (file)
index 0000000..78177a1
--- /dev/null
@@ -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 <QtGui/QtGui>
+#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 (file)
index 0000000..50fd8b1
--- /dev/null
@@ -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 (file)
index 0000000..1cd080c
--- /dev/null
@@ -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.left()) {
+                       r.setLeft(rect.left());
+       }
+       if (rect.right()>r.right()) {
+                       r.setRight(rect.right());
+       }
+       if (rect.top()<r.top()) {
+                       r.setTop(rect.top());
+       }
+       if (rect.bottom()>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<count; cursor++) {
+               if (cluster[cursor].contains(rect)) {
+                       // Wholly contained already.
+                       return;
+               }
+       }
+
+       int lowestcost=9999999;
+       int cheapest=-1;
+       for (cursor=0; cursor<count; cursor++) {
+               if (cluster[cursor].intersects(biggerrect)) {
+                       QRect larger=cluster[cursor];
+                       include(larger,rect);
+                       int cost=larger.width()*larger.height()
+                                       - cluster[cursor].width()*cluster[cursor].height();
+
+                       if (cost < lowestcost) {
+                               bool bad=false;
+                               for (int c=0; c<count && !bad; c++) {
+                                       bad=cluster[c].intersects(larger) && c!=cursor;
+                               }
+                               if (!bad) {
+                                       cheapest=cursor;
+                                       lowestcost=cost;
+                               }
+                       }
+               }
+       }
+       if (cheapest>=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<count; cursor++) {
+               QRect larger=cluster[cursor];
+               include(larger,rect);
+               int cost=larger.width()*larger.height()
+                               - cluster[cursor].width()*cluster[cursor].height();
+               if (cost < lowestcost) {
+                       bool bad=false;
+                       for (int c=0; c<count && !bad; c++) {
+                               bad=cluster[c].intersects(larger) && c!=cursor;
+                       }
+                       if (!bad) {
+                               cheapest=cursor;
+                               lowestcost=cost;
+                       }
+               }
+       }
+
+       // XXX could make an heuristic guess as to whether we
+       // XXX need to bother looking for a cheap merge.
+
+       int cheapestmerge1=-1;
+       int cheapestmerge2=-1;
+
+       for (int merge1=0; merge1<count; merge1++) {
+               for (int merge2=0; merge2<count; merge2++) {
+                       if (merge1!=merge2) {
+                               QRect larger=cluster[merge1];
+                               include(larger,cluster[merge2]);
+                               int cost=larger.width()*larger.height()
+                                       - cluster[merge1].width()*cluster[merge1].height()
+                                       - cluster[merge2].width()*cluster[merge2].height();
+                               if (cost < lowestcost) {
+                                       bool bad=false;
+                                       for (int c=0; c<count && !bad; c++) {
+                                               bad=cluster[c].intersects(larger) && c!=cursor;
+                                       }
+                                       if (!bad) {
+                                               cheapestmerge1=merge1;
+                                               cheapestmerge2=merge2;
+                                               lowestcost=cost;
+                                       }
+                               }
+                       }
+               }
+       }
+
+       if (cheapestmerge1>=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 (file)
index 0000000..c7112ea
--- /dev/null
@@ -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 <QtCore/QRect>
+
+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 (file)
index 0000000..eadf42a
--- /dev/null
@@ -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 <QtGui/QtGui>
+#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 (file)
index 0000000..2e0085e
--- /dev/null
@@ -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 (file)
index 0000000..3ba56be
--- /dev/null
@@ -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 <QtGui/QtGui>
+#if QT_VERSION >= 0x050000
+#include <QtWidgets/QtWidgets>
+#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 (file)
index 0000000..12fd915
--- /dev/null
@@ -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 (file)
index 0000000..0876cb5
--- /dev/null
@@ -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 <QtGui/QtGui>
+#if QT_VERSION >= 0x050000
+#include <QtWidgets/QtWidgets>
+#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 (file)
index 0000000..bdaf818
--- /dev/null
@@ -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 (file)
index 0000000..9165d58
--- /dev/null
@@ -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 <QtGui/QtGui>
+#if QT_VERSION >= 0x050000
+#include <QtWidgets/QtWidgets>
+#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 (file)
index 0000000..51cdd4d
--- /dev/null
@@ -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 (file)
index 0000000..27a678c
--- /dev/null
@@ -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 (file)
index 0000000..ff16616
--- /dev/null
@@ -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 <QtGui/QtGui>
+#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 (file)
index 0000000..0333269
--- /dev/null
@@ -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 (file)
index 0000000..dd9b18c
--- /dev/null
@@ -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 <QtGui/QtGui>
+#if QT_VERSION >= 0x050000
+#include <QtWidgets/QtWidgets>
+#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 (file)
index 0000000..bb5067f
--- /dev/null
@@ -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 (file)
index 0000000..ec7e583
--- /dev/null
@@ -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 <QtGui/QtGui>
+#if QT_VERSION >= 0x050000
+#include <QtWidgets/QtWidgets>
+#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<QWidget *>(stack) : static_cast<QWidget *>(hsplitter);
+       QWidget* vp = qt_compact_mode ? static_cast<QWidget *>(stack) : static_cast<QWidget *>(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 (file)
index 0000000..33f5b26
--- /dev/null
@@ -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 <kapp.h>
+#include <ktopwidget.h>
+#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 (file)
index 0000000..39c915e
--- /dev/null
@@ -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 <QtGui/QtGui>
+#if QT_VERSION >= 0x050000
+#include <QtWidgets/QtWidgets>
+#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; j<ROWNO; j++) {
+       for (int i=0; i<COLNO; i++) {
+           Glyph(i,j)=stone;
+       }
+    }
+
+    change.clear();
+    change.add(0,0,COLNO,ROWNO);
+}
+
+void NetHackQtMapViewport::Display(bool block)
+{
+    for (int i=0; i<change.clusters(); i++) {
+       const QRect& ch=change[i];
+       repaint(
+           ch.x()*qt_settings->glyphs().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; j<ROWNO; j++) {
+       for (int i=0; i<COLNO; i++) {
+           Glyph(i,j)=stone;
+       }
+    }
+
+    change.clear();
+    change.add(0,0,COLNO,ROWNO);
+}
+
+void NetHackQtMapWindow::clickCursor()
+{
+    clicksink.Put(cursor.x(),cursor.y(),CLICK_1);
+    qApp->exit();
+}
+
+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; i<change.clusters(); i++) {
+       const QRect& ch=change[i];
+       repaint(
+           ch.x()*qt_settings->glyphs().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 (file)
index 0000000..da2df88
--- /dev/null
@@ -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 (file)
index 0000000..b15aa0e
--- /dev/null
@@ -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 <QtGui/QtGui>
+#if QT_VERSION >= 0x050000
+#include <QtWidgets/QtWidgets>
+#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<int> 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; i<itemcount && !isSelected(i); i++)
+               ;
+           if (i<itemcount) {
+               *menu_list=(MENU_ITEM_P *)alloc(sizeof(MENU_ITEM_P)*1);
+               (*menu_list)[0].item=itemlist[i].identifier;
+               (*menu_list)[0].count=count(i);
+               return 1;
+           } else {
+               return 0;
+           }
+       } else {
+           int selcount=0;
+           for (int i=0; i<itemcount; i++)
+               if (isSelected(i)) selcount++;
+           if (selcount) {
+               *menu_list=(MENU_ITEM_P *)alloc(sizeof(MENU_ITEM_P)*selcount);
+               int j=0;
+               for (int i=0; i<itemcount; i++) {
+                   if (isSelected(i)) {
+                       (*menu_list)[j].item=itemlist[i].identifier;
+                       (*menu_list)[j].count=count(i);
+                       j++;
+                   }
+               }
+               return selcount;
+           } else {
+               return 0;
+           }
+       }
+    } else {
+       return -1;
+    }
+}
+
+void NetHackQtMenuWindow::AddRow(int row, const MenuItem& mi)
+{
+    static const QColor colors[] = {
+       QColor(64, 64, 64),
+       QColor(Qt::red),
+       QColor(0, 191, 0),
+       QColor(127, 127, 0),
+       QColor(Qt::blue),
+       QColor(Qt::magenta),
+       QColor(Qt::cyan),
+       QColor(Qt::gray),
+       QColor(Qt::white),
+       QColor(255, 127, 0),
+       QColor(127, 255, 127),
+       QColor(Qt::yellow),
+       QColor(127, 127, 255),
+       QColor(255, 127, 255),
+       QColor(127, 255, 255),
+       QColor(Qt::white)
+    };
+    QFontMetrics fm(table->font());
+    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; i<itemcount; i++) {
+               if (itemlist[i].ch == key || itemlist[i].gch == key)
+                   ToggleSelect(i);
+           }
+       }
+    }
+}
+
+void NetHackQtMenuWindow::All()
+{
+    for (int i=0; i<itemcount; i++) {
+       QTableWidgetItem *count = table->item(i, 0);
+       if (count != NULL) count->setText("");
+
+       QCheckBox *cb = dynamic_cast<QCheckBox *>(table->cellWidget(i, 1));
+       if (cb != NULL) cb->setChecked(true);
+    }
+}
+void NetHackQtMenuWindow::ChooseNone()
+{
+    for (int i=0; i<itemcount; i++) {
+       QTableWidgetItem *count = table->item(i, 0);
+       if (count != NULL) count->setText("");
+
+       QCheckBox *cb = dynamic_cast<QCheckBox *>(table->cellWidget(i, 1));
+       if (cb != NULL) cb->setChecked(false);
+    }
+}
+void NetHackQtMenuWindow::Invert()
+{
+    for (int i=0; i<itemcount; i++) {
+       QTableWidgetItem *count = table->item(i, 0);
+       if (count != NULL) count->setText("");
+
+       QCheckBox *cb = dynamic_cast<QCheckBox *>(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<itemcount; i++) {
+           if (itemlist[i].str.contains(line))
+               ToggleSelect(i);
+       }
+    }
+}
+void NetHackQtMenuWindow::ToggleSelect(int i)
+{
+    if (itemlist[i].Selectable()) {
+       QCheckBox *cb = dynamic_cast<QCheckBox *>(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<QCheckBox *>(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<YEAR_LINE+1; i++) {
+           rip_line[i]=new char[STONE_LINE_LEN+1];
+       }
+    }
+
+    /* Follows same algorithm as genl_outrip() */
+
+    char buf[BUFSZ];
+    char *dpx;
+    int line;
+
+    /* Put name on stone */
+    snprintf(rip_line[NAME_LINE], STONE_LINE_LEN+1, "%s", plname);
+
+    /* Put $ on stone */
+    snprintf(rip_line[GOLD_LINE], STONE_LINE_LEN+1, "%ld Au", money_cnt(invent));
+
+    /* Put together death description */
+    formatkiller(buf, sizeof buf, how, FALSE);
+    //str_copy(buf, killer, SIZE(buf));
+
+    /* Put death type on stone */
+    for (line=DEATH_LINE, dpx = buf; line<YEAR_LINE; line++) {
+       int i,i0;
+       char tmpchar;
+
+       if ( (i0=strlen(dpx)) > 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; i<lines->count(); 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 (file)
index 0000000..ad15841
--- /dev/null
@@ -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<MenuItem> 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 (file)
index 0000000..e119479
--- /dev/null
@@ -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 <QtGui/QtGui>
+#if QT_VERSION >= 0x050000
+#include <QtWidgets/QtWidgets>
+#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 (file)
index 0000000..5c391cc
--- /dev/null
@@ -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 (file)
index 0000000..56b8849
--- /dev/null
@@ -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 <QtGui/QtGui>
+#if QT_VERSION >= 0x050000
+#include <QtWidgets/QtWidgets>
+#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[] = "<center><big>NetHack</big>"
+       "<br><small>by the NetHack DevTeam</small></center>";
+
+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; i<ROLE_GENDERS; i++) {
+       gender[i] = new QRadioButton( genders[i].adj, genderbox );
+       genderbox->layout()->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; i<ROLE_ALIGNS; i++) {
+       alignment[i] = new QRadioButton( aligns[i].adj, alignbox );
+       alignbox->layout()->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; j<ROLE_GENDERS; j++) {
+       bool v = validgend(ro,ra,j);
+       if ( gender[j]->isChecked() )
+           c = j;
+       gender[j]->setEnabled(v);
+       if ( valid<0 && v ) valid = j;
+    }
+    if ( !validgend(ro,ra,c) )
+       c = valid;
+    int k;
+    for (k=0; k<ROLE_GENDERS; k++) {
+       gender[k]->setChecked(c==k);
+    }
+    selectGender(c);
+
+    valid=-1;
+    for (j=0; j<ROLE_ALIGNS; j++) {
+       bool v = validalign(ro,ra,j);
+       if ( alignment[j]->isChecked() )
+           c = j;
+       alignment[j]->setEnabled(v);
+       if ( valid<0 && v ) valid = j;
+    }
+    if ( !validalign(ro,ra,c) )
+       c = valid;
+    for (k=0; k<ROLE_ALIGNS; k++) {
+       alignment[k]->setChecked(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 (file)
index 0000000..b823412
--- /dev/null
@@ -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 (file)
index 0000000..d509f88
--- /dev/null
@@ -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 <QtGui/QtGui>
+#if QT_VERSION >= 0x050000
+#include <QtWidgets/QtWidgets>
+#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<riplines; i++) {
+           painter.drawText(rip_text_x-i/2,rip_text_y+i*rip_text_h,
+               1,1,Qt::TextDontClip|Qt::AlignHCenter,line[i]);
+       }
+       painter.end();
+    }
+}
+
+} // namespace nethack_qt4
diff --git a/win/Qt4/qt4rip.h b/win/Qt4/qt4rip.h
new file mode 100644 (file)
index 0000000..792a4e3
--- /dev/null
@@ -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.
+
+// qt4rip.h -- tombstone window
+
+#ifndef QT4RIP_H
+#define QT4RIP_H
+
+namespace nethack_qt4 {
+
+class NetHackQtRIP : public QWidget {
+private:
+       static QPixmap* pixmap;
+       char** line;
+       int riplines;
+
+public:
+       NetHackQtRIP(QWidget* parent);
+
+       void setLines(char** l, int n);
+
+protected:
+       virtual void paintEvent(QPaintEvent* event);
+       QSize sizeHint() const;
+};
+
+} // namespace nethack_qt4
+
+#endif
diff --git a/win/Qt4/qt4set.cpp b/win/Qt4/qt4set.cpp
new file mode 100644 (file)
index 0000000..03de1f6
--- /dev/null
@@ -0,0 +1,186 @@
+// Copyright (c) Warwick Allison, 1999.
+// Qt4 conversion copyright (c) Ray Chason, 2012-2014.
+// NetHack may be freely redistributed.  See license for details.
+
+// qt4set.cpp -- the Qt settings
+
+#include "hack.h"
+#undef Invisible
+#undef Warning
+#undef index
+#undef msleep
+#undef rindex
+#undef wizard
+#undef yn
+#undef min
+#undef max
+
+#include <QtGui/QtGui>
+#if QT_VERSION >= 0x050000
+#include <QtWidgets/QtWidgets>
+#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 (file)
index 0000000..e2253a8
--- /dev/null
@@ -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 (file)
index 0000000..3a15604
--- /dev/null
@@ -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 <QtGui/QtGui>
+#if QT_VERSION >= 0x050000
+#include <QtWidgets/QtWidgets>
+#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; //     <Name> the <Class> (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 (file)
index 0000000..a0a00a4
--- /dev/null
@@ -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 (file)
index 0000000..b6b4440
--- /dev/null
@@ -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 <QtCore/QString>
+#include <QtCore/QStringList>
+#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 (file)
index 0000000..05f25f4
--- /dev/null
@@ -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 (file)
index 0000000..5d03bb9
--- /dev/null
@@ -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 <QtGui/QtGui>
+#if QT_VERSION >= 0x050000
+#include <QtWidgets/QtWidgets>
+#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 (file)
index 0000000..a5f05d7
--- /dev/null
@@ -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 (file)
index 0000000..0b52715
--- /dev/null
@@ -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 <QtGui/QtGui>
+#if QT_VERSION >= 0x050000
+#include <QtWidgets/QtWidgets>
+#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 (file)
index 0000000..918e6f7
--- /dev/null
@@ -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 (file)
index 0000000..ca0abe5
--- /dev/null
@@ -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 <hohmuth@inf.tu-dresden.de>
+//       - Userid control
+//    Svante Gerhard <svante@algonet.se>
+//       - .nethackrc tile and font size settings
+//    Dirk Schoenberger <schoenberger@signsoft.com>
+//       - 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 <QtGui/QtGui>
+#if QT_VERSION >= 0x050000
+#include <QtWidgets/QtWidgets>
+#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 <ctype.h>
+
+#include "qt4clust.h"
+
+#include <dirent.h>
+
+#ifdef _WS_X11_
+// For userid control
+#include <unistd.h>
+#endif
+
+#ifdef USER_SOUNDS
+#if QT_VERSION >= 0x050000
+#  include <QtMultimedia/QSound>
+# else
+#  include <QtGui/QSound>
+# 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 (file)
index 0000000..02e96cd
--- /dev/null
@@ -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 (file)
index 0000000..3e8703e
--- /dev/null
@@ -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 <QtGui/QtGui>
+#if QT_VERSION >= 0x050000
+#include <QtWidgets/QtWidgets>
+#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 (file)
index 0000000..29ba23f
--- /dev/null
@@ -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 (file)
index 0000000..e315189
--- /dev/null
@@ -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 <QtGui/QtGui>
+#if QT_VERSION >= 0x050000
+#include <QtWidgets/QtWidgets>
+#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; i<nchoices && ch[i]!='\033'; i++) {
+           QString button_name = QString(ch[i]);
+           if (is_ynq) {
+               if (button_name == ynq.mid(0, 1)) {
+                   button_name = "Yes";
+               } else if (button_name == ynq.mid(1, 1)) {
+                   button_name = "No";
+               } else if (button_name == ynq.mid(2, 1)) {
+                   button_name = "Cancel";
+               }
+           }
+           button=new QPushButton(button_name);
+           if ( !enable.isNull() ) {
+               if ( !enable.contains(ch[i]) )
+                   button->setEnabled(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<ch.length(); i++) {
+           if (ch[i].unicode()=='q') ch_esc='q';
+           else if (!ch_esc && ch[i].unicode()=='n') ch_esc='n';
+       }
+       exec();
+       if ( result() == 0) {
+           choice = ch_esc ? ch_esc : def ? def : ' ';
+       } else if ( result() == 1 ) {
+           choice = def ? def : ch_esc ? ch_esc : ' ';
+       } else if ( result() >= 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 (file)
index 0000000..d1474f5
--- /dev/null
@@ -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