commit
1d84cbe963
@ -0,0 +1,3 @@
|
||||
[submodule "extern/breakpad"]
|
||||
path = extern/breakpad
|
||||
url = https://chromium.googlesource.com/breakpad/breakpad
|
@ -1,21 +1,24 @@
|
||||
#!/bin/sh
|
||||
echo -n | openssl s_client -connect scan.coverity.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | sudo tee -a /etc/ssl/certs/ca-
|
||||
sudo ln -s /usr/bin/clang-3.6 /usr/local/bin/clang
|
||||
sudo ln -s /usr/bin/clang++-3.6 /usr/local/bin/clang++
|
||||
|
||||
if [ "${ANALYZE}" ]; then
|
||||
echo "yes" | sudo add-apt-repository "deb http://llvm.org/apt/`lsb_release -sc`/ llvm-toolchain-`lsb_release -sc`-3.6 main"
|
||||
wget -O - http://llvm.org/apt/llvm-snapshot.gpg.key|sudo apt-key add -
|
||||
fi
|
||||
|
||||
echo "yes" | sudo add-apt-repository "deb http://archive.ubuntu.com/ubuntu `lsb_release -sc` main universe restricted multiverse"
|
||||
echo "yes" | sudo apt-add-repository ppa:openmw/openmw
|
||||
sudo apt-get update -qq
|
||||
sudo apt-get install -qq libgtest-dev google-mock
|
||||
sudo apt-get install -qq libboost-filesystem-dev libboost-program-options-dev libboost-system-dev libboost-thread-dev
|
||||
sudo apt-get install -qq libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libswresample-dev
|
||||
sudo apt-get install -qq libbullet-dev libopenscenegraph-dev libmygui-dev libsdl2-dev libunshield-dev libtinyxml-dev libopenal-dev libqt4-dev
|
||||
if [ "${ANALYZE}" ]; then sudo apt-get install -qq clang-3.6; fi
|
||||
# build libgtest & libgtest_main
|
||||
sudo mkdir /usr/src/gtest/build
|
||||
cd /usr/src/gtest/build
|
||||
sudo cmake .. -DBUILD_SHARED_LIBS=1
|
||||
sudo make -j4
|
||||
sudo ln -s /usr/src/gtest/build/libgtest.so /usr/lib/libgtest.so
|
||||
sudo ln -s /usr/src/gtest/build/libgtest_main.so /usr/lib/libgtest_main.so
|
||||
|
||||
cd ~/
|
||||
git clone https://github.com/TES3MP/RakNet
|
||||
cd RakNet
|
||||
cmake . -DRAKNET_ENABLE_DLL=OFF -DRAKNET_ENABLE_SAMPLES=OFF -DCMAKE_BUILD_TYPE=Release
|
||||
mkdir ./lib
|
||||
make -j3 install
|
||||
cp ./Lib/RakNetLibStatic/libRakNetLibStatic.a ./lib
|
||||
cd ..
|
||||
|
||||
wget https://github.com/zdevito/terra/releases/download/release-2016-03-25/terra-Linux-x86_64-332a506.zip
|
||||
unzip terra-Linux-x86_64-332a506.zip
|
||||
|
@ -1,9 +1,11 @@
|
||||
#!/bin/sh
|
||||
|
||||
export CXX=clang++
|
||||
export CC=clang
|
||||
|
||||
brew tap openmw/openmw
|
||||
brew update
|
||||
brew unlink boost
|
||||
brew install openmw-mygui openmw-bullet openmw-sdl2 openmw-ffmpeg openmw/openmw/qt unshield
|
||||
|
||||
brew rm cmake || true
|
||||
brew rm pkgconfig || true
|
||||
brew rm qt5 || true
|
||||
brew install cmake pkgconfig $macos_qt_formula
|
||||
|
||||
curl https://downloads.openmw.org/osx/dependencies/openmw-deps-0ecece4.zip -o ~/openmw-deps.zip
|
||||
unzip ~/openmw-deps.zip -d /private/tmp/openmw-deps > /dev/null
|
||||
|
@ -1,5 +1,22 @@
|
||||
#!/bin/sh
|
||||
|
||||
export CXX=clang++
|
||||
export CC=clang
|
||||
|
||||
DEPENDENCIES_ROOT="/private/tmp/openmw-deps/openmw-deps"
|
||||
QT_PATH=`brew --prefix $macos_qt_formula`
|
||||
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DCMAKE_FRAMEWORK_PATH="/usr/local/lib/macosx/Release" -DCMAKE_EXE_LINKER_FLAGS="-F/usr/local/lib/macosx/Release" -DCMAKE_CXX_FLAGS="-stdlib=libstdc++" -DCMAKE_BUILD_TYPE=Debug -DBUILD_MYGUI_PLUGIN=OFF -G"Unix Makefiles" ..
|
||||
|
||||
cmake \
|
||||
-D CMAKE_PREFIX_PATH="$DEPENDENCIES_ROOT;$QT_PATH" \
|
||||
-D CMAKE_OSX_DEPLOYMENT_TARGET="10.8" \
|
||||
-D CMAKE_OSX_SYSROOT="macosx10.12" \
|
||||
-D CMAKE_BUILD_TYPE=Debug \
|
||||
-D OPENMW_OSX_DEPLOYMENT=TRUE \
|
||||
-D DESIRED_QT_VERSION=5 \
|
||||
-D BUILD_ESMTOOL=FALSE \
|
||||
-D BUILD_MYGUI_PLUGIN=FALSE \
|
||||
-G"Unix Makefiles" \
|
||||
..
|
||||
|
@ -1,107 +1,41 @@
|
||||
OpenMW
|
||||
TES3MP
|
||||
======
|
||||
|
||||
[![Build Status](https://api.travis-ci.org/OpenMW/openmw.svg)](https://travis-ci.org/OpenMW/openmw) [![Build status](https://ci.appveyor.com/api/projects/status/e6bqw8oouy8ufd46?svg=true)](https://ci.appveyor.com/project/scrawl/openmw) [![Coverity Scan Build Status](https://scan.coverity.com/projects/3740/badge.svg)](https://scan.coverity.com/projects/3740)
|
||||
[![Build Status](https://travis-ci.org/TES3MP/openmw-tes3mp.svg?branch=master)](https://travis-ci.org/TES3MP/openmw-tes3mp)
|
||||
|
||||
OpenMW is a recreation of the engine for the popular role-playing game Morrowind by Bethesda Softworks. You need to own and install the original game for OpenMW to work.
|
||||
TES3MP is a project aiming to add multiplayer functionality to [OpenMW](https://github.com/OpenMW/openmw), a free and open source recreation of the popular Bethesda Softworks game "The Elder Scrolls III: Morrowind".
|
||||
|
||||
OpenMW also comes with OpenMW-CS, a replacement for Morrowind's TES Construction Set.
|
||||
|
||||
* Version: 0.38.0
|
||||
* License: GPL (see docs/license/GPL3.txt for more information)
|
||||
* Website: http://www.openmw.org
|
||||
* IRC: #openmw on irc.freenode.net
|
||||
* Version: 0.5.1
|
||||
* License: GPLv3 (see docs/license/GPL3.txt for more information)
|
||||
* Website: https://steamcommunity.com/groups/mwmulti
|
||||
|
||||
Font Licenses:
|
||||
* DejaVuLGCSansMono.ttf: custom (see docs/license/DejaVu Font License.txt for more information)
|
||||
|
||||
Current Status
|
||||
Project Status
|
||||
--------------
|
||||
|
||||
The main quests in Morrowind, Tribunal and Bloodmoon are all completable. Some issues with side quests are to be expected (but rare). Check the [bug tracker](https://bugs.openmw.org/versions/21) for a list of issues we need to resolve before the "1.0" release. Even before the "1.0" release however, OpenMW boasts some new [features](https://wiki.openmw.org/index.php?title=Features), such as improved graphics and user interfaces.
|
||||
|
||||
Pre-existing modifications created for the original Morrowind engine can be hit-and-miss. The OpenMW script compiler performs more thorough error-checking than Morrowind does, meaning that a mod created for Morrowind may not necessarily run in OpenMW. Some mods also rely on quirky behaviour or engine bugs in order to work. We are considering such compatibility issues on a case-by-case basis - in some cases adding a workaround to OpenMW may be feasible, in other cases fixing the mod will be the only option. If you know of any mods that work or don't work, feel free to add them to the [Mod status](https://wiki.openmw.org/index.php?title=Mod_status) wiki page.
|
||||
[Version changelog](https://github.com/TES3MP/openmw-tes3mp/blob/master/tes3mp-changelog.md)
|
||||
|
||||
Getting Started
|
||||
---------------
|
||||
Our project is not yet in a playable state, though we are getting close. At the moment we have synchronization of character appearance, character skills, stats, attributes and death, movement in interiors and exteriors, melee and ranged combat, spell casting, picking up and dropping items in the world, using doors and levers, and adding and removing items from containers, as well as [serverside Lua scripts](https://github.com/TES3MP/PluginExamples) used to save and load the state of most of the aforementioned.
|
||||
|
||||
* [Official forums](https://forum.openmw.org/)
|
||||
* [Installation instructions](https://wiki.openmw.org/index.php?title=Installation_Instructions)
|
||||
* [Build from source](https://wiki.openmw.org/index.php?title=Development_Environment_Setup)
|
||||
* [Testing the game](https://wiki.openmw.org/index.php?title=Testing)
|
||||
* [How to contribute](https://wiki.openmw.org/index.php?title=Contribution_Wanted)
|
||||
* [Report a bug](http://bugs.openmw.org/projects/openmw) - read the [guidelines](https://wiki.openmw.org/index.php?title=Bug_Reporting_Guidelines) before submitting your first bug!
|
||||
* [Known issues](http://bugs.openmw.org/projects/openmw/issues?utf8=%E2%9C%93&set_filter=1&f%5B%5D=status_id&op%5Bstatus_id%5D=%3D&v%5Bstatus_id%5D%5B%5D=7&f%5B%5D=tracker_id&op%5Btracker_id%5D=%3D&v%5Btracker_id%5D%5B%5D=1&f%5B%5D=&c%5B%5D=project&c%5B%5D=tracker&c%5B%5D=status&c%5B%5D=priority&c%5B%5D=subject&c%5B%5D=assigned_to&c%5B%5D=updated_on&group_by=tracker)
|
||||
Contributing
|
||||
--------------
|
||||
|
||||
The data path
|
||||
-------------
|
||||
Development has been relatively fast, but any contribution regarding [code](https://github.com/TES3MP/openmw-tes3mp/blob/master/CONTRIBUTING.md), documentation, bug hunting or video showcases is greatly appreciated.
|
||||
|
||||
The data path tells OpenMW where to find your Morrowind files. If you run the launcher, OpenMW should be able to pick up the location of these files on its own, if both Morrowind and OpenMW are installed properly (installing Morrowind under WINE is considered a proper install).
|
||||
Test sessions are often advertised in [our Steam group](https://steamcommunity.com/groups/mwmulti) or [our Discord server](https://discord.gg/H8zhhuk).
|
||||
|
||||
Command line options
|
||||
--------------------
|
||||
Feel free to contact the [team members](https://github.com/TES3MP/openmw-tes3mp/blob/master/tes3mp-credits.md) for any questions you might have.
|
||||
|
||||
Syntax: openmw <options>
|
||||
Allowed options:
|
||||
--help print help message
|
||||
--version print version information and quit
|
||||
--data arg (=data) set data directories (later directories
|
||||
have higher priority)
|
||||
--data-local arg set local data directory (highest
|
||||
priority)
|
||||
--fallback-archive arg (=fallback-archive)
|
||||
set fallback BSA archives (later
|
||||
archives have higher priority)
|
||||
--resources arg (=resources) set resources directory
|
||||
--start arg set initial cell
|
||||
--content arg content file(s): esm/esp, or
|
||||
omwgame/omwaddon
|
||||
--no-sound [=arg(=1)] (=0) disable all sounds
|
||||
--script-verbose [=arg(=1)] (=0) verbose script output
|
||||
--script-all [=arg(=1)] (=0) compile all scripts (excluding dialogue
|
||||
scripts) at startup
|
||||
--script-all-dialogue [=arg(=1)] (=0) compile all dialogue scripts at startup
|
||||
--script-console [=arg(=1)] (=0) enable console-only script
|
||||
functionality
|
||||
--script-run arg select a file containing a list of
|
||||
console commands that is executed on
|
||||
startup
|
||||
--script-warn [=arg(=1)] (=1) handling of warnings when compiling
|
||||
scripts
|
||||
0 - ignore warning
|
||||
1 - show warning but consider script as
|
||||
correctly compiled anyway
|
||||
2 - treat warnings as errors
|
||||
--script-blacklist arg ignore the specified script (if the use
|
||||
of the blacklist is enabled)
|
||||
--script-blacklist-use [=arg(=1)] (=1)
|
||||
enable script blacklisting
|
||||
--load-savegame arg load a save game file on game startup
|
||||
(specify an absolute filename or a
|
||||
filename relative to the current
|
||||
working directory)
|
||||
--skip-menu [=arg(=1)] (=0) skip main menu on game startup
|
||||
--new-game [=arg(=1)] (=0) run new game sequence (ignored if
|
||||
skip-menu=0)
|
||||
--fs-strict [=arg(=1)] (=0) strict file system handling (no case
|
||||
folding)
|
||||
--encoding arg (=win1252) Character encoding used in OpenMW game
|
||||
messages:
|
||||
Getting Started
|
||||
---------------
|
||||
|
||||
win1250 - Central and Eastern European
|
||||
such as Polish, Czech, Slovak,
|
||||
Hungarian, Slovene, Bosnian, Croatian,
|
||||
Serbian (Latin script), Romanian and
|
||||
Albanian languages
|
||||
* [Community forums](https://steamcommunity.com/groups/mwmulti)
|
||||
* [Installation and build instructions](https://github.com/TES3MP/openmw-tes3mp/wiki/Installation-and-build-instructions)
|
||||
* [Known issues and bug reports](https://github.com/TES3MP/openmw-tes3mp/issues)
|
||||
|
||||
win1251 - Cyrillic alphabet such as
|
||||
Russian, Bulgarian, Serbian Cyrillic
|
||||
and other languages
|
||||
Donations
|
||||
---------------
|
||||
|
||||
win1252 - Western European (Latin)
|
||||
alphabet, used by default
|
||||
--fallback arg fallback values
|
||||
--no-grab Don't grab mouse cursor
|
||||
--export-fonts [=arg(=1)] (=0) Export Morrowind .fnt fonts to PNG
|
||||
image and XML file in current directory
|
||||
--activate-dist arg (=-1) activation distance override
|
||||
You can benefit the project by supporting OpenMW and/or by [becoming Koncord's patron](https://www.patreon.com/Koncord).
|
||||
|
@ -0,0 +1,93 @@
|
||||
|
||||
set (CMAKE_CXX_STANDARD 11)
|
||||
|
||||
set(BROWSER_UI
|
||||
${CMAKE_SOURCE_DIR}/files/tes3mp/ui/Main.ui
|
||||
${CMAKE_SOURCE_DIR}/files/tes3mp/ui/ServerInfo.ui
|
||||
)
|
||||
set(BROWSER
|
||||
main.cpp
|
||||
MainWindow.cpp
|
||||
ServerModel.cpp
|
||||
NetController.cpp
|
||||
ServerInfoDialog.cpp
|
||||
MySortFilterProxyModel.cpp
|
||||
netutils/HTTPNetwork.cpp
|
||||
netutils/Utils.cpp
|
||||
${CMAKE_SOURCE_DIR}/files/tes3mp/browser.rc
|
||||
)
|
||||
|
||||
set(BROWSER_HEADER_MOC
|
||||
MainWindow.hpp
|
||||
ServerModel.hpp
|
||||
ServerInfoDialog.hpp
|
||||
MySortFilterProxyModel.hpp
|
||||
)
|
||||
|
||||
set(BROWSER_HEADER
|
||||
${BROWSER_HEADER_MOC}
|
||||
NetController.hpp
|
||||
netutils/HTTPNetwork.hpp
|
||||
netutils/Utils.hpp
|
||||
)
|
||||
|
||||
source_group(browser FILES ${BROWSER} ${BROWSER_HEADER})
|
||||
|
||||
set(QT_USE_QTGUI 1)
|
||||
|
||||
# Set some platform specific settings
|
||||
if(WIN32)
|
||||
set(GUI_TYPE WIN32)
|
||||
set(QT_USE_QTMAIN TRUE)
|
||||
endif(WIN32)
|
||||
|
||||
if (DESIRED_QT_VERSION MATCHES 4)
|
||||
message(SEND_ERROR "QT4 is not supported.")
|
||||
#include(${QT_USE_FILE})
|
||||
#QT4_ADD_RESOURCES(RCC_SRCS ${CMAKE_SOURCE_DIR}/files/launcher/launcher.qrc)
|
||||
#QT4_WRAP_CPP(MOC_SRCS ${BROWSER_HEADER_MOC})
|
||||
#QT4_WRAP_UI(UI_HDRS ${BROWSER_UI})
|
||||
else()
|
||||
QT5_ADD_RESOURCES(RCC_SRCS ${CMAKE_SOURCE_DIR}/files/launcher/launcher.qrc)
|
||||
QT5_WRAP_CPP(MOC_SRCS ${BROWSER_HEADER_MOC})
|
||||
QT5_WRAP_UI(UI_HDRS ${BROWSER_UI})
|
||||
endif()
|
||||
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||
if(NOT WIN32)
|
||||
include_directories(${LIBUNSHIELD_INCLUDE_DIR})
|
||||
endif(NOT WIN32)
|
||||
|
||||
# Main executable
|
||||
add_executable(tes3mp-browser
|
||||
${GUI_TYPE}
|
||||
${BROWSER}
|
||||
${BROWSER_HEADER}
|
||||
${RCC_SRCS}
|
||||
${MOC_SRCS}
|
||||
${UI_HDRS}
|
||||
)
|
||||
|
||||
if (WIN32)
|
||||
INSTALL(TARGETS tes3mp-browser RUNTIME DESTINATION ".")
|
||||
endif (WIN32)
|
||||
|
||||
target_link_libraries(tes3mp-browser
|
||||
${SDL2_LIBRARY_ONLY}
|
||||
${RakNet_LIBRARY}
|
||||
components
|
||||
)
|
||||
|
||||
if (DESIRED_QT_VERSION MATCHES 4)
|
||||
# target_link_libraries(tes3mp-browser ${QT_QTGUI_LIBRARY} ${QT_QTCORE_LIBRARY})
|
||||
# if(WIN32)
|
||||
# target_link_libraries(tes3mp-browser ${QT_QTMAIN_LIBRARY})
|
||||
# endif(WIN32)
|
||||
else()
|
||||
qt5_use_modules(tes3mp-browser Widgets Core)
|
||||
endif()
|
||||
|
||||
if (BUILD_WITH_CODE_COVERAGE)
|
||||
add_definitions (--coverage)
|
||||
target_link_libraries(tes3mp-browser gcov)
|
||||
endif()
|
@ -0,0 +1,228 @@
|
||||
//
|
||||
// Created by koncord on 06.01.17.
|
||||
//
|
||||
|
||||
#include "MainWindow.hpp"
|
||||
#include "NetController.hpp"
|
||||
#include "ServerInfoDialog.hpp"
|
||||
#include "components/files/configurationmanager.hpp"
|
||||
#include <qdebug.h>
|
||||
#include <QInputDialog>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonArray>
|
||||
#include <QFile>
|
||||
#include <QJsonDocument>
|
||||
|
||||
using namespace Process;
|
||||
|
||||
MainWindow::MainWindow(QWidget *parent)
|
||||
{
|
||||
setupUi(this);
|
||||
|
||||
mGameInvoker = new ProcessInvoker();
|
||||
|
||||
browser = new ServerModel;
|
||||
favorites = new ServerModel;
|
||||
proxyModel = new MySortFilterProxyModel(this);
|
||||
proxyModel->setSourceModel(browser);
|
||||
tblServerBrowser->setModel(proxyModel);
|
||||
tblFavorites->setModel(proxyModel);
|
||||
|
||||
tblServerBrowser->hideColumn(ServerData::ADDR);
|
||||
tblFavorites->hideColumn(ServerData::ADDR);
|
||||
|
||||
connect(tabWidget, SIGNAL(currentChanged(int)), this, SLOT(tabSwitched(int)));
|
||||
connect(actionAdd, SIGNAL(triggered(bool)), this, SLOT(addServer()));
|
||||
connect(actionAdd_by_IP, SIGNAL(triggered(bool)), this, SLOT(addServerByIP()));
|
||||
connect(actionDelete, SIGNAL(triggered(bool)), this, SLOT(deleteServer()));
|
||||
connect(actionRefresh, SIGNAL(triggered(bool)), this, SLOT(refresh()));
|
||||
connect(actionPlay, SIGNAL(triggered(bool)), this, SLOT(play()));
|
||||
connect(tblServerBrowser, SIGNAL(clicked(QModelIndex)), this, SLOT(serverSelected()));
|
||||
connect(tblFavorites, SIGNAL(clicked(QModelIndex)), this, SLOT(serverSelected()));
|
||||
connect(tblFavorites, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(play()));
|
||||
connect(tblServerBrowser, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(play()));
|
||||
connect(cBoxNotFull, SIGNAL(toggled(bool)), this, SLOT(notFullSwitch(bool)));
|
||||
connect(cBoxWithPlayers, SIGNAL(toggled(bool)), this, SLOT(havePlayersSwitch(bool)));
|
||||
connect(comboLatency, SIGNAL(currentIndexChanged(int)), this, SLOT(maxLatencyChanged(int)));
|
||||
connect(leGamemode, SIGNAL(textChanged(const QString &)), this, SLOT(gamemodeChanged(const QString &)));
|
||||
loadFavorites();
|
||||
}
|
||||
|
||||
MainWindow::~MainWindow()
|
||||
{
|
||||
delete mGameInvoker;
|
||||
}
|
||||
|
||||
void MainWindow::addServerAndUpdate(QString addr)
|
||||
{
|
||||
favorites->insertRow(0);
|
||||
QModelIndex mi = favorites->index(0, ServerData::ADDR);
|
||||
favorites->setData(mi, addr, Qt::EditRole);
|
||||
NetController::get()->updateInfo(favorites, mi);
|
||||
}
|
||||
|
||||
void MainWindow::addServer()
|
||||
{
|
||||
int id = tblServerBrowser->selectionModel()->currentIndex().row();
|
||||
|
||||
if(id >= 0)
|
||||
{
|
||||
int sourceId = proxyModel->mapToSource(proxyModel->index(id, ServerData::ADDR)).row();
|
||||
favorites->myData.push_back(browser->myData[sourceId]);
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::addServerByIP()
|
||||
{
|
||||
bool ok;
|
||||
QString text = QInputDialog::getText(this, tr("Add Server by address"), tr("Address:"), QLineEdit::Normal, "", &ok);
|
||||
if(ok && !text.isEmpty())
|
||||
addServerAndUpdate(text);
|
||||
}
|
||||
|
||||
void MainWindow::deleteServer()
|
||||
{
|
||||
if(tabWidget->currentIndex() != 1)
|
||||
return;
|
||||
int id = tblFavorites->selectionModel()->currentIndex().row();
|
||||
if(id >= 0)
|
||||
{
|
||||
int sourceId = proxyModel->mapToSource(proxyModel->index(id, ServerData::ADDR)).row();
|
||||
favorites->removeRow(sourceId);
|
||||
if(favorites->myData.isEmpty())
|
||||
{
|
||||
actionPlay->setEnabled(false);
|
||||
actionDelete->setEnabled(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool MainWindow::refresh()
|
||||
{
|
||||
return NetController::get()->updateInfo(proxyModel->sourceModel());
|
||||
/*tblServerBrowser->resizeColumnToContents(ServerData::HOSTNAME);
|
||||
tblServerBrowser->resizeColumnToContents(ServerData::MODNAME);
|
||||
tblFavorites->resizeColumnToContents(ServerData::HOSTNAME);
|
||||
tblFavorites->resizeColumnToContents(ServerData::MODNAME);*/
|
||||
}
|
||||
|
||||
void MainWindow::play()
|
||||
{
|
||||
QTableView *curTable = tabWidget->currentIndex() ? tblFavorites : tblServerBrowser;
|
||||
int id = curTable->selectionModel()->currentIndex().row();
|
||||
if(id < 0)
|
||||
return;
|
||||
|
||||
ServerInfoDialog infoDialog(this);
|
||||
ServerModel *sm = ((ServerModel*)proxyModel->sourceModel());
|
||||
|
||||
int sourceId = proxyModel->mapToSource(proxyModel->index(id, ServerData::ADDR)).row();
|
||||
NetController::get()->selectServer(&sm->myData[sourceId]);
|
||||
infoDialog.refresh();
|
||||
if(!infoDialog.exec())
|
||||
return;
|
||||
|
||||
QStringList arguments;
|
||||
arguments.append(QLatin1String("--connect=") + sm->myData[sourceId].addr.toLatin1());
|
||||
|
||||
if(sm->myData[sourceId].needPassw)
|
||||
{
|
||||
bool ok;
|
||||
QString passw = QInputDialog::getText(this, "Connecting to: " + sm->myData[sourceId].addr, "Password: ", QLineEdit::Password, "", &ok);
|
||||
if(!ok)
|
||||
return;
|
||||
arguments.append(QLatin1String("--password=") + passw.toLatin1());
|
||||
}
|
||||
|
||||
if (mGameInvoker->startProcess(QLatin1String("tes3mp"), arguments, true))
|
||||
return qApp->quit();
|
||||
}
|
||||
|
||||
void MainWindow::tabSwitched(int index)
|
||||
{
|
||||
if(index == 0)
|
||||
{
|
||||
proxyModel->setSourceModel(browser);
|
||||
actionDelete->setEnabled(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
proxyModel->setSourceModel(favorites);
|
||||
}
|
||||
actionPlay->setEnabled(false);
|
||||
actionAdd->setEnabled(false);
|
||||
}
|
||||
|
||||
void MainWindow::serverSelected()
|
||||
{
|
||||
actionPlay->setEnabled(true);
|
||||
if(tabWidget->currentIndex() == 0)
|
||||
actionAdd->setEnabled(true);
|
||||
if(tabWidget->currentIndex() == 1)
|
||||
actionDelete->setEnabled(true);
|
||||
}
|
||||
|
||||
void MainWindow::closeEvent(QCloseEvent *event)
|
||||
{
|
||||
Files::ConfigurationManager cfgMgr;
|
||||
QString cfgPath = QString::fromStdString((cfgMgr.getUserConfigPath() / "favorites.dat").string());
|
||||
|
||||
QJsonArray saveData;
|
||||
for(auto server : favorites->myData)
|
||||
saveData.push_back(server.addr);
|
||||
|
||||
QFile file(cfgPath);
|
||||
|
||||
if(!file.open(QIODevice::WriteOnly))
|
||||
{
|
||||
qDebug() << "Cannot save " << cfgPath;
|
||||
return;
|
||||
}
|
||||
|
||||
file.write(QJsonDocument(saveData).toJson());
|
||||
file.close();
|
||||
}
|
||||
|
||||
|
||||
void MainWindow::loadFavorites()
|
||||
{
|
||||
Files::ConfigurationManager cfgMgr;
|
||||
QString cfgPath = QString::fromStdString((cfgMgr.getUserConfigPath() / "favorites.dat").string());
|
||||
|
||||
QFile file(cfgPath);
|
||||
if(!file.open(QIODevice::ReadOnly))
|
||||
{
|
||||
qDebug() << "Cannot open " << cfgPath;
|
||||
return;
|
||||
}
|
||||
|
||||
QJsonDocument jsonDoc(QJsonDocument::fromJson(file.readAll()));
|
||||
|
||||
for(auto server : jsonDoc.array())
|
||||
addServerAndUpdate(server.toString());
|
||||
|
||||
file.close();
|
||||
}
|
||||
|
||||
void MainWindow::notFullSwitch(bool state)
|
||||
{
|
||||
proxyModel->filterFullServer(state);
|
||||
}
|
||||
|
||||
void MainWindow::havePlayersSwitch(bool state)
|
||||
{
|
||||
proxyModel->filterEmptyServers(state);
|
||||
}
|
||||
|
||||
void MainWindow::maxLatencyChanged(int index)
|
||||
{
|
||||
int maxLatency = index * 50;
|
||||
proxyModel->pingLessThan(maxLatency);
|
||||
|
||||
}
|
||||
|
||||
void MainWindow::gamemodeChanged(const QString &text)
|
||||
{
|
||||
proxyModel->setFilterFixedString(text);
|
||||
proxyModel->setFilterKeyColumn(ServerData::MODNAME);
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
//
|
||||
// Created by koncord on 06.01.17.
|
||||
//
|
||||
|
||||
#ifndef NEWLAUNCHER_MAIN_HPP
|
||||
#define NEWLAUNCHER_MAIN_HPP
|
||||
|
||||
|
||||
#include "ui_Main.h"
|
||||
#include "ServerModel.hpp"
|
||||
#include "MySortFilterProxyModel.hpp"
|
||||
#include <components/process/processinvoker.hpp>
|
||||
|
||||
class MainWindow : public QMainWindow, private Ui::MainWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit MainWindow(QWidget *parent = 0);
|
||||
virtual ~MainWindow();
|
||||
protected:
|
||||
void closeEvent(QCloseEvent * event) Q_DECL_OVERRIDE;
|
||||
void addServerAndUpdate(QString addr);
|
||||
public slots:
|
||||
bool refresh();
|
||||
protected slots:
|
||||
void tabSwitched(int index);
|
||||
void addServer();
|
||||
void addServerByIP();
|
||||
void deleteServer();
|
||||
void play();
|
||||
void serverSelected();
|
||||
void notFullSwitch(bool state);
|
||||
void havePlayersSwitch(bool state);
|
||||
void maxLatencyChanged(int index);
|
||||
void gamemodeChanged(const QString &text);
|
||||
private:
|
||||
Process::ProcessInvoker *mGameInvoker;
|
||||
ServerModel *browser, *favorites;
|
||||
MySortFilterProxyModel *proxyModel;
|
||||
void loadFavorites();
|
||||
};
|
||||
|
||||
|
||||
#endif //NEWLAUNCHER_MAIN_HPP
|
@ -0,0 +1,54 @@
|
||||
//
|
||||
// Created by koncord on 30.01.17.
|
||||
//
|
||||
|
||||
#include "MySortFilterProxyModel.hpp"
|
||||
#include "ServerModel.hpp"
|
||||
|
||||
#include <qdebug.h>
|
||||
|
||||
bool MySortFilterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
|
||||
{
|
||||
|
||||
QModelIndex pingIndex = sourceModel()->index(sourceRow, ServerData::PING, sourceParent);
|
||||
QModelIndex plIndex = sourceModel()->index(sourceRow, ServerData::PLAYERS, sourceParent);
|
||||
QModelIndex maxPlIndex = sourceModel()->index(sourceRow, ServerData::MAX_PLAYERS, sourceParent);
|
||||
|
||||
int ping = sourceModel()->data(pingIndex).toInt();
|
||||
int players = sourceModel()->data(plIndex).toInt();
|
||||
int maxPlayers = sourceModel()->data(maxPlIndex).toInt();
|
||||
|
||||
if(maxPing > 0 && (ping == -1 || ping > maxPing))
|
||||
return false;
|
||||
if(filterEmpty && players == 0)
|
||||
return false;
|
||||
if(filterFull && players >= maxPlayers)
|
||||
return false;
|
||||
|
||||
return QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent);
|
||||
}
|
||||
|
||||
MySortFilterProxyModel::MySortFilterProxyModel(QObject *parent) : QSortFilterProxyModel(parent)
|
||||
{
|
||||
filterEmpty = false;
|
||||
filterFull = false;
|
||||
maxPing = 0;
|
||||
}
|
||||
|
||||
void MySortFilterProxyModel::filterEmptyServers(bool state)
|
||||
{
|
||||
filterEmpty = state;
|
||||
invalidateFilter();
|
||||
}
|
||||
|
||||
void MySortFilterProxyModel::filterFullServer(bool state)
|
||||
{
|
||||
filterFull = state;
|
||||
invalidateFilter();
|
||||
}
|
||||
|
||||
void MySortFilterProxyModel::pingLessThan(int maxPing)
|
||||
{
|
||||
this->maxPing = maxPing;
|
||||
invalidateFilter();
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
//
|
||||
// Created by koncord on 30.01.17.
|
||||
//
|
||||
|
||||
#ifndef OPENMW_MYSORTFILTERPROXYMODEL_HPP
|
||||
#define OPENMW_MYSORTFILTERPROXYMODEL_HPP
|
||||
|
||||
|
||||
#include <QSortFilterProxyModel>
|
||||
|
||||
class MySortFilterProxyModel : public QSortFilterProxyModel
|
||||
{
|
||||
Q_OBJECT
|
||||
protected:
|
||||
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const Q_DECL_FINAL;
|
||||
public:
|
||||
MySortFilterProxyModel(QObject *parent);
|
||||
void filterFullServer(bool state);
|
||||
void filterEmptyServers(bool state);
|
||||
void pingLessThan(int maxPing);
|
||||
private:
|
||||
bool filterEmpty, filterFull;
|
||||
int maxPing;
|
||||
};
|
||||
|
||||
|
||||
#endif //OPENMW_MYSORTFILTERPROXYMODEL_HPP
|
@ -0,0 +1,266 @@
|
||||
//
|
||||
// Created by koncord on 07.01.17.
|
||||
//
|
||||
|
||||
#include <cassert>
|
||||
#include <QtCore/QTime>
|
||||
#include "NetController.hpp"
|
||||
#include "qdebug.h"
|
||||
|
||||
#include <RakPeer.h>
|
||||
#include <RakSleep.h>
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonObject>
|
||||
#include <memory>
|
||||
#include <QtWidgets/QMessageBox>
|
||||
|
||||
using namespace std;
|
||||
|
||||
NetController *NetController::mThis = nullptr;
|
||||
|
||||
NetController *NetController::get()
|
||||
{
|
||||
assert(mThis);
|
||||
return mThis;
|
||||
}
|
||||
|
||||
void NetController::Create(std::string addr, unsigned short port)
|
||||
{
|
||||
assert(!mThis);
|
||||
mThis = new NetController(addr, port);
|
||||
}
|
||||
|
||||
void NetController::Destroy()
|
||||
{
|
||||
assert(mThis);
|
||||
delete mThis;
|
||||
mThis = nullptr;
|
||||
}
|
||||
|
||||
NetController::NetController(std::string addr, unsigned short port) : httpNetwork(addr, port)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
NetController::~NetController()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
struct pattern
|
||||
{
|
||||
pattern(QString value): value(value) {}
|
||||
bool operator()(const ServerData &data)
|
||||
{
|
||||
return value == data.addr;
|
||||
}
|
||||
QString value;
|
||||
};
|
||||
|
||||
void NetController::setData(QString address, QJsonObject server, ServerModel *model)
|
||||
{
|
||||
QModelIndex mi = model->index(0, ServerData::ADDR);
|
||||
model->setData(mi, address);
|
||||
|
||||
mi = model->index(0, ServerData::PLAYERS);
|
||||
model->setData(mi, server["players"].toInt());
|
||||
|
||||
mi = model->index(0, ServerData::MAX_PLAYERS);
|
||||
model->setData(mi, server["max_players"].toInt());
|
||||
|
||||
mi = model->index(0, ServerData::HOSTNAME);
|
||||
model->setData(mi, server["hostname"].toString());
|
||||
|
||||
mi = model->index(0, ServerData::MODNAME);
|
||||
model->setData(mi, server["modname"].toString());
|
||||
|
||||
mi = model->index(0, ServerData::VERSION);
|
||||
model->setData(mi, server["version"].toString());
|
||||
|
||||
mi = model->index(0, ServerData::PASSW);
|
||||
model->setData(mi, server["passw"].toBool());
|
||||
|
||||
mi = model->index(0, ServerData::PING);
|
||||
|
||||
// This *should* fix a crash when a port isn't returned by data.
|
||||
if(!address.contains(":"))
|
||||
address.append(":25565");
|
||||
QStringList addr = address.split(":");
|
||||
model->setData(mi, PingRakNetServer(addr[0].toLatin1().data(), addr[1].toUShort()));
|
||||
}
|
||||
|
||||
bool NetController::downloadInfo(QAbstractItemModel *pModel, QModelIndex index)
|
||||
{
|
||||
ServerModel *model = ((ServerModel *) pModel);
|
||||
|
||||
/*
|
||||
* download stuff
|
||||
*/
|
||||
|
||||
QString data;
|
||||
QJsonParseError err;
|
||||
|
||||
if(index.isValid() && index.row() >= 0)
|
||||
{
|
||||
const ServerData &sd = model->myData[index.row()];
|
||||
while(true)
|
||||
{
|
||||
data = QString::fromStdString(httpNetwork.getData((QString("/api/servers/") + sd.addr).toLatin1()));
|
||||
if (!data.isEmpty() && data != "NO_CONTENT" && data != "LOST_CONNECTION")
|
||||
break;
|
||||
RakSleep(30);
|
||||
}
|
||||
qDebug() << "Content for \"" << sd.addr << "\": " << data;
|
||||
|
||||
if(data == "bad request" || data == "not found") // TODO: if server is not registered we should download info directly from the server
|
||||
{
|
||||
qDebug() << "Server is not registered";
|
||||
return false;
|
||||
}
|
||||
|
||||
QJsonDocument jsonDocument = QJsonDocument::fromJson(data.toLatin1(), &err);
|
||||
QJsonObject server = jsonDocument.object()["server"].toObject();
|
||||
|
||||
setData(sd.addr, server, model);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
data = QString::fromStdString(httpNetwork.getData("/api/servers"));
|
||||
if (!data.isEmpty() && data != "NO_CONTENT" && data != "LOST_CONNECTION")
|
||||
break;
|
||||
RakSleep(30);
|
||||
}
|
||||
|
||||
if(data == "UNKNOWN_ADDRESS")
|
||||
{
|
||||
QMessageBox::critical(0, "Error", "Cannot connect to the master server!");
|
||||
return false;
|
||||
}
|
||||
|
||||
qDebug() << "Content: " << data;
|
||||
|
||||
QJsonDocument jsonDocument = QJsonDocument::fromJson(data.toLatin1(), &err);
|
||||
|
||||
QJsonObject listServers = jsonDocument.object()["list servers"].toObject();
|
||||
|
||||
for(auto iter = listServers.begin(); iter != listServers.end(); iter++)
|
||||
{
|
||||
QJsonObject server = iter->toObject();
|
||||
qDebug() << iter.key();
|
||||
qDebug() << server["hostname"].toString();
|
||||
qDebug() << server["modname"].toString();
|
||||
qDebug() << server["players"].toInt();
|
||||
qDebug() << server["max_players"].toInt();
|
||||
qDebug() << server["version"].toString();
|
||||
qDebug() << server["passw"].toBool();
|
||||
|
||||
QVector<ServerData>::Iterator value = std::find_if(model->myData.begin(), model->myData.end(), pattern(iter.key()));
|
||||
if(value == model->myData.end())
|
||||
model->insertRow(0);
|
||||
|
||||
setData(iter.key(), server, model);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NetController::updateInfo(QAbstractItemModel *pModel, QModelIndex index)
|
||||
{
|
||||
ServerModel *model = ((ServerModel*)pModel);
|
||||
|
||||
bool result;
|
||||
if (index.isValid() && index.row() >= 0)
|
||||
result = downloadInfo(pModel, index);
|
||||
else
|
||||
{
|
||||
for (auto iter = model->myData.begin(); iter != model->myData.end(); iter++)
|
||||
{
|
||||
qDebug() << iter->addr;
|
||||
}
|
||||
model->removeRows(0, model->rowCount(index));
|
||||
result = downloadInfo(pModel, index);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void NetController::updateInfo()
|
||||
{
|
||||
QString data;
|
||||
QString uri = "/api/servers/" + sd->addr;
|
||||
while (true)
|
||||
{
|
||||
data = QString::fromStdString(httpNetwork.getData(uri.toLatin1()));
|
||||
if (!data.isEmpty() && data != "NO_CONTENT" && data != "LOST_CONNECTION")
|
||||
break;
|
||||
RakSleep(30);
|
||||
}
|
||||
|
||||
if(data == "UNKNOWN_ADDRESS")
|
||||
{
|
||||
QMessageBox::critical(0, "Error", "Cannot connect to the master server!");
|
||||
return;
|
||||
}
|
||||
|
||||
qDebug() << "Content: " << data;
|
||||
|
||||
QJsonParseError err;
|
||||
QJsonDocument jsonDocument = QJsonDocument::fromJson(data.toLatin1(), &err);
|
||||
|
||||
QMap<QString, QVariant> map = jsonDocument.toVariant().toMap()["server"].toMap();
|
||||
|
||||
qDebug() << sd->addr;
|
||||
qDebug() << map["hostname"].toString();
|
||||
qDebug() << map["modname"].toString();
|
||||
qDebug() << map["players"].toInt();
|
||||
qDebug() << map["max_players"].toInt();
|
||||
qDebug() << map["version"].toString();
|
||||
qDebug() << map["passw"].toBool();
|
||||
|
||||
sd->hostName = map["hostname"].toString();
|
||||
sd->modName = map["modname"].toString();
|
||||
sd->players = map["players"].toInt();
|
||||
sd->maxPlayers = map["max_players"].toInt();
|
||||
|
||||
if(!sd->addr.contains(":"))
|
||||
sd->addr.append(":25565");
|
||||
QStringList addr = sd->addr.split(":");
|
||||
sd->ping = PingRakNetServer(addr[0].toLatin1(), addr[1].toUShort());
|
||||
if(sd->ping != PING_UNREACHABLE)
|
||||
sed = getExtendedData(addr[0].toLatin1(), addr[1].toUShort());
|
||||
else
|
||||
qDebug() << "Server is unreachable";
|
||||
}
|
||||
|
||||
QStringList NetController::players()
|
||||
{
|
||||
QStringList listPlayers;
|
||||
for(auto player = sed.players.begin(); player != sed.players.end(); player++)
|
||||
listPlayers.push_back(player->c_str());
|
||||
return listPlayers;
|
||||
}
|
||||
|
||||
QStringList NetController::plugins()
|
||||
{
|
||||
QStringList listPlugins;
|
||||
for(auto plugin = sed.plugins.begin(); plugin != sed.plugins.end(); plugin++)
|
||||
listPlugins.push_back(plugin->c_str());
|
||||
return listPlugins;
|
||||
}
|
||||
|
||||
void NetController::selectServer(ServerData *pServerData)
|
||||
{
|
||||
sd = pServerData;
|
||||
}
|
||||
|
||||
ServerData *NetController::selectedServer()
|
||||
{
|
||||
return sd;
|
||||
}
|
||||
|
@ -0,0 +1,42 @@
|
||||
//
|
||||
// Created by koncord on 07.01.17.
|
||||
//
|
||||
|
||||
#ifndef NEWLAUNCHER_NETCONTROLLER_HPP
|
||||
#define NEWLAUNCHER_NETCONTROLLER_HPP
|
||||
|
||||
|
||||
#include "ServerModel.hpp"
|
||||
#include "netutils/HTTPNetwork.hpp"
|
||||
#include "netutils/Utils.hpp"
|
||||
|
||||
struct ServerModel;
|
||||
|
||||
class NetController
|
||||
{
|
||||
public:
|
||||
static NetController *get();
|
||||
static void Create(std::string addr, unsigned short port);
|
||||
static void Destroy();
|
||||
bool updateInfo(QAbstractItemModel *pModel, QModelIndex index= QModelIndex());
|
||||
void updateInfo();
|
||||
QStringList players();
|
||||
QStringList plugins();
|
||||
void selectServer(ServerData *pServerData);
|
||||
ServerData *selectedServer();
|
||||
protected:
|
||||
NetController(std::string addr, unsigned short port);
|
||||
~NetController();
|
||||
private:
|
||||
NetController(const NetController &controller);
|
||||
bool downloadInfo(QAbstractItemModel *pModel, QModelIndex index);
|
||||
void setData(QString addr, QJsonObject server, ServerModel *model);
|
||||
|
||||
static NetController *mThis;
|
||||
ServerData *sd;
|
||||
HTTPNetwork httpNetwork;
|
||||
ServerExtendedData sed;
|
||||
};
|
||||
|
||||
|
||||
#endif //NEWLAUNCHER_NETCONTROLLER_HPP
|
@ -0,0 +1,39 @@
|
||||
//
|
||||
// Created by koncord on 07.01.17.
|
||||
//
|
||||
|
||||
#include "qdebug.h"
|
||||
#include "NetController.hpp"
|
||||
|
||||
#include "ServerInfoDialog.hpp"
|
||||
|
||||
ServerInfoDialog::ServerInfoDialog(QWidget *parent): QDialog(parent)
|
||||
{
|
||||
setupUi(this);
|
||||
connect(btnRefresh, SIGNAL(clicked()), this, SLOT(refresh()));
|
||||
}
|
||||
|
||||
ServerInfoDialog::~ServerInfoDialog()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void ServerInfoDialog::refresh()
|
||||
{
|
||||
NetController::get()->updateInfo();
|
||||
ServerData *sd = NetController::get()->selectedServer();
|
||||
if (sd)
|
||||
{
|
||||
leAddr->setText(sd->addr);
|
||||
lblName->setText(sd->hostName);
|
||||
lblPing->setNum(sd->ping);
|
||||
|
||||
listPlayers->clear();
|
||||
QStringList players = NetController::get()->players();
|
||||
listPlayers->addItems(players);
|
||||
listPlugins->clear();
|
||||
listPlugins->addItems(NetController::get()->plugins());
|
||||
|
||||
lblPlayers->setText(QString::number(players.size()) + " / " + QString::number(sd->maxPlayers));
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
//
|
||||
// Created by koncord on 07.01.17.
|
||||
//
|
||||
|
||||
#ifndef NEWLAUNCHER_SERVERINFODIALOG_HPP
|
||||
#define NEWLAUNCHER_SERVERINFODIALOG_HPP
|
||||
|
||||
#include "ui_ServerInfo.h"
|
||||
|
||||
class ServerInfoDialog : public QDialog, public Ui::Dialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ServerInfoDialog(QWidget *parent = 0);
|
||||
virtual ~ServerInfoDialog();
|
||||
public slots:
|
||||
void refresh();
|
||||
};
|
||||
|
||||
|
||||
#endif //NEWLAUNCHER_SERVERINFODIALOG_HPP
|
@ -0,0 +1,200 @@
|
||||
#include <qmessagebox.h>
|
||||
#include "ServerModel.hpp"
|
||||
#include <qdebug.h>
|
||||
|
||||
ServerModel::ServerModel(QObject *parent) : QAbstractTableModel(parent)
|
||||
{
|
||||
}
|
||||
|
||||
ServerModel::~ServerModel()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/*QHash<int, QByteArray> ServerModel::roleNames() const
|
||||
{
|
||||
return roles;
|
||||
}*/
|
||||
|
||||
QVariant ServerModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (!index.isValid())
|
||||
return QVariant();
|
||||
if (index.row() < 0 || index.row() > myData.size())
|
||||
return QVariant();
|
||||
|
||||
const ServerData &sd = myData.at(index.row());
|
||||
|
||||
if(role == Qt::DisplayRole)
|
||||
{
|
||||
QVariant var;
|
||||
switch (index.column())
|
||||
{
|
||||
case ServerData::ADDR:
|
||||
var = sd.addr;
|
||||
break;
|
||||
case ServerData::PASSW:
|
||||
var = sd.needPassw ? "Yes" : "No";
|
||||
break;
|
||||
case ServerData::VERSION:
|
||||
var = sd.version;
|
||||
break;
|
||||
case ServerData::PLAYERS:
|
||||
var = sd.players;
|
||||
break;
|
||||
case ServerData::MAX_PLAYERS:
|
||||
var = sd.maxPlayers;
|
||||
break;
|
||||
case ServerData::HOSTNAME:
|
||||
var = sd.hostName;
|
||||
break;
|
||||
case ServerData::PING:
|
||||
var = sd.ping;
|
||||
break;
|
||||
case ServerData::MODNAME:
|
||||
if(sd.modName.isEmpty())
|
||||
var = "default";
|
||||
else
|
||||
var = sd.modName;
|
||||
break;
|
||||
}
|
||||
return var;
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
QVariant ServerModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||
{
|
||||
QVariant var;
|
||||
if (orientation == Qt::Horizontal)
|
||||
{
|
||||
if (role == Qt::SizeHintRole)
|
||||
{
|
||||
/*if(section == ServerData::HOSTNAME)
|
||||
var = QSize(200, 25);*/
|
||||
}
|
||||
else if (role == Qt::DisplayRole)
|
||||
{
|
||||
|
||||
switch (section)
|
||||
{
|
||||
case ServerData::ADDR:
|
||||
var = "Address";
|
||||
break;
|
||||
case ServerData::PASSW:
|
||||
var = "Password";
|
||||
break;
|
||||
case ServerData::VERSION:
|
||||
var = "Version";
|
||||
break;
|
||||
case ServerData::HOSTNAME:
|
||||
var = "Host name";
|
||||
break;
|
||||
case ServerData::PLAYERS:
|
||||
var = "Players";
|
||||
break;
|
||||
case ServerData::MAX_PLAYERS:
|
||||
var = "Max players";
|
||||
break;
|
||||
case ServerData::PING:
|
||||
var = "Ping";
|
||||
break;
|
||||
case ServerData::MODNAME:
|
||||
var = "Game mode";
|
||||
}
|
||||
}
|
||||
}
|
||||
return var;
|
||||
}
|
||||
|
||||
int ServerModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
return myData.size();
|
||||
}
|
||||
|
||||
int ServerModel::columnCount(const QModelIndex &parent) const
|
||||
{
|
||||
return ServerData::LAST;
|
||||
}
|
||||
|
||||
bool ServerModel::setData(const QModelIndex &index, const QVariant &value, int role)
|
||||
{
|
||||
if (index.isValid() && role == Qt::EditRole)
|
||||
{
|
||||
int row = index.row();
|
||||
int col = index.column();
|
||||
|
||||
ServerData &sd = myData[row];
|
||||
bool ok = true;
|
||||
switch(col)
|
||||
{
|
||||
case ServerData::ADDR:
|
||||
sd.addr = value.toString();
|
||||
ok = !sd.addr.isEmpty();
|
||||
break;
|
||||
case ServerData::PASSW:
|
||||
sd.needPassw = value.toBool();
|
||||
break;
|
||||
case ServerData::VERSION:
|
||||
sd.version = value.toString();
|
||||
ok = !sd.addr.isEmpty();
|
||||
break;
|
||||
case ServerData::PLAYERS:
|
||||
sd.players = value.toInt(&ok);
|
||||
break;
|
||||
case ServerData::MAX_PLAYERS:
|
||||
sd.maxPlayers = value.toInt(&ok);
|
||||
break;
|
||||
case ServerData::HOSTNAME:
|
||||
sd.hostName = value.toString();
|
||||
ok = !sd.addr.isEmpty();
|
||||
break;
|
||||
case ServerData::PING:
|
||||
sd.ping = value.toInt(&ok);
|
||||
break;
|
||||
case ServerData::MODNAME:
|
||||
sd.modName = value.toString();
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
if(ok)
|
||||
emit(dataChanged(index, index));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ServerModel::insertRows(int position, int count, const QModelIndex &index)
|
||||
{
|
||||
Q_UNUSED(index);
|
||||
beginInsertRows(QModelIndex(), position, position + count - 1);
|
||||
|
||||
for (int row = 0; row < count; ++row) {
|
||||
ServerData sd {"", -1, -1, -1, "", "", false, 0};
|
||||
myData.insert(position, sd);
|
||||
}
|
||||
|
||||
endInsertRows();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ServerModel::removeRows(int position, int count, const QModelIndex &parent)
|
||||
{
|
||||
if (count == 0)
|
||||
return false;
|
||||
|
||||
beginRemoveRows(parent, position, position + count - 1);
|
||||
myData.erase(myData.begin()+position, myData.begin() + position + count);
|
||||
endRemoveRows();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
QModelIndex ServerModel::index(int row, int column, const QModelIndex &parent) const
|
||||
{
|
||||
|
||||
QModelIndex index = QAbstractTableModel::index(row, column, parent);
|
||||
//qDebug() << "Valid index? " << index.isValid() << " " << row << " " << column;
|
||||
return index;
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
#ifndef SERVERMODEL_FONTMODEL_HPP
|
||||
#define SERVERMODEL_FONTMODEL_HPP
|
||||
|
||||
#include <QObject>
|
||||
#include <vector>
|
||||
#include <QString>
|
||||
#include <QAbstractTableModel>
|
||||
|
||||
struct ServerData
|
||||
{
|
||||
QString addr;
|
||||
int players, maxPlayers;
|
||||
int ping;
|
||||
QString hostName;
|
||||
QString modName;
|
||||
bool needPassw;
|
||||
QString version;
|
||||
enum IDS
|
||||
{
|
||||
ADDR,
|
||||
HOSTNAME,
|
||||
PLAYERS,
|
||||
MAX_PLAYERS,
|
||||
PASSW,
|
||||
MODNAME,
|
||||
PING,
|
||||
VERSION,
|
||||
LAST
|
||||
};
|
||||
};
|
||||
|
||||
class ServerModel: public QAbstractTableModel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ServerModel(QObject *parent = 0);
|
||||
~ServerModel();
|
||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const Q_DECL_FINAL;
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_FINAL;
|
||||
int columnCount(const QModelIndex &parent) const Q_DECL_FINAL;
|
||||
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) Q_DECL_FINAL;
|
||||
|
||||
bool insertRows(int row, int count, const QModelIndex &index = QModelIndex()) Q_DECL_FINAL;
|
||||
bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) Q_DECL_FINAL;
|
||||
|
||||
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const Q_DECL_FINAL;
|
||||
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const Q_DECL_FINAL;
|
||||
|
||||
|
||||
public:
|
||||
//QHash<int, QByteArray> roles;
|
||||
QVector<ServerData> myData;
|
||||
};
|
||||
|
||||
|
||||
#endif //SERVERMODEL_FONTMODEL_HPP
|
@ -0,0 +1,57 @@
|
||||
#include <QApplication>
|
||||
#include <components/settings/settings.hpp>
|
||||
#include <components/files/configurationmanager.hpp>
|
||||
#include "MainWindow.hpp"
|
||||
#include "NetController.hpp"
|
||||
|
||||
std::string loadSettings (Settings::Manager & settings)
|
||||
{
|
||||
Files::ConfigurationManager mCfgMgr;
|
||||
// Create the settings manager and load default settings file
|
||||
const std::string localdefault = (mCfgMgr.getLocalPath() / "tes3mp-client-default.cfg").string();
|
||||
const std::string globaldefault = (mCfgMgr.getGlobalPath() / "tes3mp-client-default.cfg").string();
|
||||
|
||||
// prefer local
|
||||
if (boost::filesystem::exists(localdefault))
|
||||
settings.loadDefault(localdefault);
|
||||
else if (boost::filesystem::exists(globaldefault))
|
||||
settings.loadDefault(globaldefault);
|
||||
else
|
||||
throw std::runtime_error ("No default settings file found! Make sure the file \"tes3mp-client-default.cfg\" was properly installed.");
|
||||
|
||||
// load user settings if they exist
|
||||
const std::string settingspath = (mCfgMgr.getUserConfigPath() / "tes3mp-client.cfg").string();
|
||||
if (boost::filesystem::exists(settingspath))
|
||||
settings.loadUser(settingspath);
|
||||
|
||||
return settingspath;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
Settings::Manager mgr;
|
||||
|
||||
loadSettings(mgr);
|
||||
|
||||
std::string addr = mgr.getString("address", "Master");
|
||||
int port = mgr.getInt("port", "Master");
|
||||
|
||||
// initialize resources, if needed
|
||||
// Q_INIT_RESOURCE(resfile);
|
||||
|
||||
NetController::Create(addr, port);
|
||||
atexit(NetController::Destroy);
|
||||
QApplication app(argc, argv);
|
||||
MainWindow d;
|
||||
|
||||
if (d.refresh())
|
||||
{
|
||||
d.show();
|
||||
return app.exec();
|
||||
}
|
||||
else
|
||||
{
|
||||
app.exit();
|
||||
return 0;
|
||||
}
|
||||
}
|
@ -0,0 +1,99 @@
|
||||
//
|
||||
// Created by koncord on 07.01.17.
|
||||
//
|
||||
|
||||
#include <RakPeer.h>
|
||||
#include <HTTPConnection2.h>
|
||||
#include <TCPInterface.h>
|
||||
#include <RakSleep.h>
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include "HTTPNetwork.hpp"
|
||||
|
||||
using namespace RakNet;
|
||||
|
||||
HTTPNetwork::HTTPNetwork(std::string addr, unsigned short port) : address(addr), port(port)
|
||||
{
|
||||
httpConnection = HTTPConnection2::GetInstance();
|
||||
tcpInterface = new TCPInterface;
|
||||
tcpInterface->Start(0, 64);
|
||||
tcpInterface->AttachPlugin(httpConnection);
|
||||
}
|
||||
|
||||
HTTPNetwork::~HTTPNetwork()
|
||||
{
|
||||
delete tcpInterface;
|
||||
}
|
||||
|
||||
std::string HTTPNetwork::answer()
|
||||
{
|
||||
RakNet::SystemAddress sa;
|
||||
RakNet::Packet *packet;
|
||||
RakNet::SystemAddress hostReceived;
|
||||
RakNet::RakString response;
|
||||
RakNet::RakString transmitted, hostTransmitted;
|
||||
int contentOffset = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
// This is kind of crappy, but for TCP plugins, always do HasCompletedConnectionAttempt,
|
||||
// then Receive(), then HasFailedConnectionAttempt(),HasLostConnection()
|
||||
sa = tcpInterface->HasCompletedConnectionAttempt();
|
||||
if (sa != RakNet::UNASSIGNED_SYSTEM_ADDRESS)
|
||||
printf("Connected to master server: %s\n", sa.ToString());
|
||||
|
||||
sa = tcpInterface->HasFailedConnectionAttempt();
|
||||
if (sa != RakNet::UNASSIGNED_SYSTEM_ADDRESS)
|
||||
{
|
||||
printf("Failed to connect to master server: %s\n", sa.ToString());
|
||||
return "FAIL_CONNECT";
|
||||
}
|
||||
sa = tcpInterface->HasLostConnection();
|
||||
if (sa != RakNet::UNASSIGNED_SYSTEM_ADDRESS)
|
||||
{
|
||||
printf("Lost connection to master server: %s\n", sa.ToString());
|
||||
return "LOST_CONNECTION";
|
||||
}
|
||||
|
||||
for (packet = tcpInterface->Receive(); packet; tcpInterface->DeallocatePacket(
|
||||
packet), packet = tcpInterface->Receive());
|
||||
|
||||
if (httpConnection->GetResponse(transmitted, hostTransmitted, response, hostReceived, contentOffset))
|
||||
{
|
||||
if (contentOffset < 0)
|
||||
return "NO_CONTENT"; // no content
|
||||
tcpInterface->CloseConnection(sa);
|
||||
|
||||
return (response.C_String() + contentOffset);
|
||||
}
|
||||
RakSleep(30);
|
||||
}
|
||||
}
|
||||
|
||||
std::string HTTPNetwork::getData(const char *uri)
|
||||
{
|
||||
RakNet::RakString createRequest = RakNet::RakString::FormatForGET(uri);
|
||||
|
||||
if (!httpConnection->TransmitRequest(createRequest, address.c_str(), port))
|
||||
return "UNKNOWN_ADDRESS";
|
||||
return answer();
|
||||
}
|
||||
|
||||
std::string HTTPNetwork::getDataPOST(const char *uri, const char* body, const char* contentType)
|
||||
{
|
||||
RakNet::RakString createRequest = RakNet::RakString::FormatForPOST(uri, contentType, body);
|
||||
|
||||
if (!httpConnection->TransmitRequest(createRequest, address.c_str(), port))
|
||||
return "UNKNOWN_ADDRESS";
|
||||
return answer();
|
||||
}
|
||||
|
||||
std::string HTTPNetwork::getDataPUT(const char *uri, const char* body, const char* contentType)
|
||||
{
|
||||
RakNet::RakString createRequest = RakNet::RakString::FormatForPUT(uri, contentType, body);
|
||||
|
||||
if (!httpConnection->TransmitRequest(createRequest, address.c_str(), port))
|
||||
return "UNKNOWN_ADDRESS";
|
||||
return answer();
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
//
|
||||
// Created by koncord on 07.01.17.
|
||||
//
|
||||
|
||||
#ifndef NEWLAUNCHER_HTTPNETWORK_HPP
|
||||
#define NEWLAUNCHER_HTTPNETWORK_HPP
|
||||
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace RakNet
|
||||
{
|
||||
class TCPInterface;
|
||||
class HTTPConnection2;
|
||||
}
|
||||
|
||||
class HTTPNetwork
|
||||
{
|
||||
public:
|
||||
HTTPNetwork(std::string addr, unsigned short port);
|
||||
~HTTPNetwork();
|
||||
std::string getData(const char *uri);
|
||||
std::string getDataPOST(const char *uri, const char* body, const char* contentType = "application/json");
|
||||
std::string getDataPUT(const char *uri, const char* body, const char* contentType = "application/json");
|
||||
|
||||
protected:
|
||||
RakNet::TCPInterface *tcpInterface;
|
||||
RakNet::HTTPConnection2 *httpConnection;
|
||||
std::string address;
|
||||
unsigned short port;
|
||||
std::string answer();
|
||||
};
|
||||
|
||||
|
||||
#endif //NEWLAUNCHER_HTTPNETWORK_HPP
|
@ -0,0 +1,156 @@
|
||||
//
|
||||
// Created by koncord on 07.01.17.
|
||||
//
|
||||
|
||||
#include <RakPeer.h>
|
||||
#include <MessageIdentifiers.h>
|
||||
#include <RakSleep.h>
|
||||
#include <GetTime.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <components/openmw-mp/Version.hpp>
|
||||
|
||||
#include "Utils.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
unsigned int PingRakNetServer(const char *addr, unsigned short port)
|
||||
{
|
||||
RakNet::Packet *packet;
|
||||
bool done = false;
|
||||
int attempts = 0;
|
||||
RakNet::TimeMS time = PING_UNREACHABLE;
|
||||
|
||||
RakNet::SocketDescriptor socketDescriptor {0, ""};
|
||||
RakNet::RakPeerInterface *peer = RakNet::RakPeerInterface::GetInstance();
|
||||
peer->Startup(1,&socketDescriptor, 1);
|
||||
|
||||
peer->Ping(addr, port, false);
|
||||
while (!done)
|
||||
{
|
||||
for (packet=peer->Receive(); packet; peer->DeallocatePacket(packet), packet=peer->Receive())
|
||||
{
|
||||
if(packet->data[0] == ID_UNCONNECTED_PONG)
|
||||
{
|
||||
RakNet::BitStream bsIn(&packet->data[1], packet->length, false);
|
||||
bsIn.Read(time);
|
||||
time = RakNet::GetTimeMS() - time - 5;
|
||||
done = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (attempts >= 60) // wait 300 msec
|
||||
done = true;
|
||||
attempts++;
|
||||
RakSleep(5);
|
||||
}
|
||||
|
||||
peer->Shutdown(0);
|
||||
RakNet::RakPeerInterface::DestroyInstance(peer);
|
||||
return time;
|
||||
}
|
||||
|
||||
ServerExtendedData getExtendedData(const char *addr, unsigned short port)
|
||||
{
|
||||
ServerExtendedData data;
|
||||
RakNet::SocketDescriptor socketDescriptor = {0, ""};
|
||||
RakNet::RakPeerInterface *peer = RakNet::RakPeerInterface::GetInstance();
|
||||
peer->Startup(1, &socketDescriptor, 1);
|
||||
|
||||
stringstream sstr(TES3MP_VERSION);
|
||||
sstr << TES3MP_PROTO_VERSION;
|
||||
|
||||
std::string msg = "";
|
||||
|
||||
if (peer->Connect(addr, port, sstr.str().c_str(), (int)(sstr.str().size()), 0, 0, 3, 500, 0) != RakNet::CONNECTION_ATTEMPT_STARTED)
|
||||
msg = "Connection attempt failed.\n";
|
||||
|
||||
|
||||
int queue = 0;
|
||||
while (queue == 0)
|
||||
{
|
||||
for (RakNet::Packet *packet = peer->Receive(); packet; peer->DeallocatePacket(
|
||||
packet), packet = peer->Receive())
|
||||
{
|
||||
switch (packet->data[0])
|
||||
{
|
||||
case ID_CONNECTION_ATTEMPT_FAILED:
|
||||
{
|
||||
msg = "Connection failed.\n"
|
||||
"Either the IP address is wrong or a firewall on either system is blocking\n"
|
||||
"UDP packets on the port you have chosen.";
|
||||
queue = -1;
|
||||
break;
|
||||
}
|
||||
case ID_INVALID_PASSWORD:
|
||||
{
|
||||
msg = "Connection failed.\n"
|
||||
"The client or server is outdated.\n";
|
||||
queue = -1;
|
||||
break;
|
||||
}
|
||||
case ID_CONNECTION_REQUEST_ACCEPTED:
|
||||
{
|
||||
msg = "Connection accepted.\n";
|
||||
queue = 1;
|
||||
break;
|
||||
}
|
||||
case ID_DISCONNECTION_NOTIFICATION:
|
||||
throw runtime_error("ID_DISCONNECTION_NOTIFICATION.\n");
|
||||
case ID_CONNECTION_BANNED:
|
||||
throw runtime_error("ID_CONNECTION_BANNED.\n");
|
||||
case ID_CONNECTION_LOST:
|
||||
throw runtime_error("ID_CONNECTION_LOST.\n");
|
||||
default:
|
||||
printf("Connection message with identifier %i has arrived in initialization.\n", packet->data[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
puts(msg.c_str());
|
||||
|
||||
if(queue == -1) // connection is failed
|
||||
return data;
|
||||
|
||||
{
|
||||
RakNet::BitStream bs;
|
||||
bs.Write((unsigned char) (ID_USER_PACKET_ENUM + 1));
|
||||
peer->Send(&bs, HIGH_PRIORITY, RELIABLE_ORDERED, 0, RakNet::UNASSIGNED_SYSTEM_ADDRESS, true);
|
||||
}
|
||||
|
||||
RakNet::Packet *packet;
|
||||
bool done = false;
|
||||
while (!done)
|
||||
{
|
||||
for (packet = peer->Receive(); packet; peer->DeallocatePacket(packet), packet = peer->Receive())
|
||||
{
|
||||
if(packet->data[0] == (ID_USER_PACKET_ENUM+1))
|
||||
{
|
||||
RakNet::BitStream bs(packet->data, packet->length, false);
|
||||
bs.IgnoreBytes(1);
|
||||
size_t length = 0;
|
||||
bs.Read(length);
|
||||
for(int i = 0; i < length; i++)
|
||||
{
|
||||
RakNet::RakString str;
|
||||
bs.Read(str);
|
||||
data.players.push_back(str.C_String());
|
||||
}
|
||||
bs.Read(length);
|
||||
for(int i = 0; i < length; i++)
|
||||
{
|
||||
RakNet::RakString str;
|
||||
bs.Read(str);
|
||||
data.plugins.push_back(str.C_String());
|
||||
}
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
peer->Shutdown(0);
|
||||
RakSleep(10);
|
||||
RakNet::RakPeerInterface::DestroyInstance(peer);
|
||||
return data;
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
//
|
||||
// Created by koncord on 07.01.17.
|
||||
//
|
||||
|
||||
#ifndef NEWLAUNCHER_PING_HPP
|
||||
#define NEWLAUNCHER_PING_HPP
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
|
||||
#define PING_UNREACHABLE 999
|
||||
|
||||
unsigned int PingRakNetServer(const char *addr, unsigned short port);
|
||||
|
||||
struct ServerExtendedData
|
||||
{
|
||||
std::vector<std::string> players;
|
||||
std::vector<std::string> plugins;
|
||||
};
|
||||
|
||||
ServerExtendedData getExtendedData(const char *addr, unsigned short port);
|
||||
|
||||
#endif //NEWLAUNCHER_PING_HPP
|
@ -0,0 +1,90 @@
|
||||
#include "editor.hpp"
|
||||
|
||||
#include <exception>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include <QApplication>
|
||||
#include <QIcon>
|
||||
#include <QMetaType>
|
||||
|
||||
#include "model/doc/messages.hpp"
|
||||
|
||||
#include "model/world/universalid.hpp"
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
#include <QDir>
|
||||
#endif
|
||||
|
||||
Q_DECLARE_METATYPE (std::string)
|
||||
|
||||
class Application : public QApplication
|
||||
{
|
||||
private:
|
||||
|
||||
bool notify (QObject *receiver, QEvent *event)
|
||||
{
|
||||
try
|
||||
{
|
||||
return QApplication::notify (receiver, event);
|
||||
}
|
||||
catch (const std::exception& exception)
|
||||
{
|
||||
std::cerr << "An exception has been caught: " << exception.what() << std::endl;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Application (int& argc, char *argv[]) : QApplication (argc, argv) {}
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
#ifdef Q_OS_MAC
|
||||
setenv("OSG_GL_TEXTURE_STORAGE", "OFF", 0);
|
||||
#endif
|
||||
|
||||
try
|
||||
{
|
||||
// To allow background thread drawing in OSG
|
||||
QApplication::setAttribute(Qt::AA_X11InitThreads, true);
|
||||
|
||||
Q_INIT_RESOURCE (resources);
|
||||
|
||||
qRegisterMetaType<std::string> ("std::string");
|
||||
qRegisterMetaType<CSMWorld::UniversalId> ("CSMWorld::UniversalId");
|
||||
qRegisterMetaType<CSMDoc::Message> ("CSMDoc::Message");
|
||||
|
||||
Application application (argc, argv);
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
QDir dir(QCoreApplication::applicationDirPath());
|
||||
if (dir.dirName() == "MacOS") {
|
||||
dir.cdUp();
|
||||
dir.cdUp();
|
||||
dir.cdUp();
|
||||
}
|
||||
QDir::setCurrent(dir.absolutePath());
|
||||
#endif
|
||||
|
||||
application.setWindowIcon (QIcon (":./openmw-cs.png"));
|
||||
|
||||
CS::Editor editor;
|
||||
|
||||
if(!editor.makeIPCServer())
|
||||
{
|
||||
editor.connectToIPCServer();
|
||||
return 0;
|
||||
}
|
||||
return editor.run();
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << "ERROR: " << e.what() << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,147 @@
|
||||
#include "modifiersetting.hpp"
|
||||
|
||||
#include <QEvent>
|
||||
#include <QKeyEvent>
|
||||
#include <QLabel>
|
||||
#include <QMouseEvent>
|
||||
#include <QPushButton>
|
||||
#include <QWidget>
|
||||
|
||||
#include "state.hpp"
|
||||
#include "shortcutmanager.hpp"
|
||||
|
||||
namespace CSMPrefs
|
||||
{
|
||||
ModifierSetting::ModifierSetting(Category* parent, Settings::Manager* values, QMutex* mutex, const std::string& key,
|
||||
const std::string& label)
|
||||
: Setting(parent, values, mutex, key, label)
|
||||
, mButton(0)
|
||||
, mEditorActive(false)
|
||||
{
|
||||
}
|
||||
|
||||
std::pair<QWidget*, QWidget*> ModifierSetting::makeWidgets(QWidget* parent)
|
||||
{
|
||||
int modifier = 0;
|
||||
State::get().getShortcutManager().getModifier(getKey(), modifier);
|
||||
|
||||
QString text = QString::fromUtf8(State::get().getShortcutManager().convertToString(modifier).c_str());
|
||||
|
||||
QLabel* label = new QLabel(QString::fromUtf8(getLabel().c_str()), parent);
|
||||
QPushButton* widget = new QPushButton(text, parent);
|
||||
|
||||
widget->setCheckable(true);
|
||||
widget->installEventFilter(this);
|
||||
mButton = widget;
|
||||
|
||||
connect(widget, SIGNAL(toggled(bool)), this, SLOT(buttonToggled(bool)));
|
||||
|
||||
return std::make_pair(label, widget);
|
||||
}
|
||||
|
||||
bool ModifierSetting::eventFilter(QObject* target, QEvent* event)
|
||||
{
|
||||
if (event->type() == QEvent::KeyPress)
|
||||
{
|
||||
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
|
||||
if (keyEvent->isAutoRepeat())
|
||||
return true;
|
||||
|
||||
int mod = keyEvent->modifiers();
|
||||
int key = keyEvent->key();
|
||||
|
||||
return handleEvent(target, mod, key);
|
||||
}
|
||||
else if (event->type() == QEvent::MouseButtonPress)
|
||||
{
|
||||
QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
|
||||
int mod = mouseEvent->modifiers();
|
||||
int button = mouseEvent->button();
|
||||
|
||||
return handleEvent(target, mod, button);
|
||||
}
|
||||
else if (event->type() == QEvent::FocusOut)
|
||||
{
|
||||
resetState();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ModifierSetting::handleEvent(QObject* target, int mod, int value)
|
||||
{
|
||||
// For potential future exceptions
|
||||
const int Blacklist[] =
|
||||
{
|
||||
0
|
||||
};
|
||||
|
||||
const size_t BlacklistSize = sizeof(Blacklist) / sizeof(int);
|
||||
|
||||
if (!mEditorActive)
|
||||
{
|
||||
if (value == Qt::RightButton)
|
||||
{
|
||||
// Clear modifier
|
||||
int modifier = 0;
|
||||
storeValue(modifier);
|
||||
|
||||
resetState();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Handle blacklist
|
||||
for (size_t i = 0; i < BlacklistSize; ++i)
|
||||
{
|
||||
if (value == Blacklist[i])
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Update modifier
|
||||
int modifier = value;
|
||||
storeValue(modifier);
|
||||
|
||||
resetState();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ModifierSetting::storeValue(int modifier)
|
||||
{
|
||||
State::get().getShortcutManager().setModifier(getKey(), modifier);
|
||||
|
||||
// Convert to string and assign
|
||||
std::string value = State::get().getShortcutManager().convertToString(modifier);
|
||||
|
||||
{
|
||||
QMutexLocker lock(getMutex());
|
||||
getValues().setString(getKey(), getParent()->getKey(), value);
|
||||
}
|
||||
|
||||
getParent()->getState()->update(*this);
|
||||
}
|
||||
|
||||
void ModifierSetting::resetState()
|
||||
{
|
||||
mButton->setChecked(false);
|
||||
mEditorActive = false;
|
||||
|
||||
// Button text
|
||||
int modifier = 0;
|
||||
State::get().getShortcutManager().getModifier(getKey(), modifier);
|
||||
|
||||
QString text = QString::fromUtf8(State::get().getShortcutManager().convertToString(modifier).c_str());
|
||||
mButton->setText(text);
|
||||
}
|
||||
|
||||
void ModifierSetting::buttonToggled(bool checked)
|
||||
{
|
||||
if (checked)
|
||||
mButton->setText("Press keys or click here...");
|
||||
|
||||
mEditorActive = checked;
|
||||
}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
#ifndef CSM_PREFS_MODIFIERSETTING_H
|
||||
#define CSM_PREFS_MODIFIERSETTING_H
|
||||
|
||||
#include <QKeySequence>
|
||||
|
||||
#include "setting.hpp"
|
||||
|
||||
class QEvent;
|
||||
class QPushButton;
|
||||
|
||||
namespace CSMPrefs
|
||||
{
|
||||
class ModifierSetting : public Setting
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
ModifierSetting(Category* parent, Settings::Manager* values, QMutex* mutex, const std::string& key,
|
||||
const std::string& label);
|
||||
|
||||
virtual std::pair<QWidget*, QWidget*> makeWidgets(QWidget* parent);
|
||||
|
||||
protected:
|
||||
|
||||
bool eventFilter(QObject* target, QEvent* event);
|
||||
|
||||
private:
|
||||
|
||||
bool handleEvent(QObject* target, int mod, int value);
|
||||
|
||||
void storeValue(int modifier);
|
||||
void resetState();
|
||||
|
||||
QPushButton* mButton;
|
||||
bool mEditorActive;
|
||||
|
||||
private slots:
|
||||
|
||||
void buttonToggled(bool checked);
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,214 @@
|
||||
#include "shortcut.hpp"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include <QAction>
|
||||
#include <QWidget>
|
||||
|
||||
#include "state.hpp"
|
||||
#include "shortcutmanager.hpp"
|
||||
|
||||
namespace CSMPrefs
|
||||
{
|
||||
Shortcut::Shortcut(const std::string& name, QWidget* parent)
|
||||
: QObject(parent)
|
||||
, mEnabled(true)
|
||||
, mName(name)
|
||||
, mModName("")
|
||||
, mSecondaryMode(SM_Ignore)
|
||||
, mModifier(0)
|
||||
, mCurrentPos(0)
|
||||
, mLastPos(0)
|
||||
, mActivationStatus(AS_Inactive)
|
||||
, mModifierStatus(false)
|
||||
, mAction(0)
|
||||
{
|
||||
assert (parent);
|
||||
|
||||
State::get().getShortcutManager().addShortcut(this);
|
||||
State::get().getShortcutManager().getSequence(name, mSequence);
|
||||
}
|
||||
|
||||
Shortcut::Shortcut(const std::string& name, const std::string& modName, QWidget* parent)
|
||||
: QObject(parent)
|
||||
, mEnabled(true)
|
||||
, mName(name)
|
||||
, mModName(modName)
|
||||
, mSecondaryMode(SM_Ignore)
|
||||
, mModifier(0)
|
||||
, mCurrentPos(0)
|
||||
, mLastPos(0)
|
||||
, mActivationStatus(AS_Inactive)
|
||||
, mModifierStatus(false)
|
||||
, mAction(0)
|
||||
{
|
||||
assert (parent);
|
||||
|
||||
State::get().getShortcutManager().addShortcut(this);
|
||||
State::get().getShortcutManager().getSequence(name, mSequence);
|
||||
State::get().getShortcutManager().getModifier(modName, mModifier);
|
||||
}
|
||||
|
||||
Shortcut::Shortcut(const std::string& name, const std::string& modName, SecondaryMode secMode, QWidget* parent)
|
||||
: QObject(parent)
|
||||
, mEnabled(true)
|
||||
, mName(name)
|
||||
, mModName(modName)
|
||||
, mSecondaryMode(secMode)
|
||||
, mModifier(0)
|
||||
, mCurrentPos(0)
|
||||
, mLastPos(0)
|
||||
, mActivationStatus(AS_Inactive)
|
||||
, mModifierStatus(false)
|
||||
, mAction(0)
|
||||
{
|
||||
assert (parent);
|
||||
|
||||
State::get().getShortcutManager().addShortcut(this);
|
||||
State::get().getShortcutManager().getSequence(name, mSequence);
|
||||
State::get().getShortcutManager().getModifier(modName, mModifier);
|
||||
}
|
||||
|
||||
Shortcut::~Shortcut()
|
||||
{
|
||||
State::get().getShortcutManager().removeShortcut(this);
|
||||
}
|
||||
|
||||
bool Shortcut::isEnabled() const
|
||||
{
|
||||
return mEnabled;
|
||||
}
|
||||
|
||||
const std::string& Shortcut::getName() const
|
||||
{
|
||||
return mName;
|
||||
}
|
||||
|
||||
const std::string& Shortcut::getModifierName() const
|
||||
{
|
||||
return mModName;
|
||||
}
|
||||
|
||||
Shortcut::SecondaryMode Shortcut::getSecondaryMode() const
|
||||
{
|
||||
return mSecondaryMode;
|
||||
}
|
||||
|
||||
const QKeySequence& Shortcut::getSequence() const
|
||||
{
|
||||
return mSequence;
|
||||
}
|
||||
|
||||
int Shortcut::getModifier() const
|
||||
{
|
||||
return mModifier;
|
||||
}
|
||||
|
||||
int Shortcut::getPosition() const
|
||||
{
|
||||
return mCurrentPos;
|
||||
}
|
||||
|
||||
int Shortcut::getLastPosition() const
|
||||
{
|
||||
return mLastPos;
|
||||
}
|
||||
|
||||
Shortcut::ActivationStatus Shortcut::getActivationStatus() const
|
||||
{
|
||||
return mActivationStatus;
|
||||
}
|
||||
|
||||
bool Shortcut::getModifierStatus() const
|
||||
{
|
||||
return mModifierStatus;
|
||||
}
|
||||
|
||||
void Shortcut::enable(bool state)
|
||||
{
|
||||
mEnabled = state;
|
||||
}
|
||||
|
||||
void Shortcut::setSequence(const QKeySequence& sequence)
|
||||
{
|
||||
mSequence = sequence;
|
||||
mCurrentPos = 0;
|
||||
mLastPos = sequence.count() - 1;
|
||||
|
||||
if (mAction)
|
||||
{
|
||||
mAction->setText(mActionText + "\t" + State::get().getShortcutManager().convertToString(mSequence).data());
|
||||
}
|
||||
}
|
||||
|
||||
void Shortcut::setModifier(int modifier)
|
||||
{
|
||||
mModifier = modifier;
|
||||
}
|
||||
|
||||
void Shortcut::setPosition(int pos)
|
||||
{
|
||||
mCurrentPos = pos;
|
||||
}
|
||||
|
||||
void Shortcut::setActivationStatus(ActivationStatus status)
|
||||
{
|
||||
mActivationStatus = status;
|
||||
}
|
||||
|
||||
void Shortcut::setModifierStatus(bool status)
|
||||
{
|
||||
mModifierStatus = status;
|
||||
}
|
||||
|
||||
void Shortcut::associateAction(QAction* action)
|
||||
{
|
||||
if (mAction)
|
||||
{
|
||||
mAction->setText(mActionText);
|
||||
|
||||
disconnect(this, SIGNAL(activated()), mAction, SLOT(trigger()));
|
||||
disconnect(mAction, SIGNAL(destroyed()), this, SLOT(actionDeleted()));
|
||||
}
|
||||
|
||||
mAction = action;
|
||||
|
||||
if (mAction)
|
||||
{
|
||||
mActionText = mAction->text();
|
||||
mAction->setText(mActionText + "\t" + State::get().getShortcutManager().convertToString(mSequence).data());
|
||||
|
||||
connect(this, SIGNAL(activated()), mAction, SLOT(trigger()));
|
||||
connect(mAction, SIGNAL(destroyed()), this, SLOT(actionDeleted()));
|
||||
}
|
||||
}
|
||||
|
||||
void Shortcut::signalActivated(bool state)
|
||||
{
|
||||
emit activated(state);
|
||||
}
|
||||
|
||||
void Shortcut::signalActivated()
|
||||
{
|
||||
emit activated();
|
||||
}
|
||||
|
||||
void Shortcut::signalSecondary(bool state)
|
||||
{
|
||||
emit secondary(state);
|
||||
}
|
||||
void Shortcut::signalSecondary()
|
||||
{
|
||||
emit secondary();
|
||||
}
|
||||
|
||||
QString Shortcut::toString() const
|
||||
{
|
||||
return QString(State::get().getShortcutManager().convertToString(mSequence, mModifier).data());
|
||||
}
|
||||
|
||||
void Shortcut::actionDeleted()
|
||||
{
|
||||
mAction = 0;
|
||||
}
|
||||
}
|
@ -0,0 +1,122 @@
|
||||
#ifndef CSM_PREFS_SHORTCUT_H
|
||||
#define CSM_PREFS_SHORTCUT_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <QKeySequence>
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
|
||||
class QAction;
|
||||
class QWidget;
|
||||
|
||||
namespace CSMPrefs
|
||||
{
|
||||
/// A class similar in purpose to QShortcut, but with the ability to use mouse buttons
|
||||
class Shortcut : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
enum ActivationStatus
|
||||
{
|
||||
AS_Regular,
|
||||
AS_Secondary,
|
||||
AS_Inactive
|
||||
};
|
||||
|
||||
enum SecondaryMode
|
||||
{
|
||||
SM_Replace, ///< The secondary signal replaces the regular signal when the modifier is active
|
||||
SM_Detach, ///< The secondary signal is emitted independent of the regular signal, even if not active
|
||||
SM_Ignore ///< The secondary signal will not ever be emitted
|
||||
};
|
||||
|
||||
Shortcut(const std::string& name, QWidget* parent);
|
||||
Shortcut(const std::string& name, const std::string& modName, QWidget* parent);
|
||||
Shortcut(const std::string& name, const std::string& modName, SecondaryMode secMode, QWidget* parent);
|
||||
|
||||
~Shortcut();
|
||||
|
||||
bool isEnabled() const;
|
||||
|
||||
const std::string& getName() const;
|
||||
const std::string& getModifierName() const;
|
||||
|
||||
SecondaryMode getSecondaryMode() const;
|
||||
|
||||
const QKeySequence& getSequence() const;
|
||||
int getModifier() const;
|
||||
|
||||
/// The position in the sequence
|
||||
int getPosition() const;
|
||||
/// The position in the sequence
|
||||
int getLastPosition() const;
|
||||
|
||||
ActivationStatus getActivationStatus() const;
|
||||
bool getModifierStatus() const;
|
||||
|
||||
void enable(bool state);
|
||||
|
||||
void setSequence(const QKeySequence& sequence);
|
||||
void setModifier(int modifier);
|
||||
|
||||
/// The position in the sequence
|
||||
void setPosition(int pos);
|
||||
|
||||
void setActivationStatus(ActivationStatus status);
|
||||
void setModifierStatus(bool status);
|
||||
|
||||
/// Appends the sequence to the QAction text, also keeps it up to date
|
||||
void associateAction(QAction* action);
|
||||
|
||||
// Workaround for Qt4 signals being "protected"
|
||||
void signalActivated(bool state);
|
||||
void signalActivated();
|
||||
|
||||
void signalSecondary(bool state);
|
||||
void signalSecondary();
|
||||
|
||||
QString toString() const;
|
||||
|
||||
private:
|
||||
|
||||
bool mEnabled;
|
||||
|
||||
std::string mName;
|
||||
std::string mModName;
|
||||
SecondaryMode mSecondaryMode;
|
||||
QKeySequence mSequence;
|
||||
int mModifier;
|
||||
|
||||
int mCurrentPos;
|
||||
int mLastPos;
|
||||
|
||||
ActivationStatus mActivationStatus;
|
||||
bool mModifierStatus;
|
||||
|
||||
QAction* mAction;
|
||||
QString mActionText;
|
||||
|
||||
private slots:
|
||||
|
||||
void actionDeleted();
|
||||
|
||||
signals:
|
||||
|
||||
/// Triggered when the shortcut is activated or deactivated; can be determined from \p state
|
||||
void activated(bool state);
|
||||
|
||||
/// Convenience signal.
|
||||
void activated();
|
||||
|
||||
/// Triggered depending on SecondaryMode
|
||||
void secondary(bool state);
|
||||
|
||||
/// Convenience signal.
|
||||
void secondary();
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,338 @@
|
||||
#include "shortcuteventhandler.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
|
||||
#include <QEvent>
|
||||
#include <QKeyEvent>
|
||||
#include <QMouseEvent>
|
||||
#include <QWidget>
|
||||
|
||||
#include "shortcut.hpp"
|
||||
|
||||
namespace CSMPrefs
|
||||
{
|
||||
ShortcutEventHandler::ShortcutEventHandler(QObject* parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
}
|
||||
|
||||
void ShortcutEventHandler::addShortcut(Shortcut* shortcut)
|
||||
{
|
||||
// Enforced by shortcut class
|
||||
QWidget* widget = static_cast<QWidget*>(shortcut->parent());
|
||||
|
||||
// Check if widget setup is needed
|
||||
ShortcutMap::iterator shortcutListIt = mWidgetShortcuts.find(widget);
|
||||
if (shortcutListIt == mWidgetShortcuts.end())
|
||||
{
|
||||
// Create list
|
||||
shortcutListIt = mWidgetShortcuts.insert(std::make_pair(widget, ShortcutList())).first;
|
||||
|
||||
// Check if widget has a parent with shortcuts, unfortunately it is not typically set yet
|
||||
updateParent(widget);
|
||||
|
||||
// Intercept widget events
|
||||
widget->installEventFilter(this);
|
||||
connect(widget, SIGNAL(destroyed()), this, SLOT(widgetDestroyed()));
|
||||
}
|
||||
|
||||
// Add to list
|
||||
shortcutListIt->second.push_back(shortcut);
|
||||
}
|
||||
|
||||
void ShortcutEventHandler::removeShortcut(Shortcut* shortcut)
|
||||
{
|
||||
// Enforced by shortcut class
|
||||
QWidget* widget = static_cast<QWidget*>(shortcut->parent());
|
||||
|
||||
ShortcutMap::iterator shortcutListIt = mWidgetShortcuts.find(widget);
|
||||
if (shortcutListIt != mWidgetShortcuts.end())
|
||||
{
|
||||
std::remove(shortcutListIt->second.begin(), shortcutListIt->second.end(), shortcut);
|
||||
}
|
||||
}
|
||||
|
||||
bool ShortcutEventHandler::eventFilter(QObject* watched, QEvent* event)
|
||||
{
|
||||
// Process event
|
||||
if (event->type() == QEvent::KeyPress)
|
||||
{
|
||||
QWidget* widget = static_cast<QWidget*>(watched);
|
||||
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
|
||||
unsigned int mod = (unsigned int) keyEvent->modifiers();
|
||||
unsigned int key = (unsigned int) keyEvent->key();
|
||||
|
||||
if (!keyEvent->isAutoRepeat())
|
||||
return activate(widget, mod, key);
|
||||
}
|
||||
else if (event->type() == QEvent::KeyRelease)
|
||||
{
|
||||
QWidget* widget = static_cast<QWidget*>(watched);
|
||||
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
|
||||
unsigned int mod = (unsigned int) keyEvent->modifiers();
|
||||
unsigned int key = (unsigned int) keyEvent->key();
|
||||
|
||||
if (!keyEvent->isAutoRepeat())
|
||||
return deactivate(widget, mod, key);
|
||||
}
|
||||
else if (event->type() == QEvent::MouseButtonPress)
|
||||
{
|
||||
QWidget* widget = static_cast<QWidget*>(watched);
|
||||
QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
|
||||
unsigned int mod = (unsigned int) mouseEvent->modifiers();
|
||||
unsigned int button = (unsigned int) mouseEvent->button();
|
||||
|
||||
return activate(widget, mod, button);
|
||||
}
|
||||
else if (event->type() == QEvent::MouseButtonRelease)
|
||||
{
|
||||
QWidget* widget = static_cast<QWidget*>(watched);
|
||||
QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
|
||||
unsigned int mod = (unsigned int) mouseEvent->modifiers();
|
||||
unsigned int button = (unsigned int) mouseEvent->button();
|
||||
|
||||
return deactivate(widget, mod, button);
|
||||
}
|
||||
else if (event->type() == QEvent::FocusOut)
|
||||
{
|
||||
QWidget* widget = static_cast<QWidget*>(watched);
|
||||
ShortcutMap::iterator shortcutListIt = mWidgetShortcuts.find(widget);
|
||||
|
||||
// Deactivate in case events are missed
|
||||
for (ShortcutList::iterator it = shortcutListIt->second.begin(); it != shortcutListIt->second.end(); ++it)
|
||||
{
|
||||
Shortcut* shortcut = *it;
|
||||
|
||||
shortcut->setPosition(0);
|
||||
shortcut->setModifierStatus(false);
|
||||
|
||||
if (shortcut->getActivationStatus() == Shortcut::AS_Regular)
|
||||
{
|
||||
shortcut->setActivationStatus(Shortcut::AS_Inactive);
|
||||
shortcut->signalActivated(false);
|
||||
}
|
||||
else if (shortcut->getActivationStatus() == Shortcut::AS_Secondary)
|
||||
{
|
||||
shortcut->setActivationStatus(Shortcut::AS_Inactive);
|
||||
shortcut->signalSecondary(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (event->type() == QEvent::FocusIn)
|
||||
{
|
||||
QWidget* widget = static_cast<QWidget*>(watched);
|
||||
updateParent(widget);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ShortcutEventHandler::updateParent(QWidget* widget)
|
||||
{
|
||||
QWidget* parent = widget->parentWidget();
|
||||
while (parent)
|
||||
{
|
||||
ShortcutMap::iterator parentIt = mWidgetShortcuts.find(parent);
|
||||
if (parentIt != mWidgetShortcuts.end())
|
||||
{
|
||||
mChildParentRelations.insert(std::make_pair(widget, parent));
|
||||
updateParent(parent);
|
||||
break;
|
||||
}
|
||||
|
||||
// Check next
|
||||
parent = parent->parentWidget();
|
||||
}
|
||||
}
|
||||
|
||||
bool ShortcutEventHandler::activate(QWidget* widget, unsigned int mod, unsigned int button)
|
||||
{
|
||||
std::vector<std::pair<MatchResult, Shortcut*> > potentials;
|
||||
bool used = false;
|
||||
|
||||
while (widget)
|
||||
{
|
||||
ShortcutMap::iterator shortcutListIt = mWidgetShortcuts.find(widget);
|
||||
assert(shortcutListIt != mWidgetShortcuts.end());
|
||||
|
||||
// Find potential activations
|
||||
for (ShortcutList::iterator it = shortcutListIt->second.begin(); it != shortcutListIt->second.end(); ++it)
|
||||
{
|
||||
Shortcut* shortcut = *it;
|
||||
|
||||
if (!shortcut->isEnabled())
|
||||
continue;
|
||||
|
||||
if (checkModifier(mod, button, shortcut, true))
|
||||
used = true;
|
||||
|
||||
if (shortcut->getActivationStatus() != Shortcut::AS_Inactive)
|
||||
continue;
|
||||
|
||||
int pos = shortcut->getPosition();
|
||||
int lastPos = shortcut->getLastPosition();
|
||||
MatchResult result = match(mod, button, shortcut->getSequence()[pos]);
|
||||
|
||||
if (result == Matches_WithMod || result == Matches_NoMod)
|
||||
{
|
||||
if (pos < lastPos && (result == Matches_WithMod || pos > 0))
|
||||
{
|
||||
shortcut->setPosition(pos+1);
|
||||
}
|
||||
else if (pos == lastPos)
|
||||
{
|
||||
potentials.push_back(std::make_pair(result, shortcut));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Move on to parent
|
||||
WidgetMap::iterator widgetIt = mChildParentRelations.find(widget);
|
||||
widget = (widgetIt != mChildParentRelations.end()) ? widgetIt->second : 0;
|
||||
}
|
||||
|
||||
// Only activate the best match; in exact conflicts, this will favor the first shortcut added.
|
||||
if (!potentials.empty())
|
||||
{
|
||||
std::sort(potentials.begin(), potentials.end(), ShortcutEventHandler::sort);
|
||||
Shortcut* shortcut = potentials.front().second;
|
||||
|
||||
if (shortcut->getModifierStatus() && shortcut->getSecondaryMode() == Shortcut::SM_Replace)
|
||||
{
|
||||
shortcut->setActivationStatus(Shortcut::AS_Secondary);
|
||||
shortcut->signalSecondary(true);
|
||||
shortcut->signalSecondary();
|
||||
}
|
||||
else
|
||||
{
|
||||
shortcut->setActivationStatus(Shortcut::AS_Regular);
|
||||
shortcut->signalActivated(true);
|
||||
shortcut->signalActivated();
|
||||
}
|
||||
|
||||
used = true;
|
||||
}
|
||||
|
||||
return used;
|
||||
}
|
||||
|
||||
bool ShortcutEventHandler::deactivate(QWidget* widget, unsigned int mod, unsigned int button)
|
||||
{
|
||||
const int KeyMask = 0x01FFFFFF;
|
||||
|
||||
bool used = false;
|
||||
|
||||
while (widget)
|
||||
{
|
||||
ShortcutMap::iterator shortcutListIt = mWidgetShortcuts.find(widget);
|
||||
assert(shortcutListIt != mWidgetShortcuts.end());
|
||||
|
||||
for (ShortcutList::iterator it = shortcutListIt->second.begin(); it != shortcutListIt->second.end(); ++it)
|
||||
{
|
||||
Shortcut* shortcut = *it;
|
||||
|
||||
if (checkModifier(mod, button, shortcut, false))
|
||||
used = true;
|
||||
|
||||
int pos = shortcut->getPosition();
|
||||
MatchResult result = match(0, button, shortcut->getSequence()[pos] & KeyMask);
|
||||
|
||||
if (result != Matches_Not)
|
||||
{
|
||||
shortcut->setPosition(0);
|
||||
|
||||
if (shortcut->getActivationStatus() == Shortcut::AS_Regular)
|
||||
{
|
||||
shortcut->setActivationStatus(Shortcut::AS_Inactive);
|
||||
shortcut->signalActivated(false);
|
||||
used = true;
|
||||
}
|
||||
else if (shortcut->getActivationStatus() == Shortcut::AS_Secondary)
|
||||
{
|
||||
shortcut->setActivationStatus(Shortcut::AS_Inactive);
|
||||
shortcut->signalSecondary(false);
|
||||
used = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Move on to parent
|
||||
WidgetMap::iterator widgetIt = mChildParentRelations.find(widget);
|
||||
widget = (widgetIt != mChildParentRelations.end()) ? widgetIt->second : 0;
|
||||
}
|
||||
|
||||
return used;
|
||||
}
|
||||
|
||||
bool ShortcutEventHandler::checkModifier(unsigned int mod, unsigned int button, Shortcut* shortcut, bool activate)
|
||||
{
|
||||
if (!shortcut->isEnabled() || !shortcut->getModifier() || shortcut->getSecondaryMode() == Shortcut::SM_Ignore ||
|
||||
shortcut->getModifierStatus() == activate)
|
||||
return false;
|
||||
|
||||
MatchResult result = match(mod, button, shortcut->getModifier());
|
||||
bool used = false;
|
||||
|
||||
if (result != Matches_Not)
|
||||
{
|
||||
shortcut->setModifierStatus(activate);
|
||||
|
||||
if (shortcut->getSecondaryMode() == Shortcut::SM_Detach)
|
||||
{
|
||||
if (activate)
|
||||
{
|
||||
shortcut->signalSecondary(true);
|
||||
shortcut->signalSecondary();
|
||||
}
|
||||
else
|
||||
{
|
||||
shortcut->signalSecondary(false);
|
||||
}
|
||||
}
|
||||
else if (!activate && shortcut->getActivationStatus() == Shortcut::AS_Secondary)
|
||||
{
|
||||
shortcut->setActivationStatus(Shortcut::AS_Inactive);
|
||||
shortcut->setPosition(0);
|
||||
shortcut->signalSecondary(false);
|
||||
used = true;
|
||||
}
|
||||
}
|
||||
|
||||
return used;
|
||||
}
|
||||
|
||||
ShortcutEventHandler::MatchResult ShortcutEventHandler::match(unsigned int mod, unsigned int button,
|
||||
unsigned int value)
|
||||
{
|
||||
if ((mod | button) == value)
|
||||
{
|
||||
return Matches_WithMod;
|
||||
}
|
||||
else if (button == value)
|
||||
{
|
||||
return Matches_NoMod;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Matches_Not;
|
||||
}
|
||||
}
|
||||
|
||||
bool ShortcutEventHandler::sort(const std::pair<MatchResult, Shortcut*>& left,
|
||||
const std::pair<MatchResult, Shortcut*>& right)
|
||||
{
|
||||
if (left.first == Matches_WithMod && right.first == Matches_NoMod)
|
||||
return true;
|
||||
else
|
||||
return left.second->getPosition() >= right.second->getPosition();
|
||||
}
|
||||
|
||||
void ShortcutEventHandler::widgetDestroyed()
|
||||
{
|
||||
QWidget* widget = static_cast<QWidget*>(sender());
|
||||
|
||||
mWidgetShortcuts.erase(widget);
|
||||
mChildParentRelations.erase(widget);
|
||||
}
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
#ifndef CSM_PREFS_SHORTCUT_EVENT_HANDLER_H
|
||||
#define CSM_PREFS_SHORTCUT_EVENT_HANDLER_H
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class QEvent;
|
||||
class QWidget;
|
||||
|
||||
namespace CSMPrefs
|
||||
{
|
||||
class Shortcut;
|
||||
|
||||
/// Users of this class should install it as an event handler
|
||||
class ShortcutEventHandler : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
ShortcutEventHandler(QObject* parent);
|
||||
|
||||
void addShortcut(Shortcut* shortcut);
|
||||
void removeShortcut(Shortcut* shortcut);
|
||||
|
||||
protected:
|
||||
|
||||
bool eventFilter(QObject* watched, QEvent* event);
|
||||
|
||||
private:
|
||||
|
||||
typedef std::vector<Shortcut*> ShortcutList;
|
||||
// Child, Parent
|
||||
typedef std::map<QWidget*, QWidget*> WidgetMap;
|
||||
typedef std::map<QWidget*, ShortcutList> ShortcutMap;
|
||||
|
||||
enum MatchResult
|
||||
{
|
||||
Matches_WithMod,
|
||||
Matches_NoMod,
|
||||
Matches_Not
|
||||
};
|
||||
|
||||
void updateParent(QWidget* widget);
|
||||
|
||||
bool activate(QWidget* widget, unsigned int mod, unsigned int button);
|
||||
|
||||
bool deactivate(QWidget* widget, unsigned int mod, unsigned int button);
|
||||
|
||||
bool checkModifier(unsigned int mod, unsigned int button, Shortcut* shortcut, bool activate);
|
||||
|
||||
MatchResult match(unsigned int mod, unsigned int button, unsigned int value);
|
||||
|
||||
// Prefers Matches_WithMod and a larger number of buttons
|
||||
static bool sort(const std::pair<MatchResult, Shortcut*>& left,
|
||||
const std::pair<MatchResult, Shortcut*>& right);
|
||||
|
||||
WidgetMap mChildParentRelations;
|
||||
ShortcutMap mWidgetShortcuts;
|
||||
|
||||
private slots:
|
||||
|
||||
void widgetDestroyed();
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,791 @@
|
||||
#include "shortcutmanager.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <QApplication>
|
||||
#include <QStringList>
|
||||
|
||||
#include "shortcut.hpp"
|
||||
#include "shortcuteventhandler.hpp"
|
||||
|
||||
namespace CSMPrefs
|
||||
{
|
||||
ShortcutManager::ShortcutManager()
|
||||
{
|
||||
createLookupTables();
|
||||
mEventHandler = new ShortcutEventHandler(this);
|
||||
}
|
||||
|
||||
void ShortcutManager::addShortcut(Shortcut* shortcut)
|
||||
{
|
||||
mShortcuts.insert(std::make_pair(shortcut->getName(), shortcut));
|
||||
mShortcuts.insert(std::make_pair(shortcut->getModifierName(), shortcut));
|
||||
mEventHandler->addShortcut(shortcut);
|
||||
}
|
||||
|
||||
void ShortcutManager::removeShortcut(Shortcut* shortcut)
|
||||
{
|
||||
std::pair<ShortcutMap::iterator, ShortcutMap::iterator> range = mShortcuts.equal_range(shortcut->getName());
|
||||
|
||||
for (ShortcutMap::iterator it = range.first; it != range.second;)
|
||||
{
|
||||
if (it->second == shortcut)
|
||||
{
|
||||
mShortcuts.erase(it++);
|
||||
}
|
||||
else
|
||||
{
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
mEventHandler->removeShortcut(shortcut);
|
||||
}
|
||||
|
||||
bool ShortcutManager::getSequence(const std::string& name, QKeySequence& sequence) const
|
||||
{
|
||||
SequenceMap::const_iterator item = mSequences.find(name);
|
||||
if (item != mSequences.end())
|
||||
{
|
||||
sequence = item->second;
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
void ShortcutManager::setSequence(const std::string& name, const QKeySequence& sequence)
|
||||
{
|
||||
// Add to map/modify
|
||||
SequenceMap::iterator item = mSequences.find(name);
|
||||
if (item != mSequences.end())
|
||||
{
|
||||
item->second = sequence;
|
||||
}
|
||||
else
|
||||
{
|
||||
mSequences.insert(std::make_pair(name, sequence));
|
||||
}
|
||||
|
||||
// Change active shortcuts
|
||||
std::pair<ShortcutMap::iterator, ShortcutMap::iterator> rangeS = mShortcuts.equal_range(name);
|
||||
|
||||
for (ShortcutMap::iterator it = rangeS.first; it != rangeS.second; ++it)
|
||||
{
|
||||
it->second->setSequence(sequence);
|
||||
}
|
||||
}
|
||||
|
||||
bool ShortcutManager::getModifier(const std::string& name, int& modifier) const
|
||||
{
|
||||
ModifierMap::const_iterator item = mModifiers.find(name);
|
||||
if (item != mModifiers.end())
|
||||
{
|
||||
modifier = item->second;
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
void ShortcutManager::setModifier(const std::string& name, int modifier)
|
||||
{
|
||||
// Add to map/modify
|
||||
ModifierMap::iterator item = mModifiers.find(name);
|
||||
if (item != mModifiers.end())
|
||||
{
|
||||
item->second = modifier;
|
||||
}
|
||||
else
|
||||
{
|
||||
mModifiers.insert(std::make_pair(name, modifier));
|
||||
}
|
||||
|
||||
// Change active shortcuts
|
||||
std::pair<ShortcutMap::iterator, ShortcutMap::iterator> rangeS = mShortcuts.equal_range(name);
|
||||
|
||||
for (ShortcutMap::iterator it = rangeS.first; it != rangeS.second; ++it)
|
||||
{
|
||||
it->second->setModifier(modifier);
|
||||
}
|
||||
}
|
||||
|
||||
std::string ShortcutManager::convertToString(const QKeySequence& sequence) const
|
||||
{
|
||||
const int MouseKeyMask = 0x01FFFFFF;
|
||||
const int ModMask = 0x7E000000;
|
||||
|
||||
std::string result;
|
||||
|
||||
for (int i = 0; i < (int)sequence.count(); ++i)
|
||||
{
|
||||
int mods = sequence[i] & ModMask;
|
||||
int key = sequence[i] & MouseKeyMask;
|
||||
|
||||
if (key)
|
||||
{
|
||||
NameMap::const_iterator searchResult = mNames.find(key);
|
||||
if (searchResult != mNames.end())
|
||||
{
|
||||
if (mods && i == 0)
|
||||
{
|
||||
if (mods & Qt::ControlModifier)
|
||||
result.append("Ctl+");
|
||||
if (mods & Qt::ShiftModifier)
|
||||
result.append("Shift+");
|
||||
if (mods & Qt::AltModifier)
|
||||
result.append("Alt+");
|
||||
if (mods & Qt::MetaModifier)
|
||||
result.append("Meta+");
|
||||
if (mods & Qt::KeypadModifier)
|
||||
result.append("Keypad+");
|
||||
if (mods & Qt::GroupSwitchModifier)
|
||||
result.append("GroupSwitch+");
|
||||
}
|
||||
else if (i > 0)
|
||||
{
|
||||
result.append("+");
|
||||
}
|
||||
|
||||
result.append(searchResult->second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string ShortcutManager::convertToString(int modifier) const
|
||||
{
|
||||
NameMap::const_iterator searchResult = mNames.find(modifier);
|
||||
if (searchResult != mNames.end())
|
||||
{
|
||||
return searchResult->second;
|
||||
}
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string ShortcutManager::convertToString(const QKeySequence& sequence, int modifier) const
|
||||
{
|
||||
std::string concat = convertToString(sequence) + ";" + convertToString(modifier);
|
||||
return concat;
|
||||
}
|
||||
|
||||
void ShortcutManager::convertFromString(const std::string& data, QKeySequence& sequence) const
|
||||
{
|
||||
const int MaxKeys = 4; // A limitation of QKeySequence
|
||||
|
||||
size_t end = data.find(";");
|
||||
size_t size = std::min(end, data.size());
|
||||
|
||||
std::string value = data.substr(0, size);
|
||||
size_t start = 0;
|
||||
|
||||
int keyPos = 0;
|
||||
int mods = 0;
|
||||
|
||||
int keys[MaxKeys] = {};
|
||||
|
||||
while (start < value.size())
|
||||
{
|
||||
end = data.find("+", start);
|
||||
end = std::min(end, value.size());
|
||||
|
||||
std::string name = value.substr(start, end - start);
|
||||
|
||||
if (name == "Ctl")
|
||||
{
|
||||
mods |= Qt::ControlModifier;
|
||||
}
|
||||
else if (name == "Shift")
|
||||
{
|
||||
mods |= Qt::ShiftModifier;
|
||||
}
|
||||
else if (name == "Alt")
|
||||
{
|
||||
mods |= Qt::AltModifier;
|
||||
}
|
||||
else if (name == "Meta")
|
||||
{
|
||||
mods |= Qt::MetaModifier;
|
||||
}
|
||||
else if (name == "Keypad")
|
||||
{
|
||||
mods |= Qt::KeypadModifier;
|
||||
}
|
||||
else if (name == "GroupSwitch")
|
||||
{
|
||||
mods |= Qt::GroupSwitchModifier;
|
||||
}
|
||||
else
|
||||
{
|
||||
KeyMap::const_iterator searchResult = mKeys.find(name);
|
||||
if (searchResult != mKeys.end())
|
||||
{
|
||||
keys[keyPos] = mods | searchResult->second;
|
||||
|
||||
mods = 0;
|
||||
keyPos += 1;
|
||||
|
||||
if (keyPos >= MaxKeys)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
start = end + 1;
|
||||
}
|
||||
|
||||
sequence = QKeySequence(keys[0], keys[1], keys[2], keys[3]);
|
||||
}
|
||||
|
||||
void ShortcutManager::convertFromString(const std::string& data, int& modifier) const
|
||||
{
|
||||
size_t start = data.find(";") + 1;
|
||||
start = std::min(start, data.size());
|
||||
|
||||
std::string name = data.substr(start);
|
||||
KeyMap::const_iterator searchResult = mKeys.find(name);
|
||||
if (searchResult != mKeys.end())
|
||||
{
|
||||
modifier = searchResult->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
modifier = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void ShortcutManager::convertFromString(const std::string& data, QKeySequence& sequence, int& modifier) const
|
||||
{
|
||||
convertFromString(data, sequence);
|
||||
convertFromString(data, modifier);
|
||||
}
|
||||
|
||||
void ShortcutManager::createLookupTables()
|
||||
{
|
||||
// Mouse buttons
|
||||
mNames.insert(std::make_pair(Qt::LeftButton, "LMB"));
|
||||
mNames.insert(std::make_pair(Qt::RightButton, "RMB"));
|
||||
mNames.insert(std::make_pair(Qt::MiddleButton, "MMB"));
|
||||
mNames.insert(std::make_pair(Qt::XButton1, "Mouse4"));
|
||||
mNames.insert(std::make_pair(Qt::XButton2, "Mouse5"));
|
||||
|
||||
// Keyboard buttons
|
||||
for (size_t i = 0; QtKeys[i].first != 0; ++i)
|
||||
{
|
||||
mNames.insert(QtKeys[i]);
|
||||
}
|
||||
|
||||
// Generate inverse map
|
||||
for (NameMap::const_iterator it = mNames.begin(); it != mNames.end(); ++it)
|
||||
{
|
||||
mKeys.insert(std::make_pair(it->second, it->first));
|
||||
}
|
||||
}
|
||||
|
||||
QString ShortcutManager::processToolTip(const QString& toolTip) const
|
||||
{
|
||||
const QChar SequenceStart = '{';
|
||||
const QChar SequenceEnd = '}';
|
||||
|
||||
QStringList substrings;
|
||||
|
||||
int prevIndex = 0;
|
||||
int startIndex = toolTip.indexOf(SequenceStart);
|
||||
int endIndex = (startIndex != -1) ? toolTip.indexOf(SequenceEnd, startIndex) : -1;
|
||||
|
||||
// Process every valid shortcut escape sequence
|
||||
while (startIndex != -1 && endIndex != -1)
|
||||
{
|
||||
int count = startIndex - prevIndex;
|
||||
if (count > 0)
|
||||
{
|
||||
substrings.push_back(toolTip.mid(prevIndex, count));
|
||||
}
|
||||
|
||||
// Find sequence name
|
||||
startIndex += 1; // '{' character
|
||||
count = endIndex - startIndex;
|
||||
if (count > 0)
|
||||
{
|
||||
QString settingName = toolTip.mid(startIndex, count);
|
||||
|
||||
QKeySequence sequence;
|
||||
int modifier;
|
||||
|
||||
if (getSequence(settingName.toUtf8().data(), sequence))
|
||||
{
|
||||
QString value = QString::fromUtf8(convertToString(sequence).c_str());
|
||||
substrings.push_back(value);
|
||||
}
|
||||
else if (getModifier(settingName.toUtf8().data(), modifier))
|
||||
{
|
||||
QString value = QString::fromUtf8(convertToString(modifier).c_str());
|
||||
substrings.push_back(value);
|
||||
}
|
||||
|
||||
prevIndex = endIndex + 1; // '}' character
|
||||
}
|
||||
|
||||
startIndex = toolTip.indexOf(SequenceStart, endIndex);
|
||||
endIndex = (startIndex != -1) ? toolTip.indexOf(SequenceEnd, startIndex) : -1;
|
||||
}
|
||||
|
||||
if (prevIndex < toolTip.size())
|
||||
{
|
||||
substrings.push_back(toolTip.mid(prevIndex));
|
||||
}
|
||||
|
||||
return substrings.join("");
|
||||
}
|
||||
|
||||
const std::pair<int, const char*> ShortcutManager::QtKeys[] =
|
||||
{
|
||||
std::make_pair((int)Qt::Key_Space , "Space"),
|
||||
std::make_pair((int)Qt::Key_Exclam , "Exclam"),
|
||||
std::make_pair((int)Qt::Key_QuoteDbl , "QuoteDbl"),
|
||||
std::make_pair((int)Qt::Key_NumberSign , "NumberSign"),
|
||||
std::make_pair((int)Qt::Key_Dollar , "Dollar"),
|
||||
std::make_pair((int)Qt::Key_Percent , "Percent"),
|
||||
std::make_pair((int)Qt::Key_Ampersand , "Ampersand"),
|
||||
std::make_pair((int)Qt::Key_Apostrophe , "Apostrophe"),
|
||||
std::make_pair((int)Qt::Key_ParenLeft , "ParenLeft"),
|
||||
std::make_pair((int)Qt::Key_ParenRight , "ParenRight"),
|
||||
std::make_pair((int)Qt::Key_Asterisk , "Asterisk"),
|
||||
std::make_pair((int)Qt::Key_Plus , "Plus"),
|
||||
std::make_pair((int)Qt::Key_Comma , "Comma"),
|
||||
std::make_pair((int)Qt::Key_Minus , "Minus"),
|
||||
std::make_pair((int)Qt::Key_Period , "Period"),
|
||||
std::make_pair((int)Qt::Key_Slash , "Slash"),
|
||||
std::make_pair((int)Qt::Key_0 , "0"),
|
||||
std::make_pair((int)Qt::Key_1 , "1"),
|
||||
std::make_pair((int)Qt::Key_2 , "2"),
|
||||
std::make_pair((int)Qt::Key_3 , "3"),
|
||||
std::make_pair((int)Qt::Key_4 , "4"),
|
||||
std::make_pair((int)Qt::Key_5 , "5"),
|
||||
std::make_pair((int)Qt::Key_6 , "6"),
|
||||
std::make_pair((int)Qt::Key_7 , "7"),
|
||||
std::make_pair((int)Qt::Key_8 , "8"),
|
||||
std::make_pair((int)Qt::Key_9 , "9"),
|
||||
std::make_pair((int)Qt::Key_Colon , "Colon"),
|
||||
std::make_pair((int)Qt::Key_Semicolon , "Semicolon"),
|
||||
std::make_pair((int)Qt::Key_Less , "Less"),
|
||||
std::make_pair((int)Qt::Key_Equal , "Equal"),
|
||||
std::make_pair((int)Qt::Key_Greater , "Greater"),
|
||||
std::make_pair((int)Qt::Key_Question , "Question"),
|
||||
std::make_pair((int)Qt::Key_At , "At"),
|
||||
std::make_pair((int)Qt::Key_A , "A"),
|
||||
std::make_pair((int)Qt::Key_B , "B"),
|
||||
std::make_pair((int)Qt::Key_C , "C"),
|
||||
std::make_pair((int)Qt::Key_D , "D"),
|
||||
std::make_pair((int)Qt::Key_E , "E"),
|
||||
std::make_pair((int)Qt::Key_F , "F"),
|
||||
std::make_pair((int)Qt::Key_G , "G"),
|
||||
std::make_pair((int)Qt::Key_H , "H"),
|
||||
std::make_pair((int)Qt::Key_I , "I"),
|
||||
std::make_pair((int)Qt::Key_J , "J"),
|
||||
std::make_pair((int)Qt::Key_K , "K"),
|
||||
std::make_pair((int)Qt::Key_L , "L"),
|
||||
std::make_pair((int)Qt::Key_M , "M"),
|
||||
std::make_pair((int)Qt::Key_N , "N"),
|
||||
std::make_pair((int)Qt::Key_O , "O"),
|
||||
std::make_pair((int)Qt::Key_P , "P"),
|
||||
std::make_pair((int)Qt::Key_Q , "Q"),
|
||||
std::make_pair((int)Qt::Key_R , "R"),
|
||||
std::make_pair((int)Qt::Key_S , "S"),
|
||||
std::make_pair((int)Qt::Key_T , "T"),
|
||||
std::make_pair((int)Qt::Key_U , "U"),
|
||||
std::make_pair((int)Qt::Key_V , "V"),
|
||||
std::make_pair((int)Qt::Key_W , "W"),
|
||||
std::make_pair((int)Qt::Key_X , "X"),
|
||||
std::make_pair((int)Qt::Key_Y , "Y"),
|
||||
std::make_pair((int)Qt::Key_Z , "Z"),
|
||||
std::make_pair((int)Qt::Key_BracketLeft , "BracketLeft"),
|
||||
std::make_pair((int)Qt::Key_Backslash , "Backslash"),
|
||||
std::make_pair((int)Qt::Key_BracketRight , "BracketRight"),
|
||||
std::make_pair((int)Qt::Key_AsciiCircum , "AsciiCircum"),
|
||||
std::make_pair((int)Qt::Key_Underscore , "Underscore"),
|
||||
std::make_pair((int)Qt::Key_QuoteLeft , "QuoteLeft"),
|
||||
std::make_pair((int)Qt::Key_BraceLeft , "BraceLeft"),
|
||||
std::make_pair((int)Qt::Key_Bar , "Bar"),
|
||||
std::make_pair((int)Qt::Key_BraceRight , "BraceRight"),
|
||||
std::make_pair((int)Qt::Key_AsciiTilde , "AsciiTilde"),
|
||||
std::make_pair((int)Qt::Key_nobreakspace , "nobreakspace"),
|
||||
std::make_pair((int)Qt::Key_exclamdown , "exclamdown"),
|
||||
std::make_pair((int)Qt::Key_cent , "cent"),
|
||||
std::make_pair((int)Qt::Key_sterling , "sterling"),
|
||||
std::make_pair((int)Qt::Key_currency , "currency"),
|
||||
std::make_pair((int)Qt::Key_yen , "yen"),
|
||||
std::make_pair((int)Qt::Key_brokenbar , "brokenbar"),
|
||||
std::make_pair((int)Qt::Key_section , "section"),
|
||||
std::make_pair((int)Qt::Key_diaeresis , "diaeresis"),
|
||||
std::make_pair((int)Qt::Key_copyright , "copyright"),
|
||||
std::make_pair((int)Qt::Key_ordfeminine , "ordfeminine"),
|
||||
std::make_pair((int)Qt::Key_guillemotleft , "guillemotleft"),
|
||||
std::make_pair((int)Qt::Key_notsign , "notsign"),
|
||||
std::make_pair((int)Qt::Key_hyphen , "hyphen"),
|
||||
std::make_pair((int)Qt::Key_registered , "registered"),
|
||||
std::make_pair((int)Qt::Key_macron , "macron"),
|
||||
std::make_pair((int)Qt::Key_degree , "degree"),
|
||||
std::make_pair((int)Qt::Key_plusminus , "plusminus"),
|
||||
std::make_pair((int)Qt::Key_twosuperior , "twosuperior"),
|
||||
std::make_pair((int)Qt::Key_threesuperior , "threesuperior"),
|
||||
std::make_pair((int)Qt::Key_acute , "acute"),
|
||||
std::make_pair((int)Qt::Key_mu , "mu"),
|
||||
std::make_pair((int)Qt::Key_paragraph , "paragraph"),
|
||||
std::make_pair((int)Qt::Key_periodcentered , "periodcentered"),
|
||||
std::make_pair((int)Qt::Key_cedilla , "cedilla"),
|
||||
std::make_pair((int)Qt::Key_onesuperior , "onesuperior"),
|
||||
std::make_pair((int)Qt::Key_masculine , "masculine"),
|
||||
std::make_pair((int)Qt::Key_guillemotright , "guillemotright"),
|
||||
std::make_pair((int)Qt::Key_onequarter , "onequarter"),
|
||||
std::make_pair((int)Qt::Key_onehalf , "onehalf"),
|
||||
std::make_pair((int)Qt::Key_threequarters , "threequarters"),
|
||||
std::make_pair((int)Qt::Key_questiondown , "questiondown"),
|
||||
std::make_pair((int)Qt::Key_Agrave , "Agrave"),
|
||||
std::make_pair((int)Qt::Key_Aacute , "Aacute"),
|
||||
std::make_pair((int)Qt::Key_Acircumflex , "Acircumflex"),
|
||||
std::make_pair((int)Qt::Key_Atilde , "Atilde"),
|
||||
std::make_pair((int)Qt::Key_Adiaeresis , "Adiaeresis"),
|
||||
std::make_pair((int)Qt::Key_Aring , "Aring"),
|
||||
std::make_pair((int)Qt::Key_AE , "AE"),
|
||||
std::make_pair((int)Qt::Key_Ccedilla , "Ccedilla"),
|
||||
std::make_pair((int)Qt::Key_Egrave , "Egrave"),
|
||||
std::make_pair((int)Qt::Key_Eacute , "Eacute"),
|
||||
std::make_pair((int)Qt::Key_Ecircumflex , "Ecircumflex"),
|
||||
std::make_pair((int)Qt::Key_Ediaeresis , "Ediaeresis"),
|
||||
std::make_pair((int)Qt::Key_Igrave , "Igrave"),
|
||||
std::make_pair((int)Qt::Key_Iacute , "Iacute"),
|
||||
std::make_pair((int)Qt::Key_Icircumflex , "Icircumflex"),
|
||||
std::make_pair((int)Qt::Key_Idiaeresis , "Idiaeresis"),
|
||||
std::make_pair((int)Qt::Key_ETH , "ETH"),
|
||||
std::make_pair((int)Qt::Key_Ntilde , "Ntilde"),
|
||||
std::make_pair((int)Qt::Key_Ograve , "Ograve"),
|
||||
std::make_pair((int)Qt::Key_Oacute , "Oacute"),
|
||||
std::make_pair((int)Qt::Key_Ocircumflex , "Ocircumflex"),
|
||||
std::make_pair((int)Qt::Key_Otilde , "Otilde"),
|
||||
std::make_pair((int)Qt::Key_Odiaeresis , "Odiaeresis"),
|
||||
std::make_pair((int)Qt::Key_multiply , "multiply"),
|
||||
std::make_pair((int)Qt::Key_Ooblique , "Ooblique"),
|
||||
std::make_pair((int)Qt::Key_Ugrave , "Ugrave"),
|
||||
std::make_pair((int)Qt::Key_Uacute , "Uacute"),
|
||||
std::make_pair((int)Qt::Key_Ucircumflex , "Ucircumflex"),
|
||||
std::make_pair((int)Qt::Key_Udiaeresis , "Udiaeresis"),
|
||||
std::make_pair((int)Qt::Key_Yacute , "Yacute"),
|
||||
std::make_pair((int)Qt::Key_THORN , "THORN"),
|
||||
std::make_pair((int)Qt::Key_ssharp , "ssharp"),
|
||||
std::make_pair((int)Qt::Key_division , "division"),
|
||||
std::make_pair((int)Qt::Key_ydiaeresis , "ydiaeresis"),
|
||||
std::make_pair((int)Qt::Key_Escape , "Escape"),
|
||||
std::make_pair((int)Qt::Key_Tab , "Tab"),
|
||||
std::make_pair((int)Qt::Key_Backtab , "Backtab"),
|
||||
std::make_pair((int)Qt::Key_Backspace , "Backspace"),
|
||||
std::make_pair((int)Qt::Key_Return , "Return"),
|
||||
std::make_pair((int)Qt::Key_Enter , "Enter"),
|
||||
std::make_pair((int)Qt::Key_Insert , "Insert"),
|
||||
std::make_pair((int)Qt::Key_Delete , "Delete"),
|
||||
std::make_pair((int)Qt::Key_Pause , "Pause"),
|
||||
std::make_pair((int)Qt::Key_Print , "Print"),
|
||||
std::make_pair((int)Qt::Key_SysReq , "SysReq"),
|
||||
std::make_pair((int)Qt::Key_Clear , "Clear"),
|
||||
std::make_pair((int)Qt::Key_Home , "Home"),
|
||||
std::make_pair((int)Qt::Key_End , "End"),
|
||||
std::make_pair((int)Qt::Key_Left , "Left"),
|
||||
std::make_pair((int)Qt::Key_Up , "Up"),
|
||||
std::make_pair((int)Qt::Key_Right , "Right"),
|
||||
std::make_pair((int)Qt::Key_Down , "Down"),
|
||||
std::make_pair((int)Qt::Key_PageUp , "PageUp"),
|
||||
std::make_pair((int)Qt::Key_PageDown , "PageDown"),
|
||||
std::make_pair((int)Qt::Key_Shift , "Shift"),
|
||||
std::make_pair((int)Qt::Key_Control , "Control"),
|
||||
std::make_pair((int)Qt::Key_Meta , "Meta"),
|
||||
std::make_pair((int)Qt::Key_Alt , "Alt"),
|
||||
std::make_pair((int)Qt::Key_CapsLock , "CapsLock"),
|
||||
std::make_pair((int)Qt::Key_NumLock , "NumLock"),
|
||||
std::make_pair((int)Qt::Key_ScrollLock , "ScrollLock"),
|
||||
std::make_pair((int)Qt::Key_F1 , "F1"),
|
||||
std::make_pair((int)Qt::Key_F2 , "F2"),
|
||||
std::make_pair((int)Qt::Key_F3 , "F3"),
|
||||
std::make_pair((int)Qt::Key_F4 , "F4"),
|
||||
std::make_pair((int)Qt::Key_F5 , "F5"),
|
||||
std::make_pair((int)Qt::Key_F6 , "F6"),
|
||||
std::make_pair((int)Qt::Key_F7 , "F7"),
|
||||
std::make_pair((int)Qt::Key_F8 , "F8"),
|
||||
std::make_pair((int)Qt::Key_F9 , "F9"),
|
||||
std::make_pair((int)Qt::Key_F10 , "F10"),
|
||||
std::make_pair((int)Qt::Key_F11 , "F11"),
|
||||
std::make_pair((int)Qt::Key_F12 , "F12"),
|
||||
std::make_pair((int)Qt::Key_F13 , "F13"),
|
||||
std::make_pair((int)Qt::Key_F14 , "F14"),
|
||||
std::make_pair((int)Qt::Key_F15 , "F15"),
|
||||
std::make_pair((int)Qt::Key_F16 , "F16"),
|
||||
std::make_pair((int)Qt::Key_F17 , "F17"),
|
||||
std::make_pair((int)Qt::Key_F18 , "F18"),
|
||||
std::make_pair((int)Qt::Key_F19 , "F19"),
|
||||
std::make_pair((int)Qt::Key_F20 , "F20"),
|
||||
std::make_pair((int)Qt::Key_F21 , "F21"),
|
||||
std::make_pair((int)Qt::Key_F22 , "F22"),
|
||||
std::make_pair((int)Qt::Key_F23 , "F23"),
|
||||
std::make_pair((int)Qt::Key_F24 , "F24"),
|
||||
std::make_pair((int)Qt::Key_F25 , "F25"),
|
||||
std::make_pair((int)Qt::Key_F26 , "F26"),
|
||||
std::make_pair((int)Qt::Key_F27 , "F27"),
|
||||
std::make_pair((int)Qt::Key_F28 , "F28"),
|
||||
std::make_pair((int)Qt::Key_F29 , "F29"),
|
||||
std::make_pair((int)Qt::Key_F30 , "F30"),
|
||||
std::make_pair((int)Qt::Key_F31 , "F31"),
|
||||
std::make_pair((int)Qt::Key_F32 , "F32"),
|
||||
std::make_pair((int)Qt::Key_F33 , "F33"),
|
||||
std::make_pair((int)Qt::Key_F34 , "F34"),
|
||||
std::make_pair((int)Qt::Key_F35 , "F35"),
|
||||
std::make_pair((int)Qt::Key_Super_L , "Super_L"),
|
||||
std::make_pair((int)Qt::Key_Super_R , "Super_R"),
|
||||
std::make_pair((int)Qt::Key_Menu , "Menu"),
|
||||
std::make_pair((int)Qt::Key_Hyper_L , "Hyper_L"),
|
||||
std::make_pair((int)Qt::Key_Hyper_R , "Hyper_R"),
|
||||
std::make_pair((int)Qt::Key_Help , "Help"),
|
||||
std::make_pair((int)Qt::Key_Direction_L , "Direction_L"),
|
||||
std::make_pair((int)Qt::Key_Direction_R , "Direction_R"),
|
||||
std::make_pair((int)Qt::Key_Back , "Back"),
|
||||
std::make_pair((int)Qt::Key_Forward , "Forward"),
|
||||
std::make_pair((int)Qt::Key_Stop , "Stop"),
|
||||
std::make_pair((int)Qt::Key_Refresh , "Refresh"),
|
||||
std::make_pair((int)Qt::Key_VolumeDown , "VolumeDown"),
|
||||
std::make_pair((int)Qt::Key_VolumeMute , "VolumeMute"),
|
||||
std::make_pair((int)Qt::Key_VolumeUp , "VolumeUp"),
|
||||
std::make_pair((int)Qt::Key_BassBoost , "BassBoost"),
|
||||
std::make_pair((int)Qt::Key_BassUp , "BassUp"),
|
||||
std::make_pair((int)Qt::Key_BassDown , "BassDown"),
|
||||
std::make_pair((int)Qt::Key_TrebleUp , "TrebleUp"),
|
||||
std::make_pair((int)Qt::Key_TrebleDown , "TrebleDown"),
|
||||
std::make_pair((int)Qt::Key_MediaPlay , "MediaPlay"),
|
||||
std::make_pair((int)Qt::Key_MediaStop , "MediaStop"),
|
||||
std::make_pair((int)Qt::Key_MediaPrevious , "MediaPrevious"),
|
||||
std::make_pair((int)Qt::Key_MediaNext , "MediaNext"),
|
||||
std::make_pair((int)Qt::Key_MediaRecord , "MediaRecord"),
|
||||
std::make_pair((int)Qt::Key_MediaPause , "MediaPause"),
|
||||
std::make_pair((int)Qt::Key_MediaTogglePlayPause , "MediaTogglePlayPause"),
|
||||
std::make_pair((int)Qt::Key_HomePage , "HomePage"),
|
||||
std::make_pair((int)Qt::Key_Favorites , "Favorites"),
|
||||
std::make_pair((int)Qt::Key_Search , "Search"),
|
||||
std::make_pair((int)Qt::Key_Standby , "Standby"),
|
||||
std::make_pair((int)Qt::Key_OpenUrl , "OpenUrl"),
|
||||
std::make_pair((int)Qt::Key_LaunchMail , "LaunchMail"),
|
||||
std::make_pair((int)Qt::Key_LaunchMedia , "LaunchMedia"),
|
||||
std::make_pair((int)Qt::Key_Launch0 , "Launch0"),
|
||||
std::make_pair((int)Qt::Key_Launch1 , "Launch1"),
|
||||
std::make_pair((int)Qt::Key_Launch2 , "Launch2"),
|
||||
std::make_pair((int)Qt::Key_Launch3 , "Launch3"),
|
||||
std::make_pair((int)Qt::Key_Launch4 , "Launch4"),
|
||||
std::make_pair((int)Qt::Key_Launch5 , "Launch5"),
|
||||
std::make_pair((int)Qt::Key_Launch6 , "Launch6"),
|
||||
std::make_pair((int)Qt::Key_Launch7 , "Launch7"),
|
||||
std::make_pair((int)Qt::Key_Launch8 , "Launch8"),
|
||||
std::make_pair((int)Qt::Key_Launch9 , "Launch9"),
|
||||
std::make_pair((int)Qt::Key_LaunchA , "LaunchA"),
|
||||
std::make_pair((int)Qt::Key_LaunchB , "LaunchB"),
|
||||
std::make_pair((int)Qt::Key_LaunchC , "LaunchC"),
|
||||
std::make_pair((int)Qt::Key_LaunchD , "LaunchD"),
|
||||
std::make_pair((int)Qt::Key_LaunchE , "LaunchE"),
|
||||
std::make_pair((int)Qt::Key_LaunchF , "LaunchF"),
|
||||
std::make_pair((int)Qt::Key_MonBrightnessUp , "MonBrightnessUp"),
|
||||
std::make_pair((int)Qt::Key_MonBrightnessDown , "MonBrightnessDown"),
|
||||
std::make_pair((int)Qt::Key_KeyboardLightOnOff , "KeyboardLightOnOff"),
|
||||
std::make_pair((int)Qt::Key_KeyboardBrightnessUp , "KeyboardBrightnessUp"),
|
||||
std::make_pair((int)Qt::Key_KeyboardBrightnessDown , "KeyboardBrightnessDown"),
|
||||
std::make_pair((int)Qt::Key_PowerOff , "PowerOff"),
|
||||
std::make_pair((int)Qt::Key_WakeUp , "WakeUp"),
|
||||
std::make_pair((int)Qt::Key_Eject , "Eject"),
|
||||
std::make_pair((int)Qt::Key_ScreenSaver , "ScreenSaver"),
|
||||
std::make_pair((int)Qt::Key_WWW , "WWW"),
|
||||
std::make_pair((int)Qt::Key_Memo , "Memo"),
|
||||
std::make_pair((int)Qt::Key_LightBulb , "LightBulb"),
|
||||
std::make_pair((int)Qt::Key_Shop , "Shop"),
|
||||
std::make_pair((int)Qt::Key_History , "History"),
|
||||
std::make_pair((int)Qt::Key_AddFavorite , "AddFavorite"),
|
||||
std::make_pair((int)Qt::Key_HotLinks , "HotLinks"),
|
||||
std::make_pair((int)Qt::Key_BrightnessAdjust , "BrightnessAdjust"),
|
||||
std::make_pair((int)Qt::Key_Finance , "Finance"),
|
||||
std::make_pair((int)Qt::Key_Community , "Community"),
|
||||
std::make_pair((int)Qt::Key_AudioRewind , "AudioRewind"),
|
||||
std::make_pair((int)Qt::Key_BackForward , "BackForward"),
|
||||
std::make_pair((int)Qt::Key_ApplicationLeft , "ApplicationLeft"),
|
||||
std::make_pair((int)Qt::Key_ApplicationRight , "ApplicationRight"),
|
||||
std::make_pair((int)Qt::Key_Book , "Book"),
|
||||
std::make_pair((int)Qt::Key_CD , "CD"),
|
||||
std::make_pair((int)Qt::Key_Calculator , "Calculator"),
|
||||
std::make_pair((int)Qt::Key_ToDoList , "ToDoList"),
|
||||
std::make_pair((int)Qt::Key_ClearGrab , "ClearGrab"),
|
||||
std::make_pair((int)Qt::Key_Close , "Close"),
|
||||
std::make_pair((int)Qt::Key_Copy , "Copy"),
|
||||
std::make_pair((int)Qt::Key_Cut , "Cut"),
|
||||
std::make_pair((int)Qt::Key_Display , "Display"),
|
||||
std::make_pair((int)Qt::Key_DOS , "DOS"),
|
||||
std::make_pair((int)Qt::Key_Documents , "Documents"),
|
||||
std::make_pair((int)Qt::Key_Excel , "Excel"),
|
||||
std::make_pair((int)Qt::Key_Explorer , "Explorer"),
|
||||
std::make_pair((int)Qt::Key_Game , "Game"),
|
||||
std::make_pair((int)Qt::Key_Go , "Go"),
|
||||
std::make_pair((int)Qt::Key_iTouch , "iTouch"),
|
||||
std::make_pair((int)Qt::Key_LogOff , "LogOff"),
|
||||
std::make_pair((int)Qt::Key_Market , "Market"),
|
||||
std::make_pair((int)Qt::Key_Meeting , "Meeting"),
|
||||
std::make_pair((int)Qt::Key_MenuKB , "MenuKB"),
|
||||
std::make_pair((int)Qt::Key_MenuPB , "MenuPB"),
|
||||
std::make_pair((int)Qt::Key_MySites , "MySites"),
|
||||
std::make_pair((int)Qt::Key_News , "News"),
|
||||
std::make_pair((int)Qt::Key_OfficeHome , "OfficeHome"),
|
||||
std::make_pair((int)Qt::Key_Option , "Option"),
|
||||
std::make_pair((int)Qt::Key_Paste , "Paste"),
|
||||
std::make_pair((int)Qt::Key_Phone , "Phone"),
|
||||
std::make_pair((int)Qt::Key_Calendar , "Calendar"),
|
||||
std::make_pair((int)Qt::Key_Reply , "Reply"),
|
||||
std::make_pair((int)Qt::Key_Reload , "Reload"),
|
||||
std::make_pair((int)Qt::Key_RotateWindows , "RotateWindows"),
|
||||
std::make_pair((int)Qt::Key_RotationPB , "RotationPB"),
|
||||
std::make_pair((int)Qt::Key_RotationKB , "RotationKB"),
|
||||
std::make_pair((int)Qt::Key_Save , "Save"),
|
||||
std::make_pair((int)Qt::Key_Send , "Send"),
|
||||
std::make_pair((int)Qt::Key_Spell , "Spell"),
|
||||
std::make_pair((int)Qt::Key_SplitScreen , "SplitScreen"),
|
||||
std::make_pair((int)Qt::Key_Support , "Support"),
|
||||
std::make_pair((int)Qt::Key_TaskPane , "TaskPane"),
|
||||
std::make_pair((int)Qt::Key_Terminal , "Terminal"),
|
||||
std::make_pair((int)Qt::Key_Tools , "Tools"),
|
||||
std::make_pair((int)Qt::Key_Travel , "Travel"),
|
||||
std::make_pair((int)Qt::Key_Video , "Video"),
|
||||
std::make_pair((int)Qt::Key_Word , "Word"),
|
||||
std::make_pair((int)Qt::Key_Xfer , "Xfer"),
|
||||
std::make_pair((int)Qt::Key_ZoomIn , "ZoomIn"),
|
||||
std::make_pair((int)Qt::Key_ZoomOut , "ZoomOut"),
|
||||
std::make_pair((int)Qt::Key_Away , "Away"),
|
||||
std::make_pair((int)Qt::Key_Messenger , "Messenger"),
|
||||
std::make_pair((int)Qt::Key_WebCam , "WebCam"),
|
||||
std::make_pair((int)Qt::Key_MailForward , "MailForward"),
|
||||
std::make_pair((int)Qt::Key_Pictures , "Pictures"),
|
||||
std::make_pair((int)Qt::Key_Music , "Music"),
|
||||
std::make_pair((int)Qt::Key_Battery , "Battery"),
|
||||
std::make_pair((int)Qt::Key_Bluetooth , "Bluetooth"),
|
||||
std::make_pair((int)Qt::Key_WLAN , "WLAN"),
|
||||
std::make_pair((int)Qt::Key_UWB , "UWB"),
|
||||
std::make_pair((int)Qt::Key_AudioForward , "AudioForward"),
|
||||
std::make_pair((int)Qt::Key_AudioRepeat , "AudioRepeat"),
|
||||
std::make_pair((int)Qt::Key_AudioRandomPlay , "AudioRandomPlay"),
|
||||
std::make_pair((int)Qt::Key_Subtitle , "Subtitle"),
|
||||
std::make_pair((int)Qt::Key_AudioCycleTrack , "AudioCycleTrack"),
|
||||
std::make_pair((int)Qt::Key_Time , "Time"),
|
||||
std::make_pair((int)Qt::Key_Hibernate , "Hibernate"),
|
||||
std::make_pair((int)Qt::Key_View , "View"),
|
||||
std::make_pair((int)Qt::Key_TopMenu , "TopMenu"),
|
||||
std::make_pair((int)Qt::Key_PowerDown , "PowerDown"),
|
||||
std::make_pair((int)Qt::Key_Suspend , "Suspend"),
|
||||
std::make_pair((int)Qt::Key_ContrastAdjust , "ContrastAdjust"),
|
||||
std::make_pair((int)Qt::Key_LaunchG , "LaunchG"),
|
||||
std::make_pair((int)Qt::Key_LaunchH , "LaunchH"),
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5,7,0)
|
||||
std::make_pair((int)Qt::Key_TouchpadToggle , "TouchpadToggle"),
|
||||
std::make_pair((int)Qt::Key_TouchpadOn , "TouchpadOn"),
|
||||
std::make_pair((int)Qt::Key_TouchpadOff , "TouchpadOff"),
|
||||
std::make_pair((int)Qt::Key_MicMute , "MicMute"),
|
||||
std::make_pair((int)Qt::Key_Red , "Red"),
|
||||
std::make_pair((int)Qt::Key_Green , "Green"),
|
||||
std::make_pair((int)Qt::Key_Yellow , "Yellow"),
|
||||
std::make_pair((int)Qt::Key_Blue , "Blue"),
|
||||
std::make_pair((int)Qt::Key_ChannelUp , "ChannelUp"),
|
||||
std::make_pair((int)Qt::Key_ChannelDown , "ChannelDown"),
|
||||
std::make_pair((int)Qt::Key_Guide , "Guide"),
|
||||
std::make_pair((int)Qt::Key_Info , "Info"),
|
||||
std::make_pair((int)Qt::Key_Settings , "Settings"),
|
||||
std::make_pair((int)Qt::Key_MicVolumeUp , "MicVolumeUp"),
|
||||
std::make_pair((int)Qt::Key_MicVolumeDown , "MicVolumeDown"),
|
||||
std::make_pair((int)Qt::Key_New , "New"),
|
||||
std::make_pair((int)Qt::Key_Open , "Open"),
|
||||
std::make_pair((int)Qt::Key_Find , "Find"),
|
||||
std::make_pair((int)Qt::Key_Undo , "Undo"),
|
||||
std::make_pair((int)Qt::Key_Redo , "Redo"),
|
||||
#endif
|
||||
std::make_pair((int)Qt::Key_AltGr , "AltGr"),
|
||||
std::make_pair((int)Qt::Key_Multi_key , "Multi_key"),
|
||||
std::make_pair((int)Qt::Key_Kanji , "Kanji"),
|
||||
std::make_pair((int)Qt::Key_Muhenkan , "Muhenkan"),
|
||||
std::make_pair((int)Qt::Key_Henkan , "Henkan"),
|
||||
std::make_pair((int)Qt::Key_Romaji , "Romaji"),
|
||||
std::make_pair((int)Qt::Key_Hiragana , "Hiragana"),
|
||||
std::make_pair((int)Qt::Key_Katakana , "Katakana"),
|
||||
std::make_pair((int)Qt::Key_Hiragana_Katakana , "Hiragana_Katakana"),
|
||||
std::make_pair((int)Qt::Key_Zenkaku , "Zenkaku"),
|
||||
std::make_pair((int)Qt::Key_Hankaku , "Hankaku"),
|
||||
std::make_pair((int)Qt::Key_Zenkaku_Hankaku , "Zenkaku_Hankaku"),
|
||||
std::make_pair((int)Qt::Key_Touroku , "Touroku"),
|
||||
std::make_pair((int)Qt::Key_Massyo , "Massyo"),
|
||||
std::make_pair((int)Qt::Key_Kana_Lock , "Kana_Lock"),
|
||||
std::make_pair((int)Qt::Key_Kana_Shift , "Kana_Shift"),
|
||||
std::make_pair((int)Qt::Key_Eisu_Shift , "Eisu_Shift"),
|
||||
std::make_pair((int)Qt::Key_Eisu_toggle , "Eisu_toggle"),
|
||||
std::make_pair((int)Qt::Key_Hangul , "Hangul"),
|
||||
std::make_pair((int)Qt::Key_Hangul_Start , "Hangul_Start"),
|
||||
std::make_pair((int)Qt::Key_Hangul_End , "Hangul_End"),
|
||||
std::make_pair((int)Qt::Key_Hangul_Hanja , "Hangul_Hanja"),
|
||||
std::make_pair((int)Qt::Key_Hangul_Jamo , "Hangul_Jamo"),
|
||||
std::make_pair((int)Qt::Key_Hangul_Romaja , "Hangul_Romaja"),
|
||||
std::make_pair((int)Qt::Key_Codeinput , "Codeinput"),
|
||||
std::make_pair((int)Qt::Key_Hangul_Jeonja , "Hangul_Jeonja"),
|
||||
std::make_pair((int)Qt::Key_Hangul_Banja , "Hangul_Banja"),
|
||||
std::make_pair((int)Qt::Key_Hangul_PreHanja , "Hangul_PreHanja"),
|
||||
std::make_pair((int)Qt::Key_Hangul_PostHanja , "Hangul_PostHanja"),
|
||||
std::make_pair((int)Qt::Key_SingleCandidate , "SingleCandidate"),
|
||||
std::make_pair((int)Qt::Key_MultipleCandidate , "MultipleCandidate"),
|
||||
std::make_pair((int)Qt::Key_PreviousCandidate , "PreviousCandidate"),
|
||||
std::make_pair((int)Qt::Key_Hangul_Special , "Hangul_Special"),
|
||||
std::make_pair((int)Qt::Key_Mode_switch , "Mode_switch"),
|
||||
std::make_pair((int)Qt::Key_Dead_Grave , "Dead_Grave"),
|
||||
std::make_pair((int)Qt::Key_Dead_Acute , "Dead_Acute"),
|
||||
std::make_pair((int)Qt::Key_Dead_Circumflex , "Dead_Circumflex"),
|
||||
std::make_pair((int)Qt::Key_Dead_Tilde , "Dead_Tilde"),
|
||||
std::make_pair((int)Qt::Key_Dead_Macron , "Dead_Macron"),
|
||||
std::make_pair((int)Qt::Key_Dead_Breve , "Dead_Breve"),
|
||||
std::make_pair((int)Qt::Key_Dead_Abovedot , "Dead_Abovedot"),
|
||||
std::make_pair((int)Qt::Key_Dead_Diaeresis , "Dead_Diaeresis"),
|
||||
std::make_pair((int)Qt::Key_Dead_Abovering , "Dead_Abovering"),
|
||||
std::make_pair((int)Qt::Key_Dead_Doubleacute , "Dead_Doubleacute"),
|
||||
std::make_pair((int)Qt::Key_Dead_Caron , "Dead_Caron"),
|
||||
std::make_pair((int)Qt::Key_Dead_Cedilla , "Dead_Cedilla"),
|
||||
std::make_pair((int)Qt::Key_Dead_Ogonek , "Dead_Ogonek"),
|
||||
std::make_pair((int)Qt::Key_Dead_Iota , "Dead_Iota"),
|
||||
std::make_pair((int)Qt::Key_Dead_Voiced_Sound , "Dead_Voiced_Sound"),
|
||||
std::make_pair((int)Qt::Key_Dead_Semivoiced_Sound , "Dead_Semivoiced_Sound"),
|
||||
std::make_pair((int)Qt::Key_Dead_Belowdot , "Dead_Belowdot"),
|
||||
std::make_pair((int)Qt::Key_Dead_Hook , "Dead_Hook"),
|
||||
std::make_pair((int)Qt::Key_Dead_Horn , "Dead_Horn"),
|
||||
std::make_pair((int)Qt::Key_MediaLast , "MediaLast"),
|
||||
std::make_pair((int)Qt::Key_Select , "Select"),
|
||||
std::make_pair((int)Qt::Key_Yes , "Yes"),
|
||||
std::make_pair((int)Qt::Key_No , "No"),
|
||||
std::make_pair((int)Qt::Key_Cancel , "Cancel"),
|
||||
std::make_pair((int)Qt::Key_Printer , "Printer"),
|
||||
std::make_pair((int)Qt::Key_Execute , "Execute"),
|
||||
std::make_pair((int)Qt::Key_Sleep , "Sleep"),
|
||||
std::make_pair((int)Qt::Key_Play , "Play"),
|
||||
std::make_pair((int)Qt::Key_Zoom , "Zoom"),
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5,7,0)
|
||||
std::make_pair((int)Qt::Key_Exit , "Exit"),
|
||||
#endif
|
||||
std::make_pair((int)Qt::Key_Context1 , "Context1"),
|
||||
std::make_pair((int)Qt::Key_Context2 , "Context2"),
|
||||
std::make_pair((int)Qt::Key_Context3 , "Context3"),
|
||||
std::make_pair((int)Qt::Key_Context4 , "Context4"),
|
||||
std::make_pair((int)Qt::Key_Call , "Call"),
|
||||
std::make_pair((int)Qt::Key_Hangup , "Hangup"),
|
||||
std::make_pair((int)Qt::Key_Flip , "Flip"),
|
||||
std::make_pair((int)Qt::Key_ToggleCallHangup , "ToggleCallHangup"),
|
||||
std::make_pair((int)Qt::Key_VoiceDial , "VoiceDial"),
|
||||
std::make_pair((int)Qt::Key_LastNumberRedial , "LastNumberRedial"),
|
||||
std::make_pair((int)Qt::Key_Camera , "Camera"),
|
||||
std::make_pair((int)Qt::Key_CameraFocus , "CameraFocus"),
|
||||
std::make_pair(0 , (const char*) 0)
|
||||
};
|
||||
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
#ifndef CSM_PREFS_SHORTCUTMANAGER_H
|
||||
#define CSM_PREFS_SHORTCUTMANAGER_H
|
||||
|
||||
#include <map>
|
||||
|
||||
#include <QKeySequence>
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
|
||||
namespace CSMPrefs
|
||||
{
|
||||
class Shortcut;
|
||||
class ShortcutEventHandler;
|
||||
|
||||
/// Class used to track and update shortcuts/sequences
|
||||
class ShortcutManager : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
ShortcutManager();
|
||||
|
||||
/// The shortcut class will do this automatically
|
||||
void addShortcut(Shortcut* shortcut);
|
||||
|
||||
/// The shortcut class will do this automatically
|
||||
void removeShortcut(Shortcut* shortcut);
|
||||
|
||||
bool getSequence(const std::string& name, QKeySequence& sequence) const;
|
||||
void setSequence(const std::string& name, const QKeySequence& sequence);
|
||||
|
||||
bool getModifier(const std::string& name, int& modifier) const;
|
||||
void setModifier(const std::string& name, int modifier);
|
||||
|
||||
std::string convertToString(const QKeySequence& sequence) const;
|
||||
std::string convertToString(int modifier) const;
|
||||
|
||||
std::string convertToString(const QKeySequence& sequence, int modifier) const;
|
||||
|
||||
void convertFromString(const std::string& data, QKeySequence& sequence) const;
|
||||
void convertFromString(const std::string& data, int& modifier) const;
|
||||
|
||||
void convertFromString(const std::string& data, QKeySequence& sequence, int& modifier) const;
|
||||
|
||||
/// Replaces "{sequence-name}" or "{modifier-name}" with the appropriate text
|
||||
QString processToolTip(const QString& toolTip) const;
|
||||
|
||||
private:
|
||||
|
||||
// Need a multimap in case multiple shortcuts share the same name
|
||||
typedef std::multimap<std::string, Shortcut*> ShortcutMap;
|
||||
typedef std::map<std::string, QKeySequence> SequenceMap;
|
||||
typedef std::map<std::string, int> ModifierMap;
|
||||
typedef std::map<int, std::string> NameMap;
|
||||
typedef std::map<std::string, int> KeyMap;
|
||||
|
||||
ShortcutMap mShortcuts;
|
||||
SequenceMap mSequences;
|
||||
ModifierMap mModifiers;
|
||||
|
||||
NameMap mNames;
|
||||
KeyMap mKeys;
|
||||
|
||||
ShortcutEventHandler* mEventHandler;
|
||||
|
||||
void createLookupTables();
|
||||
|
||||
static const std::pair<int, const char*> QtKeys[];
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,197 @@
|
||||
#include "shortcutsetting.hpp"
|
||||
|
||||
#include <QEvent>
|
||||
#include <QKeyEvent>
|
||||
#include <QLabel>
|
||||
#include <QMouseEvent>
|
||||
#include <QPushButton>
|
||||
#include <QWidget>
|
||||
|
||||
#include "state.hpp"
|
||||
#include "shortcutmanager.hpp"
|
||||
|
||||
namespace CSMPrefs
|
||||
{
|
||||
const int ShortcutSetting::MaxKeys;
|
||||
|
||||
ShortcutSetting::ShortcutSetting(Category* parent, Settings::Manager* values, QMutex* mutex, const std::string& key,
|
||||
const std::string& label)
|
||||
: Setting(parent, values, mutex, key, label)
|
||||
, mButton(0)
|
||||
, mEditorActive(false)
|
||||
, mEditorPos(0)
|
||||
{
|
||||
for (int i = 0; i < MaxKeys; ++i)
|
||||
{
|
||||
mEditorKeys[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<QWidget*, QWidget*> ShortcutSetting::makeWidgets(QWidget* parent)
|
||||
{
|
||||
QKeySequence sequence;
|
||||
State::get().getShortcutManager().getSequence(getKey(), sequence);
|
||||
|
||||
QString text = QString::fromUtf8(State::get().getShortcutManager().convertToString(sequence).c_str());
|
||||
|
||||
QLabel* label = new QLabel(QString::fromUtf8(getLabel().c_str()), parent);
|
||||
QPushButton* widget = new QPushButton(text, parent);
|
||||
|
||||
widget->setCheckable(true);
|
||||
widget->installEventFilter(this);
|
||||
mButton = widget;
|
||||
|
||||
connect(widget, SIGNAL(toggled(bool)), this, SLOT(buttonToggled(bool)));
|
||||
|
||||
return std::make_pair(label, widget);
|
||||
}
|
||||
|
||||
bool ShortcutSetting::eventFilter(QObject* target, QEvent* event)
|
||||
{
|
||||
if (event->type() == QEvent::KeyPress)
|
||||
{
|
||||
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
|
||||
if (keyEvent->isAutoRepeat())
|
||||
return true;
|
||||
|
||||
int mod = keyEvent->modifiers();
|
||||
int key = keyEvent->key();
|
||||
|
||||
return handleEvent(target, mod, key, true);
|
||||
}
|
||||
else if (event->type() == QEvent::KeyRelease)
|
||||
{
|
||||
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
|
||||
if (keyEvent->isAutoRepeat())
|
||||
return true;
|
||||
|
||||
int mod = keyEvent->modifiers();
|
||||
int key = keyEvent->key();
|
||||
|
||||
return handleEvent(target, mod, key, false);
|
||||
}
|
||||
else if (event->type() == QEvent::MouseButtonPress)
|
||||
{
|
||||
QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
|
||||
int mod = mouseEvent->modifiers();
|
||||
int key = mouseEvent->button();
|
||||
|
||||
return handleEvent(target, mod, key, true);
|
||||
}
|
||||
else if (event->type() == QEvent::MouseButtonRelease)
|
||||
{
|
||||
QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
|
||||
int mod = mouseEvent->modifiers();
|
||||
int key = mouseEvent->button();
|
||||
|
||||
return handleEvent(target, mod, key, false);
|
||||
}
|
||||
else if (event->type() == QEvent::FocusOut)
|
||||
{
|
||||
resetState();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ShortcutSetting::handleEvent(QObject* target, int mod, int value, bool active)
|
||||
{
|
||||
// Modifiers are handled differently
|
||||
const int Blacklist[] =
|
||||
{
|
||||
Qt::Key_Shift,
|
||||
Qt::Key_Control,
|
||||
Qt::Key_Meta,
|
||||
Qt::Key_Alt,
|
||||
Qt::Key_AltGr
|
||||
};
|
||||
|
||||
const size_t BlacklistSize = sizeof(Blacklist) / sizeof(int);
|
||||
|
||||
if (!mEditorActive)
|
||||
{
|
||||
if (value == Qt::RightButton && !active)
|
||||
{
|
||||
// Clear sequence
|
||||
QKeySequence sequence = QKeySequence(0, 0, 0, 0);
|
||||
storeValue(sequence);
|
||||
|
||||
resetState();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Handle blacklist
|
||||
for (size_t i = 0; i < BlacklistSize; ++i)
|
||||
{
|
||||
if (value == Blacklist[i])
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!active || mEditorPos >= MaxKeys)
|
||||
{
|
||||
// Update key
|
||||
QKeySequence sequence = QKeySequence(mEditorKeys[0], mEditorKeys[1], mEditorKeys[2], mEditorKeys[3]);
|
||||
storeValue(sequence);
|
||||
|
||||
resetState();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mEditorPos == 0)
|
||||
{
|
||||
mEditorKeys[0] = mod | value;
|
||||
}
|
||||
else
|
||||
{
|
||||
mEditorKeys[mEditorPos] = value;
|
||||
}
|
||||
|
||||
mEditorPos += 1;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ShortcutSetting::storeValue(const QKeySequence& sequence)
|
||||
{
|
||||
State::get().getShortcutManager().setSequence(getKey(), sequence);
|
||||
|
||||
// Convert to string and assign
|
||||
std::string value = State::get().getShortcutManager().convertToString(sequence);
|
||||
|
||||
{
|
||||
QMutexLocker lock(getMutex());
|
||||
getValues().setString(getKey(), getParent()->getKey(), value);
|
||||
}
|
||||
|
||||
getParent()->getState()->update(*this);
|
||||
}
|
||||
|
||||
void ShortcutSetting::resetState()
|
||||
{
|
||||
mButton->setChecked(false);
|
||||
mEditorActive = false;
|
||||
mEditorPos = 0;
|
||||
for (int i = 0; i < MaxKeys; ++i)
|
||||
{
|
||||
mEditorKeys[i] = 0;
|
||||
}
|
||||
|
||||
// Button text
|
||||
QKeySequence sequence;
|
||||
State::get().getShortcutManager().getSequence(getKey(), sequence);
|
||||
|
||||
QString text = QString::fromUtf8(State::get().getShortcutManager().convertToString(sequence).c_str());
|
||||
mButton->setText(text);
|
||||
}
|
||||
|
||||
void ShortcutSetting::buttonToggled(bool checked)
|
||||
{
|
||||
if (checked)
|
||||
mButton->setText("Press keys or click here...");
|
||||
|
||||
mEditorActive = checked;
|
||||
}
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
#ifndef CSM_PREFS_SHORTCUTSETTING_H
|
||||
#define CSM_PREFS_SHORTCUTSETTING_H
|
||||
|
||||
#include <QKeySequence>
|
||||
|
||||
#include "setting.hpp"
|
||||
|
||||
class QEvent;
|
||||
class QPushButton;
|
||||
|
||||
namespace CSMPrefs
|
||||
{
|
||||
class ShortcutSetting : public Setting
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
ShortcutSetting(Category* parent, Settings::Manager* values, QMutex* mutex, const std::string& key,
|
||||
const std::string& label);
|
||||
|
||||
virtual std::pair<QWidget*, QWidget*> makeWidgets(QWidget* parent);
|
||||
|
||||
protected:
|
||||
|
||||
bool eventFilter(QObject* target, QEvent* event);
|
||||
|
||||
private:
|
||||
|
||||
bool handleEvent(QObject* target, int mod, int value, bool active);
|
||||
|
||||
void storeValue(const QKeySequence& sequence);
|
||||
void resetState();
|
||||
|
||||
static const int MaxKeys = 4;
|
||||
|
||||
QPushButton* mButton;
|
||||
|
||||
bool mEditorActive;
|
||||
int mEditorPos;
|
||||
int mEditorKeys[MaxKeys];
|
||||
|
||||
private slots:
|
||||
|
||||
void buttonToggled(bool checked);
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,79 @@
|
||||
#include "journalcheck.hpp"
|
||||
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
|
||||
CSMTools::JournalCheckStage::JournalCheckStage(const CSMWorld::IdCollection<ESM::Dialogue> &journals,
|
||||
const CSMWorld::InfoCollection& journalInfos)
|
||||
: mJournals(journals), mJournalInfos(journalInfos)
|
||||
{}
|
||||
|
||||
int CSMTools::JournalCheckStage::setup()
|
||||
{
|
||||
return mJournals.getSize();
|
||||
}
|
||||
|
||||
void CSMTools::JournalCheckStage::perform(int stage, CSMDoc::Messages& messages)
|
||||
{
|
||||
const CSMWorld::Record<ESM::Dialogue> &journalRecord = mJournals.getRecord(stage);
|
||||
|
||||
if (journalRecord.isDeleted())
|
||||
return;
|
||||
|
||||
const ESM::Dialogue &journal = journalRecord.get();
|
||||
int statusNamedCount = 0;
|
||||
int totalInfoCount = 0;
|
||||
std::set<int> questIndices;
|
||||
|
||||
CSMWorld::InfoCollection::Range range = mJournalInfos.getTopicRange(journal.mId);
|
||||
|
||||
for (CSMWorld::InfoCollection::RecordConstIterator it = range.first; it != range.second; ++it)
|
||||
{
|
||||
const CSMWorld::Record<CSMWorld::Info> infoRecord = (*it);
|
||||
|
||||
if (infoRecord.isDeleted())
|
||||
continue;
|
||||
|
||||
const CSMWorld::Info& journalInfo = infoRecord.get();
|
||||
|
||||
totalInfoCount += 1;
|
||||
|
||||
if (journalInfo.mQuestStatus == ESM::DialInfo::QS_Name)
|
||||
{
|
||||
statusNamedCount += 1;
|
||||
}
|
||||
|
||||
if (journalInfo.mResponse.empty())
|
||||
{
|
||||
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_JournalInfo, journalInfo.mId);
|
||||
|
||||
messages.add(id, "Journal Info: missing description", "", CSMDoc::Message::Severity_Warning);
|
||||
}
|
||||
|
||||
std::pair<std::set<int>::iterator, bool> result = questIndices.insert(journalInfo.mData.mJournalIndex);
|
||||
|
||||
// Duplicate index
|
||||
if (result.second == false)
|
||||
{
|
||||
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_JournalInfo, journalInfo.mId);
|
||||
|
||||
std::ostringstream stream;
|
||||
stream << "Journal: duplicated quest index " << journalInfo.mData.mJournalIndex;
|
||||
|
||||
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error);
|
||||
}
|
||||
}
|
||||
|
||||
if (totalInfoCount == 0)
|
||||
{
|
||||
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Journal, journal.mId);
|
||||
|
||||
messages.add(id, "Journal: no defined Journal Infos", "", CSMDoc::Message::Severity_Warning);
|
||||
}
|
||||
else if (statusNamedCount > 1)
|
||||
{
|
||||
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Journal, journal.mId);
|
||||
|
||||
messages.add(id, "Journal: multiple infos with quest status \"Named\"", "", CSMDoc::Message::Severity_Error);
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
#ifndef CSM_TOOLS_JOURNALCHECK_H
|
||||
#define CSM_TOOLS_JOURNALCHECK_H
|
||||
|
||||
#include <components/esm/loaddial.hpp>
|
||||
|
||||
#include "../world/idcollection.hpp"
|
||||
#include "../world/infocollection.hpp"
|
||||
|
||||
#include "../doc/stage.hpp"
|
||||
|
||||
namespace CSMTools
|
||||
{
|
||||
/// \brief VerifyStage: make sure that journal infos are good
|
||||
class JournalCheckStage : public CSMDoc::Stage
|
||||
{
|
||||
public:
|
||||
|
||||
JournalCheckStage(const CSMWorld::IdCollection<ESM::Dialogue>& journals,
|
||||
const CSMWorld::InfoCollection& journalInfos);
|
||||
|
||||
virtual int setup();
|
||||
///< \return number of steps
|
||||
|
||||
virtual void perform(int stage, CSMDoc::Messages& messages);
|
||||
///< Messages resulting from this stage will be appended to \a messages
|
||||
|
||||
private:
|
||||
|
||||
const CSMWorld::IdCollection<ESM::Dialogue>& mJournals;
|
||||
const CSMWorld::InfoCollection& mJournalInfos;
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,441 @@
|
||||
#include "topicinfocheck.hpp"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include "../world/infoselectwrapper.hpp"
|
||||
|
||||
CSMTools::TopicInfoCheckStage::TopicInfoCheckStage(
|
||||
const CSMWorld::InfoCollection& topicInfos,
|
||||
const CSMWorld::IdCollection<CSMWorld::Cell>& cells,
|
||||
const CSMWorld::IdCollection<ESM::Class>& classes,
|
||||
const CSMWorld::IdCollection<ESM::Faction>& factions,
|
||||
const CSMWorld::IdCollection<ESM::GameSetting>& gmsts,
|
||||
const CSMWorld::IdCollection<ESM::Global>& globals,
|
||||
const CSMWorld::IdCollection<ESM::Dialogue>& journals,
|
||||
const CSMWorld::IdCollection<ESM::Race>& races,
|
||||
const CSMWorld::IdCollection<ESM::Region>& regions,
|
||||
const CSMWorld::IdCollection<ESM::Dialogue> &topics,
|
||||
const CSMWorld::RefIdData& referencables,
|
||||
const CSMWorld::Resources& soundFiles)
|
||||
: mTopicInfos(topicInfos),
|
||||
mCells(cells),
|
||||
mClasses(classes),
|
||||
mFactions(factions),
|
||||
mGameSettings(gmsts),
|
||||
mGlobals(globals),
|
||||
mJournals(journals),
|
||||
mRaces(races),
|
||||
mRegions(regions),
|
||||
mTopics(topics),
|
||||
mReferencables(referencables),
|
||||
mSoundFiles(soundFiles)
|
||||
{}
|
||||
|
||||
int CSMTools::TopicInfoCheckStage::setup()
|
||||
{
|
||||
// Generate list of cell names for reference checking
|
||||
|
||||
mCellNames.clear();
|
||||
for (int i = 0; i < mCells.getSize(); ++i)
|
||||
{
|
||||
const CSMWorld::Record<CSMWorld::Cell>& cellRecord = mCells.getRecord(i);
|
||||
|
||||
if (cellRecord.isDeleted())
|
||||
continue;
|
||||
|
||||
mCellNames.insert(cellRecord.get().mName);
|
||||
}
|
||||
// Cell names can also include region names
|
||||
for (int i = 0; i < mRegions.getSize(); ++i)
|
||||
{
|
||||
const CSMWorld::Record<ESM::Region>& regionRecord = mRegions.getRecord(i);
|
||||
|
||||
if (regionRecord.isDeleted())
|
||||
continue;
|
||||
|
||||
mCellNames.insert(regionRecord.get().mName);
|
||||
}
|
||||
// Default cell name
|
||||
int index = mGameSettings.searchId("sDefaultCellname");
|
||||
if (index != -1)
|
||||
{
|
||||
const CSMWorld::Record<ESM::GameSetting>& gmstRecord = mGameSettings.getRecord(index);
|
||||
|
||||
if (!gmstRecord.isDeleted() && gmstRecord.get().mValue.getType() == ESM::VT_String)
|
||||
{
|
||||
mCellNames.insert(gmstRecord.get().mValue.getString());
|
||||
}
|
||||
}
|
||||
|
||||
return mTopicInfos.getSize();
|
||||
}
|
||||
|
||||
void CSMTools::TopicInfoCheckStage::perform(int stage, CSMDoc::Messages& messages)
|
||||
{
|
||||
const CSMWorld::Record<CSMWorld::Info>& infoRecord = mTopicInfos.getRecord(stage);
|
||||
|
||||
if (infoRecord.isDeleted())
|
||||
return;
|
||||
|
||||
const CSMWorld::Info& topicInfo = infoRecord.get();
|
||||
|
||||
// There should always be a topic that matches
|
||||
int topicIndex = mTopics.searchId(topicInfo.mTopicId);
|
||||
|
||||
const CSMWorld::Record<ESM::Dialogue>& topicRecord = mTopics.getRecord(topicIndex);
|
||||
|
||||
if (topicRecord.isDeleted())
|
||||
return;
|
||||
|
||||
const ESM::Dialogue& topic = topicRecord.get();
|
||||
|
||||
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_TopicInfo, topicInfo.mId);
|
||||
|
||||
// Check fields
|
||||
|
||||
if (!topicInfo.mActor.empty())
|
||||
{
|
||||
verifyActor(topicInfo.mActor, id, messages);
|
||||
}
|
||||
|
||||
if (!topicInfo.mClass.empty())
|
||||
{
|
||||
verifyId(topicInfo.mClass, mClasses, id, messages);
|
||||
}
|
||||
|
||||
if (!topicInfo.mCell.empty())
|
||||
{
|
||||
verifyCell(topicInfo.mCell, id, messages);
|
||||
}
|
||||
|
||||
if (!topicInfo.mFaction.empty() && !topicInfo.mFactionLess)
|
||||
{
|
||||
if (verifyId(topicInfo.mFaction, mFactions, id, messages))
|
||||
{
|
||||
verifyFactionRank(topicInfo.mFaction, topicInfo.mData.mRank, id, messages);
|
||||
}
|
||||
}
|
||||
|
||||
if (!topicInfo.mPcFaction.empty())
|
||||
{
|
||||
if (verifyId(topicInfo.mPcFaction, mFactions, id, messages))
|
||||
{
|
||||
verifyFactionRank(topicInfo.mPcFaction, topicInfo.mData.mPCrank, id, messages);
|
||||
}
|
||||
}
|
||||
|
||||
if (topicInfo.mData.mGender < -1 || topicInfo.mData.mGender > 1)
|
||||
{
|
||||
std::ostringstream stream;
|
||||
messages.add(id, "Gender: Value is invalid", "", CSMDoc::Message::Severity_Error);
|
||||
}
|
||||
|
||||
if (!topicInfo.mRace.empty())
|
||||
{
|
||||
verifyId(topicInfo.mRace, mRaces, id, messages);
|
||||
}
|
||||
|
||||
if (!topicInfo.mSound.empty())
|
||||
{
|
||||
verifySound(topicInfo.mSound, id, messages);
|
||||
}
|
||||
|
||||
if (topicInfo.mResponse.empty() && topic.mType != ESM::Dialogue::Voice)
|
||||
{
|
||||
messages.add(id, "Response is empty", "", CSMDoc::Message::Severity_Warning);
|
||||
}
|
||||
|
||||
// Check info conditions
|
||||
|
||||
for (std::vector<ESM::DialInfo::SelectStruct>::const_iterator it = topicInfo.mSelects.begin();
|
||||
it != topicInfo.mSelects.end(); ++it)
|
||||
{
|
||||
verifySelectStruct((*it), id, messages);
|
||||
}
|
||||
}
|
||||
|
||||
// Verification functions
|
||||
|
||||
bool CSMTools::TopicInfoCheckStage::verifyActor(const std::string& actor, const CSMWorld::UniversalId& id,
|
||||
CSMDoc::Messages& messages)
|
||||
{
|
||||
const std::string specifier = "Actor";
|
||||
|
||||
CSMWorld::RefIdData::LocalIndex index = mReferencables.searchId(actor);
|
||||
|
||||
if (index.first == -1)
|
||||
{
|
||||
writeMissingIdError(specifier, actor, id, messages);
|
||||
return false;
|
||||
}
|
||||
else if (mReferencables.getRecord(index).isDeleted())
|
||||
{
|
||||
writeDeletedRecordError(specifier, actor, id, messages);
|
||||
return false;
|
||||
}
|
||||
else if (index.second != CSMWorld::UniversalId::Type_Npc && index.second != CSMWorld::UniversalId::Type_Creature)
|
||||
{
|
||||
writeInvalidTypeError(specifier, actor, index.second, "NPC or Creature", id, messages);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CSMTools::TopicInfoCheckStage::verifyCell(const std::string& cell, const CSMWorld::UniversalId& id,
|
||||
CSMDoc::Messages& messages)
|
||||
{
|
||||
const std::string specifier = "Cell";
|
||||
|
||||
if (mCellNames.find(cell) == mCellNames.end())
|
||||
{
|
||||
writeMissingIdError(specifier, cell, id, messages);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CSMTools::TopicInfoCheckStage::verifyFactionRank(const std::string& factionName, int rank, const CSMWorld::UniversalId& id,
|
||||
CSMDoc::Messages& messages)
|
||||
{
|
||||
if (rank < -1)
|
||||
{
|
||||
std::ostringstream stream;
|
||||
stream << "Rank or PC Rank is set to " << rank << ", but should be set to -1 if no rank is required";
|
||||
|
||||
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
int index = mFactions.searchId(factionName);
|
||||
|
||||
const ESM::Faction &faction = mFactions.getRecord(index).get();
|
||||
|
||||
int limit = 0;
|
||||
for (; limit < 10; ++limit)
|
||||
{
|
||||
if (faction.mRanks[limit].empty())
|
||||
break;
|
||||
}
|
||||
|
||||
if (rank >= limit)
|
||||
{
|
||||
std::ostringstream stream;
|
||||
stream << "Rank or PC Rank is set to " << rank << " which is more than the maximum of " << limit - 1
|
||||
<< " for the " << factionName << " faction";
|
||||
|
||||
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CSMTools::TopicInfoCheckStage::verifyItem(const std::string& item, const CSMWorld::UniversalId& id,
|
||||
CSMDoc::Messages& messages)
|
||||
{
|
||||
const std::string specifier = "Item";
|
||||
|
||||
CSMWorld::RefIdData::LocalIndex index = mReferencables.searchId(item);
|
||||
|
||||
if (index.first == -1)
|
||||
{
|
||||
writeMissingIdError(specifier, item, id, messages);
|
||||
return false;
|
||||
}
|
||||
else if (mReferencables.getRecord(index).isDeleted())
|
||||
{
|
||||
writeDeletedRecordError(specifier, item, id, messages);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (index.second)
|
||||
{
|
||||
case CSMWorld::UniversalId::Type_Potion:
|
||||
case CSMWorld::UniversalId::Type_Apparatus:
|
||||
case CSMWorld::UniversalId::Type_Armor:
|
||||
case CSMWorld::UniversalId::Type_Book:
|
||||
case CSMWorld::UniversalId::Type_Clothing:
|
||||
case CSMWorld::UniversalId::Type_Ingredient:
|
||||
case CSMWorld::UniversalId::Type_Light:
|
||||
case CSMWorld::UniversalId::Type_Lockpick:
|
||||
case CSMWorld::UniversalId::Type_Miscellaneous:
|
||||
case CSMWorld::UniversalId::Type_Probe:
|
||||
case CSMWorld::UniversalId::Type_Repair:
|
||||
case CSMWorld::UniversalId::Type_Weapon:
|
||||
case CSMWorld::UniversalId::Type_ItemLevelledList:
|
||||
break;
|
||||
|
||||
default:
|
||||
writeInvalidTypeError(specifier, item, index.second, "Potion, Armor, Book, etc.", id, messages);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CSMTools::TopicInfoCheckStage::verifySelectStruct(const ESM::DialInfo::SelectStruct& select,
|
||||
const CSMWorld::UniversalId& id, CSMDoc::Messages& messages)
|
||||
{
|
||||
CSMWorld::ConstInfoSelectWrapper infoCondition(select);
|
||||
|
||||
if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_None)
|
||||
{
|
||||
messages.add(id, "Invalid Info Condition: " + infoCondition.toString(), "", CSMDoc::Message::Severity_Error);
|
||||
return false;
|
||||
}
|
||||
else if (!infoCondition.variantTypeIsValid())
|
||||
{
|
||||
std::ostringstream stream;
|
||||
stream << "Info Condition: Value for \"" << infoCondition.toString() << "\" has a type of ";
|
||||
|
||||
switch (select.mValue.getType())
|
||||
{
|
||||
case ESM::VT_None: stream << "None"; break;
|
||||
case ESM::VT_Short: stream << "Short"; break;
|
||||
case ESM::VT_Int: stream << "Int"; break;
|
||||
case ESM::VT_Long: stream << "Long"; break;
|
||||
case ESM::VT_Float: stream << "Float"; break;
|
||||
case ESM::VT_String: stream << "String"; break;
|
||||
default: stream << "Unknown"; break;
|
||||
}
|
||||
|
||||
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error);
|
||||
return false;
|
||||
}
|
||||
else if (infoCondition.conditionIsAlwaysTrue())
|
||||
{
|
||||
std::ostringstream stream;
|
||||
stream << "Info Condition: " << infoCondition.toString() << " is always true";
|
||||
|
||||
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Warning);
|
||||
return false;
|
||||
}
|
||||
else if (infoCondition.conditionIsNeverTrue())
|
||||
{
|
||||
std::ostringstream stream;
|
||||
stream << "Info Condition: " << infoCondition.toString() << " is never true";
|
||||
|
||||
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Warning);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Id checks
|
||||
if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_Global &&
|
||||
!verifyId(infoCondition.getVariableName(), mGlobals, id, messages))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_Journal &&
|
||||
!verifyId(infoCondition.getVariableName(), mJournals, id, messages))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_Item &&
|
||||
!verifyItem(infoCondition.getVariableName(), id, messages))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_Dead &&
|
||||
!verifyActor(infoCondition.getVariableName(), id, messages))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_NotId &&
|
||||
!verifyActor(infoCondition.getVariableName(), id, messages))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_NotFaction &&
|
||||
!verifyId(infoCondition.getVariableName(), mFactions, id, messages))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_NotClass &&
|
||||
!verifyId(infoCondition.getVariableName(), mClasses, id, messages))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_NotRace &&
|
||||
!verifyId(infoCondition.getVariableName(), mRaces, id, messages))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_NotCell &&
|
||||
!verifyCell(infoCondition.getVariableName(), id, messages))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CSMTools::TopicInfoCheckStage::verifySound(const std::string& sound, const CSMWorld::UniversalId& id,
|
||||
CSMDoc::Messages& messages)
|
||||
{
|
||||
const std::string specifier = "Sound File";
|
||||
|
||||
if (mSoundFiles.searchId(sound) == -1)
|
||||
{
|
||||
writeMissingIdError(specifier, sound, id, messages);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool CSMTools::TopicInfoCheckStage::verifyId(const std::string& name, const CSMWorld::IdCollection<T>& collection,
|
||||
const CSMWorld::UniversalId& id, CSMDoc::Messages& messages)
|
||||
{
|
||||
int index = collection.searchId(name);
|
||||
|
||||
if (index == -1)
|
||||
{
|
||||
writeMissingIdError(T::getRecordType(), name, id, messages);
|
||||
return false;
|
||||
}
|
||||
else if (collection.getRecord(index).isDeleted())
|
||||
{
|
||||
writeDeletedRecordError(T::getRecordType(), name, id, messages);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Error functions
|
||||
|
||||
void CSMTools::TopicInfoCheckStage::writeMissingIdError(const std::string& specifier, const std::string& missingId,
|
||||
const CSMWorld::UniversalId& id, CSMDoc::Messages& messages)
|
||||
{
|
||||
std::ostringstream stream;
|
||||
stream << specifier << ": ID or name \"" << missingId << "\" could not be found";
|
||||
|
||||
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error);
|
||||
}
|
||||
|
||||
void CSMTools::TopicInfoCheckStage::writeDeletedRecordError(const std::string& specifier, const std::string& recordId,
|
||||
const CSMWorld::UniversalId& id, CSMDoc::Messages& messages)
|
||||
{
|
||||
std::ostringstream stream;
|
||||
stream << specifier << ": Deleted record with ID \"" << recordId << "\" is being referenced";
|
||||
|
||||
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error);
|
||||
}
|
||||
|
||||
void CSMTools::TopicInfoCheckStage::writeInvalidTypeError(const std::string& specifier, const std::string& invalidId,
|
||||
CSMWorld::UniversalId::Type invalidType, const std::string& expectedType, const CSMWorld::UniversalId& id,
|
||||
CSMDoc::Messages& messages)
|
||||
{
|
||||
CSMWorld::UniversalId tempId(invalidType, invalidId);
|
||||
|
||||
std::ostringstream stream;
|
||||
stream << specifier << ": invalid type of " << tempId.getTypeName() << " was found for referencable \""
|
||||
<< invalidId << "\" (can be of type " << expectedType << ")";
|
||||
|
||||
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error);
|
||||
}
|
@ -0,0 +1,95 @@
|
||||
#ifndef CSM_TOOLS_TOPICINFOCHECK_HPP
|
||||
#define CSM_TOOLS_TOPICINFOCHECK_HPP
|
||||
|
||||
#include <set>
|
||||
|
||||
#include <components/esm/loadclas.hpp>
|
||||
#include <components/esm/loaddial.hpp>
|
||||
#include <components/esm/loadfact.hpp>
|
||||
#include <components/esm/loadglob.hpp>
|
||||
#include <components/esm/loadgmst.hpp>
|
||||
#include <components/esm/loadrace.hpp>
|
||||
#include <components/esm/loadregn.hpp>
|
||||
|
||||
#include "../world/cell.hpp"
|
||||
#include "../world/idcollection.hpp"
|
||||
#include "../world/infocollection.hpp"
|
||||
#include "../world/refiddata.hpp"
|
||||
#include "../world/resources.hpp"
|
||||
|
||||
#include "../doc/stage.hpp"
|
||||
|
||||
namespace CSMTools
|
||||
{
|
||||
/// \brief VerifyStage: check topics
|
||||
class TopicInfoCheckStage : public CSMDoc::Stage
|
||||
{
|
||||
public:
|
||||
|
||||
TopicInfoCheckStage(
|
||||
const CSMWorld::InfoCollection& topicInfos,
|
||||
const CSMWorld::IdCollection<CSMWorld::Cell>& cells,
|
||||
const CSMWorld::IdCollection<ESM::Class>& classes,
|
||||
const CSMWorld::IdCollection<ESM::Faction>& factions,
|
||||
const CSMWorld::IdCollection<ESM::GameSetting>& gmsts,
|
||||
const CSMWorld::IdCollection<ESM::Global>& globals,
|
||||
const CSMWorld::IdCollection<ESM::Dialogue>& journals,
|
||||
const CSMWorld::IdCollection<ESM::Race>& races,
|
||||
const CSMWorld::IdCollection<ESM::Region>& regions,
|
||||
const CSMWorld::IdCollection<ESM::Dialogue>& topics,
|
||||
const CSMWorld::RefIdData& referencables,
|
||||
const CSMWorld::Resources& soundFiles);
|
||||
|
||||
virtual int setup();
|
||||
///< \return number of steps
|
||||
|
||||
virtual void perform(int step, CSMDoc::Messages& messages);
|
||||
///< Messages resulting from this stage will be appended to \a messages
|
||||
|
||||
private:
|
||||
|
||||
const CSMWorld::InfoCollection& mTopicInfos;
|
||||
|
||||
const CSMWorld::IdCollection<CSMWorld::Cell>& mCells;
|
||||
const CSMWorld::IdCollection<ESM::Class>& mClasses;
|
||||
const CSMWorld::IdCollection<ESM::Faction>& mFactions;
|
||||
const CSMWorld::IdCollection<ESM::GameSetting>& mGameSettings;
|
||||
const CSMWorld::IdCollection<ESM::Global>& mGlobals;
|
||||
const CSMWorld::IdCollection<ESM::Dialogue>& mJournals;
|
||||
const CSMWorld::IdCollection<ESM::Race>& mRaces;
|
||||
const CSMWorld::IdCollection<ESM::Region>& mRegions;
|
||||
const CSMWorld::IdCollection<ESM::Dialogue>& mTopics;
|
||||
|
||||
const CSMWorld::RefIdData& mReferencables;
|
||||
const CSMWorld::Resources& mSoundFiles;
|
||||
|
||||
std::set<std::string> mCellNames;
|
||||
|
||||
// These return false when not successful and write an error
|
||||
bool verifyActor(const std::string& name, const CSMWorld::UniversalId& id, CSMDoc::Messages& messages);
|
||||
bool verifyCell(const std::string& name, const CSMWorld::UniversalId& id, CSMDoc::Messages& messages);
|
||||
bool verifyFactionRank(const std::string& name, int rank, const CSMWorld::UniversalId& id,
|
||||
CSMDoc::Messages& messages);
|
||||
bool verifyItem(const std::string& name, const CSMWorld::UniversalId& id, CSMDoc::Messages& messages);
|
||||
bool verifySelectStruct(const ESM::DialInfo::SelectStruct& select, const CSMWorld::UniversalId& id,
|
||||
CSMDoc::Messages& messages);
|
||||
bool verifySound(const std::string& name, const CSMWorld::UniversalId& id, CSMDoc::Messages& messages);
|
||||
|
||||
template <typename T>
|
||||
bool verifyId(const std::string& name, const CSMWorld::IdCollection<T>& collection,
|
||||
const CSMWorld::UniversalId& id, CSMDoc::Messages& messages);
|
||||
|
||||
// Common error messages
|
||||
void writeMissingIdError(const std::string& specifier, const std::string& missingId,
|
||||
const CSMWorld::UniversalId& id, CSMDoc::Messages& messages);
|
||||
|
||||
void writeDeletedRecordError(const std::string& specifier, const std::string& recordId,
|
||||
const CSMWorld::UniversalId& id, CSMDoc::Messages& messages);
|
||||
|
||||
void writeInvalidTypeError(const std::string& specifier, const std::string& invalidId,
|
||||
CSMWorld::UniversalId::Type invalidType, const std::string& expectedType,
|
||||
const CSMWorld::UniversalId& id, CSMDoc::Messages& messages);
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,26 @@
|
||||
|
||||
#include "commandmacro.hpp"
|
||||
|
||||
#include <QUndoStack>
|
||||
#include <QUndoCommand>
|
||||
|
||||
CSMWorld::CommandMacro::CommandMacro (QUndoStack& undoStack, const QString& description)
|
||||
: mUndoStack (undoStack), mDescription (description), mStarted (false)
|
||||
{}
|
||||
|
||||
CSMWorld::CommandMacro::~CommandMacro()
|
||||
{
|
||||
if (mStarted)
|
||||
mUndoStack.endMacro();
|
||||
}
|
||||
|
||||
void CSMWorld::CommandMacro::push (QUndoCommand *command)
|
||||
{
|
||||
if (!mStarted)
|
||||
{
|
||||
mUndoStack.beginMacro (mDescription.isEmpty() ? command->text() : mDescription);
|
||||
mStarted = true;
|
||||
}
|
||||
|
||||
mUndoStack.push (command);
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
#ifndef CSM_WOLRD_COMMANDMACRO_H
|
||||
#define CSM_WOLRD_COMMANDMACRO_H
|
||||
|
||||
class QUndoStack;
|
||||
class QUndoCommand;
|
||||
|
||||
#include <QString>
|
||||
|
||||
namespace CSMWorld
|
||||
{
|
||||
class CommandMacro
|
||||
{
|
||||
QUndoStack& mUndoStack;
|
||||
QString mDescription;
|
||||
bool mStarted;
|
||||
|
||||
/// not implemented
|
||||
CommandMacro (const CommandMacro&);
|
||||
|
||||
/// not implemented
|
||||
CommandMacro& operator= (const CommandMacro&);
|
||||
|
||||
public:
|
||||
|
||||
/// If \a description is empty, the description of the first command is used.
|
||||
CommandMacro (QUndoStack& undoStack, const QString& description = "");
|
||||
|
||||
~CommandMacro();
|
||||
|
||||
void push (QUndoCommand *command);
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue