summaryrefslogtreecommitdiffstats
path: root/SQLiteStudio3/guiSQLiteStudio/common
diff options
context:
space:
mode:
Diffstat (limited to 'SQLiteStudio3/guiSQLiteStudio/common')
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/common/colorbutton.cpp39
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/common/colorbutton.h30
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/common/configcombobox.cpp16
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/common/configcombobox.h34
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/common/configradiobutton.cpp36
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/common/configradiobutton.h46
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/common/datawidgetmapper.cpp138
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/common/datawidgetmapper.h54
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/common/extaction.cpp30
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/common/extaction.h20
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/common/extactioncontainer.cpp260
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/common/extactioncontainer.h254
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/common/extactionmanagementnotifier.cpp19
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/common/extactionmanagementnotifier.h30
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/common/extactionprototype.cpp65
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/common/extactionprototype.h44
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/common/extlineedit.cpp118
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/common/extlineedit.h45
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/common/fileedit.cpp98
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/common/fileedit.h52
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/common/fontedit.cpp68
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/common/fontedit.h43
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/common/fontedit.ui35
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/common/intvalidator.cpp38
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/common/intvalidator.h25
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/common/numericspinbox.cpp152
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/common/numericspinbox.h57
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/common/tablewidget.cpp49
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/common/tablewidget.h23
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/common/userinputfilter.cpp33
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/common/userinputfilter.h31
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/common/verifiablewizardpage.cpp20
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/common/verifiablewizardpage.h22
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/common/widgetcover.cpp209
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/common/widgetcover.h72
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/common/widgetstateindicator.cpp412
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/common/widgetstateindicator.h99
37 files changed, 2816 insertions, 0 deletions
diff --git a/SQLiteStudio3/guiSQLiteStudio/common/colorbutton.cpp b/SQLiteStudio3/guiSQLiteStudio/common/colorbutton.cpp
new file mode 100644
index 0000000..005de4c
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/common/colorbutton.cpp
@@ -0,0 +1,39 @@
+#include "colorbutton.h"
+#include <QResizeEvent>
+#include <QColorDialog>
+
+ColorButton::ColorButton(QWidget *parent) :
+ QPushButton(parent)
+{
+ setFixedWidth(height()*2);
+ setColor(Qt::black);
+ connect(this, SIGNAL(clicked()), this, SLOT(pickColor()));
+}
+
+QColor ColorButton::getColor() const
+{
+ return color;
+}
+
+void ColorButton::setColor(const QColor& value)
+{
+ color = value;
+ QPixmap pix(iconSize());
+ pix.fill(color);
+ setIcon(pix);
+ emit colorChanged(color);
+}
+
+void ColorButton::pickColor()
+{
+ QColor newColor = QColorDialog::getColor(color, parentWidget(), tr("Pick a color"));
+ if (!newColor.isValid())
+ return;
+
+ setColor(newColor);
+}
+
+void ColorButton::resizeEvent(QResizeEvent* e)
+{
+ setFixedWidth(e->size().height()*2);
+}
diff --git a/SQLiteStudio3/guiSQLiteStudio/common/colorbutton.h b/SQLiteStudio3/guiSQLiteStudio/common/colorbutton.h
new file mode 100644
index 0000000..237a4ee
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/common/colorbutton.h
@@ -0,0 +1,30 @@
+#ifndef COLORBUTTON_H
+#define COLORBUTTON_H
+
+#include "guiSQLiteStudio_global.h"
+#include <QPushButton>
+#include <QColor>
+
+class GUI_API_EXPORT ColorButton : public QPushButton
+{
+ Q_OBJECT
+ public:
+ explicit ColorButton(QWidget *parent = 0);
+
+ QColor getColor() const;
+ void setColor(const QColor& value);
+
+ protected:
+ void resizeEvent(QResizeEvent* e);
+
+ private:
+ QColor color;
+
+ private slots:
+ void pickColor();
+
+ signals:
+ void colorChanged(const QColor& color);
+};
+
+#endif // COLORBUTTON_H
diff --git a/SQLiteStudio3/guiSQLiteStudio/common/configcombobox.cpp b/SQLiteStudio3/guiSQLiteStudio/common/configcombobox.cpp
new file mode 100644
index 0000000..aa2f115
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/common/configcombobox.cpp
@@ -0,0 +1,16 @@
+#include "configcombobox.h"
+
+ConfigComboBox::ConfigComboBox(QWidget *parent) :
+ QComboBox(parent)
+{
+}
+
+QVariant ConfigComboBox::getModelName() const
+{
+ return modelName;
+}
+
+void ConfigComboBox::setModelName(QVariant arg)
+{
+ modelName = arg;
+}
diff --git a/SQLiteStudio3/guiSQLiteStudio/common/configcombobox.h b/SQLiteStudio3/guiSQLiteStudio/common/configcombobox.h
new file mode 100644
index 0000000..dd4c71c
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/common/configcombobox.h
@@ -0,0 +1,34 @@
+#ifndef CONFIGCOMBOBOX_H
+#define CONFIGCOMBOBOX_H
+
+#include "guiSQLiteStudio_global.h"
+#include <QComboBox>
+
+/**
+ * @brief Config-oriented combo box.
+ *
+ * It's just like a regular QComboBox, except it honors additional Qt dynamic property
+ * called "modelName". The "modelName" property should name a CfgEntry key (together with its category,
+ * just like "cfg" properties for CfgEntry linked widgets), that is of QStringList type.
+ * The QStringList is used as a data model for QComboBox. Every time that the CfgEntry
+ * with QStringList changes, the combo box data entries are updated.
+ */
+class GUI_API_EXPORT ConfigComboBox : public QComboBox
+{
+ Q_OBJECT
+
+ Q_PROPERTY(QVariant modelName READ getModelName WRITE setModelName)
+
+ public:
+ explicit ConfigComboBox(QWidget* parent = 0);
+
+ QVariant getModelName() const;
+
+ public slots:
+ void setModelName(QVariant arg);
+
+ private:
+ QVariant modelName;
+};
+
+#endif // CONFIGCOMBOBOX_H
diff --git a/SQLiteStudio3/guiSQLiteStudio/common/configradiobutton.cpp b/SQLiteStudio3/guiSQLiteStudio/common/configradiobutton.cpp
new file mode 100644
index 0000000..62ed4f8
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/common/configradiobutton.cpp
@@ -0,0 +1,36 @@
+#include "configradiobutton.h"
+
+ConfigRadioButton::ConfigRadioButton(QWidget* parent) :
+ QRadioButton(parent)
+{
+ connect(this, SIGNAL(toggled(bool)), this, SLOT(handleToggled(bool)));
+}
+
+QVariant ConfigRadioButton::getAssignedValue() const
+{
+ return assignedValue;
+}
+
+void ConfigRadioButton::setAssignedValue(const QVariant& value)
+{
+ assignedValue = value;
+}
+
+void ConfigRadioButton::handleToggled(bool checked)
+{
+ if (handlingSlot)
+ return;
+
+ if (checked)
+ emit toggledOn(assignedValue);
+ else
+ emit toggledOff(assignedValue);
+}
+
+void ConfigRadioButton::alignToValue(const QVariant& value)
+{
+ handlingSlot = true;
+ setChecked(value == assignedValue);
+ handlingSlot = false;
+}
+
diff --git a/SQLiteStudio3/guiSQLiteStudio/common/configradiobutton.h b/SQLiteStudio3/guiSQLiteStudio/common/configradiobutton.h
new file mode 100644
index 0000000..b972c26
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/common/configradiobutton.h
@@ -0,0 +1,46 @@
+#ifndef CONFIGRADIOBUTTON_H
+#define CONFIGRADIOBUTTON_H
+
+#include "guiSQLiteStudio_global.h"
+#include <QRadioButton>
+#include <QVariant>
+
+/**
+ * @brief Config-oriented radio button.
+ *
+ * It's just like a usual QRadioButton, except it has a value assigned to it
+ * and when the radio is toggled on, the signal is emitted to inform about it.
+ * To inform about the button being toggled off a different signal is emitted.
+ * It also has a slot to be called when the associated property in the application
+ * has changed and needs to be reflected in the button - the button checks
+ * if the value of the property reflects the button's assigned value
+ * and toggles on or off approprietly. In that case the signals are not emitted.
+ */
+class GUI_API_EXPORT ConfigRadioButton : public QRadioButton
+{
+ Q_OBJECT
+
+ Q_PROPERTY(QVariant assignedValue READ getAssignedValue WRITE setAssignedValue)
+
+ public:
+ explicit ConfigRadioButton(QWidget *parent = 0);
+
+ QVariant getAssignedValue() const;
+ void setAssignedValue(const QVariant& value);
+
+ private:
+ QVariant assignedValue;
+ bool handlingSlot = false;
+
+ signals:
+ void toggledOn(const QVariant& assignedBalue);
+ void toggledOff(const QVariant& assignedBalue);
+
+ private slots:
+ void handleToggled(bool checked);
+
+ public slots:
+ void alignToValue(const QVariant& value);
+};
+
+#endif // CONFIGRADIOBUTTON_H
diff --git a/SQLiteStudio3/guiSQLiteStudio/common/datawidgetmapper.cpp b/SQLiteStudio3/guiSQLiteStudio/common/datawidgetmapper.cpp
new file mode 100644
index 0000000..655a9aa
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/common/datawidgetmapper.cpp
@@ -0,0 +1,138 @@
+#include "datawidgetmapper.h"
+#include <QAbstractItemModel>
+#include <QWidget>
+
+DataWidgetMapper::DataWidgetMapper(QObject *parent) :
+ QObject(parent)
+{
+}
+QAbstractItemModel* DataWidgetMapper::getModel() const
+{
+ return model;
+}
+
+void DataWidgetMapper::setModel(QAbstractItemModel* value)
+{
+ model = value;
+}
+
+void DataWidgetMapper::addMapping(QWidget* widget, int modelColumn, const QString& propertyName)
+{
+ MappingEntry* entry = new MappingEntry;
+ entry->columnIndex = modelColumn;
+ entry->widget = widget;
+ entry->propertyName = propertyName;
+ mappings[widget] = entry;
+}
+
+void DataWidgetMapper::clearMapping()
+{
+ for (MappingEntry* entry : mappings.values())
+ delete entry;
+
+ mappings.clear();
+}
+
+int DataWidgetMapper::getCurrentIndex() const
+{
+ return currentIndex;
+}
+
+int DataWidgetMapper::mappedSection(QWidget* widget) const
+{
+ if (mappings.contains(widget))
+ return mappings[widget]->columnIndex;
+
+ return -1;
+}
+
+void DataWidgetMapper::loadFromModel()
+{
+ QModelIndex idx;
+ QVariant data;
+ for (MappingEntry* entry : mappings.values())
+ {
+ idx = model->index(currentIndex, entry->columnIndex);
+ data = model->data(idx, Qt::EditRole);
+ entry->widget->setProperty(entry->propertyName.toLatin1().constData(), data);
+ }
+}
+
+DataWidgetMapper::SubmitFilter DataWidgetMapper::getSubmitFilter() const
+{
+ return submitFilter;
+}
+
+void DataWidgetMapper::setSubmitFilter(const SubmitFilter& value)
+{
+ submitFilter = value;
+}
+
+void DataWidgetMapper::setCurrentIndex(int rowIndex)
+{
+ if (!model)
+ return;
+
+ if (rowIndex < 0)
+ return;
+
+ if (rowIndex >= model->rowCount())
+ return;
+
+ if (model->rowCount() == 0)
+ return;
+
+ currentIndex = rowIndex;
+ loadFromModel();
+ emit currentIndexChanged(rowIndex);
+}
+
+void DataWidgetMapper::toFirst()
+{
+ setCurrentIndex(0);
+}
+
+void DataWidgetMapper::toLast()
+{
+ if (!model)
+ return;
+
+ setCurrentIndex(model->rowCount() - 1);
+}
+
+void DataWidgetMapper::toNext()
+{
+ setCurrentIndex(currentIndex + 1);
+}
+
+void DataWidgetMapper::toPrevious()
+{
+ setCurrentIndex(currentIndex - 1);
+}
+
+void DataWidgetMapper::submit()
+{
+ QModelIndex idx;
+ QVariant value;
+ for (MappingEntry* entry : mappings.values())
+ {
+ if (submitFilter && !submitFilter(entry->widget))
+ continue;
+
+ idx = model->index(currentIndex, entry->columnIndex);
+ value = entry->widget->property(entry->propertyName.toLatin1().constData());
+ model->setData(idx, value, Qt::EditRole);
+ }
+}
+
+void DataWidgetMapper::revert()
+{
+ if (!model)
+ return;
+
+ if (currentIndex < 0)
+ return;
+
+ loadFromModel();
+}
+
diff --git a/SQLiteStudio3/guiSQLiteStudio/common/datawidgetmapper.h b/SQLiteStudio3/guiSQLiteStudio/common/datawidgetmapper.h
new file mode 100644
index 0000000..4df7d5e
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/common/datawidgetmapper.h
@@ -0,0 +1,54 @@
+#ifndef DATAWIDGETMAPPER_H
+#define DATAWIDGETMAPPER_H
+
+#include <QObject>
+#include <QHash>
+
+class QAbstractItemModel;
+
+class DataWidgetMapper : public QObject
+{
+ Q_OBJECT
+ public:
+ typedef std::function<bool(QWidget*)> SubmitFilter;
+
+ explicit DataWidgetMapper(QObject *parent = 0);
+
+ QAbstractItemModel* getModel() const;
+ void setModel(QAbstractItemModel* value);
+ void addMapping(QWidget* widget, int modelColumn, const QString& propertyName);
+ void clearMapping();
+ int getCurrentIndex() const;
+ int mappedSection(QWidget* widget) const;
+ SubmitFilter getSubmitFilter() const;
+ void setSubmitFilter(const SubmitFilter& value);
+
+ private:
+ struct MappingEntry
+ {
+ QWidget* widget = nullptr;
+ int columnIndex = 0;
+ QString propertyName;
+ };
+
+ void loadFromModel();
+
+ QAbstractItemModel* model = nullptr;
+ int currentIndex = -1;
+ QHash<QWidget*,MappingEntry*> mappings;
+ SubmitFilter submitFilter = nullptr;
+
+ public slots:
+ void setCurrentIndex(int rowIndex);
+ void toFirst();
+ void toLast();
+ void toNext();
+ void toPrevious();
+ void submit();
+ void revert();
+
+ signals:
+ void currentIndexChanged(int newRowIndex);
+};
+
+#endif // DATAWIDGETMAPPER_H
diff --git a/SQLiteStudio3/guiSQLiteStudio/common/extaction.cpp b/SQLiteStudio3/guiSQLiteStudio/common/extaction.cpp
new file mode 100644
index 0000000..3b09c79
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/common/extaction.cpp
@@ -0,0 +1,30 @@
+#include "extaction.h"
+#include <QDebug>
+#include <QShortcutEvent>
+
+ExtAction::ExtAction(QObject *parent) :
+ QAction(parent)
+{
+}
+
+ExtAction::ExtAction(const QString& text, QObject* parent) :
+ QAction(text, parent)
+{
+}
+
+ExtAction::ExtAction(const QIcon& icon, const QString& text, QObject* parent) :
+ QAction(icon, text, parent)
+{
+}
+
+bool ExtAction::event(QEvent* e)
+{
+ // This implementation code comes mostly from Qt 5.1.0,
+ // but it was modified to handle ambiguous shortcuts.
+ if (e->type() == QEvent::Shortcut)
+ {
+ activate(Trigger);
+ return true;
+ }
+ return QObject::event(e);
+}
diff --git a/SQLiteStudio3/guiSQLiteStudio/common/extaction.h b/SQLiteStudio3/guiSQLiteStudio/common/extaction.h
new file mode 100644
index 0000000..5530271
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/common/extaction.h
@@ -0,0 +1,20 @@
+#ifndef extaction_H
+#define extaction_H
+
+#include "guiSQLiteStudio_global.h"
+#include <QAction>
+
+class GUI_API_EXPORT ExtAction : public QAction
+{
+ Q_OBJECT
+
+ public:
+ explicit ExtAction(QObject *parent = 0);
+ ExtAction(const QString& text, QObject* parent = 0);
+ ExtAction(const QIcon& icon, const QString& text, QObject* parent = 0);
+
+ protected:
+ bool event(QEvent* e);
+};
+
+#endif // extaction_H
diff --git a/SQLiteStudio3/guiSQLiteStudio/common/extactioncontainer.cpp b/SQLiteStudio3/guiSQLiteStudio/common/extactioncontainer.cpp
new file mode 100644
index 0000000..15bf926
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/common/extactioncontainer.cpp
@@ -0,0 +1,260 @@
+#include "extactioncontainer.h"
+#include "iconmanager.h"
+#include "common/extaction.h"
+#include "common/global.h"
+#include <QSignalMapper>
+#include <QToolButton>
+#include <QToolBar>
+#include <QMenu>
+#include <QDebug>
+
+ExtActionContainer::ClassNameToToolBarAndAction ExtActionContainer::extraActions;
+QList<ExtActionContainer*> ExtActionContainer::instances;
+
+ExtActionContainer::ExtActionContainer()
+{
+ actionIdMapper = new QSignalMapper();
+
+ // We need to explicitly cast QSignalMapper::mapped to tell which overloaded version of function we want
+ QObject::connect(actionIdMapper, static_cast<void (QSignalMapper::*)(int)>(&QSignalMapper::mapped),
+ [=](int action) {refreshShortcut(action);});
+ instances << this;
+}
+
+ExtActionContainer::~ExtActionContainer()
+{
+ deleteActions();
+ safe_delete(actionIdMapper);
+ instances.removeOne(this);
+}
+
+void ExtActionContainer::initActions()
+{
+ createActions();
+ setupDefShortcuts();
+ refreshShortcuts();
+ handleExtraActions();
+}
+
+void ExtActionContainer::createAction(int action, const Icon& icon, const QString& text, const QObject* receiver, const char* slot, QWidget* container, QWidget* owner)
+{
+ QAction* qAction = new ExtAction(icon, text);
+ createAction(action, qAction, receiver, slot, container, owner);
+}
+
+void ExtActionContainer::createAction(int action, const QString& text, const QObject* receiver, const char* slot, QWidget* container, QWidget* owner)
+{
+ QAction* qAction = new ExtAction(text);
+ createAction(action, qAction, receiver, slot, container, owner);
+}
+
+void ExtActionContainer::bindShortcutsToEnum(CfgCategory &cfgCategory, const QMetaEnum &actionsEnum)
+{
+ QHash<QString, CfgEntry *>& cfgEntries = cfgCategory.getEntries();
+ QString enumName;
+ CfgStringEntry* stringEntry = nullptr;
+ for (int i = 0, total = actionsEnum.keyCount(); i < total; ++i)
+ {
+ enumName = QString::fromLatin1(actionsEnum.key(i));
+ if (!cfgEntries.contains(enumName))
+ continue;
+
+ stringEntry = dynamic_cast<CfgStringEntry*>(cfgEntries[enumName]);
+ if (!stringEntry)
+ {
+ qDebug() << "Tried to bind key sequence config entry, but its type was not QString. Ignoring entry:" << cfgEntries[enumName]->getFullKey();
+ continue;
+ }
+
+ defShortcut(actionsEnum.value(i), stringEntry);
+ }
+}
+
+void ExtActionContainer::defShortcut(int action, CfgStringEntry *cfgEntry)
+{
+ shortcuts[action] = cfgEntry;
+
+ actionIdMapper->setMapping(cfgEntry, action);
+ QObject::connect(cfgEntry, SIGNAL(changed(QVariant)), actionIdMapper, SLOT(map()));
+}
+
+void ExtActionContainer::setShortcutContext(const QList<qint32> actions, Qt::ShortcutContext context)
+{
+ foreach (qint32 act, actions)
+ actionMap[act]->setShortcutContext(context);
+}
+
+void ExtActionContainer::attachActionInMenu(int parentAction, int childAction, QToolBar* toolbar)
+{
+ attachActionInMenu(parentAction, actionMap[childAction], toolbar);
+}
+
+void ExtActionContainer::attachActionInMenu(int parentAction, QAction* childAction, QToolBar* toolbar)
+{
+ attachActionInMenu(actionMap[parentAction], childAction, toolbar);
+}
+
+void ExtActionContainer::attachActionInMenu(QAction* parentAction, QAction* childAction, QToolBar* toolbar)
+{
+ QToolButton* button = dynamic_cast<QToolButton*>(toolbar->widgetForAction(parentAction));
+ QMenu* menu = button->menu();
+
+ if (!menu)
+ {
+ menu = new QMenu(button);
+ button->setMenu(menu);
+ button->setPopupMode(QToolButton::MenuButtonPopup);
+ }
+
+ menu->addAction(childAction);
+}
+
+void ExtActionContainer::updateShortcutTips()
+{
+}
+
+void ExtActionContainer::createAction(int action, QAction* qAction, const QObject* receiver, const char* slot, QWidget* container, QWidget* owner)
+{
+ if (!owner)
+ owner = container;
+ else
+ owner->addAction(qAction);
+
+ qAction->setParent(owner);
+ actionMap[action] = qAction;
+ QObject::connect(qAction, SIGNAL(triggered()), receiver, slot);
+ container->addAction(qAction);
+}
+
+void ExtActionContainer::deleteActions()
+{
+ foreach (QAction* action, actionMap.values())
+ delete action;
+
+ actionMap.clear();
+}
+
+void ExtActionContainer::refreshShortcuts()
+{
+ foreach (int action, actionMap.keys())
+ {
+ if (!shortcuts.contains(action))
+ continue;
+
+ if (noConfigShortcutActions.contains(action))
+ continue;
+
+ refreshShortcut(action);
+ }
+}
+
+void ExtActionContainer::refreshShortcut(int action)
+{
+ QKeySequence seq(shortcuts[action]->get());
+ QString txt = seq.toString(QKeySequence::NativeText);
+ actionMap[action]->setShortcut(seq);
+ actionMap[action]->setToolTip(actionMap[action]->text() + QString(" (%1)").arg(txt));
+}
+
+QAction* ExtActionContainer::getAction(int action)
+{
+ if (!actionMap.contains(action))
+ return nullptr;
+
+ return actionMap.value(action);
+}
+
+void ExtActionContainer::handleActionInsert(int toolbar, ActionDetails* details)
+{
+ if (details->position > -1 && !actionMap.contains(details->position))
+ {
+ qWarning() << "Tried to insert action" << details->action->text() << "before action" << details->position
+ << "which is not present in action container:" << metaObject()->className();
+ return;
+ }
+
+ QToolBar* toolBar = getToolBar(toolbar);
+ if (!toolBar)
+ {
+ qWarning() << "Tried to insert action" << details->action->text() << ", but toolbar was incorrect: " << toolbar
+ << "or there is no toolbar in action container:" << metaObject()->className();
+ return;
+ }
+
+ QAction* beforeQAction = actionMap[details->position];
+ if (details->after)
+ {
+ QList<QAction*> acts = toolBar->actions();
+ int idx = acts.indexOf(beforeQAction);
+ idx++;
+ if (idx > 0 && idx < acts.size())
+ beforeQAction = acts[idx];
+ else
+ beforeQAction = nullptr;
+ }
+
+ QAction* action = details->action->create();
+ toolBar->insertAction(beforeQAction, action);
+
+ ToolbarAndProto toolbarAndProto(toolbar, details);
+ extraActionToToolbarAndProto[action] = toolbarAndProto;
+ toolbarAndProtoToAction[toolbarAndProto] = action;
+
+ QObject::connect(action, &QAction::triggered, [this, details, toolbar]()
+ {
+ details->action->emitTriggered(this, toolbar);
+ });
+
+ details->action->emitInsertedTo(this, toolbar, action);
+}
+
+void ExtActionContainer::handleActionRemoval(int toolbar, ActionDetails* details)
+{
+ QToolBar* toolBar = getToolBar(toolbar);
+ if (!toolBar)
+ {
+ qWarning() << "Tried to remove action" << details->action->text() << ", but toolbar was incorrect: " << toolbar << "or there is no toolbar in action container:"
+ << metaObject()->className();
+ return;
+ }
+
+
+ ToolbarAndProto toolbarAndProto(toolbar, details);
+ QAction* action = toolbarAndProtoToAction[toolbarAndProto];
+
+ details->action->emitAboutToRemoveFrom(this, toolbar, action);
+
+ toolBar->removeAction(action);
+ extraActionToToolbarAndProto.remove(action);
+ toolbarAndProtoToAction.remove(toolbarAndProto);
+
+ details->action->emitRemovedFrom(this, toolbar, action);
+ delete action;
+}
+
+void ExtActionContainer::handleExtraActions()
+{
+ QString clsName = metaObject()->className();
+ if (!extraActions.contains(clsName))
+ return;
+
+ // For each toolbar
+ for (int toolbarId : extraActions[clsName].keys())
+ {
+ // For each action for this toolbar
+ for (ActionDetails* actionDetails : extraActions[clsName][toolbarId])
+ {
+ // Insert action into toolbar, before action's assigned "before" action
+ handleActionInsert(toolbarId, actionDetails);
+ }
+ }
+}
+
+ExtActionContainer::ActionDetails::ActionDetails()
+{
+}
+
+ExtActionContainer::ActionDetails::ActionDetails(ExtActionPrototype* action, int position, bool after) :
+ action(action), position(position), after(after)
+{
+}
diff --git a/SQLiteStudio3/guiSQLiteStudio/common/extactioncontainer.h b/SQLiteStudio3/guiSQLiteStudio/common/extactioncontainer.h
new file mode 100644
index 0000000..808af3e
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/common/extactioncontainer.h
@@ -0,0 +1,254 @@
+#ifndef extactionCONTAINER_H
+#define extactionCONTAINER_H
+
+#include "iconmanager.h"
+#include "config_builder.h"
+#include "extactionprototype.h"
+#include <QString>
+#include <QHash>
+#include <QSet>
+#include <QKeySequence>
+#include <QShortcut>
+#include <QMetaEnum>
+
+class QAction;
+class QObject;
+class QWidget;
+class QActionGroup;
+class QToolBar;
+class QSignalMapper;
+
+#define CFG_SHORTCUTS_METANAME "Shortcuts"
+
+#define CFG_KEY_LIST(Type, Title, Entries) \
+ _CFG_CATEGORIES_WITH_METANAME(Shortcuts##Type, \
+ _CFG_CATEGORY_WITH_TITLE(ShortcutsCategory##Type, Entries, Title), \
+ CFG_SHORTCUTS_METANAME\
+ )
+
+#define CFG_KEY_ENTRY(Name, KeyStr, Title) CFG_ENTRY(QString, Name, QKeySequence(KeyStr).toString(), Title)
+
+#define CFG_KEYS_DEFINE(Type) CFG_DEFINE_LAZY(Shortcuts##Type)
+
+/**
+ * @def Declares access object for defined shortuts.
+ *
+ * This is the same as CFG_INSTANCE for regular config values.
+ * It's optional. It doesn't need to be declared, but if you want to refer
+ * to keys as to configuration values, then you will need this.
+ */
+#define CFG_KEYS_INSTANCE(Type) (*Cfg::getShortcuts##Type##Instance())
+
+/**
+ * @def Binds shortcuts configuration with actions enumerator.
+ * @param Type Shortcuts category type that was passed to CFG_KEY_LIST.
+ * @param EnumName Enumerator type which lists actions that you want bind shortcuts to.
+ *
+ * Names of shortcut entries have to match names of enumerator literals in order to bind shortcuts
+ * to proper actions.
+ */
+#define BIND_SHORTCUTS(Type, EnumName) \
+ for (int _enumCounter = 0, _totalEnums = staticMetaObject.enumeratorCount(); _enumCounter < _totalEnums; _enumCounter++) \
+ { \
+ if (QString::fromLatin1(staticMetaObject.enumerator(_enumCounter).name()) == #EnumName) \
+ { \
+ bindShortcutsToEnum(Cfg::getShortcuts##Type##Instance()->ShortcutsCategory##Type, staticMetaObject.enumerator(_enumCounter)); \
+ break; \
+ } \
+ }
+
+#define GET_SHORTCUTS(Type) ExtActionContainer::getAllShortcutSequences(Cfg::getShortcuts##Type##Instance()->ShortcutsCategory##Type)
+
+class GUI_API_EXPORT ExtActionContainer
+{
+ private:
+ struct GUI_API_EXPORT ActionDetails
+ {
+ ActionDetails();
+ ActionDetails(ExtActionPrototype* action, int position, bool after);
+
+ ExtActionPrototype* action = nullptr;
+ int position = -1;
+ bool after = false;
+ };
+
+ typedef QList<ActionDetails*> ExtraActions;
+ typedef QHash<int,ExtraActions> ToolBarToAction;
+ typedef QHash<QString,ToolBarToAction> ClassNameToToolBarAndAction;
+
+ public:
+ ExtActionContainer();
+ virtual ~ExtActionContainer();
+
+ QAction* getAction(int action);
+ virtual const QMetaObject* metaObject() const = 0;
+
+ template <class T>
+ static void insertAction(ExtActionPrototype* action, int toolbar = -1);
+
+ template <class T>
+ static void insertActionBefore(ExtActionPrototype* action, int beforeAction, int toolbar = -1);
+
+ template <class T>
+ static void insertActionAfter(ExtActionPrototype* action, int afterAction, int toolbar = -1);
+
+ template <class T>
+ static void removeAction(ExtActionPrototype* action, int toolbar = -1);
+
+ protected:
+ QHash<int,QAction*> actionMap;
+ QHash<int,CfgStringEntry*> shortcuts;
+ QSet<int> noConfigShortcutActions;
+
+ virtual void createActions() = 0;
+ virtual void setupDefShortcuts() = 0;
+
+ void initActions();
+ void createAction(int action, const Icon& icon, const QString& text, const QObject* receiver, const char* slot, QWidget* container,
+ QWidget* owner = 0);
+ void createAction(int action, const QString& text, const QObject* receiver, const char* slot, QWidget* container, QWidget* owner = 0);
+
+ /**
+ * @brief Binds config shortcut entries with action enumerator.
+ * @param cfgCategory Config category with QString entries that have shortcut definitions.
+ * @param actionsEnum Enumerator with actions.
+ *
+ * Binds shortcuts defined in given config category to actions listed by the enumerator.
+ * Binding is done by name, that is name of the config entry (in the category) is matched against enumeration name,
+ *
+ * You don't normally use this method, but instead use BIND_SHORTCUTS.
+ */
+ void bindShortcutsToEnum(CfgCategory &cfgCategory, const QMetaEnum& actionsEnum);
+ void defShortcut(int action, CfgStringEntry* cfgEntry);
+ void setShortcutContext(const QList<qint32> actions, Qt::ShortcutContext context);
+
+ /**
+ * @brief attachActionInMenu
+ * @param parentAction Action that will have a submenu. Must already exist.
+ * @param childAction Action to add to the submenu. Must already exist.
+ * @param toolbar Toolbar that parentAction is already added to.
+ * Puts childAction into submenu of parentAction.
+ */
+ void attachActionInMenu(int parentAction, int childAction, QToolBar* toolbar);
+ void attachActionInMenu(int parentAction, QAction* childAction, QToolBar* toolbar);
+ void attachActionInMenu(QAction* parentAction, QAction* childAction, QToolBar* toolbar);
+ void updateShortcutTips();
+
+ /**
+ * @brief Tells the toolbar object for given toolbar enum value.
+ * @param toolbar Toolbar enum value for specific implementation of MdiChild.
+ * @return Toolbar object or null of there's no toolbar for given value, or no toolbar at all.
+ *
+ * The \p toolbar argument should be enum value from the specific implementation of MdiChild,
+ * for example for TableWindow it could be TOOLBAR_GRID_DATA, which refers to grid data tab toolbar.
+ *
+ * For classes with no toolbar this function will always return null;
+ *
+ * For classes with only one toolbar this method will always return that toolbar, no matter
+ * if the \p toolbar argument was correct.
+ *
+ * For classes with more than one toolbar this method will return proper toolbar objects only
+ * when the \p toolbar argument was correct, otherwise it returns null (assuming correct implementation
+ * of this method).
+ */
+ virtual QToolBar* getToolBar(int toolbar) const = 0;
+
+ void handleActionInsert(int toolbar, ActionDetails* details);
+ void handleActionRemoval(int toolbar, ActionDetails* details);
+
+ private:
+ typedef QPair<int,ActionDetails*> ToolbarAndProto;
+
+ void refreshShortcuts();
+ void refreshShortcut(int action);
+ void deleteActions();
+ void createAction(int action, QAction* qAction, const QObject* receiver, const char* slot, QWidget* container, QWidget* owner);
+ void handleExtraActions();
+
+ template <class T>
+ static QList<T*> getInstances();
+
+ template <class T>
+ static void insertAction(ExtActionPrototype* action, int pos, bool after, int toolbar);
+
+ static ClassNameToToolBarAndAction extraActions;
+ static QList<ExtActionContainer*> instances;
+
+ QSignalMapper* actionIdMapper = nullptr;
+ QHash<QAction*,ToolbarAndProto> extraActionToToolbarAndProto;
+ QHash<ToolbarAndProto,QAction*> toolbarAndProtoToAction;
+};
+
+template <class T>
+void ExtActionContainer::insertAction(ExtActionPrototype* action, int pos, bool after, int toolbar)
+{
+ ActionDetails* dets = new ActionDetails(action, pos, after);
+ QString clsName = T::staticMetaObject.className();
+ extraActions[clsName][toolbar] << dets;
+ for (T* instance : getInstances<T>())
+ instance->handleActionInsert(toolbar, dets);
+}
+
+template <class T>
+void ExtActionContainer::insertAction(ExtActionPrototype* action, int toolbar)
+{
+ insertAction<T>(action, -1, false, toolbar);
+}
+
+template <class T>
+void ExtActionContainer::insertActionAfter(ExtActionPrototype* action, int afterAction, int toolbar)
+{
+ insertAction<T>(action, afterAction, true, toolbar);
+}
+
+template <class T>
+void ExtActionContainer::insertActionBefore(ExtActionPrototype* action, int beforeAction, int toolbar)
+{
+ insertAction<T>(action, beforeAction, false, toolbar);
+}
+
+template <class T>
+void ExtActionContainer::removeAction(ExtActionPrototype* action, int toolbar)
+{
+ QString clsName = T::staticMetaObject.className();
+ if (!extraActions.contains(clsName))
+ return;
+
+ if (!extraActions[clsName].contains(toolbar))
+ return;
+
+ ActionDetails* dets = nullptr;
+ for (ActionDetails* d : extraActions[clsName][toolbar])
+ {
+ if (d->action == action)
+ {
+ dets = d;
+ break;
+ }
+ }
+
+ if (!dets)
+ return;
+
+ for (T* instance : getInstances<T>())
+ instance->handleActionRemoval(toolbar, dets);
+
+ extraActions[clsName][toolbar].removeOne(dets);
+ delete dets;
+}
+
+template <class T>
+QList<T*> ExtActionContainer::getInstances()
+{
+ QList<T*> typedInstances;
+ T* typedInstance = nullptr;
+ for (ExtActionContainer* instance : instances)
+ {
+ typedInstance = dynamic_cast<T*>(instance);
+ if (typedInstance)
+ typedInstances << typedInstance;
+ }
+ return typedInstances;
+}
+
+#endif // extactionCONTAINER_H
diff --git a/SQLiteStudio3/guiSQLiteStudio/common/extactionmanagementnotifier.cpp b/SQLiteStudio3/guiSQLiteStudio/common/extactionmanagementnotifier.cpp
new file mode 100644
index 0000000..a6756b0
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/common/extactionmanagementnotifier.cpp
@@ -0,0 +1,19 @@
+#include "extactionmanagementnotifier.h"
+#include "extactioncontainer.h"
+#include <QDebug>
+
+ExtActionManagementNotifier::ExtActionManagementNotifier(QAction* action) :
+ QObject(nullptr), action(action)
+{
+ qDebug() << "create notifier" << this;
+}
+
+void ExtActionManagementNotifier::inserted(ExtActionContainer* object, QToolBar* toolbar)
+{
+ emit actionInserted(object, toolbar, action);
+}
+
+void ExtActionManagementNotifier::removed(ExtActionContainer* object, QToolBar* toolbar)
+{
+ emit actionRemoved(object, toolbar, action);
+}
diff --git a/SQLiteStudio3/guiSQLiteStudio/common/extactionmanagementnotifier.h b/SQLiteStudio3/guiSQLiteStudio/common/extactionmanagementnotifier.h
new file mode 100644
index 0000000..5ecb4e0
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/common/extactionmanagementnotifier.h
@@ -0,0 +1,30 @@
+#ifndef EXTACTIONMANAGEMENTNOTIFIER_H
+#define EXTACTIONMANAGEMENTNOTIFIER_H
+
+#include <QObject>
+#include <QSharedPointer>
+
+class QToolBar;
+class QAction;
+class ExtActionContainer;
+
+class ExtActionManagementNotifier : public QObject
+{
+ Q_OBJECT
+ public:
+ explicit ExtActionManagementNotifier(QAction* action);
+
+ void inserted(ExtActionContainer* object, QToolBar* toolbar);
+ void removed(ExtActionContainer* object, QToolBar* toolbar);
+
+ private:
+ QAction* action = nullptr;
+
+ signals:
+ void actionInserted(ExtActionContainer* object, QToolBar* toolbar, QAction* action);
+ void actionRemoved(ExtActionContainer* object, QToolBar* toolbar, QAction* action);
+};
+
+typedef QSharedPointer<ExtActionManagementNotifier> ExtActionManagementNotifierPtr;
+
+#endif // EXTACTIONMANAGEMENTNOTIFIER_H
diff --git a/SQLiteStudio3/guiSQLiteStudio/common/extactionprototype.cpp b/SQLiteStudio3/guiSQLiteStudio/common/extactionprototype.cpp
new file mode 100644
index 0000000..0dfafba
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/common/extactionprototype.cpp
@@ -0,0 +1,65 @@
+#include "extactionprototype.h"
+#include "extactioncontainer.h"
+#include <QAction>
+#include <QDebug>
+
+ExtActionPrototype::ExtActionPrototype(QObject* parent) :
+ QObject(parent)
+{
+ separator = true;
+}
+
+ExtActionPrototype::ExtActionPrototype(const QString& text, QObject* parent) :
+ QObject(parent), actionText(text)
+{
+}
+
+ExtActionPrototype::ExtActionPrototype(const QIcon& icon, const QString& text, QObject* parent) :
+ QObject(parent), icon(icon), actionText(text)
+{
+}
+
+ExtActionPrototype::~ExtActionPrototype()
+{
+
+}
+
+QString ExtActionPrototype::text() const
+{
+ return actionText;
+}
+
+QAction* ExtActionPrototype::create(QObject* parent)
+{
+ if (!parent)
+ parent = this;
+
+ if (separator)
+ {
+ QAction* act = new QAction(parent);
+ act->setSeparator(true);
+ return act;
+ }
+
+ return new QAction(icon, actionText, parent);
+}
+
+void ExtActionPrototype::emitInsertedTo(ExtActionContainer* actionContainer, int toolbar, QAction* action)
+{
+ emit insertedTo(actionContainer, toolbar, action);
+}
+
+void ExtActionPrototype::emitAboutToRemoveFrom(ExtActionContainer* actionContainer, int toolbar, QAction* action)
+{
+ emit aboutToRemoveFrom(actionContainer, toolbar, action);
+}
+
+void ExtActionPrototype::emitRemovedFrom(ExtActionContainer* actionContainer, int toolbar, QAction* action)
+{
+ emit removedFrom(actionContainer, toolbar, action);
+}
+
+void ExtActionPrototype::emitTriggered(ExtActionContainer* actionContainer, int toolbar)
+{
+ emit triggered(actionContainer, toolbar);
+}
diff --git a/SQLiteStudio3/guiSQLiteStudio/common/extactionprototype.h b/SQLiteStudio3/guiSQLiteStudio/common/extactionprototype.h
new file mode 100644
index 0000000..7fd20d1
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/common/extactionprototype.h
@@ -0,0 +1,44 @@
+#ifndef EXTACTIONPROTOTYPE_H
+#define EXTACTIONPROTOTYPE_H
+
+#include "guiSQLiteStudio_global.h"
+#include <QString>
+#include <QIcon>
+#include <QObject>
+
+class QAction;
+class ExtActionContainer;
+
+class GUI_API_EXPORT ExtActionPrototype : public QObject
+{
+ Q_OBJECT
+
+ friend class ExtActionContainer;
+
+ public:
+ explicit ExtActionPrototype(QObject* parent);
+ ExtActionPrototype(const QString& text, QObject* parent = 0);
+ ExtActionPrototype(const QIcon& icon, const QString& text, QObject* parent = 0);
+ ~ExtActionPrototype();
+
+ QString text() const;
+ QAction* create(QObject* parent = 0);
+
+ private:
+ void emitInsertedTo(ExtActionContainer* actionContainer, int toolbar, QAction* action);
+ void emitAboutToRemoveFrom(ExtActionContainer* actionContainer, int toolbar, QAction* action);
+ void emitRemovedFrom(ExtActionContainer* actionContainer, int toolbar, QAction* action);
+ void emitTriggered(ExtActionContainer* actionContainer, int toolbar);
+
+ QIcon icon;
+ QString actionText;
+ bool separator = false;
+
+ signals:
+ void insertedTo(ExtActionContainer* actionContainer, int toolbar, QAction* action);
+ void aboutToRemoveFrom(ExtActionContainer* actionContainer, int toolbar, QAction* action);
+ void removedFrom(ExtActionContainer* actionContainer, int toolbar, QAction* action);
+ void triggered(ExtActionContainer* actionContainer, int toolbar);
+};
+
+#endif // EXTACTIONPROTOTYPE_H
diff --git a/SQLiteStudio3/guiSQLiteStudio/common/extlineedit.cpp b/SQLiteStudio3/guiSQLiteStudio/common/extlineedit.cpp
new file mode 100644
index 0000000..bd0bffa
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/common/extlineedit.cpp
@@ -0,0 +1,118 @@
+#include "extlineedit.h"
+#include "iconmanager.h"
+#include <QStyle>
+#include <QAction>
+#include <QDebug>
+
+ExtLineEdit::ExtLineEdit(QWidget* parent)
+ : QLineEdit(parent)
+{
+ init();
+}
+
+ExtLineEdit::ExtLineEdit(const QString& text, QWidget *parent)
+ : QLineEdit(text, parent)
+{
+ init();
+}
+
+void ExtLineEdit::init()
+{
+ connect(this, &QLineEdit::textChanged, this, &ExtLineEdit::handleTextChanged);
+}
+
+void ExtLineEdit::updateMinSize()
+{
+ setMinimumSize(expandingMinWidth, 0);
+}
+
+int ExtLineEdit::getExpandingMaxWidth() const
+{
+ return expandingMaxWidth;
+}
+
+void ExtLineEdit::setExpandingMaxWidth(int value)
+{
+ expandingMaxWidth = value;
+ setMaximumWidth(value);
+}
+
+void ExtLineEdit::setClearButtonEnabled(bool enable)
+{
+ QLineEdit::setClearButtonEnabled(enable);
+ if (enable)
+ {
+ // This is a hack to get to know when QLineEdit's clear button is pressed.
+ // Unfortunately Qt 5.2 API doesn't provide such information,
+ // but we can find QAction responsible for it by its object name
+ // and handle its triggered() signal.
+ // This is not part of an official Qt's API and may be modified in any Qt version.
+ // Ugly, but works.
+ static const char* qtClearBtnActionName = "_q_qlineeditclearaction";
+ QAction *clearAction = findChild<QAction*>(qtClearBtnActionName);
+ if (!clearAction)
+ {
+ qWarning() << "Could not find 'clear action' in QLineEdit, so 'valueErased()' signal won't be emitted from ExtLineEdit.";
+ return;
+ }
+ connect(clearAction, SIGNAL(triggered()), this, SIGNAL(valueErased()));
+ }
+}
+
+
+bool ExtLineEdit::getExpanding() const
+{
+ return expanding;
+}
+
+void ExtLineEdit::setExpanding(bool value)
+{
+ expanding = value;
+ if (!expanding)
+ setFixedWidth(-1);
+ else
+ setFixedWidth(expandingMinWidth);
+}
+
+int ExtLineEdit::getExpandingMinWidth() const
+{
+ return expandingMinWidth;
+}
+
+void ExtLineEdit::setExpandingMinWidth(int value)
+{
+ expandingMinWidth = value;
+ updateMinSize();
+}
+
+void ExtLineEdit::handleTextChanged()
+{
+ QString txt = text();
+ if (!expanding)
+ return;
+
+ // Text width
+ int newWidth = fontMetrics().width(txt);
+
+ // Text margins
+ QMargins margins = textMargins();
+ newWidth += margins.left() + margins.right();
+
+ // Content margins
+ QMargins localContentsMargins = contentsMargins();
+ newWidth += localContentsMargins.left() + localContentsMargins.right();
+
+ // Frame
+ int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
+ newWidth += frameWidth * 2;
+
+ // Extra space
+ newWidth += expandingExtraSpace;
+
+ if (newWidth < expandingMinWidth)
+ newWidth = expandingMinWidth;
+ else if (expandingMaxWidth > 0 && newWidth > expandingMaxWidth)
+ newWidth = expandingMaxWidth;
+
+ setFixedWidth(newWidth);
+}
diff --git a/SQLiteStudio3/guiSQLiteStudio/common/extlineedit.h b/SQLiteStudio3/guiSQLiteStudio/common/extlineedit.h
new file mode 100644
index 0000000..b3bffbb
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/common/extlineedit.h
@@ -0,0 +1,45 @@
+#ifndef EXTLINEEDIT_H
+#define EXTLINEEDIT_H
+
+#include "guiSQLiteStudio_global.h"
+#include <QLineEdit>
+
+class QToolButton;
+
+class GUI_API_EXPORT ExtLineEdit : public QLineEdit
+{
+ Q_OBJECT
+
+ public:
+ explicit ExtLineEdit(QWidget *parent = 0);
+ explicit ExtLineEdit(const QString& text, QWidget *parent = 0);
+
+ bool getExpanding() const;
+ void setExpanding(bool value);
+
+ int getExpandingMinWidth() const;
+ void setExpandingMinWidth(int value);
+
+ int getExpandingMaxWidth() const;
+ void setExpandingMaxWidth(int value);
+
+ void setClearButtonEnabled(bool enable);
+
+ private:
+ void init();
+ void updateMinSize();
+
+ static const int expandingExtraSpace = 4; // QLineEdit has hardcoded horizontal margin of 2 for both sides
+
+ bool expanding = false;
+ int expandingMinWidth = 0;
+ int expandingMaxWidth = -1;
+
+ private slots:
+ void handleTextChanged();
+
+ signals:
+ void valueErased();
+};
+
+#endif // EXTLINEEDIT_H
diff --git a/SQLiteStudio3/guiSQLiteStudio/common/fileedit.cpp b/SQLiteStudio3/guiSQLiteStudio/common/fileedit.cpp
new file mode 100644
index 0000000..9f628ce
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/common/fileedit.cpp
@@ -0,0 +1,98 @@
+#include "fileedit.h"
+#include "iconmanager.h"
+#include "uiconfig.h"
+#include <QHBoxLayout>
+#include <QLineEdit>
+#include <QToolButton>
+#include <QFileDialog>
+
+FileEdit::FileEdit(QWidget *parent) :
+ QWidget(parent)
+{
+ setLayout(new QHBoxLayout());
+ layout()->setMargin(0);
+
+ lineEdit = new QLineEdit();
+ button = new QToolButton();
+ button->setIcon(ICONS.OPEN_FILE);
+ layout()->addWidget(lineEdit);
+ layout()->addWidget(button);
+
+ connect(button, SIGNAL(clicked()), this, SLOT(browse()));
+ connect(lineEdit, SIGNAL(textChanged(QString)), this, SLOT(lineTextChanged()));
+}
+
+QString FileEdit::getFile() const
+{
+ return file;
+}
+
+bool FileEdit::getSave() const
+{
+ return save;
+}
+
+QString FileEdit::getDialogTitle() const
+{
+ return dialogTitle;
+}
+
+QString FileEdit::getFilters() const
+{
+ return filters;
+}
+
+void FileEdit::browse()
+{
+ QString path;
+ QString dir = getFileDialogInitPath();
+ if (save)
+ path = QFileDialog::getSaveFileName(this, dialogTitle, dir, filters);
+ else
+ path = QFileDialog::getOpenFileName(this, dialogTitle, dir, filters);
+
+ if (path.isNull())
+ return;
+
+ setFile(path);
+ setFileDialogInitPathByFile(path);
+}
+
+void FileEdit::lineTextChanged()
+{
+ file = lineEdit->text();
+ emit fileChanged(file);
+}
+
+void FileEdit::setFile(QString arg)
+{
+ if (file != arg) {
+ file = arg;
+ lineEdit->setText(file);
+ emit fileChanged(arg);
+ }
+}
+
+void FileEdit::setSave(bool arg)
+{
+ if (save != arg) {
+ save = arg;
+ emit saveChanged(arg);
+ }
+}
+
+void FileEdit::setDialogTitle(QString arg)
+{
+ if (dialogTitle != arg) {
+ dialogTitle = arg;
+ emit dialogTitleChanged(arg);
+ }
+}
+
+void FileEdit::setFilters(QString arg)
+{
+ if (filters != arg) {
+ filters = arg;
+ emit filtersChanged(arg);
+ }
+}
diff --git a/SQLiteStudio3/guiSQLiteStudio/common/fileedit.h b/SQLiteStudio3/guiSQLiteStudio/common/fileedit.h
new file mode 100644
index 0000000..8cb62a6
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/common/fileedit.h
@@ -0,0 +1,52 @@
+#ifndef FILEEDIT_H
+#define FILEEDIT_H
+
+#include "guiSQLiteStudio_global.h"
+#include <QWidget>
+
+class QLineEdit;
+class QToolButton;
+
+class GUI_API_EXPORT FileEdit : public QWidget
+{
+ Q_OBJECT
+
+ Q_PROPERTY(QString file READ getFile WRITE setFile NOTIFY fileChanged)
+ Q_PROPERTY(bool save READ getSave WRITE setSave NOTIFY saveChanged)
+ Q_PROPERTY(QString dialogTitle READ getDialogTitle WRITE setDialogTitle NOTIFY dialogTitleChanged)
+ Q_PROPERTY(QString filters READ getFilters WRITE setFilters NOTIFY filtersChanged)
+
+ public:
+ explicit FileEdit(QWidget *parent = 0);
+
+ QString getFile() const;
+ bool getSave() const;
+ QString getDialogTitle() const;
+ QString getFilters() const;
+
+ private:
+ QString file;
+ bool save = false;
+ QString dialogTitle;
+ QString filters;
+ QLineEdit* lineEdit = nullptr;
+ QToolButton* button = nullptr;
+
+ signals:
+ void fileChanged(QString arg);
+ void saveChanged(bool arg);
+ void dialogTitleChanged(QString arg);
+ void filtersChanged(QString arg);
+
+ private slots:
+ void browse();
+ void lineTextChanged();
+
+ public slots:
+ void setFile(QString arg);
+ void setSave(bool arg);
+ void setDialogTitle(QString arg);
+ void setFilters(QString arg);
+};
+
+#endif // FILEEDIT_H
diff --git a/SQLiteStudio3/guiSQLiteStudio/common/fontedit.cpp b/SQLiteStudio3/guiSQLiteStudio/common/fontedit.cpp
new file mode 100644
index 0000000..a70122b
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/common/fontedit.cpp
@@ -0,0 +1,68 @@
+#include "fontedit.h"
+#include "ui_fontedit.h"
+#include "iconmanager.h"
+#include <QDebug>
+#include <QFontDialog>
+
+FontEdit::FontEdit(QWidget *parent) :
+ QWidget(parent),
+ ui(new Ui::FontEdit)
+{
+ init();
+}
+
+FontEdit::~FontEdit()
+{
+ delete ui;
+}
+
+QFont FontEdit::getFont() const
+{
+ return font;
+}
+
+void FontEdit::setFont(QFont arg)
+{
+ font = arg;
+ updateFont();
+}
+
+void FontEdit::changeEvent(QEvent *e)
+{
+ QWidget::changeEvent(e);
+ switch (e->type()) {
+ case QEvent::LanguageChange:
+ ui->retranslateUi(this);
+ break;
+ default:
+ break;
+ }
+}
+
+void FontEdit::init()
+{
+ ui->setupUi(this);
+ ui->button->setIcon(ICONS.FONT_BROWSE);
+ connect(ui->button, SIGNAL(clicked()), this, SLOT(browse()));
+ updateFont();
+}
+
+void FontEdit::updateFont()
+{
+ static const QString text = "%1, %2";
+ ui->label->setFont(font);
+ int size = font.pointSize() > -1 ? font.pointSize() : font.pixelSize();
+ ui->label->setText(text.arg(font.family()).arg(size));
+}
+
+void FontEdit::browse()
+{
+ bool ok;
+ QFont newFont = QFontDialog::getFont(&ok, ui->label->font(), this, tr("Choose font", "font configuration"));
+ if (!ok)
+ return;
+
+ font = newFont;
+ updateFont();
+ emit fontChanged(newFont);
+}
diff --git a/SQLiteStudio3/guiSQLiteStudio/common/fontedit.h b/SQLiteStudio3/guiSQLiteStudio/common/fontedit.h
new file mode 100644
index 0000000..e68463b
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/common/fontedit.h
@@ -0,0 +1,43 @@
+#ifndef FONTEDIT_H
+#define FONTEDIT_H
+
+#include "guiSQLiteStudio_global.h"
+#include <QWidget>
+
+namespace Ui {
+ class FontEdit;
+}
+
+class GUI_API_EXPORT FontEdit : public QWidget
+{
+ Q_OBJECT
+
+ Q_PROPERTY(QFont font READ getFont WRITE setFont NOTIFY fontChanged)
+
+ public:
+ explicit FontEdit(QWidget *parent = 0);
+ ~FontEdit();
+
+ QFont getFont() const;
+
+ public slots:
+ void setFont(QFont arg);
+
+ protected:
+ void changeEvent(QEvent *e);
+
+ private:
+ void init();
+ void updateFont();
+
+ Ui::FontEdit *ui = nullptr;
+ QFont font;
+
+ private slots:
+ void browse();
+
+ signals:
+ void fontChanged(const QFont& font);
+};
+
+#endif // FONTEDIT_H
diff --git a/SQLiteStudio3/guiSQLiteStudio/common/fontedit.ui b/SQLiteStudio3/guiSQLiteStudio/common/fontedit.ui
new file mode 100644
index 0000000..d8daa9f
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/common/fontedit.ui
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>FontEdit</class>
+ <widget class="QWidget" name="FontEdit">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>451</width>
+ <height>35</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Form</string>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>TextLabel</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="button">
+ <property name="text">
+ <string>...</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/SQLiteStudio3/guiSQLiteStudio/common/intvalidator.cpp b/SQLiteStudio3/guiSQLiteStudio/common/intvalidator.cpp
new file mode 100644
index 0000000..bfcf7b0
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/common/intvalidator.cpp
@@ -0,0 +1,38 @@
+#include "intvalidator.h"
+
+IntValidator::IntValidator(QObject *parent) :
+ QIntValidator(parent)
+{
+}
+
+IntValidator::IntValidator(int min, int max, QObject* parent)
+ : QIntValidator(min, max, parent)
+{
+}
+
+void IntValidator::fixup(QString& input) const
+{
+ QIntValidator::fixup(input);
+ if (input.trimmed().isEmpty())
+ input = QString::number(defaultValue);
+
+ bool ok;
+ int val = input.toInt(&ok);
+ if (!ok)
+ return;
+
+ if (val < bottom())
+ input = QString::number(bottom());
+ else if (val > top())
+ input = QString::number(top());
+}
+
+int IntValidator::getDefaultValue() const
+{
+ return defaultValue;
+}
+
+void IntValidator::setDefaultValue(int value)
+{
+ defaultValue = value;
+}
diff --git a/SQLiteStudio3/guiSQLiteStudio/common/intvalidator.h b/SQLiteStudio3/guiSQLiteStudio/common/intvalidator.h
new file mode 100644
index 0000000..5d2edaa
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/common/intvalidator.h
@@ -0,0 +1,25 @@
+#ifndef INTVALIDATOR_H
+#define INTVALIDATOR_H
+
+#include "guiSQLiteStudio_global.h"
+#include <QIntValidator>
+#include <QString>
+
+class GUI_API_EXPORT IntValidator : public QIntValidator
+{
+ Q_OBJECT
+
+ public:
+ explicit IntValidator(QObject *parent = 0);
+ explicit IntValidator(int min, int max, QObject *parent = 0);
+
+ void fixup(QString& input) const;
+
+ int getDefaultValue() const;
+ void setDefaultValue(int value);
+
+ private:
+ int defaultValue = 0;
+};
+
+#endif // INTVALIDATOR_H
diff --git a/SQLiteStudio3/guiSQLiteStudio/common/numericspinbox.cpp b/SQLiteStudio3/guiSQLiteStudio/common/numericspinbox.cpp
new file mode 100644
index 0000000..5a48033
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/common/numericspinbox.cpp
@@ -0,0 +1,152 @@
+#include "numericspinbox.h"
+#include "common/unused.h"
+#include <QLineEdit>
+#include <QVariant>
+#include <QDebug>
+
+NumericSpinBox::NumericSpinBox(QWidget *parent) :
+ QAbstractSpinBox(parent)
+{
+ connect(lineEdit(), &QLineEdit::textChanged, this, &NumericSpinBox::valueEdited);
+}
+
+void NumericSpinBox::stepBy(int steps)
+{
+ if (isReadOnly())
+ return;
+
+ switch (value.userType())
+ {
+ case QVariant::Double:
+ stepDoubleBy(steps);
+ break;
+ case QVariant::Int:
+ case QVariant::LongLong:
+ stepIntBy(steps);
+ break;
+ default:
+ break;
+ }
+ updateText();
+}
+
+QValidator::State NumericSpinBox::validate(QString& input, int& pos) const
+{
+ UNUSED(input);
+ UNUSED(pos);
+ emit modified();
+
+ if (strict)
+ return validateStrict(input, pos);
+
+ return QValidator::Acceptable;
+}
+
+void NumericSpinBox::stepIntBy(int steps)
+{
+ qint64 intVal = value.toLongLong();
+ intVal += steps;
+ value = intVal;
+ emit modified();
+}
+
+void NumericSpinBox::stepDoubleBy(int steps)
+{
+ double doubleVal = value.toDouble();
+ doubleVal += steps;
+ value = doubleVal;
+ emit modified();
+}
+
+void NumericSpinBox::updateText()
+{
+ lineEdit()->setText(value.toString());
+}
+
+QValidator::State NumericSpinBox::validateStrict(QString& input, int& pos) const
+{
+ if (input.trimmed().isEmpty())
+ return allowEmpty ? QValidator::Acceptable : QValidator::Invalid;
+
+ QIntValidator vint;
+ if (vint.validate(input, pos) != QValidator::Invalid)
+ return QValidator::Acceptable;
+
+ QDoubleValidator dint;
+ if (dint.validate(input, pos) != QValidator::Invalid)
+ return QValidator::Acceptable;
+
+ return QValidator::Invalid;
+}
+bool NumericSpinBox::getAllowEmpty() const
+{
+ return allowEmpty;
+}
+
+void NumericSpinBox::setAllowEmpty(bool value)
+{
+ allowEmpty = value;
+}
+
+
+bool NumericSpinBox::isStrict() const
+{
+ return strict;
+}
+
+void NumericSpinBox::setStrict(bool value, bool allowEmpty)
+{
+ strict = value;
+ this->allowEmpty = allowEmpty;
+}
+
+void NumericSpinBox::valueEdited(const QString& value)
+{
+ setValueInternal(value);
+}
+
+QAbstractSpinBox::StepEnabled NumericSpinBox::stepEnabled() const
+{
+ return StepDownEnabled|StepUpEnabled;
+}
+
+QVariant NumericSpinBox::getFixedVariant(const QVariant& value)
+{
+ bool ok;
+ qint64 longVal = value.toLongLong(&ok);
+ if (ok)
+ return longVal;
+
+ return value.toDouble();
+}
+
+void NumericSpinBox::setValueInternal(const QVariant& newValue)
+{
+ switch (newValue.userType())
+ {
+ case QVariant::String:
+ value = getFixedVariant(newValue);
+ break;
+ case QVariant::Double:
+ case QVariant::Int:
+ case QVariant::LongLong:
+ value = newValue;
+ break;
+ default:
+ value = 0;
+ }
+}
+
+QVariant NumericSpinBox::getValue() const
+{
+ return value;
+}
+
+void NumericSpinBox::setValue(const QVariant& newValue, bool nullAsZero)
+{
+ setValueInternal(newValue);
+ if (!nullAsZero && newValue.isNull())
+ value = newValue;
+
+ updateText();
+}
diff --git a/SQLiteStudio3/guiSQLiteStudio/common/numericspinbox.h b/SQLiteStudio3/guiSQLiteStudio/common/numericspinbox.h
new file mode 100644
index 0000000..79cd56c
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/common/numericspinbox.h
@@ -0,0 +1,57 @@
+#ifndef QNUMERICSPINBOX_H
+#define QNUMERICSPINBOX_H
+
+#include "guiSQLiteStudio_global.h"
+#include <QAbstractSpinBox>
+
+/**
+ * @brief The NumericSpinBox class
+ * This class implements a spinbox for numeric SQLite types.
+ * This includes integers, as well as decimals.
+ * User is also allowed to type in any text value (unless "strict" property is set),
+ * but once he uses "step up" or "step down", the text value
+ * gets replaced with 0.
+ * If strict propery is set, also the allowEmpty property starts to matter.
+ * Otherwise allowEmpty is ignored.
+ */
+class GUI_API_EXPORT NumericSpinBox : public QAbstractSpinBox
+{
+ Q_OBJECT
+ public:
+ explicit NumericSpinBox(QWidget *parent = 0);
+
+ void stepBy(int steps);
+ QValidator::State validate(QString& input, int& pos) const;
+
+ QVariant getValue() const;
+ void setValue(const QVariant& newValue, bool nullAsZero = true);
+
+ bool isStrict() const;
+ void setStrict(bool value, bool allowEmpty = true);
+
+ bool getAllowEmpty() const;
+ void setAllowEmpty(bool value);
+
+ protected:
+ StepEnabled stepEnabled() const;
+
+ private:
+ QVariant getFixedVariant(const QVariant& value);
+ void setValueInternal(const QVariant& newValue);
+ void stepIntBy(int steps);
+ void stepDoubleBy(int steps);
+ void updateText();
+ QValidator::State validateStrict(QString &input, int &pos) const;
+
+ QVariant value;
+ bool strict = false;
+ bool allowEmpty = true;
+
+ private slots:
+ void valueEdited(const QString& value);
+
+ signals:
+ void modified() const;
+};
+
+#endif // QNUMERICSPINBOX_H
diff --git a/SQLiteStudio3/guiSQLiteStudio/common/tablewidget.cpp b/SQLiteStudio3/guiSQLiteStudio/common/tablewidget.cpp
new file mode 100644
index 0000000..c9e1446
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/common/tablewidget.cpp
@@ -0,0 +1,49 @@
+#include "tablewidget.h"
+#include <QKeyEvent>
+#include <QApplication>
+#include <QClipboard>
+#include <QLabel>
+
+TableWidget::TableWidget(QWidget *parent) :
+ QTableWidget(parent)
+{
+}
+
+void TableWidget::keyPressEvent(QKeyEvent *event)
+{
+ if (event->matches(QKeySequence::Copy))
+ {
+ copy();
+ return;
+ }
+
+ QTableWidget::keyPressEvent(event);
+}
+
+void TableWidget::copy()
+{
+ QStringList strings;
+ QStringList cols;
+ for (int i = 0, total = rowCount(); i < total; ++i)
+ {
+ if (!item(i, 0)->isSelected())
+ continue;
+
+ for (int c = 1; c <= 2; c++)
+ {
+ if (cellWidget(i, c))
+ {
+ QLabel* l = dynamic_cast<QLabel*>(cellWidget(i, c));
+ if (l)
+ cols << l->text();
+ }
+ else
+ {
+ cols << item(i, c)->text();
+ }
+ }
+ strings << cols.join(" ");
+ }
+
+ QApplication::clipboard()->setText(strings.join("\n"));
+}
diff --git a/SQLiteStudio3/guiSQLiteStudio/common/tablewidget.h b/SQLiteStudio3/guiSQLiteStudio/common/tablewidget.h
new file mode 100644
index 0000000..26b424c
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/common/tablewidget.h
@@ -0,0 +1,23 @@
+#ifndef TABLEWIDGET_H
+#define TABLEWIDGET_H
+
+#include "guiSQLiteStudio_global.h"
+#include <QTableWidget>
+
+class QKeyEvent;
+
+class GUI_API_EXPORT TableWidget : public QTableWidget
+{
+ Q_OBJECT
+ public:
+ explicit TableWidget(QWidget *parent = 0);
+
+ protected:
+ void keyPressEvent(QKeyEvent *event);
+
+
+ public slots:
+ void copy();
+};
+
+#endif // TABLEWIDGET_H
diff --git a/SQLiteStudio3/guiSQLiteStudio/common/userinputfilter.cpp b/SQLiteStudio3/guiSQLiteStudio/common/userinputfilter.cpp
new file mode 100644
index 0000000..48ea46e
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/common/userinputfilter.cpp
@@ -0,0 +1,33 @@
+#include "userinputfilter.h"
+#include "common/unused.h"
+#include <QTimer>
+#include <QLineEdit>
+
+UserInputFilter::UserInputFilter(QLineEdit* lineEdit, QObject* filterHandler, const char* handlerSlot) :
+ QObject(lineEdit),
+ lineEdit(lineEdit)
+{
+ timer = new QTimer(this);
+ timer->setSingleShot(false);
+ timer->setInterval(200);
+ connect(lineEdit, SIGNAL(textChanged(QString)), this, SLOT(filterModified(QString)));
+ connect(timer, SIGNAL(timeout()), this, SLOT(applyFilter()));
+ connect(this, SIGNAL(applyFilter(QString)), filterHandler, handlerSlot);
+}
+
+void UserInputFilter::setDelay(int msecs)
+{
+ timer->setInterval(msecs);
+}
+
+void UserInputFilter::filterModified(const QString& newValue)
+{
+ UNUSED(newValue);
+ timer->start();
+}
+
+void UserInputFilter::applyFilter()
+{
+ timer->stop();
+ emit applyFilter(lineEdit->text());
+}
diff --git a/SQLiteStudio3/guiSQLiteStudio/common/userinputfilter.h b/SQLiteStudio3/guiSQLiteStudio/common/userinputfilter.h
new file mode 100644
index 0000000..1b6f7ee
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/common/userinputfilter.h
@@ -0,0 +1,31 @@
+#ifndef USERINPUTFILTER_H
+#define USERINPUTFILTER_H
+
+#include "guiSQLiteStudio_global.h"
+#include <QObject>
+
+class QTimer;
+class QLineEdit;
+
+class GUI_API_EXPORT UserInputFilter : public QObject
+{
+ Q_OBJECT
+
+ public:
+ UserInputFilter(QLineEdit* lineEdit, QObject* filterHandler, const char* handlerSlot);
+
+ void setDelay(int msecs);
+
+ private:
+ QTimer* timer = nullptr;
+ QLineEdit* lineEdit = nullptr;
+
+ private slots:
+ void filterModified(const QString& newValue);
+ void applyFilter();
+
+ signals:
+ void applyFilter(const QString& value);
+};
+
+#endif // USERINPUTFILTER_H
diff --git a/SQLiteStudio3/guiSQLiteStudio/common/verifiablewizardpage.cpp b/SQLiteStudio3/guiSQLiteStudio/common/verifiablewizardpage.cpp
new file mode 100644
index 0000000..596b000
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/common/verifiablewizardpage.cpp
@@ -0,0 +1,20 @@
+#include "verifiablewizardpage.h"
+
+VerifiableWizardPage::VerifiableWizardPage(QWidget *parent) :
+ QWizardPage(parent)
+{
+}
+
+bool VerifiableWizardPage::isComplete() const
+{
+ if (!validator)
+ return false;
+
+ return validator();
+}
+
+void VerifiableWizardPage::setValidator(const Validator& value)
+{
+ validator = value;
+}
+
diff --git a/SQLiteStudio3/guiSQLiteStudio/common/verifiablewizardpage.h b/SQLiteStudio3/guiSQLiteStudio/common/verifiablewizardpage.h
new file mode 100644
index 0000000..8912258
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/common/verifiablewizardpage.h
@@ -0,0 +1,22 @@
+#ifndef VERIFIABLEWIZARDPAGE_H
+#define VERIFIABLEWIZARDPAGE_H
+
+#include "guiSQLiteStudio_global.h"
+#include <QWizardPage>
+
+class GUI_API_EXPORT VerifiableWizardPage : public QWizardPage
+{
+ Q_OBJECT
+ public:
+ typedef std::function<bool()> Validator;
+
+ explicit VerifiableWizardPage(QWidget *parent = 0);
+
+ bool isComplete() const;
+ void setValidator(const Validator& value);
+
+ private:
+ Validator validator;
+};
+
+#endif // VERIFIABLEWIZARDPAGE_H
diff --git a/SQLiteStudio3/guiSQLiteStudio/common/widgetcover.cpp b/SQLiteStudio3/guiSQLiteStudio/common/widgetcover.cpp
new file mode 100644
index 0000000..7cc6a4e
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/common/widgetcover.cpp
@@ -0,0 +1,209 @@
+#include "widgetcover.h"
+#include "common/unused.h"
+#include <QVariantAnimation>
+#include <QDebug>
+#include <QGraphicsBlurEffect>
+#include <QPushButton>
+#include <QGridLayout>
+#include <QEvent>
+#include <QPushButton>
+#include <QProgressBar>
+
+WidgetCover::WidgetCover(QWidget *parent) :
+ QWidget(parent)
+{
+ init();
+}
+
+WidgetCover::WidgetCover(const QEasingCurve& easingCurve, QWidget* parent)
+ : QWidget(parent), easingCurve(easingCurve)
+{
+ init();
+}
+
+WidgetCover::~WidgetCover()
+{
+ interruptAction();
+}
+
+void WidgetCover::init()
+{
+ parentWidget()->installEventFilter(this);
+
+ setLayout(new QGridLayout(this));
+ layout()->setAlignment(Qt::AlignCenter);
+
+ container = new QWidget(this);
+ container->setVisible(false);
+ layout()->addWidget(container);
+
+ containerLayout = new QGridLayout(container);
+ containerLayout->setSizeConstraint(QLayout::SetFixedSize);
+
+ animation = new QVariantAnimation(this);
+ animation->setEasingCurve(easingCurve);
+ animation->setDuration(duration);
+ connect(animation, SIGNAL(valueChanged(QVariant)), this, SLOT(animationUpdate(QVariant)));
+ connect(animation, SIGNAL(finished()), this, SLOT(animationFinished()));
+
+ setAutoFillBackground(true);
+ resetBackground();
+ move(0, 0);
+ widgetResized();
+ hide();
+}
+
+void WidgetCover::interruptAction()
+{
+ setVisible(false);
+ animation->stop();
+}
+
+void WidgetCover::resetBackground()
+{
+ QPalette pal = palette();
+ pal.setBrush(QPalette::Window, QColor(0, 0, 0, 0));
+ setPalette(pal);
+}
+
+void WidgetCover::animationUpdate(const QVariant& value)
+{
+ QPalette pal = palette();
+ pal.setBrush(QPalette::Window, value.value<QColor>());
+ setPalette(pal);
+}
+
+void WidgetCover::animationFinished()
+{
+ switch (actionInProgres)
+ {
+ case Action::HIDING:
+ {
+ setVisible(false);
+ resetBackground();
+ break;
+ }
+ case Action::SHOWING:
+ {
+ container->setVisible(true);
+ break;
+ }
+ default:
+ break;
+ }
+
+ actionInProgres = Action::NONE;
+}
+
+void WidgetCover::widgetResized()
+{
+// qDebug() << parentWidget()->size();
+ setFixedSize(parentWidget()->size());
+}
+
+void WidgetCover::show()
+{
+ if (actionInProgres == Action::SHOWING)
+ return;
+
+ if (actionInProgres == Action::HIDING)
+ animation->stop();
+
+ actionInProgres = Action::SHOWING;
+
+ if (cancelButton)
+ cancelButton->setEnabled(true);
+
+ QPalette pal = palette();
+ animation->setStartValue(QVariant(pal.brush(QPalette::Window).color()));
+ animation->setEndValue(QVariant(QColor(0, 0, 0, transparency)));
+ setVisible(true);
+ container->setVisible(true);
+ animation->start();
+}
+
+void WidgetCover::hide()
+{
+ if (actionInProgres == Action::HIDING)
+ return;
+
+ if (actionInProgres == Action::SHOWING)
+ animation->stop();
+
+ actionInProgres = Action::HIDING;
+
+ container->setVisible(false);
+
+ QPalette pal = palette();
+ animation->setStartValue(QVariant(pal.brush(QPalette::Window).color()));
+ animation->setEndValue(QVariant(QColor(0, 0, 0, 0)));
+ animation->start();
+}
+
+QEasingCurve WidgetCover::getEasingCurve() const
+{
+ return easingCurve;
+}
+
+void WidgetCover::setEasingCurve(const QEasingCurve& value)
+{
+ easingCurve = value;
+ animation->setEasingCurve(easingCurve);
+}
+
+int WidgetCover::getDuration() const
+{
+ return duration;
+}
+
+void WidgetCover::setDuration(int value)
+{
+ duration = value;
+ animation->setDuration(duration);
+}
+
+int WidgetCover::getTransparency() const
+{
+ return transparency;
+}
+
+void WidgetCover::setTransparency(int value)
+{
+ if (value < 0)
+ value = 0;
+
+ if (value > 255)
+ value = 255;
+
+ transparency = value;
+}
+
+QGridLayout* WidgetCover::getContainerLayout()
+{
+ return containerLayout;
+}
+
+bool WidgetCover::eventFilter(QObject* obj, QEvent* e)
+{
+ UNUSED(obj);
+ if (e->type() == QEvent::Resize)
+ widgetResized();
+
+ return false;
+}
+
+void WidgetCover::initWithInterruptContainer(const QString& interruptButtonText)
+{
+ cancelButton = new QPushButton();
+ cancelButton->setText(interruptButtonText.isNull() ? tr("Interrupt") : interruptButtonText);
+
+ busyBar = new QProgressBar();
+ busyBar->setRange(0, 0);
+ busyBar->setTextVisible(false);
+
+ containerLayout->addWidget(busyBar, 0, 0);
+ containerLayout->addWidget(cancelButton, 1, 0);
+
+ connect(cancelButton, &QPushButton::clicked, [=]() {cancelButton->setEnabled(false);});
+ connect(cancelButton, SIGNAL(clicked()), this, SIGNAL(cancelClicked()));
+}
diff --git a/SQLiteStudio3/guiSQLiteStudio/common/widgetcover.h b/SQLiteStudio3/guiSQLiteStudio/common/widgetcover.h
new file mode 100644
index 0000000..d0ccef7
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/common/widgetcover.h
@@ -0,0 +1,72 @@
+#ifndef WIDGETCOVER_H
+#define WIDGETCOVER_H
+
+#include "guiSQLiteStudio_global.h"
+#include <QWidget>
+#include <QEasingCurve>
+#include <QVariant>
+
+class QVariantAnimation;
+class QGridLayout;
+class QPushButton;
+class QProgressBar;
+
+class GUI_API_EXPORT WidgetCover : public QWidget
+{
+ Q_OBJECT
+
+ public:
+ explicit WidgetCover(QWidget *parent);
+ explicit WidgetCover(const QEasingCurve& easingCurve, QWidget *parent);
+ virtual ~WidgetCover();
+
+ QEasingCurve getEasingCurve() const;
+ void setEasingCurve(const QEasingCurve& value);
+
+ int getDuration() const;
+ void setDuration(int value);
+
+ int getTransparency() const;
+ void setTransparency(int value);
+
+ QGridLayout* getContainerLayout();
+ bool eventFilter(QObject* obj, QEvent* e);
+
+ void initWithInterruptContainer(const QString& interruptButtonText = QString());
+
+ private:
+ enum class Action
+ {
+ SHOWING,
+ HIDING,
+ NONE
+ };
+
+ void init();
+ void interruptAction();
+ void resetBackground();
+ void widgetResized();
+
+ Action actionInProgres = Action::NONE;
+ QVariantAnimation* animation = nullptr;
+ QEasingCurve easingCurve = QEasingCurve::OutCubic;
+ int duration = 150;
+ int transparency = 128;
+ QWidget* container = nullptr;
+ QGridLayout* containerLayout = nullptr;
+ QPushButton* cancelButton = nullptr;
+ QProgressBar* busyBar = nullptr;
+
+ signals:
+ void cancelClicked();
+
+ private slots:
+ void animationUpdate(const QVariant& value);
+ void animationFinished();
+
+ public slots:
+ void show();
+ void hide();
+};
+
+#endif // WIDGETCOVER_H
diff --git a/SQLiteStudio3/guiSQLiteStudio/common/widgetstateindicator.cpp b/SQLiteStudio3/guiSQLiteStudio/common/widgetstateindicator.cpp
new file mode 100644
index 0000000..c6823e4
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/common/widgetstateindicator.cpp
@@ -0,0 +1,412 @@
+#include "widgetstateindicator.h"
+#include "iconmanager.h"
+#include "common/unused.h"
+#include "uiutils.h"
+#include "mdichild.h"
+#include <QLabel>
+#include <QCheckBox>
+#include <QGroupBox>
+#include <QEvent>
+#include <QHBoxLayout>
+#include <QGraphicsDropShadowEffect>
+#include <QSequentialAnimationGroup>
+#include <QPropertyAnimation>
+#include <QDebug>
+#include <QScrollArea>
+
+QHash<QWidget*,WidgetStateIndicator*> WidgetStateIndicator::instances;
+
+WidgetStateIndicator::WidgetStateIndicator(QWidget *widget) :
+ QObject(widget), widget(widget)
+{
+ widget->installEventFilter(this);
+ detectWindowParent();
+ initPositionMode();
+ initEffects();
+ initLabel();
+ updateMode();
+ updatePosition();
+ finalInit();
+}
+
+WidgetStateIndicator::~WidgetStateIndicator()
+{
+ instances.remove(widget);
+ widget->removeEventFilter(this);
+ windowParent->removeEventFilter(this);
+}
+
+void WidgetStateIndicator::initLabel()
+{
+ label = new QLabel();
+ label->setMargin(0);
+ label->installEventFilter(this);
+ label->setGraphicsEffect(highlightingEffect);
+
+ labelParent = new QWidget(windowParent);
+ labelParent->setLayout(new QHBoxLayout());
+ labelParent->layout()->setMargin(0);
+ labelParent->layout()->addWidget(label);
+ labelParent->setGraphicsEffect(glowEffect);
+}
+
+void WidgetStateIndicator::initEffects()
+{
+ initGlowEffects();
+ initHighlightingEffects();
+ initAnimations();
+}
+
+void WidgetStateIndicator::initGlowEffects()
+{
+ glowEffect = new QGraphicsDropShadowEffect();
+ glowEffect->setBlurRadius(10.0);
+ glowEffect->setOffset(0.0);
+ glowEffect->setEnabled(true);
+}
+
+void WidgetStateIndicator::initHighlightingEffects()
+{
+ highlightingEffect = new QGraphicsColorizeEffect();
+ highlightingEffect->setColor(Qt::white);
+ highlightingEffect->setStrength(0.3);
+ highlightingEffect->setEnabled(false);
+}
+
+void WidgetStateIndicator::initAnimations()
+{
+ animation = new QSequentialAnimationGroup(this);
+ animation->setLoopCount(-1);
+
+ // Animation of glow efect
+ QPropertyAnimation* varAnim = new QPropertyAnimation(glowEffect, "blurRadius");
+ varAnim->setStartValue(3.0);
+ varAnim->setEndValue(14.0);
+ varAnim->setEasingCurve(QEasingCurve::InOutCubic);
+ varAnim->setDuration(300);
+ animation->addAnimation(varAnim);
+
+ varAnim = new QPropertyAnimation(glowEffect, "blurRadius");
+ varAnim->setStartValue(14.0);
+ varAnim->setEndValue(3.0);
+ varAnim->setEasingCurve(QEasingCurve::InOutCubic);
+ varAnim->setDuration(300);
+ animation->addAnimation(varAnim);
+}
+
+void WidgetStateIndicator::initPositionMode()
+{
+ if (dynamic_cast<QGroupBox*>(widget))
+ positionMode = PositionMode::GROUP_BOX;
+ else if (dynamic_cast<QLabel*>(widget))
+ positionMode = PositionMode::LABEL;
+ else if (dynamic_cast<QCheckBox*>(widget))
+ positionMode = PositionMode::CHECK_BOX;
+}
+
+void WidgetStateIndicator::finalInit()
+{
+ label->setFixedSize(label->pixmap()->size());
+ labelParent->setFixedSize(label->pixmap()->size());
+ widgetVisible = widget->isVisible();
+ labelParent->setVisible(false);
+}
+
+void WidgetStateIndicator::setMessage(const QString& msg)
+{
+ static const QString paraTpl = QStringLiteral("<p>%1</p>");
+ if (msg.startsWith("<p>") && msg.endsWith("</p>"))
+ message = msg;
+ else
+ message = paraTpl.arg(msg);
+
+ label->setToolTip(message);
+ if (!msg.isNull())
+ label->setCursor(Qt::WhatsThisCursor);
+ else
+ label->unsetCursor();
+}
+
+void WidgetStateIndicator::clearMessage()
+{
+ message = QString::null;
+ label->setToolTip(message);
+ label->unsetCursor();
+}
+
+void WidgetStateIndicator::detectWindowParent()
+{
+ if (windowParent)
+ windowParent->removeEventFilter(this);
+
+ windowParent = findParentWindow(widget);
+ windowParent->installEventFilter(this);
+ if (labelParent)
+ labelParent->setParent(windowParent);
+}
+
+void WidgetStateIndicator::setMode(WidgetStateIndicator::Mode mode)
+{
+ this->mode = mode;
+ updateMode();
+}
+
+void WidgetStateIndicator::show(const QString& msg, bool animated)
+{
+ visibilityRequested = true;
+ setMessage(msg);
+ if (animated && animation->state() != QAbstractAnimation::Running)
+ animation->start();
+
+ updateVisibility();
+}
+
+void WidgetStateIndicator::hide()
+{
+ visibilityRequested = false;
+ clearMessage();
+ if (animation->state() == QAbstractAnimation::Running)
+ animation->stop();
+
+ updateVisibility();
+}
+
+void WidgetStateIndicator::setVisible(bool visible, const QString& msg)
+{
+ if (visible)
+ show(msg);
+ else
+ hide();
+}
+
+void WidgetStateIndicator::release()
+{
+ setVisible(false);
+ instances.remove(widget);
+ deleteLater();
+}
+
+void WidgetStateIndicator::info(const QString& msg, bool animated)
+{
+ setMode(Mode::INFO);
+ show(msg, animated);
+}
+
+void WidgetStateIndicator::warn(const QString& msg, bool animated)
+{
+ setMode(Mode::WARNING);
+ show(msg, animated);
+}
+
+void WidgetStateIndicator::error(const QString& msg, bool animated)
+{
+ setMode(Mode::ERROR);
+ show(msg, animated);
+}
+
+void WidgetStateIndicator::hint(const QString& msg, bool animated)
+{
+ setMode(Mode::HINT);
+ show(msg, animated);
+}
+
+bool WidgetStateIndicator::exists(QWidget* widget)
+{
+ return instances.contains(widget);
+}
+
+WidgetStateIndicator* WidgetStateIndicator::getInstance(QWidget* widget)
+{
+ if (!instances.contains(widget))
+ instances[widget] = new WidgetStateIndicator(widget);
+
+ return instances[widget];
+}
+
+bool WidgetStateIndicator::eventFilter(QObject* obj, QEvent* ev)
+{
+ if (obj == widget)
+ {
+ switch (ev->type())
+ {
+ case QEvent::Move:
+ case QEvent::Resize:
+ case QEvent::Scroll:
+ updatePosition();
+ break;
+ case QEvent::Show:
+ widgetVisible = true;
+ updateVisibility();
+ break;
+ case QEvent::Hide:
+ widgetVisible = false;
+ updateVisibility();
+ break;
+ case QEvent::EnabledChange:
+ updateVisibility();
+ break;
+ default:
+ break;
+ }
+ }
+ else if (obj == windowParent)
+ {
+ switch (ev->type())
+ {
+ case QEvent::ParentChange:
+ detectWindowParent();
+ break;
+ default:
+ break;
+ }
+ }
+ else if (obj == label)
+ {
+ if (ev->type() == QEvent::Enter)
+ highlightingEffect->setEnabled(true);
+ else if (ev->type() == QEvent::Leave)
+ highlightingEffect->setEnabled(false);
+ }
+
+ return false;
+}
+
+void WidgetStateIndicator::updateMode()
+{
+ switch (mode)
+ {
+ case Mode::ERROR:
+ label->setPixmap(ICONS.INDICATOR_ERROR);
+ glowEffect->setColor(Qt::red);
+ break;
+ case Mode::WARNING:
+ label->setPixmap(ICONS.INDICATOR_WARN);
+ glowEffect->setColor(Qt::darkYellow);
+ break;
+ case Mode::INFO:
+ label->setPixmap(ICONS.INDICATOR_INFO);
+ glowEffect->setColor(Qt::blue);
+ break;
+ case Mode::HINT:
+ label->setPixmap(ICONS.INDICATOR_HINT);
+ glowEffect->setColor(Qt::darkCyan);
+ break;
+ }
+}
+
+void WidgetStateIndicator::updatePosition()
+{
+ switch (positionMode)
+ {
+ case PositionMode::DEFAULT:
+ updatePositionDefault();
+ break;
+ case PositionMode::GROUP_BOX:
+ updatePositionGroupBox();
+ break;
+ case PositionMode::LABEL:
+ updatePositionLabel();
+ break;
+ case PositionMode::CHECK_BOX:
+ updatePositionCheckBox();
+ break;
+ }
+}
+
+void WidgetStateIndicator::updatePositionDefault()
+{
+ QPoint xy = widget->mapTo(windowParent, QPoint(0,0));
+ labelParent->move(xy + QPoint(-4, -4));
+}
+
+void WidgetStateIndicator::updatePositionGroupBox()
+{
+ QPoint xy = widget->mapTo(windowParent, QPoint(0,0));
+
+ QGroupBox* gb = dynamic_cast<QGroupBox*>(widget);
+
+ QFont font = gb->font();
+ QFontMetrics fm(font);
+ QString txt = gb->title();
+ QPoint diff(fm.width(txt), 2);
+
+ labelParent->move(xy + diff);
+}
+
+void WidgetStateIndicator::updatePositionLabel()
+{
+ updatePositionCheckBox(); // currently they're equal
+}
+
+void WidgetStateIndicator::updatePositionCheckBox()
+{
+ QPoint xy = widget->mapTo(windowParent, QPoint(0,0));
+ labelParent->move(xy + QPoint(-6, -2));
+}
+
+void WidgetStateIndicator::updateVisibility()
+{
+ if (shouldHide())
+ {
+ labelParent->setVisible(false);
+ }
+ else if (shouldShow())
+ {
+ updatePosition();
+ labelParent->setVisible(true);
+ }
+}
+
+bool WidgetStateIndicator::shouldHide()
+{
+ if (!labelParent->isVisible())
+ return false;
+
+ if (!widgetVisible)
+ return true;
+
+ if (!visibilityRequested)
+ return true;
+
+ if (!widget->isEnabled())
+ return true;
+
+ return false;
+}
+
+bool WidgetStateIndicator::shouldShow()
+{
+ if (labelParent->isVisible())
+ return false;
+
+ if (!widget->isEnabled())
+ return false;
+
+ if (!widgetVisible)
+ return false;
+
+ if (!visibilityRequested)
+ return false;
+
+ return true;
+}
+WidgetStateIndicator::PositionMode WidgetStateIndicator::getPositionMode() const
+{
+ return positionMode;
+}
+
+void WidgetStateIndicator::setPositionMode(const PositionMode& value)
+{
+ positionMode = value;
+}
+
+QWidget* WidgetStateIndicator::findParentWindow(QWidget* w)
+{
+ while (w && !w->windowFlags().testFlag(Qt::Window) && !dynamic_cast<QScrollArea*>(w) && !dynamic_cast<MdiChild*>(w))
+ w = w->parentWidget();
+
+ if (dynamic_cast<QScrollArea*>(w))
+ return dynamic_cast<QScrollArea*>(w)->widget();
+
+ return w;
+}
diff --git a/SQLiteStudio3/guiSQLiteStudio/common/widgetstateindicator.h b/SQLiteStudio3/guiSQLiteStudio/common/widgetstateindicator.h
new file mode 100644
index 0000000..eee49d5
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/common/widgetstateindicator.h
@@ -0,0 +1,99 @@
+#ifndef WIDGETSTATEINDICATOR_H
+#define WIDGETSTATEINDICATOR_H
+
+#include "guiSQLiteStudio_global.h"
+#include <QObject>
+
+class QLabel;
+class QGraphicsDropShadowEffect;
+class QGraphicsColorizeEffect;
+class QSequentialAnimationGroup;
+
+class GUI_API_EXPORT WidgetStateIndicator : public QObject
+{
+ Q_OBJECT
+ public:
+ enum class Mode
+ {
+ INFO,
+ ERROR,
+ WARNING,
+ HINT
+ };
+
+ enum class PositionMode
+ {
+ DEFAULT,
+ GROUP_BOX,
+ LABEL,
+ CHECK_BOX,
+ };
+
+ ~WidgetStateIndicator();
+
+ void setMode(Mode mode);
+ void show(const QString& msg = QString(), bool animated = true);
+ void hide();
+ void setVisible(bool visible, const QString& msg = QString());
+ void release();
+ void info(const QString& msg, bool animated = true);
+ void warn(const QString& msg, bool animated = true);
+ void error(const QString& msg, bool animated = true);
+ void hint(const QString& msg, bool animated = true);
+
+ static bool exists(QWidget* widget);
+ static WidgetStateIndicator* getInstance(QWidget* widget);
+
+ PositionMode getPositionMode() const;
+ void setPositionMode(const PositionMode& value);
+
+ protected:
+ bool eventFilter(QObject *obj, QEvent *ev);
+
+ private:
+ explicit WidgetStateIndicator(QWidget *widget);
+
+ void initLabel();
+ void initEffects();
+ void initGlowEffects();
+ void initHighlightingEffects();
+ void initAnimations();
+ void initPositionMode();
+ void finalInit();
+ void setMessage(const QString& msg);
+ void clearMessage();
+ void detectWindowParent();
+ QWidget* findParentWindow(QWidget* w);
+ bool shouldHide();
+ bool shouldShow();
+
+ QWidget* labelParent = nullptr;
+ QLabel* label = nullptr;
+ Mode mode = Mode::ERROR;
+ QWidget* widget = nullptr;
+ QString message;
+ QGraphicsColorizeEffect* highlightingEffect = nullptr;
+ QGraphicsDropShadowEffect* glowEffect = nullptr;
+ QSequentialAnimationGroup* animation = nullptr;
+ bool widgetVisible = false;
+ bool visibilityRequested = false;
+ QWidget* windowParent = nullptr;
+ PositionMode positionMode = PositionMode::DEFAULT;
+
+ static QHash<QWidget*,WidgetStateIndicator*> instances;
+
+ private slots:
+ void updateMode();
+ void updatePosition();
+ void updatePositionDefault();
+ void updatePositionGroupBox();
+ void updatePositionLabel();
+ void updatePositionCheckBox();
+ void updateVisibility();
+
+};
+
+#define INDICATOR(w) WidgetStateIndicator::getInstance(w)
+#define EXISTS_INDICATOR(w) WidgetStateIndicator::exists(w)
+
+#endif // WIDGETSTATEINDICATOR_H