diff --git a/apps/opencs/editor.cpp b/apps/opencs/editor.cpp index bd4f57304..0bcaff6a5 100644 --- a/apps/opencs/editor.cpp +++ b/apps/opencs/editor.cpp @@ -7,6 +7,9 @@ #include #include #include +#include +#include +#include #include #include @@ -253,6 +256,7 @@ void CS::Editor::createNewFile (const boost::filesystem::path &savePath) mDocumentManager.addDocument (files, savePath, true); mFileDialog.hide(); + showSplashMessage(); } void CS::Editor::createNewGame (const boost::filesystem::path& file) @@ -264,6 +268,7 @@ void CS::Editor::createNewGame (const boost::filesystem::path& file) mDocumentManager.addDocument (files, file, true); mNewGame.hide(); + showSplashMessage(); } void CS::Editor::showStartup() @@ -475,9 +480,51 @@ std::auto_ptr CS::Editor::setupGraphics() void CS::Editor::documentAdded (CSMDoc::Document *document) { mViewManager.addView (document); + showSplashMessage(); } void CS::Editor::lastDocumentDeleted() { QApplication::quit(); } + +void CS::Editor::showSplashMessage() +{ + CSMSettings::UserSettings &settings = CSMSettings::UserSettings::instance(); + if(settings.settingValue ("filter/project-added") == "true" || + settings.settingValue ("filter/project-modified") == "true") + { + QPixmap img(QPixmap(":./openmw-cs.png")); + + QString msgTop("You have active global filters."); + QString msgBottom("Some rows may be hidden!"); + QFont splashFont(QFont("Arial", 16, QFont::Bold)); // TODO: use system font? + //splashFont.setStretch(125); + + QFontMetrics fm(splashFont); + int msgWidth = std::max(fm.width(msgTop), fm.width(msgBottom)); + img.scaledToWidth(msgWidth); + + QSplashScreen *splash = new QSplashScreen(img, Qt::WindowStaysOnTopHint); + splash->setFont(splashFont); + splash->resize(msgWidth+20, splash->height()+fm.lineSpacing()*2+20); // add some margin + + // try to center the message near the center of the saved window + QWidget dummy; + bool xWorkaround = settings.settingValue ("window/x-save-state-workaround").toStdString() == "true"; + if (settings.settingValue ("window/save-state").toStdString() == "true" && + !(xWorkaround && settings.settingValue ("window/maximized").toStdString() == "true")) + { + dummy.restoreGeometry(settings.value("window/geometry").toByteArray()); + splash->move(dummy.x()+std::max(0, (dummy.width()-msgWidth)/2), + dummy.y()+std::max(0, (dummy.height()-splash->height())/2)); + } + else + splash->move(std::max(0, splash->x()-msgWidth/2), splash->y()); + + splash->show(); + splash->showMessage(msgTop+"\n"+msgBottom, Qt::AlignHCenter|Qt::AlignBottom, Qt::red); + QTimer::singleShot(4000, splash, SLOT(close())); // 4 seconds should be enough + splash->raise(); // for X windows + } +} diff --git a/apps/opencs/editor.hpp b/apps/opencs/editor.hpp index cbf306df8..040ca963f 100644 --- a/apps/opencs/editor.hpp +++ b/apps/opencs/editor.hpp @@ -60,6 +60,8 @@ namespace CS boost::filesystem::ofstream mPidFile; bool mFsStrict; + void showSplashMessage(); + void setupDataFiles (const Files::PathContainer& dataDirs); std::pair > readConfig(bool quiet=false); diff --git a/apps/opencs/model/settings/usersettings.cpp b/apps/opencs/model/settings/usersettings.cpp index 3e59d0582..9aecd9548 100644 --- a/apps/opencs/model/settings/usersettings.cpp +++ b/apps/opencs/model/settings/usersettings.cpp @@ -161,6 +161,16 @@ void CSMSettings::UserSettings::buildSettingModelDefaults() grow->setToolTip ("When \"Grow then Scroll\" option is selected, the window size grows to" " the width of the virtual desktop. \nIf this option is selected the the window growth" "is limited to the current screen."); + + Setting *saveState = createSetting (Type_CheckBox, "save-state", "Save window size and position"); + saveState->setDefaultValue ("true"); + saveState->setToolTip ("Remember window size and position between editing sessions."); + + Setting *saveX = createSetting (Type_CheckBox, "x-save-state-workaround", "X windows workaround"); + saveX->setDefaultValue ("false"); + saveX->setToolTip ("Some X window managers don't remember the windows state before being" + " maximized. In such environments exiting while maximized will correctly start in a maximized" + " window, but restoring back to the normal size won't work. Try this workaround."); } declareSection ("records", "Records"); @@ -298,7 +308,7 @@ void CSMSettings::UserSettings::buildSettingModelDefaults() Setting *lineNum = createSetting (Type_CheckBox, "show-linenum", "Show Line Numbers"); lineNum->setDefaultValue ("true"); lineNum->setToolTip ("Show line numbers to the left of the script editor window." - "The current row and column numbers of the text cursor are shown at the bottom."); + " The current row and column numbers of the text cursor are shown at the bottom."); Setting *monoFont = createSetting (Type_CheckBox, "mono-font", "Use monospace font"); monoFont->setDefaultValue ("true"); @@ -356,6 +366,19 @@ void CSMSettings::UserSettings::buildSettingModelDefaults() formatId->setToolTip ("(Default: Blue) Use one of the following formats:" + tooltip); } + declareSection ("filter", "Global Filter"); + { + Setting *projAdded = createSetting (Type_CheckBox, "project-added", "Project::added initial value"); + projAdded->setDefaultValue ("false"); + projAdded->setToolTip ("Show records added by the project when opening a table." + " Other records are filterd out."); + + Setting *projModified = createSetting (Type_CheckBox, "project-modified", "Project::modified initial value"); + projModified->setDefaultValue ("false"); + projModified->setToolTip ("Show records modified by the project when opening a table." + " Other records are filterd out."); + } + { /****************************************************************** * There are three types of values: @@ -576,6 +599,21 @@ QString CSMSettings::UserSettings::setting(const QString &viewKey, const QString return QString(); } +QVariant CSMSettings::UserSettings::value(const QString &viewKey, const QVariant &value) +{ + if(value != QVariant()) + { + mSettingDefinitions->setValue (viewKey, value); + return value; + } + else if(mSettingDefinitions->contains(viewKey)) + { + return mSettingDefinitions->value (viewKey); + } + + return QVariant(); +} + bool CSMSettings::UserSettings::hasSettingDefinitions (const QString &viewKey) const { return (mSettingDefinitions->contains (viewKey)); diff --git a/apps/opencs/model/settings/usersettings.hpp b/apps/opencs/model/settings/usersettings.hpp index 5188a9842..c4c5ea583 100644 --- a/apps/opencs/model/settings/usersettings.hpp +++ b/apps/opencs/model/settings/usersettings.hpp @@ -82,6 +82,8 @@ namespace CSMSettings { QString setting(const QString &viewKey, const QString &value = QString()); + QVariant value(const QString &viewKey, const QVariant &value = QVariant()); + private: void buildSettingModelDefaults(); diff --git a/apps/opencs/view/doc/operations.cpp b/apps/opencs/view/doc/operations.cpp index 7ee4b8726..934c94a96 100644 --- a/apps/opencs/view/doc/operations.cpp +++ b/apps/opencs/view/doc/operations.cpp @@ -19,6 +19,7 @@ CSVDoc::Operations::Operations() setVisible (false); setFixedHeight (widgetContainer->height()); setTitleBarWidget (new QWidget (this)); + setObjectName (QString("operations")); // needed to suppress warning while saving window state } void CSVDoc::Operations::setProgress (int current, int max, int type, int threads) diff --git a/apps/opencs/view/doc/subview.cpp b/apps/opencs/view/doc/subview.cpp index f4f0c6afe..1550f4be8 100644 --- a/apps/opencs/view/doc/subview.cpp +++ b/apps/opencs/view/doc/subview.cpp @@ -26,6 +26,8 @@ CSVDoc::SubView::SubView (const CSMWorld::UniversalId& id) /// \todo add a button to the title bar that clones this sub view setWindowTitle (QString::fromUtf8 (mUniversalId.toString().c_str())); + // set object name to suppress warning while saving window state + setObjectName (QString::fromUtf8 (mUniversalId.toString().c_str())); setAttribute(Qt::WA_DeleteOnClose); } diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp index c4abb2622..78203666d 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -37,6 +37,23 @@ void CSVDoc::View::closeEvent (QCloseEvent *event) event->ignore(); else { + if (mSaveWindowState) + { + CSMSettings::UserSettings &userSettings = CSMSettings::UserSettings::instance(); + if (isMaximized() && mXWorkaround) + { + userSettings.setDefinitions("window/maximized", (QStringList() << "true")); + userSettings.saveDefinitions(); // store previously saved geometry & state + } + else + { + userSettings.value("window/geometry", saveGeometry()); + userSettings.value("window/state", saveState()); + userSettings.setDefinitions("window/maximized", (QStringList() << "false")); + userSettings.saveDefinitions(); + } + } + // closeRequest() returns true if last document mViewManager.removeDocAndView(mDocument); } @@ -397,18 +414,32 @@ void CSVDoc::View::updateActions() CSVDoc::View::View (ViewManager& viewManager, CSMDoc::Document *document, int totalViews) : mViewManager (viewManager), mDocument (document), mViewIndex (totalViews-1), - mViewTotal (totalViews), mScroll(0), mScrollbarOnly(false) + mViewTotal (totalViews), mScroll(0), mScrollbarOnly(false), + mSaveWindowState(false), mXWorkaround(false) { - int width = CSMSettings::UserSettings::instance().settingValue - ("window/default-width").toInt(); + CSMSettings::UserSettings &userSettings = CSMSettings::UserSettings::instance(); + mXWorkaround = userSettings.settingValue ("window/x-save-state-workaround").toStdString() == "true"; + mSaveWindowState = userSettings.setting ("window/save-state", "true").toStdString() == "true"; - int height = CSMSettings::UserSettings::instance().settingValue - ("window/default-height").toInt(); + // check if saved state should be used and whether it is the first time + if (mSaveWindowState && userSettings.hasSettingDefinitions ("window/maximized")) + { + restoreGeometry(userSettings.value("window/geometry").toByteArray()); + restoreState(userSettings.value("window/state").toByteArray()); - width = std::max(width, 300); - height = std::max(height, 300); + if (mXWorkaround && userSettings.settingValue ("window/maximized").toStdString() == "true") + setWindowState(windowState() | Qt::WindowMaximized); + } + else + { + int width = userSettings.settingValue ("window/default-width").toInt(); + int height = userSettings.settingValue ("window/default-height").toInt(); - resize (width, height); + width = std::max(width, 300); + height = std::max(height, 300); + + resize (width, height); + } mSubViewWindow.setDockOptions (QMainWindow::AllowNestedDocks); @@ -855,6 +886,12 @@ void CSVDoc::View::updateUserSetting (const QString &name, const QStringList &li if (name=="window/hide-subview") updateSubViewIndicies (0); + if (name == "window/save-state") + mSaveWindowState = list.at(0) == "true"; + + if (name == "window/x-save-state-workaround") + mXWorkaround = list.at(0) == "true"; + foreach (SubView *subView, mSubViews) { subView->updateUserSetting (name, list); @@ -946,6 +983,36 @@ void CSVDoc::View::closeRequest (SubView *subView) mViewManager.removeDocAndView (mDocument); } +// for more reliable detetion of isMaximized(), see https://bugreports.qt.io/browse/QTBUG-30085 +void CSVDoc::View::saveWindowState() +{ + if (!isMaximized()) + { + // update but don't save to config file yet + CSMSettings::UserSettings &userSettings = CSMSettings::UserSettings::instance(); + userSettings.value("window/geometry", saveGeometry()); + userSettings.value("window/state", saveState()); + } +} + +// For X11 where Qt does not remember pre-maximised state +void CSVDoc::View::moveEvent (QMoveEvent *event) +{ + if (mXWorkaround && mSaveWindowState) + QMetaObject::invokeMethod(this, "saveWindowState", Qt::QueuedConnection); + + QMainWindow::moveEvent(event); +} + +// For X11 where Qt does not remember pre-maximised state +void CSVDoc::View::resizeEvent (QResizeEvent *event) +{ + if (mXWorkaround && mSaveWindowState) + QMetaObject::invokeMethod(this, "saveWindowState", Qt::QueuedConnection); + + QMainWindow::resizeEvent(event); +} + void CSVDoc::View::updateScrollbar() { QRect rect; diff --git a/apps/opencs/view/doc/view.hpp b/apps/opencs/view/doc/view.hpp index 7f4255f8f..dc8e14fb5 100644 --- a/apps/opencs/view/doc/view.hpp +++ b/apps/opencs/view/doc/view.hpp @@ -51,6 +51,9 @@ namespace CSVDoc QScrollArea *mScroll; bool mScrollbarOnly; + bool mSaveWindowState; + bool mXWorkaround; + // not implemented View (const View&); @@ -117,6 +120,11 @@ namespace CSVDoc /// Function called by view manager when user preferences are updated void updateEditorSetting (const QString &, const QString &); + protected: + + virtual void moveEvent(QMoveEvent * event); + virtual void resizeEvent(QResizeEvent * event); + signals: void newGameRequest(); @@ -236,6 +244,8 @@ namespace CSVDoc void closeRequest (SubView *subView); + void saveWindowState(); + void moveScrollBarToEnd(int min, int max); }; } diff --git a/apps/opencs/view/doc/viewmanager.cpp b/apps/opencs/view/doc/viewmanager.cpp index 7aec001cf..294efdc1b 100644 --- a/apps/opencs/view/doc/viewmanager.cpp +++ b/apps/opencs/view/doc/viewmanager.cpp @@ -419,6 +419,27 @@ bool CSVDoc::ViewManager::removeDocument (CSVDoc::View *view) else remainingViews.push_back(*iter); } + + // FIXME: seems removeDocument() and closeRequest() are duplicated functionality + CSMSettings::UserSettings &userSettings = CSMSettings::UserSettings::instance(); + if (remainingViews.empty() && !mViews.empty() && + userSettings.settingValue ("window/save-state").toStdString() == "true") + { + if (userSettings.settingValue ("window/x-save-state-workaround").toStdString() == "true" + && mViews.back()->isMaximized()) + { + userSettings.setDefinitions("window/maximized", (QStringList() << "true")); + userSettings.saveDefinitions(); // store previously saved geometry & state + } + else + { + userSettings.value("window/geometry", mViews.back()->saveGeometry()); + userSettings.value("window/state", mViews.back()->saveState()); + userSettings.setDefinitions("window/maximized", (QStringList() << "false")); + userSettings.saveDefinitions(); + } + } + mDocumentManager.removeDocument(document); mViews = remainingViews; } diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index d757d5bbe..cb4acf23b 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -24,6 +24,9 @@ #include "../../model/world/tablemimedata.hpp" #include "../../model/world/tablemimedata.hpp" #include "../../model/world/commanddispatcher.hpp" +#include "../../model/filter/parser.hpp" +#include "../../model/filter/andnode.hpp" +#include "../../model/filter/ornode.hpp" #include "../../model/settings/usersettings.hpp" #include "recordstatusdelegate.hpp" @@ -407,6 +410,24 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id, mDoubleClickActions.insert (std::make_pair (Qt::ShiftModifier, Action_EditRecord)); mDoubleClickActions.insert (std::make_pair (Qt::ControlModifier, Action_View)); mDoubleClickActions.insert (std::make_pair (Qt::ShiftModifier | Qt::ControlModifier, Action_EditRecordAndClose)); + + CSMFilter::Parser parser(mDocument.getData()); + + CSMSettings::UserSettings &userSettings = CSMSettings::UserSettings::instance(); + if(userSettings.settingValue ("filter/project-added") == "true") + { + parser.parse("!string(\"Modified\", \"Added\")"); + mAdded = parser.getFilter(); + } + + if(userSettings.settingValue ("filter/project-modified") == "true") + { + parser.parse("!string(\"Modified\", \"Modified\")"); + mModified = parser.getFilter(); + } + + if(mAdded || mModified) + recordFilterChanged(boost::shared_ptr()); } void CSVWorld::Table::setEditLock (bool locked) @@ -693,7 +714,37 @@ void CSVWorld::Table::requestFocus (const std::string& id) void CSVWorld::Table::recordFilterChanged (boost::shared_ptr filter) { - mProxyModel->setFilter (filter); + mFilter = filter; + + boost::shared_ptr global; + if(mAdded && mModified) + { + std::vector > nodes; + nodes.push_back(mAdded); + nodes.push_back(mModified); + global = boost::shared_ptr(new CSMFilter::OrNode (nodes)); + } + else if(mAdded) + { + global = mAdded; + } + else if(mModified) + { + global = mModified; + } + + if(filter && global) + { + std::vector > nodes; + nodes.push_back(filter); + nodes.push_back(global); + boost::shared_ptr combined(new CSMFilter::AndNode (nodes)); + mProxyModel->setFilter (combined); + } + else if(global) + mProxyModel->setFilter (global); + else + mProxyModel->setFilter (filter); tableSizeUpdate(); selectionSizeUpdate(); } @@ -737,6 +788,34 @@ std::vector< CSMWorld::UniversalId > CSVWorld::Table::getDraggedRecords() const return idToDrag; } +void CSVWorld::Table::globalFilterAddedChanged(int state) +{ + if(state == Qt::Checked && !mAdded) + { + CSMFilter::Parser parser(mDocument.getData()); + parser.parse("!string(\"Modified\", \"Added\")"); + mAdded = parser.getFilter(); + } + else if(state == Qt::Unchecked) + mAdded.reset(); + + recordFilterChanged(mFilter); +} + +void CSVWorld::Table::globalFilterModifiedChanged(int state) +{ + if(state == Qt::Checked && !mModified) + { + CSMFilter::Parser parser(mDocument.getData()); + parser.parse("!string(\"Modified\", \"Modified\")"); + mModified = parser.getFilter(); + } + else if(state == Qt::Unchecked) + mModified.reset(); + + recordFilterChanged(mFilter); +} + void CSVWorld::Table::rowsInsertedEvent(const QModelIndex& parent, int start, int end) { tableSizeUpdate(); diff --git a/apps/opencs/view/world/table.hpp b/apps/opencs/view/world/table.hpp index 38fcd83bd..20053ccd5 100644 --- a/apps/opencs/view/world/table.hpp +++ b/apps/opencs/view/world/table.hpp @@ -70,6 +70,10 @@ namespace CSVWorld bool mJumpToAddedRecord; bool mUnselectAfterJump; + boost::shared_ptr mFilter; + boost::shared_ptr mAdded; + boost::shared_ptr mModified; + private: void contextMenuEvent (QContextMenuEvent *event); @@ -140,6 +144,10 @@ namespace CSVWorld void updateUserSetting (const QString &name, const QStringList &list); + void globalFilterAddedChanged (int state); + + void globalFilterModifiedChanged (int state); + void rowsInsertedEvent(const QModelIndex& parent, int start, int end); }; } diff --git a/apps/opencs/view/world/tablesubview.cpp b/apps/opencs/view/world/tablesubview.cpp index 75671a50c..8ffcb620c 100644 --- a/apps/opencs/view/world/tablesubview.cpp +++ b/apps/opencs/view/world/tablesubview.cpp @@ -1,6 +1,8 @@ #include "tablesubview.hpp" +#include +#include #include #include #include @@ -10,6 +12,7 @@ #include "../../model/doc/document.hpp" #include "../../model/world/tablemimedata.hpp" +#include "../../model/settings/usersettings.hpp" #include "../doc/sizehint.hpp" #include "../filter/filterbox.hpp" @@ -33,7 +36,21 @@ CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::D mFilterBox = new CSVFilter::FilterBox (document.getData(), this); - layout->insertWidget (0, mFilterBox); + QHBoxLayout *hLayout = new QHBoxLayout; + QCheckBox *added = new QCheckBox("A"); + QCheckBox *modified = new QCheckBox("M"); + added->setToolTip("Apply filter project::added. Changes to\nthis filter setting is not saved in preferences."); + modified->setToolTip("Apply filter project::modified. Changes to\nthis filter setting is not saved in preferences."); + CSMSettings::UserSettings &userSettings = CSMSettings::UserSettings::instance(); + added->setCheckState( + userSettings.settingValue ("filter/project-added") == "true" ? Qt::Checked : Qt::Unchecked); + modified->setCheckState( + userSettings.settingValue ("filter/project-modified") == "true" ? Qt::Checked : Qt::Unchecked); + + hLayout->insertWidget(0,mFilterBox); + hLayout->insertWidget(1,added); + hLayout->insertWidget(2,modified); + layout->insertLayout (0, hLayout); CSVDoc::SizeHintWidget *widget = new CSVDoc::SizeHintWidget; @@ -56,6 +73,9 @@ CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::D connect (mTable, SIGNAL (tableSizeChanged (int, int, int)), mBottom, SLOT (tableSizeChanged (int, int, int))); + connect(added, SIGNAL (stateChanged(int)), mTable, SLOT (globalFilterAddedChanged(int))); + connect(modified, SIGNAL (stateChanged(int)), mTable, SLOT (globalFilterModifiedChanged(int))); + mTable->tableSizeUpdate(); mTable->selectionSizeUpdate(); mTable->viewport()->installEventFilter(this); @@ -96,8 +116,7 @@ void CSVWorld::TableSubView::editRequest (const CSMWorld::UniversalId& id, const focusId (id, hint); } -void CSVWorld::TableSubView::updateUserSetting - (const QString &name, const QStringList &list) +void CSVWorld::TableSubView::updateUserSetting (const QString &name, const QStringList &list) { mTable->updateUserSetting(name, list); }