Browse Source

Initial commit

Sonnix 6 years ago
commit
b9e20a238c
25 changed files with 4057 additions and 0 deletions
  1. 65 0
      .gitignore
  2. 3 0
      .gitmodules
  3. 158 0
      Qqsp.pro
  4. 430 0
      callbacks_gui.cpp
  5. 92 0
      callbacks_gui.h
  6. 157 0
      comtools.cpp
  7. 18 0
      comtools.h
  8. 1050 0
      frame.cpp
  9. 185 0
      frame.h
  10. 14 0
      main.cpp
  11. 680 0
      mainwindow.cpp
  12. 175 0
      mainwindow.h
  13. 1 0
      qsp
  14. 99 0
      qspimgcanvas.cpp
  15. 46 0
      qspimgcanvas.h
  16. 23 0
      qspinputbox.cpp
  17. 33 0
      qspinputbox.h
  18. 69 0
      qspinputdlg.cpp
  19. 39 0
      qspinputdlg.h
  20. 274 0
      qsplistbox.cpp
  21. 64 0
      qsplistbox.h
  22. 101 0
      qspmsgdlg.cpp
  23. 45 0
      qspmsgdlg.h
  24. 174 0
      qsptextbox.cpp
  25. 62 0
      qsptextbox.h

+ 65 - 0
.gitignore

@@ -0,0 +1,65 @@
+# In repository we don't need to have:
+# Compiled object files
+*.o
+
+# Generated MOC, resource and UI files
+moc_*.cpp
+qrc_*.cpp
+ui_*.h
+Makefile
+Makefile.*
+*.pro.user
+
+# .log files (usually created by QtTest - thanks to VestniK)
+*.log
+
+# Built windows .exe and linux binaries
+# NOTE: PROJECT is a your project's name, analog of PROJECT.exe in Linux
+*.exe
+*.dll
+
+# Windows-specific files
+Thumbs.db
+desktop.ini
+# Mac-specific things (thanks to Michael Aaron Safyan)
+.DS_Store
+
+# Editors temporary files 
+*~
+*.bak
+
+# Failed patch
+*.orig
+*.rej
+
+#Carbide project files
+*.project
+*.cproject
+*.settings
+plugin_commonU.def
+
+#Visual Studio files
+*.sln
+*.vcproj
+*.suo
+*.ncb
+*.user
+
+#from qt git
+*.dylib
+*.so.*
+*.so
+*.so.debug
+.cd.rc
+*.swp
+core
+*.Debug
+*.Release
+*.pdb
+*.idb
+*.ib_pdb_index
+config.pri
+config.in
+.qmake.cache
+
+*.qm

+ 3 - 0
.gitmodules

@@ -0,0 +1,3 @@
+[submodule "qsp"]
+	path = qsp
+	url = http://git.tfgamessite.com/Sonnix/qsp.git

+ 158 - 0
Qqsp.pro

@@ -0,0 +1,158 @@
+QT       += core gui widgets
+
+TARGET = Qqsp
+TEMPLATE = app
+
+# The following define makes your compiler emit warnings if you use
+# any feature of Qt which has been marked as deprecated (the exact warnings
+# depend on your compiler). Please consult the documentation of the
+# deprecated API in order to know how to port your code away from it.
+DEFINES += QT_DEPRECATED_WARNINGS
+
+# You can also make your code fail to compile if you use deprecated APIs.
+# In order to do so, uncomment the following line.
+# You can also select to disable deprecated APIs only up to a certain version of Qt.
+DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0
+
+CONFIG += c++14
+#CONFIG += c11
+#QMAKE_CXXFLAGS+= -std=c++14
+QMAKE_CFLAGS += -std=c11 -fshort-wchar
+
+INCLUDEPATH += $$PWD/qsp/qsp/bindings/default/
+#INCLUDEPATH += $$PWD/qsp/qsp/
+#INCLUDEPATH += $$PWD/qsp/qsp/onig/
+
+SOURCES += \
+    main.cpp \
+    mainwindow.cpp \
+    qsptextbox.cpp \
+    qsplistbox.cpp \
+    qspinputbox.cpp \
+    qspimgcanvas.cpp \
+    qspmsgdlg.cpp \
+    qspinputdlg.cpp \
+    callbacks_gui.cpp \
+    comtools.cpp
+
+HEADERS += \
+    mainwindow.h \
+    qsptextbox.h \
+    qsplistbox.h \
+    qspinputbox.h \
+    qspimgcanvas.h \
+    qspmsgdlg.h \
+    qspinputdlg.h \
+    callbacks_gui.h \
+    comtools.h
+
+DEFINES += _UNICODE
+DEFINES += NOT_RUBY
+#DEFINES += EXPORT
+
+SOURCES += \
+    qsp/qsp/actions.c \
+    qsp/qsp/errors.c \
+    qsp/qsp/callbacks.c \
+    qsp/qsp/game.c \
+    qsp/qsp/codetools.c \
+    qsp/qsp/coding.c \
+    qsp/qsp/locations.c \
+    qsp/qsp/mathops.c \
+    qsp/qsp/common.c \
+    qsp/qsp/memwatch.c \
+    qsp/qsp/objects.c \
+    qsp/qsp/playlist.c \
+    qsp/qsp/time.c \
+    qsp/qsp/towlower.c \
+    qsp/qsp/towupper.c \
+    qsp/qsp/regexp.c \
+    qsp/qsp/statements.c \
+    qsp/qsp/variables.c \
+    qsp/qsp/variant.c \
+    qsp/qsp/menu.c \
+    qsp/qsp/text.c \
+    qsp/qsp/onig/regcomp.c \
+    qsp/qsp/onig/regenc.c \
+    qsp/qsp/onig/regerror.c \
+    qsp/qsp/onig/regexec.c \
+    qsp/qsp/onig/regparse.c \
+    qsp/qsp/onig/regtrav.c \
+    qsp/qsp/onig/regversion.c \
+    qsp/qsp/onig/st.c \
+    qsp/qsp/onig/regsyntax.c \
+    qsp/qsp/onig/enc/ascii.c \
+    qsp/qsp/onig/enc/cp1251.c \
+    qsp/qsp/onig/enc/koi8_r.c \
+    qsp/qsp/onig/enc/unicode.c \
+    qsp/qsp/onig/enc/utf16_be.c \
+    qsp/qsp/onig/enc/utf16_le.c \
+    qsp/qsp/onig/enc/utf32_be.c \
+    qsp/qsp/onig/enc/utf32_le.c \
+    qsp/qsp/bindings/default/default_callbacks.c \
+    qsp/qsp/bindings/default/default_control.c \
+    qsp/qsp/bindings/default/default_platform.c
+
+HEADERS += \
+    qsp/qsp/actions.h \
+    qsp/qsp/callbacks.h \
+    qsp/qsp/errors.h \
+    qsp/qsp/game.h \
+    qsp/qsp/codetools.h \
+    qsp/qsp/locations.h \
+    qsp/qsp/coding.h \
+    qsp/qsp/mathops.h \
+    qsp/qsp/menu.h \
+    qsp/qsp/objects.h \
+    qsp/qsp/time.h \
+    qsp/qsp/playlist.h \
+    qsp/qsp/regexp.h \
+    qsp/qsp/statements.h \
+    qsp/qsp/variables.h \
+    qsp/qsp/variant.h \
+    qsp/qsp/common.h \
+    qsp/qsp/declarations.h \
+    qsp/qsp/memwatch.h \
+    qsp/qsp/text.h \
+    qsp/qsp/onig/config.h \
+    qsp/qsp/onig/oniguruma.h \
+    qsp/qsp/onig/regenc.h \
+    qsp/qsp/onig/regint.h \
+    qsp/qsp/onig/regparse.h \
+    qsp/qsp/onig/st.h \
+    qsp/qsp/bindings/default/qsp_default.h
+
+isEmpty(TARGET_EXT) {
+    win32 {
+        TARGET_CUSTOM_EXT = .exe
+    }
+    macx {
+        TARGET_CUSTOM_EXT = .app
+    }
+} else {
+    TARGET_CUSTOM_EXT = $${TARGET_EXT}
+}
+
+win32 {
+    DEPLOY_COMMAND = windeployqt
+}
+macx {
+    DEPLOY_COMMAND = macdeployqt
+}
+
+CONFIG( debug, debug|release ) {
+    # debug
+    DEPLOY_TARGET = $$shell_quote($$shell_path($${OUT_PWD}/debug/$${TARGET}$${TARGET_CUSTOM_EXT}))
+} else {
+    # release
+    DEPLOY_TARGET = $$shell_quote($$shell_path($${OUT_PWD}/release/$${TARGET}$${TARGET_CUSTOM_EXT}))
+}
+
+#  # Uncomment the following line to help debug the deploy command when running qmake
+#  warning($${DEPLOY_COMMAND} $${DEPLOY_TARGET})
+
+win32|macx {
+    QMAKE_POST_LINK = $${DEPLOY_COMMAND} $${DEPLOY_TARGET}
+}
+
+win32:RC_ICONS += qsp/players/classic/qspgui/misc/icons/logo.ico

+ 430 - 0
callbacks_gui.cpp

@@ -0,0 +1,430 @@
+#include "callbacks_gui.h"
+
+#include <QCoreApplication>
+#include <QThread>
+#include <QElapsedTimer>
+#include <QFileInfo>
+#include <QFileDialog>
+#include <QInputDialog>
+
+#include "comtools.h"
+#include "qspmsgdlg.h"
+#include "qspinputdlg.h"
+
+QString QSPCallBacks::m_gamePath;
+MainWindow *QSPCallBacks::m_frame;
+bool QSPCallBacks::m_isHtml;
+//FMOD_SYSTEM *QSPCallBacks::m_sys;
+QSPSounds QSPCallBacks::m_sounds;
+float QSPCallBacks::m_volumeCoeff;
+
+void QSPCallBacks::Init(MainWindow *frame)
+{
+	m_frame = frame;
+	m_volumeCoeff = 1.0;
+
+    //FMOD_System_Create(&m_sys);
+    QString soundPath(QSPTools::GetAppPath() + QSP_SOUNDPLUGINS);
+    //FMOD_System_SetPluginPath(m_sys, wxConvFile.cWX2MB(soundPath.c_str()));
+//	#ifdef __WXMSW__
+//		FMOD_System_SetOutput(m_sys, FMOD_OUTPUTTYPE_DSOUND);
+//	#elif __WXOSX__
+//		FMOD_System_SetOutput(m_sys, FMOD_OUTPUTTYPE_COREAUDIO);
+//	#else
+//		FMOD_System_SetOutput(m_sys, FMOD_OUTPUTTYPE_ALSA);
+//	#endif
+//	FMOD_System_Init(m_sys, 32, FMOD_INIT_NORMAL, 0);
+
+	QSPSetCallBack(QSP_CALL_SETTIMER, (QSP_CALLBACK)&SetTimer);
+	QSPSetCallBack(QSP_CALL_REFRESHINT, (QSP_CALLBACK)&RefreshInt);
+	QSPSetCallBack(QSP_CALL_SETINPUTSTRTEXT, (QSP_CALLBACK)&SetInputStrText);
+	QSPSetCallBack(QSP_CALL_ISPLAYINGFILE, (QSP_CALLBACK)&IsPlay);
+	QSPSetCallBack(QSP_CALL_PLAYFILE, (QSP_CALLBACK)&PlayFile);
+	QSPSetCallBack(QSP_CALL_CLOSEFILE, (QSP_CALLBACK)&CloseFile);
+	QSPSetCallBack(QSP_CALL_SHOWMSGSTR, (QSP_CALLBACK)&Msg);
+	QSPSetCallBack(QSP_CALL_SLEEP, (QSP_CALLBACK)&Sleep);
+	QSPSetCallBack(QSP_CALL_GETMSCOUNT, (QSP_CALLBACK)&GetMSCount);
+	QSPSetCallBack(QSP_CALL_SHOWMENU, (QSP_CALLBACK)&ShowMenu);
+	QSPSetCallBack(QSP_CALL_INPUTBOX, (QSP_CALLBACK)&Input);
+	QSPSetCallBack(QSP_CALL_SHOWIMAGE, (QSP_CALLBACK)&ShowImage);
+	QSPSetCallBack(QSP_CALL_SHOWWINDOW, (QSP_CALLBACK)&ShowPane);
+	QSPSetCallBack(QSP_CALL_OPENGAME, (QSP_CALLBACK)&OpenGame);
+	QSPSetCallBack(QSP_CALL_OPENGAMESTATUS, (QSP_CALLBACK)&OpenGameStatus);
+	QSPSetCallBack(QSP_CALL_SAVEGAMESTATUS, (QSP_CALLBACK)&SaveGameStatus);
+    //TODO: implement this?
+    //QSP_CALL_DEBUG, /* void func(QSPString str) */
+}
+
+void QSPCallBacks::DeInit()
+{
+	CloseFile(qspStringFromPair(0, 0));
+    //FMOD_System_Close(m_sys);
+    //FMOD_System_Release(m_sys);
+}
+
+void QSPCallBacks::SetTimer(int msecs)
+{
+	if (m_frame->IsQuit()) return;
+	if (msecs)
+        m_frame->GetTimer()->start(msecs);
+	else
+        m_frame->GetTimer()->stop();
+}
+
+void QSPCallBacks::RefreshInt(QSP_BOOL isRedraw)
+{
+	static int oldFullRefreshCount = 0;
+	int i, numVal;
+	bool isScroll, isCanSave;
+	QSPString strVal;
+	QSPListItem items[MAX_LIST_ITEMS];
+	if (m_frame->IsQuit()) return;
+	// -------------------------------
+	isScroll = !(QSPGetVarValues(QSP_STATIC_STR(QSP_FMT("DISABLESCROLL")), 0, &numVal, &strVal) && numVal);
+	isCanSave = !(QSPGetVarValues(QSP_STATIC_STR(QSP_FMT("NOSAVE")), 0, &numVal, &strVal) && numVal);
+	m_isHtml = QSPGetVarValues(QSP_STATIC_STR(QSP_FMT("USEHTML")), 0, &numVal, &strVal) && numVal;
+	// -------------------------------
+	m_frame->GetVars()->SetIsHtml(m_isHtml);
+	if (QSPIsVarsDescChanged())
+	{
+		QSPString varsDesc = QSPGetVarsDesc();
+        m_frame->GetVars()->SetText(QSPTools::qspStrToQt(varsDesc), isScroll);
+	}
+	// -------------------------------
+	int fullRefreshCount = QSPGetFullRefreshCount();
+	if (oldFullRefreshCount != fullRefreshCount)
+	{
+		isScroll = false;
+		oldFullRefreshCount = fullRefreshCount;
+	}
+	m_frame->GetDesc()->SetIsHtml(m_isHtml);
+	if (QSPIsMainDescChanged())
+	{
+		QSPString mainDesc = QSPGetMainDesc();
+        m_frame->GetDesc()->SetText(QSPTools::qspStrToQt(mainDesc), isScroll);
+	}
+	// -------------------------------
+	m_frame->GetActions()->SetIsHtml(m_isHtml);
+	m_frame->GetActions()->SetIsShowNums(m_frame->IsShowHotkeys());
+	if (QSPIsActionsChanged())
+	{
+		int actionsCount = QSPGetActions(items, MAX_LIST_ITEMS);
+		m_frame->GetActions()->BeginItems();
+		for (i = 0; i < actionsCount; ++i)
+            m_frame->GetActions()->AddItem(QSPTools::qspStrToQt(items[i].Image), QSPTools::qspStrToQt(items[i].Name));
+		m_frame->GetActions()->EndItems();
+	}
+	m_frame->GetActions()->SetSelection(QSPGetSelActionIndex());
+	m_frame->GetObjects()->SetIsHtml(m_isHtml);
+	if (QSPIsObjectsChanged())
+	{
+		int objectsCount = QSPGetObjects(items, MAX_LIST_ITEMS);
+		m_frame->GetObjects()->BeginItems();
+		for (i = 0; i < objectsCount; ++i)
+            m_frame->GetObjects()->AddItem(QSPTools::qspStrToQt(items[i].Image), QSPTools::qspStrToQt(items[i].Name));
+		m_frame->GetObjects()->EndItems();
+	}
+	m_frame->GetObjects()->SetSelection(QSPGetSelObjectIndex());
+	// -------------------------------
+	if (QSPGetVarValues(QSP_STATIC_STR(QSP_FMT("BACKIMAGE")), 0, &numVal, &strVal) && strVal.Str && strVal.Str != strVal.End)
+        m_frame->GetDesc()->LoadBackImage(m_gamePath + QSPTools::qspStrToQt(strVal));
+	else
+        m_frame->GetDesc()->LoadBackImage(QString(""));
+	// -------------------------------
+	if (isRedraw)
+	{
+		m_frame->ApplyParams(); //Moved to inside avoid constant update?
+		m_frame->EnableControls(false, true);
+        //m_frame->Update();
+        QCoreApplication::processEvents();
+		if (m_frame->IsQuit()) return;
+		m_frame->EnableControls(true, true);
+	}
+    m_frame->GetGameMenu()->setEnabled(isCanSave);
+}
+
+void QSPCallBacks::SetInputStrText(QSPString text)
+{
+	if (m_frame->IsQuit()) return;
+    m_frame->GetInput()->SetText(QSPTools::qspStrToQt(text));
+}
+
+QSP_BOOL QSPCallBacks::IsPlay(QSPString file)
+{
+    //FMOD_BOOL playing = FALSE;
+//	QSPSounds::iterator elem = m_sounds.find(wxFileName(wxString(file.Str, file.End), wxPATH_DOS).GetFullPath().Upper());
+//	if (elem != m_sounds.end())
+//		FMOD_Channel_IsPlaying(((QSPSound *)(&elem->second))->Channel, &playing);
+//	return (playing == TRUE);
+    return QSP_FALSE;
+}
+
+void QSPCallBacks::CloseFile(QSPString file)
+{
+	if (file.Str)
+	{
+        QSPSounds::iterator elem = m_sounds.find(QFileInfo(QSPTools::qspStrToQt(file)).absoluteFilePath().toUpper());
+		if (elem != m_sounds.end())
+		{
+            elem.value().Free();
+            //((QSPSound *)(&elem->second))->Free();
+			m_sounds.erase(elem);
+		}
+	}
+	else
+	{
+		for (QSPSounds::iterator i = m_sounds.begin(); i != m_sounds.end(); ++i)
+            i.value().Free();
+            //((QSPSound *)(&i->second))->Free();
+		m_sounds.clear();
+	}
+}
+
+void QSPCallBacks::PlayFile(QSPString file, int volume)
+{
+//	FMOD_SOUND *newSound;
+//	FMOD_CHANNEL *newChannel;
+//	QSPSound snd;
+//	if (SetVolume(file, volume)) return;
+//	CloseFile(file);
+//	wxString strFile(wxFileName(wxString(file.Str, file.End), wxPATH_DOS).GetFullPath());
+//	#if defined(__WXMSW__) || defined(__WXOSX__)
+//	if (!FMOD_System_CreateSound(m_sys, wxConvFile.cWX2MB(strFile.c_str()), FMOD_SOFTWARE | FMOD_CREATESTREAM, 0, &newSound))
+//	#else
+//	FMOD_CREATESOUNDEXINFO exInfo;
+//	memset(&exInfo, 0, sizeof(FMOD_CREATESOUNDEXINFO));
+//	exInfo.cbsize = sizeof(FMOD_CREATESOUNDEXINFO);
+//	wxString dlsPath(QSPTools::GetAppPath() + QSP_MIDIDLS);
+//	wxCharBuffer dlsCharPath(wxConvFile.cWX2MB(dlsPath.c_str()));
+//	exInfo.dlsname = dlsCharPath;
+//	if (!FMOD_System_CreateSound(m_sys, wxConvFile.cWX2MB(strFile.c_str()), FMOD_SOFTWARE | FMOD_CREATESTREAM, &exInfo, &newSound))
+//	#endif
+//	{
+//		UpdateSounds();
+//		FMOD_System_PlaySound(m_sys, FMOD_CHANNEL_FREE, newSound, FALSE, &newChannel);
+//		snd.Channel = newChannel;
+//		snd.Sound = newSound;
+//		snd.Volume = volume;
+//		m_sounds.insert(QSPSounds::value_type(strFile.Upper(), snd));
+//		SetVolume(file, volume);
+//	}
+}
+
+void QSPCallBacks::ShowPane(int type, QSP_BOOL isShow)
+{
+	if (m_frame->IsQuit()) return;
+	switch (type)
+	{
+	case QSP_WIN_ACTS:
+        m_frame->GetActionsDock()->setVisible(isShow != QSP_FALSE);
+		break;
+	case QSP_WIN_OBJS:
+        m_frame->GetObjectsDock()->setVisible(isShow != QSP_FALSE);
+		break;
+	case QSP_WIN_VARS:
+        m_frame->GetVarsDock()->setVisible(isShow != QSP_FALSE);
+		break;
+	case QSP_WIN_INPUT:
+        m_frame->GetInputDock()->setVisible(isShow != QSP_FALSE);
+		break;
+	}
+}
+
+void QSPCallBacks::Sleep(int msecs)
+{
+	if (m_frame->IsQuit()) return;
+    bool isSave = m_frame->GetGameMenu()->isEnabled();
+	bool isBreak = false;
+	m_frame->EnableControls(false, true);
+	int i, count = msecs / 50;
+	for (i = 0; i < count; ++i)
+	{
+        QThread::msleep(50);
+        //m_frame->Update();
+        QCoreApplication::processEvents();
+		if (m_frame->IsQuit() ||
+			m_frame->IsKeyPressedWhileDisabled())
+		{
+			isBreak = true;
+			break;
+		}
+	}
+	if (!isBreak)
+	{
+        QThread::msleep(msecs % 50);
+        //m_frame->Update();
+        QCoreApplication::processEvents();
+	}
+	m_frame->EnableControls(true, true);
+    m_frame->GetGameMenu()->setEnabled(isSave);
+}
+
+int QSPCallBacks::GetMSCount()
+{
+    static QElapsedTimer stopWatch;
+    if(stopWatch.isValid() == false)
+        stopWatch.start();
+    int ret = stopWatch.restart();
+	return ret;
+}
+
+void QSPCallBacks::Msg(QSPString str)
+{
+	if (m_frame->IsQuit()) return;
+	RefreshInt(QSP_FALSE);
+    QspMsgDlg dialog(m_frame->GetDesc()->GetBackgroundColour(),
+		m_frame->GetDesc()->GetForegroundColour(),
+		m_frame->GetDesc()->GetTextFont(),
+        "Info", //caption
+        QSPTools::qspStrToQt(str),
+		m_isHtml,
+        m_gamePath,
+        m_frame
+	);
+	m_frame->EnableControls(false);
+    dialog.exec();
+	m_frame->EnableControls(true);
+}
+
+int QSPCallBacks::ShowMenu(QSPListItem *items, int count)
+{
+	if (m_frame->IsQuit()) return -1;
+	m_frame->EnableControls(false);
+	m_frame->DeleteMenu();
+	for (int i = 0; i < count; ++i)
+        m_frame->AddMenuItem(QSPTools::qspStrToQt(items[i].Name), QSPTools::qspStrToQt(items[i].Image));
+	int index = m_frame->ShowMenu();
+	m_frame->EnableControls(true);
+	return index;
+}
+
+void QSPCallBacks::Input(QSPString text, QSP_CHAR *buffer, int maxLen)
+{
+	if (m_frame->IsQuit()) return;
+	RefreshInt(QSP_FALSE);
+//	QSPInputDlg dialog(m_frame,
+//		wxID_ANY,
+//		m_frame->GetDesc()->GetBackgroundColour(),
+//		m_frame->GetDesc()->GetForegroundColour(),
+//		m_frame->GetDesc()->GetTextFont(),
+//		_("Input data"),
+//		wxString(text.Str, text.End),
+//		m_isHtml,
+//		m_gamePath
+//	);
+//	m_frame->EnableControls(false);
+//	dialog.ShowModal();
+//	m_frame->EnableControls(true);
+//	#ifdef _UNICODE
+//		wcsncpy(buffer, dialog.GetText().c_str(), maxLen);
+//	#else
+//		strncpy(buffer, dialog.GetText().c_str(), maxLen);
+//	#endif
+    QString inputText = QInputDialog::getMultiLineText(m_frame, "Input data", QSPTools::qspStrToQt(text));
+    c16sncpy(buffer, (QSP_CHAR *)(inputText.utf16()), maxLen);
+}
+
+void QSPCallBacks::ShowImage(QSPString file)
+{
+	if (m_frame->IsQuit()) return;
+    m_frame->GetImgView()->OpenFile(QSPTools::qspStrToQt(file)); //NOTE: will not display image if file is not found
+    //m_frame->GetImgView()->setVisible(true);
+}
+
+void QSPCallBacks::OpenGame(QSPString file, QSP_BOOL isNewGame)
+{
+	if (m_frame->IsQuit()) return;
+	if (QSPLoadGameWorld(file, isNewGame) && isNewGame)
+	{
+        QFileInfo fileName(QSPTools::qspStrToQt(file));
+        m_gamePath = fileName.canonicalPath();
+        if(!m_gamePath.endsWith('/')) m_gamePath+="/";
+		m_frame->UpdateGamePath(m_gamePath);
+	}
+}
+
+void QSPCallBacks::OpenGameStatus(QSPString file)
+{
+	if (m_frame->IsQuit()) return;
+	if (file.Str)
+	{
+        QFileInfo fileInfo(QSPTools::qspStrToQt(file));
+        if ( fileInfo.exists() && fileInfo.isFile() ) QSPOpenSavedGame(file, QSP_FALSE);
+	}
+	else
+	{
+        m_frame->EnableControls(false);
+        QString path = QFileDialog::getOpenFileName(m_frame, "Select saved game file", m_frame->GetLastPath(), "Saved game files (*.sav)");
+        m_frame->EnableControls(true);
+        if (!path.isEmpty())
+		{
+            m_frame->SetLastPath(QFileInfo(path).canonicalPath());
+            QSPOpenSavedGame(qspStringFromQString(path), QSP_FALSE);
+		}
+	}
+}
+
+void QSPCallBacks::SaveGameStatus(QSPString file)
+{
+	if (m_frame->IsQuit()) return;
+	if (file.Str)
+		QSPSaveGame(file, QSP_FALSE);
+	else
+	{
+        m_frame->EnableControls(false);
+        QString path = QFileDialog::getSaveFileName(m_frame, "Select file to save", m_frame->GetLastPath(), "Saved game files (*.sav)");
+		m_frame->EnableControls(true);
+        if (!path.isEmpty())
+		{
+            m_frame->SetLastPath(QFileInfo(path).canonicalPath());
+            QSPSaveGame(qspStringFromQString(path), QSP_FALSE);
+		}
+	}
+}
+
+bool QSPCallBacks::SetVolume(QSPString file, int volume)
+{
+//	if (!IsPlay(file)) return false;
+//	QSPSounds::iterator elem = m_sounds.find(wxFileName(wxString(file.Str, file.End), wxPATH_DOS).GetFullPath().Upper());
+//	QSPSound *snd = (QSPSound *)&elem->second;
+//	snd->Volume = volume;
+//	FMOD_Channel_SetVolume(snd->Channel, (float)(m_volumeCoeff * volume) / 100);
+	return true;
+}
+
+void QSPCallBacks::SetOverallVolume(float coeff)
+{
+//	QSPSound *snd;
+//	FMOD_BOOL playing = FALSE;
+//	if (coeff < 0.0)
+//		coeff = 0.0;
+//	else if (coeff > 1.0)
+//		coeff = 1.0;
+//	m_volumeCoeff = coeff;
+//	for (QSPSounds::iterator i = m_sounds.begin(); i != m_sounds.end(); ++i)
+//	{
+//		snd = (QSPSound *)&i->second;
+//		FMOD_Channel_IsPlaying(snd->Channel, &playing);
+//		if (playing)
+//			FMOD_Channel_SetVolume(snd->Channel, (float)(m_volumeCoeff * snd->Volume) / 100);
+//	}
+}
+
+void QSPCallBacks::UpdateSounds()
+{
+//	QSPSound *snd;
+//	FMOD_BOOL playing = FALSE;
+//	QSPSounds::iterator i = m_sounds.begin();
+//	while (i != m_sounds.end())
+//	{
+//		snd = (QSPSound *)&i->second;
+//		FMOD_Channel_IsPlaying(snd->Channel, &playing);
+//		if (playing)
+//			++i;
+//		else
+//		{
+//			snd->Free();
+//			m_sounds.erase(i++);
+//		}
+//	}
+}

+ 92 - 0
callbacks_gui.h

@@ -0,0 +1,92 @@
+#ifndef CALLBACKS_GUI_H
+#define CALLBACKS_GUI_H
+
+#include <QString>
+#include <qsp_default.h>
+#include <QMap>
+#include "mainwindow.h"
+
+typedef struct
+{
+    //FMOD_CHANNEL *Channel;
+    //FMOD_SOUND *Sound;
+    int Volume;
+
+    void Free() const
+    {
+        //FMOD_Sound_Release(Sound);
+    }
+} QSPSound;
+
+typedef QMap<QString, QSPSound> QSPSounds;
+
+static QSPString qspStringFromPair(const QSP_CHAR *start, const QSP_CHAR *end)
+{
+    QSPString string;
+    string.Str = (QSP_CHAR *)start;
+    string.End = (QSP_CHAR *)end;
+    return string;
+}
+
+static QSPString qspStringFromLen(const QSP_CHAR *s, int len)
+{
+    QSPString string;
+    string.Str = (QSP_CHAR *)s;
+    string.End = (QSP_CHAR *)s + len;
+    return string;
+}
+
+static QSPString qspStringFromQString(const QString &s)
+{
+    QSPString string;
+    string.Str = (QSP_CHAR *)s.utf16();
+    string.End = (QSP_CHAR *)s.utf16() + s.length();
+    return string;
+}
+
+/* Helpers */
+#define QSP_STATIC_LEN(x) (sizeof(x) / sizeof(QSP_CHAR) - 1)
+#define QSP_STATIC_STR(x) (qspStringFromLen(x, QSP_STATIC_LEN(x)))
+
+class QSPCallBacks
+{
+public:
+    // Methods
+    static void Init(MainWindow *frame);
+    static void DeInit();
+    static void SetOverallVolume(float coeff);
+
+    // CallBacks
+    static void RefreshInt(QSP_BOOL isRedraw);
+    static void SetTimer(int msecs);
+    static void SetInputStrText(QSPString text);
+    static QSP_BOOL IsPlay(QSPString file);
+    static void CloseFile(QSPString file);
+    static void PlayFile(QSPString file, int volume);
+    static void ShowPane(int type, QSP_BOOL isShow);
+    static void Sleep(int msecs);
+    static int GetMSCount();
+    static void Msg(QSPString str);
+    static int ShowMenu(QSPListItem *items, int count);
+    static void Input(QSPString text, QSP_CHAR *buffer, int maxLen);
+    static void ShowImage(QSPString file);
+    static void OpenGame(QSPString file, QSP_BOOL isNewGame);
+    static void OpenGameStatus(QSPString file);
+    static void SaveGameStatus(QSPString file);
+private:
+    // Internal methods
+    static bool SetVolume(QSPString file, int volume);
+    static void UpdateSounds();
+
+    // Fields
+    static QString m_gamePath;
+    static MainWindow *m_frame;
+    static bool m_isHtml;
+    //static FMOD_SYSTEM *m_sys;
+    static QSPSounds m_sounds;
+    static float m_volumeCoeff;
+
+    static const int MAX_LIST_ITEMS = 1000;
+};
+
+#endif

+ 157 - 0
comtools.cpp

@@ -0,0 +1,157 @@
+#include "comtools.h"
+
+#include <QCoreApplication>
+
+QString QSPTools::GetHexColor(const QColor color)
+{
+    return QString("%1%2%3").arg(color.red(), 2, 16, QLatin1Char( '0' )).arg(color.green(), 2, 16, QLatin1Char( '0' )).arg(color.blue(), 2, 16, QLatin1Char( '0' ));
+}
+
+QString QSPTools::HtmlizeWhitespaces(const QString& str)
+{
+    QString::const_iterator i;
+    QChar ch, quote;
+    QString out;
+    size_t j, linepos = 0;
+    bool isLastSpace = true;
+    for (i = str.begin(); i != str.end(); ++i)
+    {
+        ch = *i;
+        if(ch == QChar('<'))
+        {
+            quote = 0;
+            while (i != str.end())
+            {
+                ch = *i;
+                if (quote.unicode())
+                {
+                    if (ch == QChar('\\'))
+                    {
+                        if (++i == str.end()) break;
+                        ch = *i;
+                        if (ch == quote)
+                        {
+                            if(ch == QChar('"'))
+                            {
+                                out.append( QString("&quot;") );
+                            }
+                            else if(ch == QChar('\''))
+                            {
+                                out.append( QString("&apos;") );
+                            }
+                            ++i;
+                            continue;
+                        }
+                        out.append( QChar('\\') );
+                    }
+                    if(ch == QChar('&'))
+                    {
+                        out.append( QString("&amp;") );
+                    }
+                    else if(ch == QChar('<'))
+                    {
+                        out.append( QString("&lt;") );
+                    }
+                    else if (ch == QChar('>'))
+                    {
+                        out.append( QString("&gt;") );
+                    }
+                    else
+                    {
+                        if (ch == quote)
+                            quote = 0;
+                        out.append(ch);
+                    }
+                }
+                else
+                {
+                    out.append(ch);
+                    if (ch == QChar('>'))
+                        break;
+                    else if (ch == QChar('"') || ch == QChar('\''))
+                        quote = ch;
+                }
+                ++i;
+            }
+            if (i == str.end()) return out;
+            isLastSpace = true;
+        }
+        else if(ch == QChar(' '))
+        {
+            if (isLastSpace)
+                out.append( QString("&nbsp;") );
+            else
+                out.append( QChar(' ') );
+            isLastSpace = !isLastSpace;
+            ++linepos;
+        }
+        else if(ch == QChar('\r'))
+        {
+
+        }
+        else if(ch == QChar('\n'))
+        {
+            out.append( QString("<br />") );
+            isLastSpace = true;
+            linepos = 0;
+        }
+        else if(ch == QChar('\t'))
+        {
+            for (j = 4 - linepos % 4; j > 0; --j)
+            {
+                if (isLastSpace)
+                    out.append( QString("&nbsp;") );
+                else
+                    out.append( QChar(' ') );
+                isLastSpace = !isLastSpace;
+            }
+            linepos += 4 - linepos % 4;
+        }
+        else
+        {
+            out.append(ch);
+            isLastSpace = false;
+            ++linepos;
+        }
+    }
+    return out;
+}
+
+QString QSPTools::ProceedAsPlain(const QString& str)
+{
+    QString::const_iterator i;
+    QChar ch;
+    QString out;
+    for (i = str.begin(); i != str.end(); ++i)
+    {
+        ch = *i;
+        if( ch == QChar('<'))
+        {
+            out.append( QString("&lt;") );
+        }
+        else if(ch == QChar('>'))
+        {
+            out.append( QString("&gt;") );
+        }
+        else if(ch == QChar('&'))
+        {
+            out.append( QString("&amp;") );
+        }
+        else
+        {
+            out.append(ch);
+        }
+    }
+    return out;
+}
+
+QString QSPTools::GetAppPath()
+{
+    return QCoreApplication::applicationDirPath();
+}
+
+QString QSPTools::qspStrToQt(const QSPString &str)
+{
+    //return QString::fromWCharArray(str.Str, (int)(str.End - str.Str));
+    return QString::fromUtf16(str.Str, (int)(str.End - str.Str));
+}

+ 18 - 0
comtools.h

@@ -0,0 +1,18 @@
+#ifndef TOOLS_H
+#define TOOLS_H
+
+#include <QString>
+#include <QColor>
+#include <qsp_default.h>
+
+class QSPTools
+{
+public:
+    static QString GetHexColor(const QColor color);
+    static QString HtmlizeWhitespaces(const QString& str);
+    static QString ProceedAsPlain(const QString& str);
+    static QString GetAppPath();
+    static QString qspStrToQt(const QSPString& str);
+};
+
+#endif

+ 1050 - 0
frame.cpp

@@ -0,0 +1,1050 @@
+#include "frame.h"
+#include "callbacks_gui.h"
+
+//BEGIN_EVENT_TABLE(QSPFrame, wxFrame)
+//	EVT_INIT(QSPFrame::OnInit)
+//	EVT_CLOSE(QSPFrame::OnClose)
+//	EVT_IDLE(QSPFrame::OnIdle)
+//	EVT_TIMER(ID_TIMER, QSPFrame::OnTimer)
+//	EVT_MENU(wxID_EXIT, QSPFrame::OnQuit)
+//	EVT_MENU(ID_OPENGAME, QSPFrame::OnOpenGame)
+//	EVT_MENU(ID_NEWGAME, QSPFrame::OnNewGame)
+//	EVT_MENU(ID_OPENGAMESTAT, QSPFrame::OnOpenGameStat)
+//	EVT_MENU(ID_SAVEGAMESTAT, QSPFrame::OnSaveGameStat)
+//	EVT_MENU(ID_QUICKSAVE, QSPFrame::OnQuickSave)
+//	EVT_MENU(ID_SELECTFONT, QSPFrame::OnSelectFont)
+//	EVT_MENU(ID_USEFONTSIZE, QSPFrame::OnUseFontSize)
+//	EVT_MENU(ID_SELECTFONTCOLOR, QSPFrame::OnSelectFontColor)
+//	EVT_MENU(ID_SELECTBACKCOLOR, QSPFrame::OnSelectBackColor)
+//	EVT_MENU(ID_SELECTLINKCOLOR, QSPFrame::OnSelectLinkColor)
+//	EVT_MENU(ID_SELECTLANG, QSPFrame::OnSelectLang)
+//	EVT_MENU(ID_TOGGLEWINMODE, QSPFrame::OnToggleWinMode)
+//	EVT_MENU(ID_TOGGLEOBJS, QSPFrame::OnToggleObjs)
+//	EVT_MENU(ID_TOGGLEACTS, QSPFrame::OnToggleActs)
+//	EVT_MENU(ID_TOGGLEDESC, QSPFrame::OnToggleDesc)
+//	EVT_MENU(ID_TOGGLEINPUT, QSPFrame::OnToggleInput)
+//	EVT_MENU(ID_TOGGLECAPTIONS, QSPFrame::OnToggleCaptions)
+//	EVT_MENU(ID_TOGGLEHOTKEYS, QSPFrame::OnToggleHotkeys)
+//	EVT_MENU(ID_VOLUME0, QSPFrame::OnVolume)
+//	EVT_MENU(ID_VOLUME20, QSPFrame::OnVolume)
+//	EVT_MENU(ID_VOLUME40, QSPFrame::OnVolume)
+//	EVT_MENU(ID_VOLUME60, QSPFrame::OnVolume)
+//	EVT_MENU(ID_VOLUME80, QSPFrame::OnVolume)
+//	EVT_MENU(ID_VOLUME100, QSPFrame::OnVolume)
+//	EVT_MENU(wxID_ABOUT, QSPFrame::OnAbout)
+//	EVT_HTML_LINK_CLICKED(ID_MAINDESC, QSPFrame::OnLinkClicked)
+//	EVT_HTML_LINK_CLICKED(ID_VARSDESC, QSPFrame::OnLinkClicked)
+//	EVT_LISTBOX(ID_OBJECTS, QSPFrame::OnObjectChange)
+//	EVT_LISTBOX(ID_ACTIONS, QSPFrame::OnActionChange)
+//	EVT_LISTBOX_DCLICK(ID_ACTIONS, QSPFrame::OnActionDblClick)
+//	EVT_TEXT(ID_INPUT, QSPFrame::OnInputTextChange)
+//	EVT_ENTER(ID_INPUT, QSPFrame::OnInputTextEnter)
+//	EVT_KEY_UP(QSPFrame::OnKey)
+//	EVT_MOUSEWHEEL(QSPFrame::OnWheel)
+//	EVT_LEFT_DOWN(QSPFrame::OnMouseClick)
+//	EVT_AUI_PANE_CLOSE(QSPFrame::OnPaneClose)
+//	EVT_DROP_FILES(QSPFrame::OnDropFiles)
+//END_EVENT_TABLE()
+
+//IMPLEMENT_CLASS(QSPFrame, wxFrame)
+
+QSPFrame::QSPFrame(const QString &configPath, QWidget *parent) :
+    QFrame( parent ),
+	m_configDefPath(configPath),
+    m_configPath(configPath)
+{
+	SetIcon(wxICON(logo));
+	DragAcceptFiles(true);
+	m_timer = new wxTimer(this, ID_TIMER);
+	m_menu = new wxMenu;
+	// Menu
+	wxMenuBar *menuBar = new wxMenuBar;
+	m_fileMenu = new wxMenu;
+	wxMenuItem *fileOpenItem = new wxMenuItem(m_fileMenu, ID_OPENGAME, wxT("-"));
+	fileOpenItem->SetBitmap(wxBitmap(open_xpm));
+	m_fileMenu->Append(fileOpenItem);
+	wxMenuItem *fileNewItem = new wxMenuItem(m_fileMenu, ID_NEWGAME, wxT("-"));
+	fileNewItem->SetBitmap(wxBitmap(new_xpm));
+	m_fileMenu->Append(fileNewItem);
+	m_fileMenu->AppendSeparator();
+	wxMenuItem *fileExitItem = new wxMenuItem(m_fileMenu, wxID_EXIT);
+	fileExitItem->SetBitmap(wxBitmap(exit_xpm));
+	m_fileMenu->Append(fileExitItem);
+	// ------------
+	m_gameMenu = new wxMenu;
+	wxMenuItem *gameOpenItem = new wxMenuItem(m_gameMenu, ID_OPENGAMESTAT, wxT("-"));
+	gameOpenItem->SetBitmap(wxBitmap(statusopen_xpm));
+	m_gameMenu->Append(gameOpenItem);
+	m_gameMenu->Append(ID_SAVEGAMESTAT, wxT("-"));
+	wxMenuItem *gameSaveItem = new wxMenuItem(m_gameMenu, ID_QUICKSAVE, wxT("-"));
+	gameSaveItem->SetBitmap(wxBitmap(statussave_xpm));
+	m_gameMenu->Append(gameSaveItem);
+	// ------------
+	wxMenu *wndsMenu = new wxMenu;
+	wndsMenu->Append(ID_TOGGLEOBJS, wxT("-"));
+	wndsMenu->Append(ID_TOGGLEACTS, wxT("-"));
+	wndsMenu->Append(ID_TOGGLEDESC, wxT("-"));
+	wndsMenu->Append(ID_TOGGLEINPUT, wxT("-"));
+	wndsMenu->AppendSeparator();
+	wndsMenu->Append(ID_TOGGLECAPTIONS, wxT("-"));
+	wndsMenu->Append(ID_TOGGLEHOTKEYS, wxT("-"));
+	// ------------
+	wxMenu *fontMenu = new wxMenu;
+	fontMenu->Append(ID_SELECTFONT, wxT("-"));
+	fontMenu->AppendCheckItem(ID_USEFONTSIZE, wxT("-"));
+	// ------------
+	wxMenu *colorsMenu = new wxMenu;
+	colorsMenu->Append(ID_SELECTFONTCOLOR, wxT("-"));
+	colorsMenu->Append(ID_SELECTBACKCOLOR, wxT("-"));
+	colorsMenu->Append(ID_SELECTLINKCOLOR, wxT("-"));
+	// ------------
+	wxMenu *volumeMenu = new wxMenu;
+	volumeMenu->AppendRadioItem(ID_VOLUME0, wxT("-"));
+	volumeMenu->AppendRadioItem(ID_VOLUME20, wxT("-"));
+	volumeMenu->AppendRadioItem(ID_VOLUME40, wxT("-"));
+	volumeMenu->AppendRadioItem(ID_VOLUME60, wxT("-"));
+	volumeMenu->AppendRadioItem(ID_VOLUME80, wxT("-"));
+	volumeMenu->AppendRadioItem(ID_VOLUME100, wxT("-"));
+	// ------------
+	m_settingsMenu = new wxMenu;
+	m_settingsMenu->Append(ID_SHOWHIDE, wxT("-"), wndsMenu);
+	m_settingsMenu->Append(ID_FONT, wxT("-"), fontMenu);
+	m_settingsMenu->Append(ID_COLORS, wxT("-"), colorsMenu);
+	m_settingsMenu->Append(ID_VOLUME, wxT("-"), volumeMenu);
+	m_settingsMenu->AppendSeparator();
+	wxMenuItem *settingsWinModeItem = new wxMenuItem(m_settingsMenu, ID_TOGGLEWINMODE, wxT("-"));
+	settingsWinModeItem->SetBitmap(wxBitmap(windowmode_xpm));
+	m_settingsMenu->Append(settingsWinModeItem);
+	m_settingsMenu->AppendSeparator();
+	m_settingsMenu->Append(ID_SELECTLANG, wxT("-"));
+	// ------------
+	wxMenu *helpMenu = new wxMenu;
+	wxMenuItem *helpAboutItem = new wxMenuItem(helpMenu, wxID_ABOUT, wxT("-"));
+	helpAboutItem->SetBitmap(wxBitmap(about_xpm));
+	helpMenu->Append(helpAboutItem);
+	// ------------
+	menuBar->Append(m_fileMenu, wxT("-"));
+	menuBar->Append(m_gameMenu, wxT("-"));
+	menuBar->Append(m_settingsMenu, wxT("-"));
+	menuBar->Append(helpMenu, wxT("-"));
+	SetMenuBar(menuBar);
+	// --------------------------------------
+	m_manager = new wxAuiManager(this);
+	m_manager->SetDockSizeConstraint(0.5, 0.5);
+	m_imgView = new QSPImgCanvas(this, ID_VIEWPIC);
+	m_manager->AddPane(m_imgView, wxAuiPaneInfo().Name(wxT("imgview")).MinSize(50, 50).BestSize(150, 150).Top().MaximizeButton().Hide());
+	m_desc = new QSPTextBox(this, ID_MAINDESC);
+	m_manager->AddPane(m_desc, wxAuiPaneInfo().Name(wxT("desc")).CenterPane());
+	m_objects = new QSPListBox(this, ID_OBJECTS);
+	m_manager->AddPane(m_objects, wxAuiPaneInfo().Name(wxT("objs")).MinSize(50, 50).BestSize(100, 100).Right().MaximizeButton());
+	m_actions = new QSPListBox(this, ID_ACTIONS, LB_EXTENDED);
+	m_manager->AddPane(m_actions, wxAuiPaneInfo().Name(wxT("acts")).MinSize(50, 50).BestSize(100, 100).Bottom().MaximizeButton());
+	m_vars = new QSPTextBox(this, ID_VARSDESC);
+	m_manager->AddPane(m_vars, wxAuiPaneInfo().Name(wxT("vars")).MinSize(50, 50).BestSize(100, 100).Bottom().MaximizeButton());
+	m_input = new QSPInputBox(this, ID_INPUT);
+	m_manager->AddPane(m_input, wxAuiPaneInfo().Name(wxT("input")).MinSize(50, 20).BestSize(100, 20).Bottom().Layer(1));
+	// --------------------------------------
+	SetMinClientSize(wxSize(450, 300));
+	SetOverallVolume(100);
+	m_savedGamePath.Clear();
+	m_isQuit = false;
+	m_keyPressedWhileDisabled = false;
+	m_isGameOpened = false;
+}
+
+QSPFrame::~QSPFrame()
+{
+	m_manager->UnInit();
+	delete m_manager;
+	delete m_menu;
+	delete m_timer;
+}
+
+void QSPFrame::SaveSettings()
+{
+	int x, y, w, h;
+	bool isMaximized;
+	if (IsFullScreen()) ShowFullScreen(false);
+	if (IsIconized()) Iconize(false);
+	if (isMaximized = IsMaximized()) Maximize(false);
+	wxFileConfig cfg(wxEmptyString, wxEmptyString, m_configPath);
+	cfg.Write(wxT("Colors/BackColor"), m_backColor.Blue() << 16 | m_backColor.Green() << 8 | m_backColor.Red());
+	cfg.Write(wxT("Colors/FontColor"), m_fontColor.Blue() << 16 | m_fontColor.Green() << 8 | m_fontColor.Red());
+	cfg.Write(wxT("Colors/LinkColor"), m_linkColor.Blue() << 16 | m_linkColor.Green() << 8 | m_linkColor.Red());
+	cfg.Write(wxT("Font/FontSize"), m_fontSize);
+	cfg.Write(wxT("Font/FontName"), m_fontName);
+	cfg.Write(wxT("Font/UseFontSize"), m_isUseFontSize);
+	cfg.Write(wxT("General/Volume"), m_volume);
+	cfg.Write(wxT("General/ShowHotkeys"), m_isShowHotkeys);
+	cfg.Write(wxT("General/Panels"), m_manager->SavePerspective());
+	m_transhelper->Save(cfg, wxT("General/Language"));
+	GetPosition(&x, &y);
+	GetClientSize(&w, &h);
+	cfg.Write(wxT("Pos/Left"), x);
+	cfg.Write(wxT("Pos/Top"), y);
+	cfg.Write(wxT("Pos/Width"), w);
+	cfg.Write(wxT("Pos/Height"), h);
+	cfg.Write(wxT("Pos/Maximize"), isMaximized);
+}
+
+void QSPFrame::LoadSettings()
+{
+	bool isMaximize;
+	int x, y, w, h, temp;
+	Hide();
+	wxFileConfig cfg(wxEmptyString, wxEmptyString, m_configPath);
+	cfg.Read(wxT("Colors/BackColor"), &temp, 0xE0E0E0);
+	m_backColor = wxColour(temp);
+	cfg.Read(wxT("Colors/FontColor"), &temp, 0x000000);
+	m_fontColor = wxColour(temp);
+	cfg.Read(wxT("Colors/LinkColor"), &temp, 0xFF0000);
+	m_linkColor = wxColour(temp);
+	temp = wxNORMAL_FONT->GetPointSize();
+	if (temp < 12) temp = 12;
+	cfg.Read(wxT("Font/FontSize"), &m_fontSize, temp);
+	cfg.Read(wxT("Font/FontName"), &m_fontName, wxNORMAL_FONT->GetFaceName());
+	cfg.Read(wxT("Font/UseFontSize"), &m_isUseFontSize, false);
+	cfg.Read(wxT("General/ShowHotkeys"), &m_isShowHotkeys, false);
+	cfg.Read(wxT("General/Volume"), &m_volume, 100);
+	cfg.Read(wxT("Pos/Left"), &x, 10);
+	cfg.Read(wxT("Pos/Top"), &y, 10);
+	cfg.Read(wxT("Pos/Width"), &w, 850);
+	cfg.Read(wxT("Pos/Height"), &h, 650);
+	cfg.Read(wxT("Pos/Maximize"), &isMaximize, false);
+	wxString panels(wxT("layout2|") \
+		wxT("name=imgview;state=1080035327;dir=1;layer=0;row=0;pos=0;prop=100000;bestw=832;besth=150;minw=50;minh=50;maxw=-1;maxh=-1;floatx=175;floaty=148;floatw=518;floath=372|") \
+		wxT("name=desc;state=768;dir=5;layer=0;row=0;pos=0;prop=100000;bestw=613;besth=341;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1|") \
+		wxT("name=objs;state=6293500;dir=2;layer=0;row=0;pos=0;prop=100000;bestw=213;besth=324;minw=50;minh=50;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1|") \
+		wxT("name=acts;state=6293500;dir=3;layer=0;row=0;pos=0;prop=117349;bestw=475;besth=185;minw=50;minh=50;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1|") \
+		wxT("name=vars;state=6293500;dir=3;layer=0;row=0;pos=1;prop=82651;bestw=351;besth=185;minw=50;minh=50;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1|") \
+		wxT("name=input;state=2099196;dir=3;layer=1;row=0;pos=0;prop=100000;bestw=832;besth=22;minw=50;minh=20;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1|") \
+		wxT("dock_size(5,0,0)=22|dock_size(2,0,0)=215|dock_size(3,0,0)=204|dock_size(3,1,0)=41|"));
+	cfg.Read(wxT("General/Panels"), &panels);
+	m_transhelper->Load(cfg, wxT("General/Language"));
+	// -------------------------------------------------
+	SetOverallVolume(m_volume);
+	ApplyBackColor(m_backColor);
+	ApplyFontColor(m_fontColor);
+	ApplyLinkColor(m_linkColor);
+	ApplyFontSize(m_fontSize);
+	if (!ApplyFontName(m_fontName))
+	{
+		m_fontName = wxNORMAL_FONT->GetFaceName();
+		ApplyFontName(m_fontName);
+	}
+	RefreshUI();
+	m_settingsMenu->Check(ID_USEFONTSIZE, m_isUseFontSize);
+	m_manager->LoadPerspective(panels);
+	m_manager->RestoreMaximizedPane();
+	// Check for correct position
+	wxSize winSize(ClientToWindowSize(wxSize(w, h)));
+	w = winSize.GetWidth();
+	h = winSize.GetHeight();
+	wxRect dispRect(wxGetClientDisplayRect());
+	if (w > dispRect.GetWidth()) w = dispRect.GetWidth();
+	if (h > dispRect.GetHeight()) h = dispRect.GetHeight();
+	if (x < dispRect.GetLeft()) x = dispRect.GetLeft();
+	if (y < dispRect.GetTop()) y = dispRect.GetTop();
+	if (x + w - 1 > dispRect.GetRight()) x = dispRect.GetRight() - w + 1;
+	if (y + h - 1 > dispRect.GetBottom()) y = dispRect.GetBottom() - h + 1;
+	// --------------------------
+	SetSize(x, y, w, h);
+	ShowPane(ID_VIEWPIC, false);
+	ShowPane(ID_ACTIONS, true);
+	ShowPane(ID_OBJECTS, true);
+	ShowPane(ID_VARSDESC, true);
+	ShowPane(ID_INPUT, true);
+	ReCreateGUI();
+	if (isMaximize) Maximize();
+	Show();
+	m_manager->Update();
+}
+
+void QSPFrame::EnableControls(bool status, bool isExtended)
+{
+	if (isExtended) m_fileMenu->Enable(ID_OPENGAME, status);
+	m_fileMenu->Enable(ID_NEWGAME, status);
+	m_gameMenu->Enable(ID_OPENGAMESTAT, status);
+	m_gameMenu->Enable(ID_SAVEGAMESTAT, status);
+	m_gameMenu->Enable(ID_QUICKSAVE, status);
+	m_settingsMenu->Enable(ID_TOGGLEOBJS, status);
+	m_settingsMenu->Enable(ID_TOGGLEACTS, status);
+	m_settingsMenu->Enable(ID_TOGGLEDESC, status);
+	m_settingsMenu->Enable(ID_TOGGLEINPUT, status);
+	m_objects->Enable(status);
+	m_actions->Enable(status);
+	m_input->SetEditable(status);
+	m_isProcessEvents = status;
+	m_keyPressedWhileDisabled = false;
+}
+
+void QSPFrame::ShowPane(wxWindowID id, bool isShow)
+{
+	int i;
+	wxAuiPaneInfo &pane = m_manager->GetPane(FindWindow(id));
+	wxAuiPaneInfoArray& allPanes = m_manager->GetAllPanes();
+	wxON_BLOCK_EXIT_THIS0(QSPFrame::Thaw);
+	Freeze();
+	for (i = (int)allPanes.GetCount() - 1; i >= 0; --i)
+		if (allPanes.Item(i).IsMaximized())
+		{
+			if (&allPanes.Item(i) == &pane)
+			{
+				if (!isShow)
+				{
+					m_manager->RestorePane(pane);
+					pane.Hide();
+					m_manager->Update();
+				}
+			}
+			else if (pane.HasFlag(wxAuiPaneInfo::savedHiddenState) == isShow)
+				pane.SetFlag(wxAuiPaneInfo::savedHiddenState, !isShow);
+			return;
+		}
+	if (pane.IsShown() != isShow)
+	{
+		pane.Show(isShow);
+		m_manager->Update();
+	}
+}
+
+void QSPFrame::ApplyParams()
+{
+	int numVal;
+	QSPString strVal;
+	wxColour setBackColor, setFontColor, setLinkColor;
+	wxString setFontName;
+	int setFontSize;
+	bool isRefresh = false;
+	// --------------
+	setBackColor = ((QSPGetVarValues(QSP_STATIC_STR(QSP_FMT("BCOLOR")), 0, &numVal, &strVal) && numVal) ? wxColour(numVal) : m_backColor);
+	if (setBackColor != m_desc->GetBackgroundColour())
+	{
+		if (ApplyBackColor(setBackColor)) isRefresh = true;
+	}
+	// --------------
+	setFontColor = ((QSPGetVarValues(QSP_STATIC_STR(QSP_FMT("FCOLOR")), 0, &numVal, &strVal) && numVal) ? wxColour(numVal) : m_fontColor);
+	if (setFontColor != m_desc->GetForegroundColour())
+	{
+		if (ApplyFontColor(setFontColor)) isRefresh = true;
+	}
+	// --------------
+	setLinkColor = ((QSPGetVarValues(QSP_STATIC_STR(QSP_FMT("LCOLOR")), 0, &numVal, &strVal) && numVal) ? wxColour(numVal) : m_linkColor);
+	if (setLinkColor != m_desc->GetLinkColor())
+	{
+		if (ApplyLinkColor(setLinkColor)) isRefresh = true;
+	}
+	// --------------
+	if (m_isUseFontSize)
+		setFontSize = m_fontSize;
+	else
+		setFontSize = ((QSPGetVarValues(QSP_STATIC_STR(QSP_FMT("FSIZE")), 0, &numVal, &strVal) && numVal) ? numVal : m_fontSize);
+	if (setFontSize != m_desc->GetTextFont().GetPointSize())
+	{
+		if (ApplyFontSize(setFontSize)) isRefresh = true;
+	}
+	// --------------
+	setFontName = ((QSPGetVarValues(QSP_STATIC_STR(QSP_FMT("FNAME")), 0, &numVal, &strVal) &&
+		strVal.Str && strVal.Str != strVal.End) ? wxString(strVal.Str, strVal.End) : m_fontName);
+	if (!setFontName.IsSameAs(m_desc->GetTextFont().GetFaceName(), false))
+	{
+		if (ApplyFontName(setFontName))
+			isRefresh = true;
+		else if (!m_fontName.IsSameAs(m_desc->GetTextFont().GetFaceName(), false))
+		{
+			if (ApplyFontName(m_fontName)) isRefresh = true;
+		}
+	}
+	// --------------
+	if (isRefresh) RefreshUI();
+}
+
+void QSPFrame::DeleteMenu()
+{
+	delete m_menu;
+	m_menu = new wxMenu;
+	m_menuItemId = ID_BEGOFDYNMENU;
+}
+
+void QSPFrame::AddMenuItem(const wxString &name, const wxString &imgPath)
+{
+	Connect(m_menuItemId, wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(QSPFrame::OnMenu));
+	if (name == wxT("-"))
+		m_menu->AppendSeparator();
+	else
+	{
+		wxMenuItem *item = new wxMenuItem(m_menu, m_menuItemId, name);
+		wxString itemPath(wxFileName(imgPath, wxPATH_DOS).GetFullPath());
+		if (wxFileExists(itemPath))
+		{
+			wxBitmap itemBmp(itemPath, wxBITMAP_TYPE_ANY);
+			if (itemBmp.Ok()) item->SetBitmap(itemBmp);
+		}
+		m_menu->Append(item);
+	}
+	++m_menuItemId;
+}
+
+int QSPFrame::ShowMenu()
+{
+	m_menuIndex = -1;
+	PopupMenu(m_menu);
+	return m_menuIndex;
+}
+
+void QSPFrame::UpdateGamePath(const wxString &path)
+{
+	m_desc->SetGamePath(path);
+	m_vars->SetGamePath(path);
+	m_actions->SetGamePath(path);
+	m_objects->SetGamePath(path);
+}
+
+void QSPFrame::ShowError()
+{
+	bool oldIsProcessEvents;
+	wxString wxMessage;
+	QSPString loc;
+	int code, actIndex, line;
+	if (m_isQuit) return;
+	QSPGetLastErrorData(&code, &loc, &actIndex, &line);
+	QSPString desc = QSPGetErrorDesc(code);
+	if (loc.Str)
+		wxMessage = wxString::Format(
+			_("Location: %s\nArea: %s\nLine: %ld\nCode: %ld\nDesc: %s"),
+			wxString(loc.Str, loc.End).wx_str(),
+			(actIndex < 0 ? _("on visit").wx_str() : _("on action").wx_str()),
+			(size_t)line,
+			(size_t)code,
+			wxGetTranslation(wxString(desc.Str, desc.End)).wx_str()
+		);
+	else
+		wxMessage = wxString::Format(
+			_("Code: %ld\nDesc: %s"),
+			(size_t)code,
+			wxGetTranslation(wxString(desc.Str, desc.End)).wx_str()
+		);
+	wxMessageDialog dialog(this, wxMessage, _("Error"), wxOK | wxICON_ERROR);
+	oldIsProcessEvents = m_isProcessEvents;
+	m_isProcessEvents = false;
+	dialog.ShowModal();
+	m_isProcessEvents = oldIsProcessEvents;
+	if (m_isGameOpened) QSPCallBacks::RefreshInt(QSP_FALSE);
+}
+
+void QSPFrame::UpdateTitle()
+{
+	wxString title(QSP_LOGO);
+	#ifdef _DEBUG
+		title = wxString::Format(wxT("%s (DEBUG)"), title.wx_str());
+	#endif
+	if (m_configPath != m_configDefPath)
+		title = wxString::Format(wxT("%s [+]"), title.wx_str());
+	SetTitle(title);
+}
+
+void QSPFrame::ReCreateGUI()
+{
+	wxMenuBar *menuBar = GetMenuBar();
+	UpdateTitle();
+	// ------------
+	menuBar->SetMenuLabel(0, _("&Quest"));
+	menuBar->SetMenuLabel(1, _("&Game"));
+	menuBar->SetMenuLabel(2, _("&Settings"));
+	menuBar->SetMenuLabel(3, _("&Help"));
+	// ------------
+	menuBar->SetLabel(ID_OPENGAME, _("&Open game...\tAlt-O"));
+	menuBar->SetLabel(ID_NEWGAME, _("&Restart game\tAlt-N"));
+	menuBar->SetLabel(wxID_EXIT, _("&Quit\tAlt-X"));
+	menuBar->SetLabel(ID_OPENGAMESTAT, _("&Open saved game...\tCtrl-O"));
+	menuBar->SetLabel(ID_SAVEGAMESTAT, _("&Save game..."));
+	menuBar->SetLabel(ID_QUICKSAVE, _("&Quicksave\tCtrl-S"));
+	menuBar->SetLabel(ID_TOGGLEOBJS, _("&Objects\tCtrl-1"));
+	menuBar->SetLabel(ID_TOGGLEACTS, _("&Actions\tCtrl-2"));
+	menuBar->SetLabel(ID_TOGGLEDESC, _("A&dditional desc\tCtrl-3"));
+	menuBar->SetLabel(ID_TOGGLEINPUT, _("&Input area\tCtrl-4"));
+	menuBar->SetLabel(ID_TOGGLECAPTIONS, _("&Captions\tCtrl-5"));
+	menuBar->SetLabel(ID_TOGGLEHOTKEYS, _("&Hotkeys for actions\tCtrl-6"));
+	menuBar->SetLabel(ID_SHOWHIDE, _("&Show / Hide"));
+	menuBar->SetLabel(ID_FONT, _("&Font"));
+	menuBar->SetLabel(ID_SELECTFONT, _("Select &font...\tAlt-F"));
+	menuBar->SetLabel(ID_USEFONTSIZE, _("&Always use selected font's size"));
+	menuBar->SetLabel(ID_COLORS, _("&Colors"));
+	menuBar->SetLabel(ID_SELECTFONTCOLOR, _("Select font's &color...\tAlt-C"));
+	menuBar->SetLabel(ID_SELECTBACKCOLOR, _("Select &background's color...\tAlt-B"));
+	menuBar->SetLabel(ID_SELECTLINKCOLOR, _("Select l&inks' color...\tAlt-I"));
+	menuBar->SetLabel(ID_VOLUME, _("Sound &volume"));
+	menuBar->SetLabel(ID_VOLUME0, _("No sound\tAlt-1"));
+	menuBar->SetLabel(ID_VOLUME20, _("20%\tAlt-2"));
+	menuBar->SetLabel(ID_VOLUME40, _("40%\tAlt-3"));
+	menuBar->SetLabel(ID_VOLUME60, _("60%\tAlt-4"));
+	menuBar->SetLabel(ID_VOLUME80, _("80%\tAlt-5"));
+	menuBar->SetLabel(ID_VOLUME100, _("Initial volume\tAlt-6"));
+	menuBar->SetLabel(ID_TOGGLEWINMODE, _("Window / Fullscreen &mode\tAlt-Enter"));
+	menuBar->SetLabel(ID_SELECTLANG, _("Select &language...\tAlt-L"));
+	menuBar->SetLabel(wxID_ABOUT, _("&About...\tCtrl-H"));
+	// --------------------------------------
+	m_manager->GetPane(wxT("imgview")).Caption(_("Preview"));
+	m_manager->GetPane(wxT("objs")).Caption(_("Objects"));
+	m_manager->GetPane(wxT("acts")).Caption(_("Actions"));
+	m_manager->GetPane(wxT("vars")).Caption(_("Additional desc"));
+	m_manager->GetPane(wxT("input")).Caption(_("Input area"));
+	// --------------------------------------
+	m_manager->Update();
+}
+
+void QSPFrame::RefreshUI()
+{
+	m_desc->RefreshUI();
+	m_objects->RefreshUI();
+	m_actions->RefreshUI();
+	m_vars->RefreshUI();
+	m_input->Refresh();
+	m_imgView->RefreshUI();
+}
+
+void QSPFrame::ApplyFont(const wxFont& font)
+{
+	m_desc->SetTextFont(font);
+	m_objects->SetTextFont(font);
+	m_actions->SetTextFont(font);
+	m_vars->SetTextFont(font);
+	m_input->SetFont(font);
+}
+
+bool QSPFrame::ApplyFontSize(int size)
+{
+	wxFont font(m_desc->GetTextFont());
+	font.SetPointSize(size);
+	ApplyFont(font);
+	return true;
+}
+
+bool QSPFrame::ApplyFontName(const wxString& name)
+{
+	if (wxFontEnumerator::IsValidFacename(name))
+	{
+		wxFont font(m_desc->GetTextFont());
+		font.SetFaceName(name);
+		ApplyFont(font);
+		return true;
+	}
+	return false;
+}
+
+bool QSPFrame::ApplyFontColor(const wxColour& color)
+{
+	m_desc->SetForegroundColour(color);
+	m_objects->SetForegroundColour(color);
+	m_actions->SetForegroundColour(color);
+	m_vars->SetForegroundColour(color);
+	m_input->SetForegroundColour(color);
+	return true;
+}
+
+bool QSPFrame::ApplyBackColor(const wxColour& color)
+{
+	m_desc->SetBackgroundColour(color);
+	m_objects->SetBackgroundColour(color);
+	m_actions->SetBackgroundColour(color);
+	m_vars->SetBackgroundColour(color);
+	m_input->SetBackgroundColour(color);
+	m_imgView->SetBackgroundColour(color);
+	return true;
+}
+
+bool QSPFrame::ApplyLinkColor(const wxColour& color)
+{
+	m_desc->SetLinkColor(color);
+	m_objects->SetLinkColor(color);
+	m_actions->SetLinkColor(color);
+	m_vars->SetLinkColor(color);
+	return true;
+}
+
+void QSPFrame::CallPaneFunc(wxWindowID id, QSP_BOOL isShow) const
+{
+	switch (id)
+	{
+	case ID_ACTIONS:
+		QSPShowWindow(QSP_WIN_ACTS, isShow);
+		break;
+	case ID_OBJECTS:
+		QSPShowWindow(QSP_WIN_OBJS, isShow);
+		break;
+	case ID_VARSDESC:
+		QSPShowWindow(QSP_WIN_VARS, isShow);
+		break;
+	case ID_INPUT:
+		QSPShowWindow(QSP_WIN_INPUT, isShow);
+		break;
+	}
+}
+
+void QSPFrame::SetOverallVolume(int percents)
+{
+	int id = wxNOT_FOUND;
+	switch (percents)
+	{
+	case 0: id = ID_VOLUME0; break;
+	case 20: id = ID_VOLUME20; break;
+	case 40: id = ID_VOLUME40; break;
+	case 60: id = ID_VOLUME60; break;
+	case 80: id = ID_VOLUME80; break;
+	case 100: id = ID_VOLUME100; break;
+	}
+	if (id >= 0) m_settingsMenu->Check(id, true);
+	QSPCallBacks::SetOverallVolume((float)percents / 100);
+	m_volume = percents;
+}
+
+void QSPFrame::TogglePane(wxWindowID id)
+{
+	bool isShow = !m_manager->GetPane(FindWindow(id)).IsShown();
+	CallPaneFunc(id, (QSP_BOOL)isShow);
+	ShowPane(id, isShow);
+}
+
+void QSPFrame::OpenGameFile(const wxString& path)
+{
+	if (QSPLoadGameWorld(qspStringFromLen(path.c_str(), path.Length()), QSP_TRUE))
+	{
+		m_isGameOpened = true;
+		wxCommandEvent dummy;
+		wxFileName file(path);
+		wxString filePath(file.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR));
+		wxString configString(filePath + QSP_CONFIG);
+		wxString newPath(wxFileExists(configString) ? configString : m_configDefPath);
+		if (newPath != m_configPath)
+		{
+			SaveSettings();
+			m_configPath = newPath;
+			LoadSettings();
+		}
+		UpdateGamePath(filePath);
+		OnNewGame(dummy);
+		if (m_isQuit) return;
+		UpdateTitle();
+		EnableControls(true);
+		m_savedGamePath.Clear();
+	}
+	else
+		ShowError();
+}
+
+void QSPFrame::OnInit(wxInitEvent& event)
+{
+	OpenGameFile(event.GetInitString());
+}
+
+void QSPFrame::OnClose(wxCloseEvent& event)
+{
+	if (event.CanVeto()) event.Veto();
+	SaveSettings();
+	EnableControls(false, true);
+	Hide();
+	m_isQuit = true;
+}
+
+void QSPFrame::OnIdle(wxIdleEvent& event)
+{
+	if (m_isQuit) Destroy();
+}
+
+void QSPFrame::OnTimer(wxTimerEvent& event)
+{
+	if (m_isProcessEvents && !QSPExecCounter(QSP_TRUE))
+		ShowError();
+}
+
+void QSPFrame::OnMenu(wxCommandEvent& event)
+{
+	m_menuIndex = event.GetId() - ID_BEGOFDYNMENU;
+}
+
+void QSPFrame::OnQuit(wxCommandEvent& event)
+{
+	Close();
+}
+
+void QSPFrame::OnOpenGame(wxCommandEvent& event)
+{
+	wxFileDialog dialog(this, _("Select game file"), wxEmptyString, wxEmptyString, _("QSP games (*.qsp;*.gam)|*.qsp;*.gam"), wxFD_OPEN);
+	if (dialog.ShowModal() == wxID_OK)
+		OpenGameFile(dialog.GetPath());
+}
+
+void QSPFrame::OnNewGame(wxCommandEvent& event)
+{
+	if (!QSPRestartGame(QSP_TRUE))
+		ShowError();
+}
+
+void QSPFrame::OnOpenGameStat(wxCommandEvent& event)
+{
+	wxFileDialog dialog(this, _("Select saved game file"), wxEmptyString, wxEmptyString, _("Saved game files (*.sav)|*.sav"), wxFD_OPEN);
+	if (dialog.ShowModal() == wxID_OK)
+	{
+		wxString path(dialog.GetPath());
+		if (!QSPOpenSavedGame(qspStringFromLen(path.c_str(), path.Length()), QSP_TRUE))
+			ShowError();
+	}
+}
+
+void QSPFrame::OnSaveGameStat(wxCommandEvent& event)
+{
+	wxFileDialog dialog(this, _("Select file to save"), wxEmptyString, wxEmptyString, _("Saved game files (*.sav)|*.sav"), wxFD_SAVE);
+	if (dialog.ShowModal() == wxID_OK)
+	{
+		wxString path(dialog.GetPath());
+		if (QSPSaveGame(qspStringFromLen(path.c_str(), path.Length()), QSP_TRUE))
+			m_savedGamePath = path;
+		else
+			ShowError();
+	}
+}
+
+void QSPFrame::OnQuickSave(wxCommandEvent& event)
+{
+	if (m_savedGamePath.IsEmpty())
+		OnSaveGameStat(event);
+	else
+	{
+		if (!QSPSaveGame(qspStringFromLen(m_savedGamePath.c_str(), m_savedGamePath.Length()), QSP_TRUE))
+			ShowError();
+	}
+}
+
+void QSPFrame::OnSelectFont(wxCommandEvent& event)
+{
+	wxFontData data;
+	wxFont font(m_desc->GetTextFont());
+	font.SetPointSize(m_fontSize);
+	font.SetFaceName(m_fontName);
+	data.EnableEffects(false);
+	data.SetInitialFont(font);
+	wxFontDialog dialog(this, data);
+	dialog.SetTitle(_("Select font"));
+	if (dialog.ShowModal() == wxID_OK)
+	{
+		font = dialog.GetFontData().GetChosenFont();
+		m_fontSize = font.GetPointSize();
+		m_fontName = font.GetFaceName();
+		if (m_isProcessEvents)
+			ApplyParams();
+		else
+		{
+			ApplyFontSize(m_fontSize);
+			ApplyFontName(m_fontName);
+			RefreshUI();
+		}
+	}
+}
+
+void QSPFrame::OnUseFontSize(wxCommandEvent& event)
+{
+	m_isUseFontSize = !m_isUseFontSize;
+	if (m_isProcessEvents)
+		ApplyParams();
+	else
+	{
+		ApplyFontSize(m_fontSize);
+		RefreshUI();
+	}
+}
+
+void QSPFrame::OnSelectFontColor(wxCommandEvent& event)
+{
+	wxColourData data;
+	data.SetColour(m_fontColor);
+	wxColourDialog dialog(this, &data);
+	dialog.SetTitle(_("Select font's color"));
+	if (dialog.ShowModal() == wxID_OK)
+	{
+		m_fontColor = dialog.GetColourData().GetColour();
+		if (m_isProcessEvents)
+			ApplyParams();
+		else
+		{
+			ApplyFontColor(m_fontColor);
+			RefreshUI();
+		}
+	}
+}
+
+void QSPFrame::OnSelectBackColor(wxCommandEvent& event)
+{
+	wxColourData data;
+	data.SetColour(m_backColor);
+	wxColourDialog dialog(this, &data);
+	dialog.SetTitle(_("Select background's color"));
+	if (dialog.ShowModal() == wxID_OK)
+	{
+		m_backColor = dialog.GetColourData().GetColour();
+		if (m_isProcessEvents)
+			ApplyParams();
+		else
+		{
+			ApplyBackColor(m_backColor);
+			RefreshUI();
+		}
+	}
+}
+
+void QSPFrame::OnSelectLinkColor(wxCommandEvent& event)
+{
+	wxColourData data;
+	data.SetColour(m_linkColor);
+	wxColourDialog dialog(this, &data);
+	dialog.SetTitle(_("Select links' color"));
+	if (dialog.ShowModal() == wxID_OK)
+	{
+		m_linkColor = dialog.GetColourData().GetColour();
+		if (m_isProcessEvents)
+			ApplyParams();
+		else
+		{
+			ApplyLinkColor(m_linkColor);
+			RefreshUI();
+		}
+	}
+}
+
+void QSPFrame::OnSelectLang(wxCommandEvent& event)
+{
+	if (m_transhelper->AskUserForLanguage()) ReCreateGUI();
+}
+
+void QSPFrame::OnVolume(wxCommandEvent& event)
+{
+	int volume = 100;
+	switch (event.GetId())
+	{
+	case ID_VOLUME0: volume = 0; break;
+	case ID_VOLUME20: volume = 20; break;
+	case ID_VOLUME40: volume = 40; break;
+	case ID_VOLUME60: volume = 60; break;
+	case ID_VOLUME80: volume = 80; break;
+	}
+	SetOverallVolume(volume);
+}
+
+void QSPFrame::OnToggleWinMode(wxCommandEvent& event)
+{
+	ShowFullScreen(!IsFullScreen());
+}
+
+void QSPFrame::OnToggleObjs(wxCommandEvent& event)
+{
+	TogglePane(ID_OBJECTS);
+}
+
+void QSPFrame::OnToggleActs(wxCommandEvent& event)
+{
+	TogglePane(ID_ACTIONS);
+}
+
+void QSPFrame::OnToggleDesc(wxCommandEvent& event)
+{
+	TogglePane(ID_VARSDESC);
+}
+
+void QSPFrame::OnToggleInput(wxCommandEvent& event)
+{
+	TogglePane(ID_INPUT);
+}
+
+void QSPFrame::OnToggleCaptions(wxCommandEvent& event)
+{
+	int i;
+	bool isShow = !m_manager->GetPane(m_objects).HasCaption();
+	wxAuiPaneInfoArray& allPanes = m_manager->GetAllPanes();
+	for (i = (int)allPanes.GetCount() - 1; i >= 0; --i)
+		allPanes.Item(i).CaptionVisible(isShow);
+	m_manager->GetPane(m_desc).CaptionVisible(false);
+	m_manager->Update();
+}
+
+void QSPFrame::OnToggleHotkeys(wxCommandEvent& event)
+{
+	m_isShowHotkeys = !m_isShowHotkeys;
+	if (m_isProcessEvents) QSPCallBacks::RefreshInt(QSP_FALSE);
+}
+
+void QSPFrame::OnAbout(wxCommandEvent& event)
+{
+	wxAboutDialogInfo info;
+	info.SetIcon(wxIcon(logo_big_xpm));
+	info.SetName(QSP_LOGO);
+	info.SetCopyright(wxT("Byte Soft, 2001-2010"));
+	QSPString version = QSPGetVersion();
+	QSPString libCompiledDate = QSPGetCompiledDateTime();
+	wxString guiCompiledDate(wxT(__DATE__) wxT(", ") wxT(__TIME__));
+	info.SetDescription(wxString::Format(
+		_("Version: %s\nEngine Compiled: %s\nGUI Compiled: %s"),
+		wxString(version.Str, version.End).wx_str(),
+		wxString(libCompiledDate.Str, libCompiledDate.End).wx_str(),
+		guiCompiledDate.wx_str()
+	));
+	info.SetWebSite(wxT("http://qsp.su"));
+	// ----
+	info.AddDeveloper(wxT("Byte [[email protected]]"));
+	info.AddDocWriter(wxT("Korwin [[email protected]]"));
+	info.AddArtist(wxT("3dEyes [[email protected]]"));
+	info.AddArtist(wxT("AI [[email protected]]"));
+	info.AddArtist(wxT("Ajenta [[email protected]]"));
+	info.AddArtist(wxT("Alex [[email protected]]"));
+	info.AddArtist(wxT("BalleR [[email protected]]"));
+	info.AddArtist(wxT("BaxZzZz [[email protected]]"));
+	info.AddArtist(wxT("Belial [[email protected]]"));
+	info.AddArtist(wxT("DzafT [[email protected]]"));
+	info.AddArtist(wxT("Fireton [[email protected]]"));
+	info.AddArtist(wxT("Gilving [[email protected]]"));
+	info.AddArtist(wxT("Goraph [[email protected]]"));
+	info.AddArtist(wxT("HIman [[email protected]]"));
+	info.AddArtist(wxT("Lostas [[email protected]]"));
+	info.AddArtist(wxT("Mioirel [[email protected]]"));
+	info.AddArtist(wxT("Morgan [[email protected]]"));
+	info.AddArtist(wxT("Mortem [[email protected]]"));
+	info.AddArtist(wxT("Nex [[email protected]]"));
+	info.AddArtist(wxT("Ntropy [[email protected]]"));
+	info.AddArtist(wxT("Ondoo [[email protected]]"));
+	info.AddArtist(wxT("RB [[email protected]]"));
+	info.AddArtist(wxT("rrock.ru [[email protected]]"));
+	info.AddArtist(wxT("WladySpb [[email protected]]"));
+	// ----
+	wxAboutBox(info, this);
+}
+
+void QSPFrame::OnLinkClicked(wxHtmlLinkEvent& event)
+{
+	wxString href;
+	wxHtmlLinkInfo info(event.GetLinkInfo());
+	if (info.GetEvent()->LeftUp())
+	{
+		href = info.GetHref();
+		if (href.StartsWith(wxT("#")))
+		{
+			if (event.GetId() == m_desc->GetId())
+				m_desc->LoadPage(href);
+			else
+				m_vars->LoadPage(href);
+		}
+		else if (href.Upper().StartsWith(wxT("EXEC:")))
+		{
+			wxString string = href.Mid(5);
+			if (m_isProcessEvents && !QSPExecString(qspStringFromLen(string.c_str(), string.Length()), QSP_TRUE))
+				ShowError();
+		}
+		else
+			wxLaunchDefaultBrowser(href);
+	}
+	else
+		event.Skip();
+}
+
+void QSPFrame::OnObjectChange(wxCommandEvent& event)
+{
+	m_objects->Update();
+	wxThread::Sleep(20);
+	if (!QSPSetSelObjectIndex(event.GetInt(), QSP_TRUE))
+		ShowError();
+}
+
+void QSPFrame::OnActionChange(wxCommandEvent& event)
+{
+	if (!QSPSetSelActionIndex(event.GetInt(), QSP_TRUE))
+		ShowError();
+}
+
+void QSPFrame::OnActionDblClick(wxCommandEvent& event)
+{
+	if (!QSPExecuteSelActionCode(QSP_TRUE))
+		ShowError();
+}
+
+void QSPFrame::OnInputTextChange(wxCommandEvent& event)
+{
+	wxString text(event.GetString());
+	m_input->SetText(text, false);
+	QSPSetInputStrText(qspStringFromLen(text.c_str(), text.Length()));
+}
+
+void QSPFrame::OnInputTextEnter(wxCommandEvent& event)
+{
+	if (!QSPExecUserInput(QSP_TRUE))
+		ShowError();
+}
+
+void QSPFrame::OnKey(wxKeyEvent& event)
+{
+	event.Skip();
+	// Exit fullscreen mode
+	if (IsFullScreen() && event.GetKeyCode() == WXK_ESCAPE)
+	{
+		ShowFullScreen(false);
+		return;
+	}
+	// Process key pressed event
+	if (event.GetKeyCode() == WXK_SPACE)
+		m_keyPressedWhileDisabled = true;
+	// Process action shortcut
+	if (m_isProcessEvents && !event.HasModifiers() && wxWindow::FindFocus() != m_input)
+	{
+		int ind = -1;
+		int actsCount = QSPGetActions(NULL, 0);
+		switch (event.GetKeyCode())
+		{
+		case '1': case WXK_NUMPAD1: case WXK_NUMPAD_END: ind = 0; break;
+		case '2': case WXK_NUMPAD2: case WXK_NUMPAD_DOWN: ind = 1; break;
+		case '3': case WXK_NUMPAD3: case WXK_NUMPAD_PAGEDOWN: ind = 2; break;
+		case '4': case WXK_NUMPAD4: case WXK_NUMPAD_LEFT: ind = 3; break;
+		case '5': case WXK_NUMPAD5: case WXK_CLEAR: ind = 4; break;
+		case '6': case WXK_NUMPAD6: case WXK_NUMPAD_RIGHT: ind = 5; break;
+		case '7': case WXK_NUMPAD7: case WXK_NUMPAD_HOME: ind = 6; break;
+		case '8': case WXK_NUMPAD8: case WXK_NUMPAD_UP: ind = 7; break;
+		case '9': case WXK_NUMPAD9: case WXK_NUMPAD_PAGEUP: ind = 8; break;
+		case WXK_SPACE:
+			if (actsCount == 1) ind = 0;
+			break;
+		}
+		if (ind >= 0 && ind < actsCount)
+		{
+			wxCommandEvent e;
+			if (QSPSetSelActionIndex(ind, QSP_TRUE))
+				OnActionDblClick(e);
+			else
+				ShowError();
+		}
+	}
+}
+
+void QSPFrame::OnWheel(wxMouseEvent& event)
+{
+	wxWindow *win = wxFindWindowAtPoint(wxGetMousePosition());
+	if (win) win->ScrollLines(-event.GetWheelRotation() / event.GetWheelDelta() * event.GetLinesPerAction());
+}
+
+void QSPFrame::OnMouseClick(wxMouseEvent& event)
+{
+	event.Skip();
+	m_keyPressedWhileDisabled = true;
+}
+
+void QSPFrame::OnPaneClose(wxAuiManagerEvent& event)
+{
+	if (m_isProcessEvents)
+		CallPaneFunc(event.GetPane()->window->GetId(), QSP_FALSE);
+	else
+		event.Veto();
+}
+
+void QSPFrame::OnDropFiles(wxDropFilesEvent& event)
+{
+	if (event.GetNumberOfFiles() && (!m_isGameOpened || m_isProcessEvents))
+	{
+		wxFileName path(*event.GetFiles());
+		path.MakeAbsolute();
+		OpenGameFile(path.GetFullPath());
+	}
+}

+ 185 - 0
frame.h

@@ -0,0 +1,185 @@
+#ifndef MAIN_H
+#define MAIN_H
+
+#include <qsp_default.h>
+
+#include <QFrame>
+#include <QString>
+#include <QColor>
+
+//#include "inputbox.h"
+//#include "textbox.h"
+//#include "listbox.h"
+//#include "imgcanvas.h"
+//#include "initevent.h"
+
+#define QSP_LOGO "Quest Soft Player 5"
+#define QSP_APPNAME "qspgui"
+#define QSP_CONFIG "qspgui.cfg"
+#define QSP_SOUNDPLUGINS "sound"
+#define QSP_MIDIDLS "sound/midi.dls"
+
+enum
+{
+    ID_BEGOFDYNMENU = 1,
+    ID_ENDOFDYNMENU = 150,
+    ID_OPENGAME,
+    ID_NEWGAME,
+    ID_OPENGAMESTAT,
+    ID_SAVEGAMESTAT,
+    ID_QUICKSAVE,
+    ID_VOLUME,
+    ID_VOLUME0,
+    ID_VOLUME20,
+    ID_VOLUME40,
+    ID_VOLUME60,
+    ID_VOLUME80,
+    ID_VOLUME100,
+    ID_FONT,
+    ID_SELECTFONT,
+    ID_USEFONTSIZE,
+    ID_COLORS,
+    ID_SELECTFONTCOLOR,
+    ID_SELECTBACKCOLOR,
+    ID_SELECTLINKCOLOR,
+    ID_SELECTLANG,
+    ID_TOGGLEWINMODE,
+    ID_TOGGLEOBJS,
+    ID_TOGGLEACTS,
+    ID_TOGGLEDESC,
+    ID_TOGGLEINPUT,
+    ID_TOGGLECAPTIONS,
+    ID_TOGGLEHOTKEYS,
+    ID_SHOWHIDE,
+    ID_MAINDESC,
+    ID_VARSDESC,
+    ID_OBJECTS,
+    ID_ACTIONS,
+    ID_VIEWPIC,
+    ID_INPUT,
+    ID_TIMER,
+
+    ID_DUMMY
+};
+
+class QSPFrame : public QFrame
+{
+    Q_OBJECT
+public:
+    // C-tors / D-tor
+    QSPFrame(const QString &configPath, QWidget *parent = 0);
+    virtual ~QSPFrame();
+
+    // Methods
+    void SaveSettings();
+    void LoadSettings();
+    void EnableControls(bool status, bool isExtended = false);
+    void ShowPane(wxWindowID id, bool isShow);
+    void ApplyParams();
+    void DeleteMenu();
+    void AddMenuItem(const wxString &name, const wxString &imgPath);
+    int ShowMenu();
+    void UpdateGamePath(const wxString &path);
+
+    // Accessors
+    wxTimer *GetTimer() const { return m_timer; }
+    QSPTextBox *GetDesc() const { return m_desc; }
+    QSPTextBox *GetVars() const { return m_vars; }
+    QSPInputBox *GetInput() const { return m_input; }
+    QSPListBox *GetActions() const { return m_actions; }
+    QSPListBox *GetObjects() const { return m_objects; }
+    QSPImgCanvas *GetImgView() const { return m_imgView; }
+    wxMenu *GetGameMenu() const { return m_gameMenu; }
+    bool IsShowHotkeys() const { return m_isShowHotkeys; }
+    bool IsQuit() const { return m_isQuit; }
+    bool IsKeyPressedWhileDisabled() const { return m_keyPressedWhileDisabled; }
+protected:
+    // Internal methods
+    void ShowError();
+    void UpdateTitle();
+    void ReCreateGUI();
+    void RefreshUI();
+    void ApplyFont(const wxFont& font);
+    bool ApplyFontSize(int size);
+    bool ApplyFontName(const wxString& name);
+    bool ApplyFontColor(const wxColour& color);
+    bool ApplyBackColor(const wxColour& color);
+    bool ApplyLinkColor(const wxColour& color);
+    void CallPaneFunc(wxWindowID id, QSP_BOOL isShow) const;
+    void TogglePane(wxWindowID id);
+    void SetOverallVolume(int percents);
+    void OpenGameFile(const wxString& path);
+
+    // Events
+    void OnInit(wxInitEvent& event);
+    void OnClose(wxCloseEvent& event);
+    void OnIdle(wxIdleEvent& event);
+    void OnTimer(wxTimerEvent& event);
+    void OnMenu(wxCommandEvent& event);
+    void OnQuit(wxCommandEvent& event);
+    void OnOpenGame(wxCommandEvent& event);
+    void OnNewGame(wxCommandEvent& event);
+    void OnOpenGameStat(wxCommandEvent& event);
+    void OnSaveGameStat(wxCommandEvent& event);
+    void OnQuickSave(wxCommandEvent& event);
+    void OnSelectFont(wxCommandEvent& event);
+    void OnUseFontSize(wxCommandEvent& event);
+    void OnSelectFontColor(wxCommandEvent& event);
+    void OnSelectBackColor(wxCommandEvent& event);
+    void OnSelectLinkColor(wxCommandEvent& event);
+    void OnSelectLang(wxCommandEvent& event);
+    void OnToggleWinMode(wxCommandEvent& event);
+    void OnToggleObjs(wxCommandEvent& event);
+    void OnToggleActs(wxCommandEvent& event);
+    void OnToggleDesc(wxCommandEvent& event);
+    void OnToggleInput(wxCommandEvent& event);
+    void OnToggleCaptions(wxCommandEvent& event);
+    void OnToggleHotkeys(wxCommandEvent& event);
+    void OnVolume(wxCommandEvent& event);
+    void OnAbout(wxCommandEvent& event);
+    void OnLinkClicked(wxHtmlLinkEvent& event);
+    void OnObjectChange(wxCommandEvent& event);
+    void OnActionChange(wxCommandEvent& event);
+    void OnActionDblClick(wxCommandEvent& event);
+    void OnInputTextChange(wxCommandEvent& event);
+    void OnInputTextEnter(wxCommandEvent& event);
+    void OnKey(wxKeyEvent& event);
+    void OnMouseClick(wxMouseEvent& event);
+    void OnWheel(wxMouseEvent& event);
+    void OnPaneClose(wxAuiManagerEvent& event);
+    void OnDropFiles(wxDropFilesEvent& event);
+
+    // Fields
+    bool m_isGameOpened;
+    QString m_savedGamePath;
+    QString m_configPath;
+    QString m_configDefPath;
+    QSPTranslationHelper *m_transhelper;
+    wxTimer *m_timer;
+    QSPTextBox *m_desc;
+    QSPTextBox *m_vars;
+    QSPInputBox *m_input;
+    QSPListBox *m_objects;
+    QSPListBox *m_actions;
+    QSPImgCanvas *m_imgView;
+    wxMenu *m_gameMenu;
+    int m_menuItemId;
+    wxMenu *m_menu;
+    wxMenu *m_fileMenu;
+    wxMenu *m_settingsMenu;
+    wxAuiManager *m_manager;
+    QColor m_backColor;
+    QColor m_linkColor;
+    QColor m_fontColor;
+    int m_fontSize;
+    QString m_fontName;
+    bool m_isUseFontSize;
+    bool m_isProcessEvents;
+    bool m_isQuit;
+    bool m_keyPressedWhileDisabled;
+    bool m_isShowHotkeys;
+    int m_volume;
+    int m_menuIndex;
+};
+
+#endif

+ 14 - 0
main.cpp

@@ -0,0 +1,14 @@
+#include "mainwindow.h"
+#include <QApplication>
+
+int main(int argc, char *argv[])
+{
+    QApplication a(argc, argv);
+    a.setApplicationName("Quest Soft Player");
+    a.setApplicationVersion("5.7.0");
+
+    MainWindow w;
+    w.show();
+
+    return a.exec();
+}

+ 680 - 0
mainwindow.cpp

@@ -0,0 +1,680 @@
+#include "mainwindow.h"
+
+#include <QSettings>
+#include <QApplication>
+#include <QFileInfo>
+#include <QFileDialog>
+#include <QMessageBox>
+#include <QThread>
+#include <QCursor>
+
+#include "callbacks_gui.h"
+#include "comtools.h"
+
+MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
+{
+    resize(600, 450);
+    setMinimumSize(240, 180);
+    setWindowTitle(QSP_LOGO);
+    setUnifiedTitleAndToolBarOnMac(true);
+    setDockNestingEnabled(true);
+
+    mainMenuBar = new QMenuBar(this);
+    setMenuBar(mainMenuBar);
+    mainToolBar = new QToolBar(this);
+    addToolBar(mainToolBar);
+    mainStatusBar = new QStatusBar(this);
+    setStatusBar(mainStatusBar);
+
+    // Set QMainWindow in the center of desktop
+    //QRect rect = geometry();
+    //rect.moveCenter(QApplication::desktop()->availableGeometry().center());
+    //setGeometry(rect);
+
+    mainStatusBar->setVisible(false);
+    mainToolBar->setVisible(false);
+
+    //SetIcon(QIcon(logo));
+    //DragAcceptFiles(true);
+    m_timer = new QTimer(this);
+    connect(m_timer, SIGNAL(timeout()), this, SLOT(OnTimer()));
+    //SetOverallVolume(100);
+    m_savedGamePath.clear();
+    m_isQuit = false;
+    m_keyPressedWhileDisabled = false;
+    m_isGameOpened = false;
+    showPlainText = false;
+
+    _mainDescTextBox = new QspTextBox(this);
+    connect(_mainDescTextBox, SIGNAL(anchorClicked(QUrl)), this, SLOT(OnLinkClicked(QUrl)));
+    setCentralWidget(_mainDescTextBox);
+
+    m_imgView = new QspImgCanvas(this);
+
+    CreateDockWindows();
+    //LoadSettings();
+    CreateMenuBar();
+    m_menu = new QMenu(this);
+    connect(m_menu, SIGNAL(triggered(QAction*)), this, SLOT(OnMenu(QAction*)) );
+
+    QSPInit();
+    QSPCallBacks::Init(this);
+}
+
+MainWindow::~MainWindow()
+{
+
+}
+
+void MainWindow::EnableControls(bool status, bool isExtended)
+{
+    if (isExtended) _fileMenu->setEnabled(status); //TODO: ???
+    _fileMenu->setEnabled(status); //TODO: ???
+    _gameMenu->setEnabled(status);
+    _settingsMenu->setEnabled(status);
+    _objectsListBox->setEnabled(status);
+    _actionsListBox->setEnabled(status);
+    _inputTextBox->setEnabled(status);
+    m_isProcessEvents = status;
+    m_keyPressedWhileDisabled = false;
+}
+
+void MainWindow::ApplyParams()
+{
+    int numVal;
+    QSPString strVal;
+    QColor setBackColor, setFontColor, setLinkColor;
+    QString setFontName;
+    int setFontSize;
+    bool isRefresh = false;
+    // --------------
+    setBackColor = ((QSPGetVarValues(QSP_STATIC_STR(QSP_FMT("BCOLOR")), 0, &numVal, &strVal) && numVal) ? QColor(numVal) : m_backColor); //TODO: check how wxColour(int) is handled
+    if (setBackColor != _mainDescTextBox->GetBackgroundColour())
+    {
+        if (ApplyBackColor(setBackColor)) isRefresh = true;
+    }
+    // --------------
+    setFontColor = ((QSPGetVarValues(QSP_STATIC_STR(QSP_FMT("FCOLOR")), 0, &numVal, &strVal) && numVal) ? QColor(numVal) : m_fontColor);
+    if (setFontColor != _mainDescTextBox->GetForegroundColour())
+    {
+        if (ApplyFontColor(setFontColor)) isRefresh = true;
+    }
+    // --------------
+    setLinkColor = ((QSPGetVarValues(QSP_STATIC_STR(QSP_FMT("LCOLOR")), 0, &numVal, &strVal) && numVal) ? QColor(numVal) : m_linkColor);
+    if (setLinkColor != _mainDescTextBox->GetLinkColor())
+    {
+        if (ApplyLinkColor(setLinkColor)) isRefresh = true;
+    }
+    // --------------
+//	if (m_isUseFontSize)
+//		setFontSize = m_fontSize;
+//	else
+//		setFontSize = ((QSPGetVarValues(QSP_STATIC_STR(QSP_FMT("FSIZE")), 0, &numVal, &strVal) && numVal) ? numVal : m_fontSize);
+//	if (setFontSize != _mainDescTextBox->GetTextFont().GetPointSize())
+//	{
+//		if (ApplyFontSize(setFontSize)) isRefresh = true;
+//	}
+//	// --------------
+//	setFontName = ((QSPGetVarValues(QSP_STATIC_STR(QSP_FMT("FNAME")), 0, &numVal, &strVal) &&
+//		strVal.Str && strVal.Str != strVal.End) ? wxString(strVal.Str, strVal.End) : m_fontName);
+//	if (!setFontName.IsSameAs(_mainDescTextBox->GetTextFont().GetFaceName(), false))
+//	{
+//		if (ApplyFontName(setFontName))
+//			isRefresh = true;
+//		else if (!m_fontName.IsSameAs(m_desc->GetTextFont().GetFaceName(), false))
+//		{
+//			if (ApplyFontName(m_fontName)) isRefresh = true;
+//		}
+//	}
+    // --------------
+    if (isRefresh) RefreshUI();
+}
+
+void MainWindow::DeleteMenu()
+{
+    m_menu->clear();
+    m_menuItemId = 0;
+}
+
+void MainWindow::AddMenuItem(const QString &name, const QString &imgPath)
+{
+    if (name == QString("-"))
+        m_menu->addSeparator();
+    else
+    {
+        bool pixmap_ok = false;
+        QPixmap itemPixmap;
+        QFileInfo file(imgPath);
+        QString itemPath(file.absoluteFilePath());
+        if (file.exists() && file.isFile())
+        {
+            if(itemPixmap.load(itemPath))
+                pixmap_ok = true;
+        }
+        QAction *action;
+        if(pixmap_ok)
+        {
+            action = m_menu->addAction(QIcon(itemPixmap), name);
+            //m_menu->addAction(QIcon(itemPixmap), name, this, SLOT(OnMenu(bool)));
+
+        }
+        else
+        {
+            action = m_menu->addAction(name);
+            //m_menu->addAction(name, this, SLOT(OnMenu(bool)));
+        }
+        action->setData(m_menuItemId);
+    }
+    m_menuItemId++;
+}
+
+int MainWindow::ShowMenu()
+{
+    m_menuIndex = -1;
+    m_menu->exec(QCursor::pos());
+    return m_menuIndex;
+}
+
+void MainWindow::UpdateGamePath(const QString &path)
+{
+    _mainDescTextBox->SetGamePath(path);
+    _descTextBox->SetGamePath(path);
+    _actionsListBox->SetGamePath(path);
+    _objectsListBox->SetGamePath(path);
+}
+
+void MainWindow::ShowError()
+{
+    bool oldIsProcessEvents;
+    QString errorMessage;
+    QSPString loc;
+    int code, actIndex, line;
+    if (m_isQuit) return;
+    QSPGetLastErrorData(&code, &loc, &actIndex, &line);
+    QSPString desc = QSPGetErrorDesc(code);
+    if (loc.Str)
+        errorMessage = QString("Location: %1\nArea: %2\nLine: %3\nCode: %4\nDesc: %5")
+                .arg(QSPTools::qspStrToQt(loc))
+                .arg(actIndex < 0 ? QString("on visit") : QString("on action"))
+                .arg(line)
+                .arg(code)
+                .arg(QSPTools::qspStrToQt(desc));
+    else
+        errorMessage = QString("Code: %1\nDesc: %2")
+                .arg(code)
+                .arg(QSPTools::qspStrToQt(desc));
+    QMessageBox dialog(QMessageBox::Critical, tr("Error"), errorMessage, QMessageBox::Ok, this);
+    oldIsProcessEvents = m_isProcessEvents;
+    m_isProcessEvents = false;
+    dialog.exec();
+    m_isProcessEvents = oldIsProcessEvents;
+    if (m_isGameOpened) QSPCallBacks::RefreshInt(QSP_FALSE);
+}
+
+void MainWindow::SetShowPlainText(bool isPlain)
+{
+    showPlainText = isPlain;
+    _mainDescTextBox->SetShowPlainText(showPlainText);
+    _descTextBox->SetShowPlainText(showPlainText);
+    _actionsListBox->SetShowPlainText(showPlainText);
+    _objectsListBox->SetShowPlainText(showPlainText);
+}
+
+void MainWindow::RefreshUI()
+{
+    _mainDescTextBox->RefreshUI();
+    _objectsListBox->RefreshUI();
+    _actionsListBox->RefreshUI();
+    _descTextBox->RefreshUI();
+    //m_input->Refresh();
+    m_imgView->RefreshUI();
+}
+
+bool MainWindow::ApplyFontColor(const QColor &color)
+{
+    return true;
+}
+
+bool MainWindow::ApplyBackColor(const QColor &color)
+{
+    return true;
+}
+
+bool MainWindow::ApplyLinkColor(const QColor &color)
+{
+    return true;
+}
+
+void MainWindow::LoadSettings()
+{
+    QSettings settings(QApplication::applicationDirPath() + "/config.cfg", QSettings::IniFormat);
+
+    restoreGeometry(settings.value("mainWindow/geometry").toByteArray());
+    restoreState(settings.value("mainWindow/windowState").toByteArray());
+
+    if (settings.value("mainWindow/isMaximized", false).toBool())
+        showMaximized();
+    if (settings.value("mainWindow/isFullScreen", false).toBool())
+        showFullScreen();
+
+    SetShowPlainText(settings.value("application/isShowPlainText", false).toBool());
+
+//    cfg.Read(wxT("Colors/BackColor"), &temp, 0xE0E0E0);
+//	m_backColor = wxColour(temp);
+//	cfg.Read(wxT("Colors/FontColor"), &temp, 0x000000);
+//	m_fontColor = wxColour(temp);
+//	cfg.Read(wxT("Colors/LinkColor"), &temp, 0xFF0000);
+//	m_linkColor = wxColour(temp);
+//	temp = wxNORMAL_FONT->GetPointSize();
+//	if (temp < 12) temp = 12;
+//	cfg.Read(wxT("Font/FontSize"), &m_fontSize, temp);
+//	cfg.Read(wxT("Font/FontName"), &m_fontName, wxNORMAL_FONT->GetFaceName());
+//	cfg.Read(wxT("Font/UseFontSize"), &m_isUseFontSize, false);
+//	cfg.Read(wxT("General/ShowHotkeys"), &m_isShowHotkeys, false);
+//	cfg.Read(wxT("General/Volume"), &m_volume, 100);
+//	cfg.Read(wxT("Pos/Left"), &x, 10);
+//	cfg.Read(wxT("Pos/Top"), &y, 10);
+//	cfg.Read(wxT("Pos/Width"), &w, 850);
+//	cfg.Read(wxT("Pos/Height"), &h, 650);
+//	cfg.Read(wxT("Pos/Maximize"), &isMaximize, false);
+//	wxString panels(wxT("layout2|") \
+//		wxT("name=imgview;state=1080035327;dir=1;layer=0;row=0;pos=0;prop=100000;bestw=832;besth=150;minw=50;minh=50;maxw=-1;maxh=-1;floatx=175;floaty=148;floatw=518;floath=372|") \
+//		wxT("name=desc;state=768;dir=5;layer=0;row=0;pos=0;prop=100000;bestw=613;besth=341;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1|") \
+//		wxT("name=objs;state=6293500;dir=2;layer=0;row=0;pos=0;prop=100000;bestw=213;besth=324;minw=50;minh=50;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1|") \
+//		wxT("name=acts;state=6293500;dir=3;layer=0;row=0;pos=0;prop=117349;bestw=475;besth=185;minw=50;minh=50;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1|") \
+//		wxT("name=vars;state=6293500;dir=3;layer=0;row=0;pos=1;prop=82651;bestw=351;besth=185;minw=50;minh=50;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1|") \
+//		wxT("name=input;state=2099196;dir=3;layer=1;row=0;pos=0;prop=100000;bestw=832;besth=22;minw=50;minh=20;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1|") \
+//		wxT("dock_size(5,0,0)=22|dock_size(2,0,0)=215|dock_size(3,0,0)=204|dock_size(3,1,0)=41|"));
+//	cfg.Read(wxT("General/Panels"), &panels);
+//	m_transhelper->Load(cfg, wxT("General/Language"));
+//	// -------------------------------------------------
+//	SetOverallVolume(m_volume);
+//	ApplyBackColor(m_backColor);
+//	ApplyFontColor(m_fontColor);
+//	ApplyLinkColor(m_linkColor);
+//	ApplyFontSize(m_fontSize);
+//	if (!ApplyFontName(m_fontName))
+//	{
+//		m_fontName = wxNORMAL_FONT->GetFaceName();
+//		ApplyFontName(m_fontName);
+//	}
+    RefreshUI();
+//	m_settingsMenu->Check(ID_USEFONTSIZE, m_isUseFontSize);
+//	m_manager->LoadPerspective(panels);
+//	m_manager->RestoreMaximizedPane();
+//	// Check for correct position
+//	wxSize winSize(ClientToWindowSize(wxSize(w, h)));
+//	w = winSize.GetWidth();
+//	h = winSize.GetHeight();
+//	wxRect dispRect(wxGetClientDisplayRect());
+//	if (w > dispRect.GetWidth()) w = dispRect.GetWidth();
+//	if (h > dispRect.GetHeight()) h = dispRect.GetHeight();
+//	if (x < dispRect.GetLeft()) x = dispRect.GetLeft();
+//	if (y < dispRect.GetTop()) y = dispRect.GetTop();
+//	if (x + w - 1 > dispRect.GetRight()) x = dispRect.GetRight() - w + 1;
+//	if (y + h - 1 > dispRect.GetBottom()) y = dispRect.GetBottom() - h + 1;
+//	// --------------------------
+//	SetSize(x, y, w, h);
+//	ShowPane(ID_VIEWPIC, false);
+//	ShowPane(ID_ACTIONS, true);
+//	ShowPane(ID_OBJECTS, true);
+//	ShowPane(ID_VARSDESC, true);
+//	ShowPane(ID_INPUT, true);
+//	ReCreateGUI();
+//	if (isMaximize) Maximize();
+//	Show();
+//	m_manager->Update();
+}
+
+void MainWindow::SaveSettings()
+{
+    QSettings settings(QApplication::applicationDirPath() + "/config.cfg", QSettings::IniFormat);
+
+    bool maximized = isMaximized();
+    if (maximized) showNormal();
+
+    bool fullscreen = isFullScreen();
+    if (fullscreen) showNormal();
+
+    settings.setValue("mainWindow/geometry", saveGeometry());
+    settings.setValue("mainWindow/windowState", saveState());
+    settings.setValue("mainWindow/isMaximized", maximized);
+    settings.setValue("mainWindow/isFullScreen", fullscreen);
+
+
+//    cfg.Write(wxT("Colors/BackColor"), m_backColor.Blue() << 16 | m_backColor.Green() << 8 | m_backColor.Red());
+//	cfg.Write(wxT("Colors/FontColor"), m_fontColor.Blue() << 16 | m_fontColor.Green() << 8 | m_fontColor.Red());
+//	cfg.Write(wxT("Colors/LinkColor"), m_linkColor.Blue() << 16 | m_linkColor.Green() << 8 | m_linkColor.Red());
+//	cfg.Write(wxT("Font/FontSize"), m_fontSize);
+//	cfg.Write(wxT("Font/FontName"), m_fontName);
+//	cfg.Write(wxT("Font/UseFontSize"), m_isUseFontSize);
+//	cfg.Write(wxT("General/Volume"), m_volume);
+//	cfg.Write(wxT("General/ShowHotkeys"), m_isShowHotkeys);
+//	cfg.Write(wxT("General/Panels"), m_manager->SavePerspective());
+//	m_transhelper->Save(cfg, wxT("General/Language"));
+//	GetPosition(&x, &y);
+//	GetClientSize(&w, &h);
+//	cfg.Write(wxT("Pos/Left"), x);
+//	cfg.Write(wxT("Pos/Top"), y);
+//	cfg.Write(wxT("Pos/Width"), w);
+//	cfg.Write(wxT("Pos/Height"), h);
+//	cfg.Write(wxT("Pos/Maximize"), isMaximized);
+}
+
+void MainWindow::CreateMenuBar()
+{
+    //------------------------------------------------------------------
+    // File menu
+    _fileMenu = menuBar()->addMenu(tr("&Quest"));
+
+    // Open item
+    _fileMenu->addAction(QIcon(":/menu/open"), tr("Open game..."),
+        this, SLOT(OnOpenGame()), QKeySequence(Qt::ALT + Qt::Key_O));
+
+    // New game item
+    _fileMenu->addAction(QIcon(":/menu/new"),tr("Restart game"),
+        this, SLOT(OnRestartGame()), QKeySequence(Qt::ALT + Qt::Key_N));
+
+    _fileMenu->addSeparator();
+
+    // Exit item
+    _fileMenu->addAction(QIcon(":/menu/exit"), tr("Exit"),
+        this, SLOT(close()), QKeySequence(Qt::ALT + Qt::Key_X));
+    //------------------------------------------------------------------
+    // Game menu
+    _gameMenu = menuBar()->addMenu(tr("&Game"));
+
+    // Open saved game item
+    _gameMenu->addAction(QIcon(":/menu/statusopen"), tr("Open saved game..."),
+        this, SLOT(OnOpenSavedGame()), QKeySequence(Qt::CTRL + Qt::Key_O));
+
+    // Save game item
+    _gameMenu->addAction(QIcon(":/menu/statussave"), tr("Save game..."),
+        this, SLOT(OnSaveGame()), QKeySequence(Qt::CTRL + Qt::Key_S));
+    //------------------------------------------------------------------
+    // Settings menu
+    _settingsMenu = menuBar()->addMenu(tr("&Settings"));
+
+    // Show / Hide submenu
+    _showHideMenu = _settingsMenu->addMenu(tr("Show / Hide"));
+
+    // Objects item
+    QAction* action = _objectsWidget->toggleViewAction();
+    action->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_1));
+    _showHideMenu->addAction(action);
+
+    // Actions item
+    action = _actionsWidget->toggleViewAction();
+    action->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_2));
+    _showHideMenu->addAction(action);
+
+    // Additional desc item
+    action = _descWidget->toggleViewAction();
+    action->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_3));
+    _showHideMenu->addAction(action);
+
+    // Input area item
+    action = _inputWidget->toggleViewAction();
+    action->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_4));
+    _showHideMenu->addAction(action);
+
+    _showHideMenu->addSeparator();
+
+    // Captions item
+    _showHideMenu->addAction(tr("Captions"), this, SLOT(OnToggleCaptions()),
+        QKeySequence(Qt::CTRL + Qt::Key_5));
+
+    // Hotkeys for actions item
+    _showHideMenu->addAction(tr("Hotkeys for actions"), this, SLOT(OnToggleHotkeys()),
+        QKeySequence(Qt::CTRL + Qt::Key_6));
+
+    // Sound volume item
+    _settingsMenu->addAction(tr("Sound volume..."),
+        this, SLOT(OnChangeSoundVolume()), QKeySequence(Qt::ALT + Qt::Key_V));
+
+    // Window / Fullscreen mode item
+    _settingsMenu->addAction(QIcon(":/menu/windowmode"), tr("Window / Fullscreen mode"),
+        this, SLOT(OnToggleWinMode()), QKeySequence(Qt::ALT + Qt::Key_Enter));
+
+    _settingsMenu->addSeparator();
+
+    // Display HTML code as plain text
+    QAction * ActionToggleShowPlainText = _settingsMenu->addAction(tr("Display HTML code as plain text"));
+    ActionToggleShowPlainText->setShortcut(QKeySequence(Qt::ALT + Qt::Key_D));
+    ActionToggleShowPlainText->setCheckable(true);
+    ActionToggleShowPlainText->setChecked(showPlainText);
+    connect(ActionToggleShowPlainText, SIGNAL(toggled(bool)), this, SLOT(OnToggleShowPlainText(bool)));
+//    _settingsMenu->addAction(tr("Display HTML code as plain text"),
+//        this, SLOT(OnToggleShowPlainText()), QKeySequence(Qt::ALT + Qt::Key_D))->setCheckable(true);
+
+    _settingsMenu->addSeparator();
+
+    // Options item
+    _settingsMenu->addAction(tr("Options..."),
+        this, SLOT(OnOptions()), QKeySequence(Qt::CTRL + Qt::ALT + Qt::Key_O));
+    //------------------------------------------------------------------
+    // Help menu
+    QMenu* helpMenu(menuBar()->addMenu(tr("&Help")));
+
+    // About item
+    helpMenu->addAction(QIcon(":/menu/about"), tr("About..."),
+        this, SLOT(OnAbout()), QKeySequence(Qt::CTRL + Qt::Key_H));
+}
+
+void MainWindow::CreateDockWindows()
+{
+    // "Objects" widget
+    _objectsWidget = new QDockWidget(tr("Objects"), this);
+    addDockWidget(Qt::RightDockWidgetArea, _objectsWidget, Qt::Vertical);
+    _objectsListBox = new QspListBox(this);
+    connect(_objectsListBox, SIGNAL(itemClicked(QListWidgetItem *)), this, SLOT(OnObjectListBoxItemClicked(QListWidgetItem *)));
+    connect(_objectsListBox, SIGNAL(itemPressed(QListWidgetItem *)), this, SLOT(OnObjectListBoxItemClicked(QListWidgetItem *)));
+    connect(_objectsListBox, SIGNAL(itemDoubleClicked(QListWidgetItem *)), this, SLOT(OnObjectListBoxItemClicked(QListWidgetItem *)));
+    //connect(_objectsListBox, SIGNAL(currentRowChanged(int)), this, SLOT(OnObjectChange(int)));
+    _objectsWidget->setWidget(_objectsListBox);
+
+    // "Actions" widget
+    _actionsWidget = new QDockWidget(tr("Actions"), this);
+    addDockWidget(Qt::BottomDockWidgetArea, _actionsWidget, Qt::Vertical);
+    _actionsListBox = new QspListBox(this);
+    connect(_actionsListBox, SIGNAL(itemClicked(QListWidgetItem *)), this, SLOT(OnActionsListBoxItemClicked(QListWidgetItem *)));
+    connect(_actionsListBox, SIGNAL(itemPressed(QListWidgetItem *)), this, SLOT(OnActionsListBoxItemClicked(QListWidgetItem *)));
+    connect(_actionsListBox, SIGNAL(itemDoubleClicked(QListWidgetItem *)), this, SLOT(OnActionsListBoxItemClicked(QListWidgetItem *)));
+    //connect(_actionsListBox, SIGNAL(currentRowChanged(int)), this, SLOT(OnActionChange(int)));
+    _actionsWidget->setWidget(_actionsListBox);
+
+    // "Additional desc" widget
+    _descWidget = new QDockWidget(tr("Additional desc"), this);
+    addDockWidget(Qt::BottomDockWidgetArea, _descWidget, Qt::Horizontal);
+    _descTextBox = new QspTextBox(this);
+    connect(_descTextBox, SIGNAL(anchorClicked(QUrl)), this, SLOT(OnLinkClicked(QUrl)));
+    _descWidget->setWidget(_descTextBox);
+
+    // "Input area" widget
+    _inputWidget = new QDockWidget(tr("Input area"), this);
+    addDockWidget(Qt::BottomDockWidgetArea, _inputWidget, Qt::Vertical);
+    _inputTextBox = new QspInputBox(this);
+    _inputWidget->setWidget(_inputTextBox);
+
+    splitDockWidget(_actionsWidget, _inputWidget, Qt::Vertical);
+}
+
+void MainWindow::closeEvent(QCloseEvent *event)
+{
+    //SaveSettings();
+    EnableControls(false, true);
+    setVisible(false);
+    m_isQuit = true;
+
+    QSPDeInit();
+    QSPCallBacks::DeInit();
+
+    QCoreApplication::processEvents();
+    QMainWindow::closeEvent(event);
+}
+
+void MainWindow::OpenGameFile(const QString &path)
+{
+    if (QSPLoadGameWorld(qspStringFromQString(path), QSP_TRUE))
+    {
+        m_isGameOpened = true;
+        QFileInfo file(path);
+        QString filePath(file.canonicalPath());
+        if(!filePath.endsWith('/')) filePath+="/";
+        QString configString(filePath + QSP_CONFIG);
+        QFileInfo configStringFile(configString);
+        QString newPath((configStringFile.exists() && configStringFile.isFile()) ? configString : m_configDefPath);
+        if (newPath != m_configPath)
+        {
+            SaveSettings();
+            m_configPath = newPath;
+            LoadSettings();
+        }
+        UpdateGamePath(filePath);
+        OnNewGame();
+        if (m_isQuit) return;
+        //UpdateTitle();
+        EnableControls(true);
+        m_savedGamePath.clear();
+    }
+    else
+        ShowError();
+}
+
+void MainWindow::OnOpenGame()
+{
+    QString path = QFileDialog::getOpenFileName(this, tr("Select game file"), GetLastPath(), tr("QSP games (*.qsp *.gam)"));
+    if (!path.isEmpty())
+    {
+        SetLastPath(QFileInfo(path).canonicalPath());
+        OpenGameFile(path);
+    }
+}
+
+void MainWindow::OnRestartGame()
+{
+    if(m_isGameOpened)
+        if (!QSPRestartGame(QSP_TRUE))
+            ShowError();
+}
+
+void MainWindow::OnOpenSavedGame()
+{
+    if(!m_isGameOpened)
+        return;
+    QString path = QFileDialog::getOpenFileName(this, tr("Select saved game file"), GetLastPath(), tr("Saved game files (*.sav)"));
+    if (!path.isEmpty())
+    {
+        SetLastPath(QFileInfo(path).canonicalPath());
+        if (!QSPOpenSavedGame(qspStringFromQString(path), QSP_TRUE))
+            ShowError();
+    }
+}
+
+void MainWindow::OnSaveGame()
+{
+    if(!m_isGameOpened)
+        return;
+    QString path = QFileDialog::getSaveFileName(this, tr("Select file to save"), GetLastPath(), tr("Saved game files (*.sav)"));
+    if (!path.isEmpty())
+    {
+        if(!path.endsWith(".sav"))
+            path.append(".sav");
+        QString p = GetLastPath();
+        if (QSPSaveGame(qspStringFromQString(path), QSP_TRUE))
+        {
+            SetLastPath(QFileInfo(path).canonicalPath());
+            m_savedGamePath = path;
+        }
+        else
+            ShowError();
+    }
+}
+
+void MainWindow::OnToggleWinMode()
+{
+    if(isFullScreen())
+    {
+        showNormal();
+    }
+    else
+    {
+        showFullScreen();
+    }
+}
+
+void MainWindow::OnToggleShowPlainText(bool checked)
+{
+    SetShowPlainText(checked);
+}
+
+void MainWindow::OnNewGame()
+{
+    if (!QSPRestartGame(QSP_TRUE))
+        ShowError();
+}
+
+void MainWindow::OnTimer()
+{
+    if (m_isProcessEvents && !QSPExecCounter(QSP_TRUE))
+        ShowError();
+}
+
+void MainWindow::OnLinkClicked(const QUrl &url)
+{
+    QString href;
+    href = url.toString();
+
+    if (href.startsWith("#"))
+    {
+        QObject* obj = sender();
+        if (obj == _mainDescTextBox)
+            _mainDescTextBox->setSource(url);
+        else
+            _descTextBox->setSource(url);
+    }
+    else if (href.toUpper().startsWith("EXEC:", Qt::CaseInsensitive))
+    {
+        QString string = href.mid(5);
+        if (m_isProcessEvents && !QSPExecString(qspStringFromQString(string), QSP_TRUE))
+            ShowError();
+    }
+    else
+    {
+        //wxLaunchDefaultBrowser(href); //TODO: this
+    }
+}
+
+void MainWindow::OnObjectListBoxItemClicked(QListWidgetItem *itemClicked)
+{
+    int object = _objectsListBox->row(itemClicked);
+    if (!QSPSetSelObjectIndex(object, QSP_TRUE))
+        ShowError();
+}
+
+void MainWindow::OnActionsListBoxItemClicked(QListWidgetItem *itemClicked)
+{
+    int action = _actionsListBox->row(itemClicked);
+    if (!QSPSetSelActionIndex(action, QSP_TRUE))
+        ShowError();
+    if (!QSPExecuteSelActionCode(QSP_TRUE))
+        ShowError();
+}
+
+void MainWindow::OnObjectChange(int currentRow)
+{
+    //QThread::msleep(20);
+    if (!QSPSetSelObjectIndex(currentRow, QSP_TRUE))
+        ShowError();
+}
+
+void MainWindow::OnActionChange(int currentRow)
+{
+    if (!QSPSetSelActionIndex(currentRow, QSP_TRUE))
+        ShowError();
+}
+
+void MainWindow::OnMenu(QAction* action)
+{
+    m_menuIndex = action->data().toInt();
+}

+ 175 - 0
mainwindow.h

@@ -0,0 +1,175 @@
+#ifndef MAINWINDOW_H
+#define MAINWINDOW_H
+
+#include <QMainWindow>
+#include <QWidget>
+#include <QDockWidget>
+#include <QString>
+#include <QColor>
+#include <QTimer>
+#include <QMenuBar>
+#include <QMenu>
+#include <QAction>
+#include <QToolBar>
+#include <QStatusBar>
+
+#include "qsptextbox.h"
+#include "qsplistbox.h"
+#include "qspinputbox.h"
+#include "qspimgcanvas.h"
+
+#include <qsp_default.h>
+
+#define QSP_LOGO "Quest Soft Player 5"
+#define QSP_APPNAME "qspgui"
+#define QSP_CONFIG "qspgui.cfg"
+#define QSP_SOUNDPLUGINS "sound"
+#define QSP_MIDIDLS "sound/midi.dls"
+
+namespace Ui {
+class MainWindow;
+}
+
+class MainWindow : public QMainWindow
+{
+    Q_OBJECT
+
+public:
+    explicit MainWindow(QWidget *parent = 0);
+    ~MainWindow();
+
+    // Methods
+    void EnableControls(bool status, bool isExtended = false);
+    void ApplyParams();
+    void DeleteMenu();
+    void AddMenuItem(const QString &name, const QString &imgPath);
+    int ShowMenu();
+    void UpdateGamePath(const QString &path);
+    void ShowError();
+
+    // Accessors
+    QTimer *GetTimer() const { return m_timer; }
+    QspTextBox *GetDesc() const { return _mainDescTextBox; }
+    QspTextBox *GetVars() const { return _descTextBox; }
+    QspInputBox *GetInput() const { return _inputTextBox; }
+    QspListBox *GetActions() const { return _actionsListBox; }
+    QspListBox *GetObjects() const { return _objectsListBox; }
+
+    QDockWidget *GetVarsDock() const { return _descWidget; }
+    QDockWidget *GetInputDock() const { return _inputWidget; }
+    QDockWidget *GetActionsDock() const { return _actionsWidget; }
+    QDockWidget *GetObjectsDock() const { return _objectsWidget; }
+
+    QString GetLastPath() { return lastPath; }
+    void SetLastPath(const QString &path) { lastPath = path; } //TODO: save to config
+
+    QspImgCanvas *GetImgView() const { return m_imgView; }
+    QMenu *GetGameMenu() const { return _gameMenu; }
+    bool IsShowHotkeys() const { return m_isShowHotkeys; }
+    bool IsQuit() const { return m_isQuit; }
+    bool IsKeyPressedWhileDisabled() const { return m_keyPressedWhileDisabled; }
+
+    void SetShowPlainText(bool isPlain);
+
+private:
+    void CreateMenuBar();
+    void CreateDockWindows();
+    void LoadSettings();
+    void SaveSettings();
+    void closeEvent(QCloseEvent *event);
+    void OpenGameFile(const QString& path);
+
+    // Internal methods
+    void UpdateTitle();
+    void ReCreateGUI();
+    void RefreshUI();
+//    void ApplyFont(const wxFont& font);
+//    bool ApplyFontSize(int size);
+//    bool ApplyFontName(const wxString& name);
+    bool ApplyFontColor(const QColor& color);
+    bool ApplyBackColor(const QColor& color);
+    bool ApplyLinkColor(const QColor& color);
+//    void SetOverallVolume(int percents);
+
+    QMenuBar*       mainMenuBar;
+    QToolBar*       mainToolBar;
+    QStatusBar*     mainStatusBar;
+    QMenu*			_fileMenu; // was wxMenu *m_fileMenu;
+    QMenu*			_gameMenu; // was wxMenu *m_gameMenu;
+    QMenu*			_settingsMenu; // was wxMenu *m_settingsMenu;
+    QMenu*			_showHideMenu; //Show / Hide submenu
+    QspTextBox*		_mainDescTextBox; //m_desc
+    QspListBox*		_objectsListBox; //m_objects
+    QspListBox*		_actionsListBox; //m_actions
+    QspTextBox*		_descTextBox; //m_vars ID_VARSDESC
+    QspInputBox*	_inputTextBox; //m_input
+    QDockWidget*	_objectsWidget; //m_objects
+    QDockWidget*	_actionsWidget; //m_actions
+    QDockWidget*	_descWidget; //m_vars ID_VARSDESC
+    QDockWidget*	_inputWidget; //m_input
+    QString lastPath; //For QFileDialog
+
+    // Fields
+    bool m_isGameOpened;
+    QString m_savedGamePath;
+    QString m_configPath;
+    QString m_configDefPath;
+    QTimer *m_timer;
+    QspImgCanvas *m_imgView;
+    int m_menuItemId;
+    QMenu *m_menu; //qsp callback menu
+    QColor m_backColor;
+    QColor m_linkColor;
+    QColor m_fontColor;
+    int m_fontSize;
+    QString m_fontName;
+    bool m_isUseFontSize;
+    bool m_isProcessEvents;
+    bool m_isQuit;
+    bool m_keyPressedWhileDisabled;
+    bool m_isShowHotkeys;
+    int m_volume;
+    int m_menuIndex;
+    bool showPlainText;
+
+private slots:
+    void OnOpenGame();
+    void OnRestartGame();
+    void OnOpenSavedGame();
+    void OnSaveGame(); //TODO: add quick save/load
+//    void OnOptions();
+//    void OnAbout();
+//    void OnToggleCaptions();
+//    void OnToggleHotkeys();
+    void OnToggleWinMode();
+    void OnToggleShowPlainText(bool checked);
+//    void OnChangeSoundVolume();
+    void OnNewGame();
+    void OnTimer();
+    void OnLinkClicked(const QUrl& url);
+    void OnObjectListBoxItemClicked (QListWidgetItem * itemClicked);
+    void OnActionsListBoxItemClicked (QListWidgetItem * itemClicked);
+    void OnObjectChange(int currentRow);
+    void OnActionChange(int currentRow);
+    void OnMenu(QAction* action);
+
+    // Events
+//    void OnInit(); //TODO: add autorun event
+
+//    void OnQuickSave(wxCommandEvent& event);
+//    void OnSelectFont(wxCommandEvent& event);
+//    void OnUseFontSize(wxCommandEvent& event);
+//    void OnSelectFontColor(wxCommandEvent& event);
+//    void OnSelectBackColor(wxCommandEvent& event);
+//    void OnSelectLinkColor(wxCommandEvent& event);
+//    void OnSelectLang(wxCommandEvent& event);
+//    void OnToggleCaptions(wxCommandEvent& event);
+//    void OnToggleHotkeys(wxCommandEvent& event);
+//    void OnVolume(wxCommandEvent& event);
+//    void OnAbout(wxCommandEvent& event);
+//    void OnInputTextChange(wxCommandEvent& event);
+//    void OnInputTextEnter(wxCommandEvent& event);
+//    void OnDropFiles(wxDropFilesEvent& event);
+};
+
+#endif // MAINWINDOW_H

+ 1 - 0
qsp

@@ -0,0 +1 @@
+Subproject commit 4cfd701de34e0d4b0bd3f3ecfd9de403947a6467

+ 99 - 0
qspimgcanvas.cpp

@@ -0,0 +1,99 @@
+#include "qspimgcanvas.h"
+
+#include <QFileInfo>
+
+QspImgCanvas::QspImgCanvas(QWidget *parent) : QDialog(parent)
+{
+    m_posX = m_posY = 0;
+    m_isAnim = false;
+    setWindowTitle(tr("Image"));
+    sizePolicy().setHorizontalPolicy(QSizePolicy::Expanding);
+    sizePolicy().setVerticalPolicy(QSizePolicy::Expanding);
+    label_image.sizePolicy().setHorizontalPolicy(QSizePolicy::Expanding);
+    label_image.sizePolicy().setVerticalPolicy(QSizePolicy::Expanding);
+    layout.addWidget(&label_image);
+    setLayout(&layout);
+    setVisible(false);
+    setModal(false);
+}
+
+QspImgCanvas::~QspImgCanvas()
+{
+
+}
+
+bool QspImgCanvas::OpenFile(const QString &fileName)
+{
+    bool ret;
+    QFileInfo file(fileName);
+    QString path(file.absoluteFilePath());
+    setWindowTitle(path);
+    if (m_path != path || path.isEmpty())
+    {
+        if (file.exists() && file.isFile())
+        {
+            if ( false /* m_isAnim = m_animation->LoadFile(path) */)
+            {
+                //m_animation->Show();
+                ret = true;
+            }
+            else
+            {
+                //m_animation->Hide();
+                ret = m_image.load(path);
+                if(ret)
+                    label_image.setPixmap(m_image);
+            }
+            if (ret)
+            {
+                if (m_isAnim)
+                {
+                    //m_animation->Play();
+                }
+                else
+                {
+                    //Refresh();
+                }
+                m_path = path;
+                setVisible(true);
+            }
+            return ret;
+        }
+        return false;
+    }
+    if(!fileName.isEmpty())
+    {
+        setVisible(true);
+    }
+    return true;
+}
+
+void QspImgCanvas::RefreshUI()
+{
+//    if (m_isAnim)
+//		m_animation->RefreshUI();
+//	else
+    //		Refresh();
+}
+
+bool QspImgCanvas::SetBackgroundColour(const QColor &color)
+{
+    //wxWindow::SetBackgroundColour(color);
+    //m_animation->SetBackgroundColour(color);
+    return true;
+}
+
+void QspImgCanvas::keyPressEvent(QKeyEvent *event)
+{
+    if(event->key() == Qt::Key_Escape)
+    {
+        setVisible(false);
+        event->ignore();
+    }
+}
+
+void QspImgCanvas::closeEvent(QCloseEvent *event)
+{
+    setVisible(false);
+    event->ignore();
+}

+ 46 - 0
qspimgcanvas.h

@@ -0,0 +1,46 @@
+#ifndef QSPIMGCANVAS_H
+#define QSPIMGCANVAS_H
+
+#include <QWidget>
+#include <QLabel>
+#include <QString>
+#include <QColor>
+#include <QPixmap>
+#include <QDialog>
+#include <QVBoxLayout>
+#include <QKeyEvent>
+#include <QCloseEvent>
+
+namespace Ui {
+class QspImgCanvas;
+}
+
+class QspImgCanvas : public QDialog
+{
+    Q_OBJECT
+
+public:
+    explicit QspImgCanvas(QWidget *parent = 0);
+    ~QspImgCanvas();
+
+    // Methods
+    bool OpenFile(const QString& fileName);
+    void RefreshUI();
+
+    // Overloaded methods
+    virtual bool SetBackgroundColour(const QColor& color);
+
+private:
+    // Fields
+    bool m_isAnim;
+    QPixmap m_image;
+    QLabel label_image;
+    QVBoxLayout layout;
+    QString m_path;
+    int m_posX;
+    int m_posY;
+    void keyPressEvent(QKeyEvent *event);
+    void closeEvent(QCloseEvent *event);
+};
+
+#endif // QSPIMGCANVAS_H

+ 23 - 0
qspinputbox.cpp

@@ -0,0 +1,23 @@
+#include "qspinputbox.h"
+
+QspInputBox::QspInputBox(QWidget *parent) : QTextEdit(parent)
+{
+    m_selIndex = -1;
+}
+
+QspInputBox::~QspInputBox()
+{
+
+}
+
+void QspInputBox::SetText(const QString& text, bool isChangeValue)
+{
+    if (m_text != text)
+    {
+        m_text = text;
+        //if (isChangeValue) ChangeValue(m_text); It also marks the control as not-modified which means that IsModified() would return false immediately after the call to ChangeValue().
+        //The insertion point is set to the start of the control (i.e. position 0) by this function.
+        //This functions does not generate the wxEVT_TEXT event but otherwise is identical to SetValue().
+        if (isChangeValue) setText(m_text);
+    }
+}

+ 33 - 0
qspinputbox.h

@@ -0,0 +1,33 @@
+#ifndef QSPINPUTBOX_H
+#define QSPINPUTBOX_H
+
+#include <QWidget>
+#include <QTextEdit>
+#include <QString>
+#include <QStringList>
+
+namespace Ui {
+class QspInputBox;
+}
+
+//TODO: implement
+class QspInputBox : public QTextEdit
+{
+    Q_OBJECT
+
+public:
+    explicit QspInputBox(QWidget *parent = 0);
+    ~QspInputBox();
+
+    // Accessors
+    void SetText(const QString& text, bool isChangeValue = true);
+    QString GetText() const { return m_text; }
+
+private:
+    // Fields
+    QString m_text;
+    QStringList m_strings;
+    int m_selIndex;
+};
+
+#endif // QSPINPUTBOX_H

+ 69 - 0
qspinputdlg.cpp

@@ -0,0 +1,69 @@
+#include "qspinputdlg.h"
+
+QspInputDlg::QspInputDlg(QWidget *parent) :
+    QWidget(parent)
+{
+}
+
+QspInputDlg::~QspInputDlg()
+{
+
+}
+
+QspInputDlg::QspInputDlg(const QColor &backColor, const QColor &fontColor, const QFont &font, const QString &caption, const QString &text, bool isHtml, const QString &gamePath, QWidget *parent) : QWidget(parent)
+{
+//    // ----------
+//	SetBackgroundColour(backColor);
+//	wxSizer *sizerUp = new wxBoxSizer(wxVERTICAL);
+//	m_desc = new QSPTextBox(this, ID_INPUT_DESC);
+//	m_desc->SetGamePath(gamePath);
+//	m_desc->SetIsHtml(isHtml);
+//	m_desc->SetBackgroundColour(backColor);
+//	m_desc->SetForegroundColour(fontColor);
+//	m_desc->SetTextFont(font);
+//	m_desc->SetText(text);
+//	wxTextCtrl *inputStr = new wxTextCtrl(this, wxID_ANY);
+//	inputStr->SetBackgroundColour(backColor);
+//	inputStr->SetForegroundColour(fontColor);
+//	inputStr->SetFont(font);
+//	wxStaticLine* line = new wxStaticLine(this, wxID_STATIC, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL);
+//	sizerUp->Add(m_desc, 1, wxALL | wxGROW, 2);
+//	sizerUp->Add(inputStr, 0, wxALL | wxGROW, 2);
+//	sizerUp->Add(line, 0, wxALL | wxGROW, 2);
+//	// ----------
+//	wxSizer *sizerBottom = new wxBoxSizer(wxHORIZONTAL);
+//	wxButton *btnOk = new wxButton(this, wxID_OK, _("OK"));
+//	wxButton *btnCancel = new wxButton(this, wxID_CANCEL, _("Cancel"));
+//	btnOk->SetDefault();
+//	btnOk->SetFont(font);
+//	btnCancel->SetFont(font);
+//	#ifdef __WXMSW__
+//		btnOk->SetBackgroundColour(backColor);
+//		btnOk->SetForegroundColour(fontColor);
+//		btnCancel->SetBackgroundColour(backColor);
+//		btnCancel->SetForegroundColour(fontColor);
+//	#endif
+//	sizerBottom->Add(btnOk, 0, wxALL, 2);
+//	sizerBottom->Add(btnCancel, 0, wxALL, 2);
+//	// ----------
+//	wxSizer *sizerMain = new wxBoxSizer(wxVERTICAL);
+//	sizerMain->Add(sizerUp, 1, wxGROW, 0);
+//	sizerMain->Add(sizerBottom, 0, wxALIGN_RIGHT, 0);
+//	// ----------
+//	inputStr->SetValidator(wxGenericValidator(&m_text));
+//	static const int minWidth = 420;
+//	static const int maxWidth = 550;
+//	static const int minHeight = 150;
+//	static const int maxHeight = 350;
+//	sizerMain->SetMinSize(minWidth, minHeight);
+//	SetSizerAndFit(sizerMain);
+//	int deltaH = GetClientSize().GetHeight() - m_desc->GetSize().GetHeight();
+//	int deltaW = GetClientSize().GetWidth() - m_desc->GetSize().GetWidth();
+//	int height = m_desc->GetInternalRepresentation()->GetHeight() + m_desc->GetCharHeight() + deltaH;
+//	int width = m_desc->GetInternalRepresentation()->GetWidth() + deltaW;
+//	height = wxMin(wxMax(height, minHeight), maxHeight);
+//	width = wxMin(wxMax(width, minWidth), maxWidth);
+//	SetClientSize(width, height);
+//	Center();
+//	inputStr->SetFocus();
+}

+ 39 - 0
qspinputdlg.h

@@ -0,0 +1,39 @@
+#ifndef QSPINPUTDLG_H
+#define QSPINPUTDLG_H
+
+#include <QWidget>
+#include <QString>
+#include <QFont>
+#include <QColor>
+
+#include "qsptextbox.h"
+
+//TODO: implement this to replace generic QInputDialog
+
+namespace Ui {
+class QspInputDlg;
+}
+
+class QspInputDlg : public QWidget
+{
+    Q_OBJECT
+
+public:
+    explicit QspInputDlg(QWidget *parent = 0);
+    ~QspInputDlg();
+    QspInputDlg(const QColor& backColor,
+                const QColor& fontColor,
+                const QFont& font,
+                const QString& caption,
+                const QString& text,
+                bool isHtml,
+                const QString& gamePath,
+                QWidget *parent = 0);
+
+private:
+    QString GetText() const { return m_text; }
+    QspTextBox m_desc;
+    QString m_text;
+};
+
+#endif // QSPINPUTDLG_H

+ 274 - 0
qsplistbox.cpp

@@ -0,0 +1,274 @@
+#include "qsplistbox.h"
+
+#include <QListWidgetItem>
+#include <QLabel>
+#include <QList>
+#include <QFileInfo>
+#include <QScrollBar>
+
+#include "comtools.h"
+
+#include "qsptextbox.h"
+
+QspListBox::QspListBox(QWidget *parent) : QListWidget(parent)
+{
+    setSelectionMode(QAbstractItemView::SingleSelection);
+    m_isUseHtml = false;
+    m_isShowNums = false;
+    showPlainText = false;
+//	SetStandardFonts(m_font.GetPointSize(), fontName, fontName);
+//	SetSelectionBackground(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT));
+}
+
+QspListBox::~QspListBox()
+{
+
+}
+
+void QspListBox::SetStandardFonts(const QFont &font)
+{
+    //	RefreshUI();
+}
+
+void QspListBox::RefreshUI()
+{
+//	RefreshAll();
+}
+
+void QspListBox::BeginItems()
+{
+    m_newImages.clear();
+    m_newDescs.clear();
+}
+
+void QspListBox::AddItem(const QString& image, const QString& desc)
+{
+    m_newImages.append(image);
+    m_newDescs.append(desc);
+}
+
+void QspListBox::EndItems()
+{
+    size_t count;
+    if (m_images != m_newImages || m_descs != m_newDescs)
+    {
+        m_images = m_newImages;
+        m_descs = m_newDescs;
+        count = m_descs.count();
+        createList();
+        RefreshUI();
+        if (count) scrollToItem(item(0));
+    }
+}
+
+void QspListBox::SetIsHtml(bool isHtml)
+{
+    if (m_isUseHtml != isHtml)
+    {
+        m_isUseHtml = isHtml;
+        createList();
+        RefreshUI();
+    }
+}
+
+void QspListBox::SetIsShowNums(bool isShow)
+{
+    if (m_isShowNums != isShow)
+    {
+        m_isShowNums = isShow;
+        createList();
+        RefreshUI();
+    }
+}
+
+void QspListBox::SetTextFont(const QFont& font)
+{
+//	int fontSize = font.GetPointSize();
+//	wxString fontName(font.GetFaceName());
+//	if (!m_font.GetFaceName().IsSameAs(fontName, false) || m_font.GetPointSize() != fontSize)
+//	{
+//		m_font = font;
+//		SetStandardFonts(fontSize, fontName, fontName);
+//	}
+}
+
+void QspListBox::SetLinkColor(const QColor& clr)
+{
+//	RefreshUI();
+}
+
+QColor QspListBox::GetLinkColor()
+{
+    return m_linkColor;
+}
+
+QColor QspListBox::GetTextColour()
+{
+    return QColor(Qt::black);
+}
+
+void QspListBox::SetSelection(int selection)
+{
+    if(selection == -1)
+        clearSelection();
+    else
+    {
+        return;
+        if(selection < count())
+        {
+            if(item(selection) != 0)
+            {
+                clearSelection();
+                setCurrentItem(item(selection), QItemSelectionModel::SelectCurrent);
+                item(selection)->setSelected(true);
+                scrollToItem(item(selection));
+                //setFocus();//TODO: check if required
+            }
+        }
+        else
+        {
+            clearSelection();
+            //clearFocus();
+        }
+    }
+}
+
+void QspListBox::SetShowPlainText(bool isPlain)
+{
+    showPlainText = isPlain;
+    createList();
+    RefreshUI();
+}
+
+void QspListBox::createList()
+{
+    //clear(); //NOTE: clear() only deletes items but does not delete the widgets belonging to it. The widgets will be deleted if the QListWidget is deleted.
+    bool oldState = blockSignals(true);
+    int selection = -1;
+    QList<QListWidgetItem *> selList = selectedItems();
+    if(selList.size() != 0)
+        selection = row(selList.at(0));
+    for(int i = 0; i<count(); i++)
+    {
+        removeItemWidget(item(i));
+    }
+    clear();
+    for(int i = 0; i < qMin(m_images.size(), m_descs.size()); i++)
+    {
+        QString item_tmp;
+        item_tmp = formatItem(i);
+
+        QListWidgetItem* item;
+        item = new QListWidgetItem(this);
+        addItem(item);
+        QspTextBox *item_widget;
+        item_widget = new QspTextBox(this);
+        item_widget->SetIsHtml(m_isUseHtml);
+        item_widget->SetGamePath(m_path);
+        //item_widget->setMaximumHeight(800);
+        item_widget->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+        item_widget->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+        item_widget->verticalScrollBar()->setEnabled(false);
+        item_widget->horizontalScrollBar()->setEnabled(false);
+        item_widget->setReadOnly(true);
+        item_widget->setBackgroundRole(QPalette::NoRole);
+        item_widget->setTextInteractionFlags(Qt::NoTextInteraction);
+        item_widget->setWordWrapMode(QTextOption::NoWrap);
+        QFontMetrics font_metrics(item_widget->font());
+        //item_widget->setFixedHeight(font_metrics.height() + 4* item_widget->frameWidth());
+        item_widget->setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContentsOnFirstShow);
+        item_widget->sizePolicy().setHorizontalPolicy(QSizePolicy::Expanding);
+        item_widget->sizePolicy().setVerticalPolicy(QSizePolicy::Expanding);
+
+        item_widget->SetShowPlainText(showPlainText);
+        item_widget->SetText(item_tmp);
+
+        //QSize sizehint = QSize(item->sizeHint().width(), font_metrics.height()*2 + item_widget->frameWidth()*2);
+        //QSize sizehint = item_widget->sizeHint();
+        //sizehint.setHeight(item_widget->document()->size().toSize().height());
+        //sizehint.setHeight(item_widget->heightForWidth(this->width()));
+        item_widget->document()->setTextWidth(this->width() - style()->pixelMetric(QStyle::PM_ScrollBarExtent) - item_widget->frameWidth()*2);
+        QSize sizehint = QSize(this->width() - style()->pixelMetric(QStyle::PM_ScrollBarExtent) - item_widget->frameWidth()*2, item_widget->document()->size().toSize().height() + item_widget->frameWidth()*2);
+        item->setSizeHint(sizehint);
+        setItemWidget(item, item_widget);
+    }
+    if(selection != -1)
+        SetSelection(selection);
+    blockSignals(oldState);
+}
+
+QString QspListBox::formatItem(int itemIndex)
+{
+    if(itemIndex >= m_images.size() || itemIndex >= m_descs.size())
+        return QString("");
+
+    bool isImage = false;
+    QString imgPath;
+    if(!m_images.at(itemIndex).isEmpty())
+    {
+        QFileInfo imgFile(m_images.at(itemIndex));
+        if (imgFile.exists() && imgFile.isFile())
+        {
+            imgPath = imgFile.absoluteFilePath();
+            isImage = true;
+        }
+    }
+    QString color(QSPTools::GetHexColor(GetTextColour()));
+    QString formatedText;
+    if(isImage)
+    {
+        formatedText.append(QString("<img src=\"%1\">").arg(m_images.at(itemIndex)));
+    }
+    if(!m_descs.at(itemIndex).isEmpty())
+    {
+        formatedText.append(m_descs.at(itemIndex));
+    }
+    return formatedText;
+
+    //TODO: make this variant work
+    if(m_descs.at(itemIndex).isEmpty())
+    {
+        formatedText = "";
+    }
+    else
+    {
+        QString text(QSPTools::HtmlizeWhitespaces(m_isUseHtml ? m_descs.at(itemIndex) : QSPTools::ProceedAsPlain(m_descs.at(itemIndex))));
+        formatedText = QString("<div style=\"padding:0px; margin-right:4px;\">%1</div>").arg(text);
+        formatedText = m_descs.at(itemIndex);
+    }
+
+    if (m_isShowNums && itemIndex < 9)
+    {
+        if (isImage)
+        {
+            return QString("<div style=\"color : #%1; -qt-block-indent:0; text-indent:0px;\"><div style=\"padding:0px; margin-right:4px;\">[%2]</div><div style=\"padding:0px; margin-right:4px;\"><img src=\"%3\"></div>%4</div>")
+                    .arg(color)
+                    .arg(itemIndex+1)
+                    .arg(imgPath)
+                    .arg(formatedText);
+        }
+        else
+        {
+            return QString("<div style=\"color : #%1; -qt-block-indent:0; text-indent:0px;\"><div style=\"padding:0px; margin-right:4px;\">[%2]</div>%3</div>")
+                    .arg(color)
+                    .arg(itemIndex+1)
+                    .arg(formatedText);
+        }
+    }
+    else
+    {
+        if (isImage)
+        {
+            return QString("<div style=\"color : #%1; -qt-block-indent:0; text-indent:0px;\"><div style=\"padding:0px; margin-right:4px;\"><img src=\"%2\"></div>%3</div>")
+                    .arg(color)
+                    .arg(imgPath)
+                    .arg(formatedText);
+        }
+        else
+        {
+            return QString("<div style=\"color : #%1; -qt-block-indent:0; text-indent:0px;\">%2</div>")
+                    .arg(color)
+                    .arg(formatedText);
+        }
+    }
+}

+ 64 - 0
qsplistbox.h

@@ -0,0 +1,64 @@
+#ifndef QSPLISTBOX_H
+#define QSPLISTBOX_H
+
+#include <QWidget>
+#include <QListWidget>
+#include <QString>
+#include <QStringList>
+#include <QFont>
+#include <QColor>
+
+namespace Ui {
+class QspListBox;
+}
+
+class QspListBox : public QListWidget
+{
+    Q_OBJECT
+
+public:
+    explicit QspListBox(QWidget *parent = 0);
+    ~QspListBox();
+
+    // Methods
+    void SetStandardFonts(const QFont& font);
+    void RefreshUI();
+    void BeginItems();
+    void AddItem(const QString& image, const QString& desc);
+    void EndItems();
+
+    // Accessors
+    void SetIsHtml(bool isHtml);
+    void SetIsShowNums(bool isShow);
+    void SetTextFont(const QFont& font);
+    QFont GetTextFont() const { return m_font; }
+    void SetLinkColor(const QColor& clr);
+    QColor GetLinkColor();
+    QColor GetTextColour(); //GetForegroundColour()
+    void SetGamePath(const QString& path) { m_path = path; }
+    void SetSelection(int selection);
+    void SetShowPlainText(bool isPlain);
+
+private:
+    // Internal methods
+    void createList();
+    QString formatItem(int itemIndex);
+
+    // Fields
+    QString m_outFormat;
+    QString m_outFormatNums;
+    QString m_outFormatImage;
+    QString m_outFormatImageNums;
+    bool m_isUseHtml;
+    bool m_isShowNums;
+    QString m_path;
+    QFont m_font;
+    QStringList m_images;
+    QStringList m_descs;
+    QStringList m_newImages;
+    QStringList m_newDescs;
+    QColor m_linkColor;
+    bool showPlainText;
+};
+
+#endif // QSPLISTBOX_H

+ 101 - 0
qspmsgdlg.cpp

@@ -0,0 +1,101 @@
+#include "qspmsgdlg.h"
+
+#include <QRect>
+
+#include "mainwindow.h"
+
+#include "callbacks_gui.h"
+#include "comtools.h"
+
+QspMsgDlg::QspMsgDlg(QWidget *parent) : QDialog(parent)
+{
+
+}
+
+QspMsgDlg::~QspMsgDlg()
+{
+
+}
+
+QspMsgDlg::QspMsgDlg(const QString &caption, const QString &text, QWidget *parent) : QDialog(parent) {
+    setWindowTitle(caption);
+    sizePolicy().setHorizontalPolicy(QSizePolicy::Expanding);
+    sizePolicy().setVerticalPolicy(QSizePolicy::Expanding);
+    connect(&m_desc, SIGNAL(anchorClicked(QUrl)), this, SLOT(OnLinkClicked(QUrl)));
+    m_desc.setHtml(text);
+    m_desc.sizePolicy().setHorizontalPolicy(QSizePolicy::Expanding);
+    m_desc.sizePolicy().setVerticalPolicy(QSizePolicy::Expanding);
+    connect(&okButton, SIGNAL(clicked()), this, SLOT(close()));
+    okButton.setGeometry(QRect(10, 130, 100, 20));
+    okButton.setText(tr("OK"));
+    resize(320,240);
+    layout.addWidget(&m_desc);
+    layout.addWidget(&okButton);
+    setLayout(&layout);
+    setModal(true);
+    //exec();
+}
+
+QspMsgDlg::QspMsgDlg(const QColor& backColor,
+                     const QColor& fontColor,
+                     const QFont& font,
+                     const QString& caption,
+                     const QString& text,
+                     bool isHtml,
+                     const QString& gamePath,
+                     QWidget *parent) : QDialog(parent)
+{
+    //SetBackgroundColour(backColor);
+    m_desc.SetGamePath(gamePath);
+    m_desc.SetIsHtml(isHtml);
+    m_desc.SetBackgroundColour(backColor);
+    m_desc.SetForegroundColour(fontColor);
+    m_desc.SetTextFont(font);
+    m_desc.SetText(text);
+    //#ifdef __WXMSW__
+    //	btnOk->SetBackgroundColour(backColor);
+    //	btnOk->SetForegroundColour(fontColor);
+    //#endif
+    // ----------
+    setWindowTitle(caption);
+    sizePolicy().setHorizontalPolicy(QSizePolicy::Expanding);
+    sizePolicy().setVerticalPolicy(QSizePolicy::Expanding);
+    m_desc.sizePolicy().setHorizontalPolicy(QSizePolicy::Expanding);
+    m_desc.sizePolicy().setVerticalPolicy(QSizePolicy::Expanding);
+    connect(&okButton, SIGNAL(clicked()), this, SLOT(close()));
+    okButton.setGeometry(QRect(10, 130, 100, 20));
+    okButton.setText("OK");
+    okButton.setDefault(true);
+    okButton.setFont(font);
+    okButton.setFocus(Qt::PopupFocusReason);
+
+    resize(450,100);
+
+    layout.addWidget(&m_desc);
+    layout.addWidget(&okButton);
+    setLayout(&layout);
+    setModal(true);
+    //exec();
+}
+
+void QspMsgDlg::OnLinkClicked(const QUrl &url)
+{
+    QString href;
+    href = url.toString();
+
+    if (href.startsWith("#"))
+    {
+        m_desc.setSource(url);
+    }
+    else if (href.toUpper().startsWith("EXEC:", Qt::CaseInsensitive)) //NOTE: was not part of original player
+    {
+        QString string = href.mid(5);
+        if (!QSPExecString(qspStringFromQString(string), QSP_TRUE))
+            if(parent() != 0)
+                qobject_cast<MainWindow*>(this->parent())->ShowError(); //TODO: replace with signal
+    }
+    else
+    {
+        //LaunchDefaultBrowser(url); //TODO: this
+    }
+}

+ 45 - 0
qspmsgdlg.h

@@ -0,0 +1,45 @@
+#ifndef QSPMSGDLG_H
+#define QSPMSGDLG_H
+
+#include <QWidget>
+#include <QDialog>
+#include <QPushButton>
+#include <QVBoxLayout>
+#include <QString>
+#include <QFont>
+#include <QColor>
+
+#include "qsptextbox.h"
+
+namespace Ui {
+class QspMsgDlg;
+}
+
+class QspMsgDlg : public QDialog
+{
+    Q_OBJECT
+
+public:
+    explicit QspMsgDlg(QWidget *parent = 0);
+    QspMsgDlg(const QString &caption = "", const QString &text = "", QWidget *parent = 0);
+    QspMsgDlg(const QColor& backColor,
+              const QColor& fontColor,
+              const QFont& font,
+              const QString& caption,
+              const QString& text,
+              bool isHtml,
+              const QString& gamePath,
+              QWidget *parent = 0);
+    ~QspMsgDlg();
+
+private:
+    // Events
+    void OnLinkClicked(const QUrl &url);
+
+    // Fields
+    QspTextBox m_desc;
+    QVBoxLayout layout;
+    QPushButton okButton;
+};
+
+#endif // QSPMSGDLG_H

+ 174 - 0
qsptextbox.cpp

@@ -0,0 +1,174 @@
+#include "qsptextbox.h"
+
+#include <QFileInfo>
+#include <QPalette>
+
+#include "comtools.h"
+
+QspTextBox::QspTextBox(QWidget *parent) : QTextBrowser(parent)
+{
+    //SetBorders(5);
+    m_isUseHtml = false;
+    showPlainText = false;
+//	m_font = *wxNORMAL_FONT;
+//	m_outFormat = wxString::Format(
+//		wxT("<HTML><META HTTP-EQUIV = \"Content-Type\" CONTENT = \"text/html; charset=%s\">")
+//		wxT("<BODY><FONT COLOR = #%%s>%%s</FONT></BODY></HTML>"),
+//		wxFontMapper::GetEncodingName(wxLocale::GetSystemEncoding()).wx_str()
+//	);
+//	wxString fontName(m_font.GetFaceName());
+//	SetStandardFonts(m_font.GetPointSize(), fontName, fontName);
+}
+
+QspTextBox::~QspTextBox()
+{
+
+}
+
+void QspTextBox::SetIsHtml(bool isHtml)
+{
+    if (m_isUseHtml != isHtml)
+    {
+        m_isUseHtml = isHtml;
+        RefreshUI();
+    }
+}
+
+void QspTextBox::RefreshUI(bool isScroll)
+{
+    QString color(QSPTools::GetHexColor(GetForegroundColour()));
+    QString text(QSPTools::HtmlizeWhitespaces(m_isUseHtml ? m_text : QSPTools::ProceedAsPlain(m_text)));
+    //TODO: set colour and font
+    if(showPlainText)
+        setPlainText(text);
+    else
+        setHtml(text);
+    //TODO: scroll
+//	if (isScroll) Scroll(0, 0x7FFFFFFF);
+}
+
+void QspTextBox::LoadBackImage(const QString& fileName)
+{
+    QFileInfo file(fileName);
+    QString path(file.absoluteFilePath());
+    if (m_imagePath != path)
+    {
+        m_imagePath = path;
+        if (file.exists() && file.isFile())
+        {
+            QPixmap image;
+            if (image.load(path))
+            {
+                SetBackgroundImage(image);
+                //Refresh();
+                return;
+            }
+        }
+        SetBackgroundImage(QPixmap());
+        //Refresh();
+    }
+}
+
+void QspTextBox::SetText(const QString& text, bool isScroll)
+{
+    if (m_text != text)
+    {
+        if (isScroll)
+        {
+            if (m_text.isEmpty() || !text.startsWith(m_text))
+                isScroll = false;
+        }
+        m_text = text;
+        RefreshUI(isScroll);
+    }
+}
+
+void QspTextBox::SetTextFont(const QFont& font)
+{
+    if (m_font != font)
+    {
+        m_font = font;
+        setFont(font); //TODO: check which one of this to use
+        setCurrentFont(font);
+    }
+}
+
+void QspTextBox::SetLinkColor(const QColor& clr)
+{
+    //QPalette p = palette();
+    //p.setBrush( QPalette::Link, clr);
+    //setPalette( p );
+    if(m_linkColor != clr)
+    {
+        QString sheet = QString::fromLatin1("a { text-decoration: underline; color: %1 }").arg(clr.name());
+        document()->setDefaultStyleSheet(sheet);
+        m_linkColor = clr;
+    }
+    //update();
+    RefreshUI();
+}
+
+void QspTextBox::SetGamePath(const QString &path)
+{
+    m_path = path;
+    setSearchPaths(QStringList(path));
+}
+
+void QspTextBox::SetBackgroundImage(const QPixmap& bmpBg)
+{
+    m_bmpBg = bmpBg;
+    //CalcImageSize();
+}
+
+//TODO: this
+//Returns the background colour of the window.
+QColor QspTextBox::GetBackgroundColour()
+{
+    return QColor(Qt::black);
+}
+
+//The meaning of foreground colour varies according to the window class; it may be the text colour or other colour, or it may not be used at all. Additionally, not all native controls support changing their foreground colour so this method may change their colour only partially or even not at all.
+QColor QspTextBox::GetForegroundColour()
+{
+    return QColor(Qt::black);
+}
+
+//Returns true if the colour was really changed, false if it was already set to this colour and nothing was done.
+bool QspTextBox::SetBackgroundColour(const QColor &colour)
+{
+    return true;
+}
+
+bool QspTextBox::SetForegroundColour(const QColor &colour)
+{
+    return true;
+}
+
+void QspTextBox::SetShowPlainText(bool isPlain)
+{
+    showPlainText = isPlain;
+    RefreshUI();
+}
+
+//void QSPTextBox::CalcImageSize()
+//{
+//    if (m_bmpBg.Ok())
+//    {
+//        int w, h;
+//        GetClientSize(&w, &h);
+//        if (w < 1) w = 1;
+//        if (h < 1) h = 1;
+//        int srcW = m_bmpBg.GetWidth(), srcH = m_bmpBg.GetHeight();
+//        int destW = srcW * h / srcH, destH = srcH * w / srcW;
+//        if (destW > w)
+//            destW = w;
+//        else
+//            destH = h;
+//        m_posX = (w - destW) / 2;
+//        m_posY = (h - destH) / 2;
+//        if (destW > 0 && destH > 0)
+//            m_bmpRealBg = wxBitmap(m_bmpBg.ConvertToImage().Scale(destW, destH));
+//        else
+//            m_bmpRealBg = wxNullBitmap;
+//    }
+//}

+ 62 - 0
qsptextbox.h

@@ -0,0 +1,62 @@
+#ifndef QSPTEXTBOX_H
+#define QSPTEXTBOX_H
+
+#include <QWidget>
+#include <QTextBrowser>
+#include <QString>
+#include <QFont>
+#include <QColor>
+#include <QPixmap>
+
+namespace Ui {
+class QspTextBox;
+}
+
+class QspTextBox : public QTextBrowser
+{
+    Q_OBJECT
+
+public:
+    explicit QspTextBox(QWidget *parent = 0);
+    ~QspTextBox();
+
+    // Methods
+    void RefreshUI(bool isScroll = false);
+    void LoadBackImage(const QString& fileName);
+
+    // Accessors
+    void SetIsHtml(bool isHtml);
+    void SetText(const QString& text, bool isScroll = false);
+    void SetTextFont(const QFont& font);
+    QFont GetTextFont() const { return m_font; }
+    QString GetText() const { return m_text; }
+    void SetLinkColor(const QColor& clr);
+    QColor GetLinkColor() { return m_linkColor; }
+    void SetGamePath(const QString& path);
+    void SetBackgroundImage(const QPixmap& bmpBg);
+    QColor GetBackgroundColour();
+    QColor GetForegroundColour();
+    bool SetBackgroundColour(const QColor& colour);
+    bool SetForegroundColour(const QColor& colour);
+    void SetShowPlainText(bool isPlain);
+
+private:
+    // Internal methods
+    void CalcImageSize();
+
+    // Fields
+    bool m_isUseHtml;
+    QString m_outFormat;
+    QString m_path;
+    QString m_imagePath;
+    QFont m_font;
+    QString m_text;
+    QPixmap m_bmpBg;
+    QPixmap m_bmpRealBg;
+    int m_posX;
+    int m_posY;
+    QColor m_linkColor;
+    bool showPlainText;
+};
+
+#endif // QSPTEXTBOX_H