diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index fed3c0d17..441a70d1a 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -1,70 +1,78 @@ add_definitions(-DKDE_DEFAULT_DEBUG_AREA=44020) set(kexicore_LIB_SRCS KexiGlobal.cpp kexi.cpp kexiaboutdata.cpp KexiMainWindowIface.cpp KexiStandardAction.cpp kexidbconnectionset.cpp kexiprojectset.cpp kexiactionproxy.cpp kexisharedactionhost.cpp kexiactioncategories.cpp kexiproject.cpp KexiWindow.cpp KexiWindowData.cpp KexiView.cpp kexipartmanager.cpp kexipartinfo.cpp kexipartitem.cpp kexipartbase.cpp kexipart.cpp kexipartguiclient.cpp kexiprojectdata.cpp KexiRecentProjects.cpp kexiinternalpart.cpp kexidragobjects.cpp kexistartupdata.cpp KexiCommandLineOptions.cpp kexiguimsghandler.cpp kexitextmsghandler.cpp kexidataiteminterface.cpp kexidbshortcutfile.cpp kexiblobbuffer.cpp #TODO KEXI3 kexistaticpart.cpp kexitabledesignerinterface.cpp kexisearchandreplaceiface.cpp kexitemplateloader.cpp KexiRecordNavigatorHandler.cpp KexiRecordNavigatorIface.cpp KexiSearchableModel.cpp KexiGroupButton.cpp #TODO belongs to widget/? ) kexi_add_library(kexicore SHARED ${kexicore_LIB_SRCS}) generate_export_header(kexicore) target_link_libraries(kexicore PUBLIC Qt5::Core Qt5::Gui Qt5::Widgets KF5::CoreAddons KF5::XmlGui kexiutils KDb KPropertyWidgets ) if(WIN32) target_include_directories(kexicore PUBLIC "${KDEWIN_INCLUDES}") target_link_libraries(kexicore PUBLIC ${KDEWIN_LIBRARIES}) endif() +<<<<<<< HEAD +======= +include_directories(${CMAKE_SOURCE_DIR}/src/kexiutils/style) + +set_target_properties(kexicore PROPERTIES + VERSION ${GENERIC_KEXI_LIB_VERSION} SOVERSION ${GENERIC_KEXI_LIB_SOVERSION} +) +>>>>>>> Move KexiStyle to kexiutils/style/, add KexiStyle::propertyPane(), add KexiPropertyPaneLineEditStyle install(TARGETS kexicore ${INSTALL_TARGETS_DEFAULT_ARGS}) #install(FILES kexihandler.desktop DESTINATION ${SERVICETYPES_INSTALL_DIR}) diff --git a/src/core/KexiView.cpp b/src/core/KexiView.cpp index 70fd4165a..99d3c1558 100644 --- a/src/core/KexiView.cpp +++ b/src/core/KexiView.cpp @@ -1,772 +1,772 @@ /* This file is part of the KDE project Copyright (C) 2004-2012 Jarosław Staniek This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KexiView.h" #include "KexiMainWindowIface.h" #include "KexiWindow.h" #include "kexiproject.h" #include "kexipartinfo.h" #include #include #include -#include +#include #include "KexiGroupButton.h" #include #include #include #include #include #include #include #include #include #include //! @internal Action for toggling view mode class KEXICORE_EXPORT KexiToggleViewModeAction : public QAction { Q_OBJECT public: //! Creates action for toggling to view mode @a mode. @a slot should have signature //! matching switchedTo(Kexi::ViewMode mode) signal. KexiToggleViewModeAction(Kexi::ViewMode mode, QObject* parent) : QAction( QIcon::fromTheme(Kexi::iconNameForViewMode(mode)), Kexi::nameForViewMode(mode, true/*withAmpersand*/), parent) { setCheckable(true); if (mode == Kexi::DataViewMode) { setObjectName("view_data_mode"); setToolTip(xi18n("Switch to data view")); setWhatsThis(xi18n("Switches to data view.")); } else if (mode == Kexi::DesignViewMode) { setObjectName("view_design_mode"); setToolTip(xi18n("Switch to design view")); setWhatsThis(xi18n("Switches to design view.")); } else if (mode == Kexi::TextViewMode) { setObjectName("view_text_mode"); setToolTip(xi18n("Switch to text view")); setWhatsThis(xi18n("Switches to text view.")); } else { qWarning() << "KexiToggleViewModeAction: invalid mode " << mode; } } }; //------------------------- class Q_DECL_HIDDEN KexiView::Private { public: explicit Private(KexiView *qq) : q(qq) , viewWidget(0) , parentView(0) , newlyAssignedID(-1) , viewMode(Kexi::NoViewMode) //unknown! , isDirty(false) , slotSwitchToViewModeInternalEnabled(true) , sortedProperties(false) , recentResultOfSwitchToViewModeInternal(true) , m_mainMenu(0) { } ~Private() { } void toggleViewModeButtonBack(Kexi::ViewMode mode) { QAction *a = toggleViewModeActions.value(mode); if (a) { slotSwitchToViewModeInternalEnabled = false; toggleViewModeActions.value(mode)->blockSignals(true); toggleViewModeButtons.value(mode)->blockSignals(true); toggleViewModeButtons.value(mode)->setChecked(viewMode == mode); toggleViewModeActions.value(mode)->blockSignals(false); toggleViewModeButtons.value(mode)->blockSignals(false); slotSwitchToViewModeInternalEnabled = true; } } QMenu* mainMenu() { if (m_mainMenu) { return m_mainMenu; } if (!window) { return 0; } KexiSmallToolButton* menuButton = new KexiSmallToolButton( QIcon(), window->part()->info()->name() + " ", topBarHWidget); menuButton->setToolTip(xi18n("Menu for the current window")); menuButton->setWhatsThis(xi18n("Shows menu for the current window.")); menuButton->setPopupMode(QToolButton::InstantPopup); topBarLyr->insertWidget(0, menuButton); m_mainMenu = new QMenu(menuButton); menuButton->setMenu(m_mainMenu); return m_mainMenu; } KexiGroupButton *addViewButton(KexiGroupButton::GroupPosition pos, Kexi::ViewMode mode, QWidget *parent, const char *slot, const QString &text, QHBoxLayout *btnLyr) { if (!window->supportsViewMode(mode)) { return 0; } QAction *a = new KexiToggleViewModeAction(mode, q); toggleViewModeActions.insert(mode, a); KexiGroupButton *btn = new KexiGroupButton(pos, parent); toggleViewModeButtons.insert(mode, btn); connect(btn, SIGNAL(toggled(bool)), q, slot); btn->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); btn->setText(text); btn->setIcon(a->icon()); QFont f(q->font()); f.setPointSizeF(KexiUtils::smallestReadableFont().pointSizeF()); btn->setFont(f); btn->setToolTip(a->toolTip()); btn->setWhatsThis(a->whatsThis()); btn->setCheckable(true); btn->setAutoRaise(true); btnLyr->addWidget(btn); return btn; } KexiView *q; QVBoxLayout* mainLyr; QWidget *topBarHWidget; KexiFlowLayout *topBarLyr; QHash toggleViewModeActions; QHash toggleViewModeButtons; KexiSmallToolButton* saveDesignButton; QString defaultIconName; KexiWindow *window; QWidget *viewWidget; KexiView *parentView; QPointer lastFocusedChildBeforeFocusOut; /*! Member set to newly assigned object's ID in storeNewData() and used in storeDataBlock(). This is needed because usually, storeDataBlock() can be called from storeNewData() and in this case window has not yet assigned valid identifier (it has just negative temp. number). \sa KexiWindow::id() */ int newlyAssignedID; /*! Mode for this view. Initialized by KexiWindow::switchToViewMode(). Can be useful when single class is used for more than one view (e.g. KexiDBForm). */ Kexi::ViewMode viewMode; QList children; /*! View-level actions (not shared), owned by the view. */ QList viewActions; QHash viewActionsHash; /*! Main-meny-level actions (not shared), owned by the view. */ QList mainMenuActions; QHash mainMenuActionsHash; bool isDirty; //! Used in slotSwitchToViewModeInternal() to disabling it bool slotSwitchToViewModeInternalEnabled; bool sortedProperties; //! Used in slotSwitchToViewModeInternal() to disabling d->window->switchToViewModeInternal(mode) call. //! Needed because there is another slotSwitchToViewModeInternal() calls if d->window->switchToViewModeInternal(mode) //! did not succeed, so for the second time we block this call. tristate recentResultOfSwitchToViewModeInternal; private: QMenu* m_mainMenu; }; //---------------------------------------------------------- KexiView::KexiView(QWidget *parent) : QWidget(parent) , KexiActionProxy(this) , d(new Private(this)) { QWidget *wi = this; while ((wi = wi->parentWidget()) && !qobject_cast(wi)) { } d->window = (wi && qobject_cast(wi)) ? qobject_cast(wi) : nullptr; if (d->window) { //init view mode number for this view (obtained from window where this view is created) if (d->window->supportsViewMode(d->window->creatingViewsMode())) d->viewMode = d->window->creatingViewsMode(); } setObjectName( QString("%1_for_%2_object") .arg(Kexi::nameForViewMode(d->viewMode).replace(' ', '_')) .arg(d->window ? d->window->partItem()->name() : QString("??"))); installEventFilter(this); d->mainLyr = new QVBoxLayout(this); d->mainLyr->setContentsMargins(0, 0, 0, 0); if (qobject_cast(parentWidget())) { d->topBarHWidget = new QWidget(this); d->topBarHWidget->setFont(KexiUtils::smallestReadableFont()); d->mainLyr->addWidget(d->topBarHWidget); QHBoxLayout *topBarHLyr = new QHBoxLayout(d->topBarHWidget); //needed unless KexiFlowLayout properly handles contents margins topBarHLyr->setContentsMargins(0, 0, 0, 0); topBarHLyr->addSpacing(KexiUtils::spacingHint() / 2); d->topBarLyr = new KexiFlowLayout(topBarHLyr, 0, 2); const bool userMode = KexiMainWindowIface::global()->userMode(); if (userMode || d->window->supportedViewModes() == Kexi::DataViewMode || d->window->supportedViewModes() == Kexi::DesignViewMode || d->window->supportedViewModes() == Kexi::TextViewMode) { // nothing to do: only single view mode supported } else { createViewModeToggleButtons(); } (void)d->mainMenu(); if (d->viewMode == Kexi::DesignViewMode || d->viewMode == Kexi::TextViewMode) { QAction *a = sharedAction("project_save"); d->saveDesignButton = new KexiSmallToolButton(a, d->topBarHWidget); d->saveDesignButton->setText(xi18n("Save")); d->saveDesignButton->setToolTip(xi18n("Save current design")); d->saveDesignButton->setWhatsThis(xi18n("Saves changes made to the current design.")); d->topBarLyr->addWidget(d->saveDesignButton); a = sharedAction("project_saveas"); d->mainMenu()->addAction(a); } else { d->saveDesignButton = 0; } } else { // no toolbar d->saveDesignButton = 0; d->topBarHWidget = 0; d->topBarLyr = 0; } } KexiView::~KexiView() { delete d; } KexiWindow* KexiView::window() const { return d->window; } bool KexiView::isDirty() const { return d->isDirty; } bool KexiView::isDataEditingInProgress() const { return false; } tristate KexiView::saveDataChanges() { return true; } tristate KexiView::cancelDataChanges() { return true; } Kexi::ViewMode KexiView::viewMode() const { return d->viewMode; } KexiPart::Part* KexiView::part() const { return d->window ? d->window->part() : 0; } tristate KexiView::beforeSwitchTo(Kexi::ViewMode mode, bool *dontStore) { Q_UNUSED(mode); Q_UNUSED(dontStore); return true; } tristate KexiView::afterSwitchFrom(Kexi::ViewMode mode) { Q_UNUSED(mode); return true; } QSize KexiView::preferredSizeHint(const QSize& otherSize) { return otherSize; } void KexiView::closeEvent(QCloseEvent * e) { bool cancel = false; emit closing(&cancel); if (cancel) { e->ignore(); return; } QWidget::closeEvent(e); } KPropertySet *KexiView::propertySet() { return 0; } void KexiView::propertySetSwitched() { if (window()) { KexiMainWindowIface::global()->propertySetSwitched(window(), false/*force*/, true/*preservePrevSelection*/, d->sortedProperties); } } void KexiView::propertySetReloaded(bool preservePrevSelection, const QByteArray& propertyToSelect) { if (window()) KexiMainWindowIface::global()->propertySetSwitched( window(), true, preservePrevSelection, d->sortedProperties, propertyToSelect); } void KexiView::setDirty(bool set) { const bool changed = (d->isDirty != set); d->isDirty = set; d->isDirty = isDirty(); if (d->saveDesignButton) d->saveDesignButton->setEnabled(d->isDirty); if (d->parentView) { d->parentView->setDirty(d->isDirty); } else { if (changed && d->window) d->window->dirtyChanged(this); } } void KexiView::setDirty() { setDirty(true); } KDbObject* KexiView::storeNewData(const KDbObject& object, KexiView::StoreNewDataOptions options, bool *cancel) { Q_ASSERT(cancel); Q_UNUSED(options) Q_UNUSED(cancel) QScopedPointer newObject(new KDbObject); *newObject = object; KDbConnection *conn = KexiMainWindowIface::global()->project()->dbConnection(); if (!conn->storeNewObjectData(newObject.data()) || !conn->removeDataBlock(newObject->id()) // for sanity || !KexiMainWindowIface::global()->project()->removeUserDataBlock(newObject->id()) // for sanity ) { return 0; } d->newlyAssignedID = newObject->id(); return newObject.take(); } KDbObject* KexiView::copyData(const KDbObject& object, KexiView::StoreNewDataOptions options, bool *cancel) { Q_ASSERT(cancel); Q_UNUSED(options) Q_UNUSED(cancel) QScopedPointer newObject(new KDbObject); *newObject = object; KDbConnection *conn = KexiMainWindowIface::global()->project()->dbConnection(); if (!conn->storeNewObjectData(newObject.data()) || !conn->copyDataBlock(d->window->id(), newObject->id()) || !KexiMainWindowIface::global()->project()->copyUserDataBlock(d->window->id(), newObject->id()) ) { return 0; } d->newlyAssignedID = newObject->id(); return newObject.take(); } tristate KexiView::storeData(bool dontAsk) { Q_UNUSED(dontAsk); if (!d->window || !d->window->schemaObject()) return false; if (!KexiMainWindowIface::global()->project() ->dbConnection()->storeObjectData(d->window->schemaObject())) { return false; } setDirty(false); return true; } bool KexiView::loadDataBlock(QString *dataString, const QString& dataID, bool canBeEmpty) { if (!d->window) return false; const tristate res = KexiMainWindowIface::global()->project()->dbConnection() ->loadDataBlock(d->window->id(), dataString, dataID); if (canBeEmpty && ~res) { dataString->clear(); return true; } return res == true; } bool KexiView::storeDataBlock(const QString &dataString, const QString &dataID) { if (!d->window) return false; int effectiveID; if (d->newlyAssignedID > 0) {//ID not yet stored within window, but we've got ID here effectiveID = d->newlyAssignedID; d->newlyAssignedID = -1; } else effectiveID = d->window->id(); return effectiveID > 0 && KexiMainWindowIface::global()->project()->dbConnection()->storeDataBlock( effectiveID, dataString, dataID); } bool KexiView::removeDataBlock(const QString& dataID) { if (!d->window) return false; return KexiMainWindowIface::global()->project()->dbConnection() ->removeDataBlock(d->window->id(), dataID); } bool KexiView::eventFilter(QObject *o, QEvent *e) { if (e->type() == QEvent::FocusIn || e->type() == QEvent::FocusOut) { // qDebug() << "this=[" << o->metaObject()->className() // << objectName() << "] o=[" << o->metaObject()->className() << o->objectName() // << "] focusWidget=[" << (qApp->focusWidget() ? qApp->focusWidget()->metaObject()->className() : QString()) // << (qApp->focusWidget() ? qApp->focusWidget()->objectName() : QString()) << "] ev.type=" << e->type(); if (KDbUtils::hasParent(this, o)) { if (e->type() == QEvent::FocusOut && qApp->focusWidget() && !KDbUtils::hasParent(this, qApp->focusWidget())) { //focus out: when currently focused widget is not a parent of this view emit focus(false); } else if (e->type() == QEvent::FocusIn) { emit focus(true); } if (e->type() == QEvent::FocusOut) { // qDebug() << focusWidget()->className() << " " << focusWidget()->name(); // qDebug() << o->className() << " " << o->name(); KexiView *v = KDbUtils::findParent(o); if (v) { while (v->d->parentView) v = v->d->parentView; if (KDbUtils::hasParent(this, static_cast(v->focusWidget()))) v->d->lastFocusedChildBeforeFocusOut = static_cast(v->focusWidget()); } } if (e->type() == QEvent::FocusIn && m_actionProxyParent) { m_actionProxyParent->m_focusedChild = this; } } } return false; } void KexiView::setViewWidget(QWidget* w, bool focusProxy) { if (d->viewWidget == w) return; if (d->viewWidget) { d->viewWidget->removeEventFilter(this); d->mainLyr->removeWidget(d->viewWidget); } d->viewWidget = w; if (d->viewWidget) { QFrame *frameWidget = qobject_cast(d->viewWidget); KexiStyle::setupFrame(frameWidget); d->viewWidget->setParent(this); d->mainLyr->addWidget(d->viewWidget, 1); d->viewWidget->installEventFilter(this); //} if (focusProxy) setFocusProxy(d->viewWidget); //js: ok? } } void KexiView::addChildView(KexiView* childView) { d->children.append(childView); addActionProxyChild(childView); childView->d->parentView = this; childView->installEventFilter(this); } void KexiView::removeView(Kexi::ViewMode mode) { window()->removeView(mode); } void KexiView::setFocus() { if (!d->lastFocusedChildBeforeFocusOut.isNull()) { // qDebug() << "FOCUS: " << d->lastFocusedChildBeforeFocusOut->className() << " " << d->lastFocusedChildBeforeFocusOut->name(); QWidget *w = d->lastFocusedChildBeforeFocusOut; d->lastFocusedChildBeforeFocusOut = 0; w->setFocus(); } else { setFocusInternal(); } KexiMainWindowIface::global()->invalidateSharedActions(this); } QAction* KexiView::sharedAction(const QString& action_name) { if (part()) { KActionCollection *ac; if ((ac = part()->actionCollectionForMode(viewMode()))) { QAction* a = ac->action(action_name); if (a) return a; } } return KexiActionProxy::sharedAction(action_name); } void KexiView::setAvailable(const QString& action_name, bool set) { if (part()) { KActionCollection *ac; QAction* a; if ((ac = part()->actionCollectionForMode(viewMode())) && (a = ac->action(action_name))) { a->setEnabled(set); } } KexiActionProxy::setAvailable(action_name, set); } void KexiView::updateActions(bool activated) { //do nothing here //do the same for children :) foreach(KexiView* view, d->children) { view->updateActions(activated); } } void KexiView::setViewActions(const QList& actions) { d->viewActions = actions; d->viewActionsHash.clear(); foreach(QAction* action, d->viewActions) { d->viewActionsHash.insert(action->objectName().toLatin1(), action); } } void KexiView::setMainMenuActions(const QList& actions) { d->mainMenuActions = actions; d->mainMenuActionsHash.clear(); foreach(QAction* action, d->mainMenuActions) { d->mainMenuActionsHash.insert(action->objectName().toLatin1(), action); } } QAction* KexiView::viewAction(const char* name) const { return d->viewActionsHash.value(name); } QList KexiView::viewActions() const { return d->viewActions; } void KexiView::toggleViewModeButtonBack() { d->toggleViewModeButtonBack(Kexi::DataViewMode); d->toggleViewModeButtonBack(Kexi::DesignViewMode); d->toggleViewModeButtonBack(Kexi::TextViewMode); } void KexiView::createViewModeToggleButtons() { d->topBarLyr->addSpacing(KexiUtils::spacingHint()); QWidget *btnCont = new QWidget(d->topBarHWidget); QHBoxLayout *btnLyr = new QHBoxLayout; btnLyr->setSpacing(0); btnLyr->setContentsMargins(0, 0, 0, 0); btnCont->setLayout(btnLyr); d->topBarLyr->addWidget(btnCont); d->topBarLyr->addSpacing(KexiUtils::spacingHint()); d->addViewButton(KexiGroupButton::GroupLeft, Kexi::DataViewMode, btnCont, SLOT(slotSwitchToDataViewModeInternal(bool)), xi18n("Data"), btnLyr); d->addViewButton(d->window->supportsViewMode(Kexi::TextViewMode) ? KexiGroupButton::GroupCenter : KexiGroupButton::GroupRight, Kexi::DesignViewMode, btnCont, SLOT(slotSwitchToDesignViewModeInternal(bool)), xi18n("Design"), btnLyr); KexiGroupButton *btn = d->addViewButton(KexiGroupButton::GroupRight, Kexi::TextViewMode, btnCont, SLOT(slotSwitchToTextViewModeInternal(bool)), QString(), btnLyr); if (btn) { QString customTextViewModeCaption(d->window->internalPropertyValue("textViewModeCaption").toString()); if (customTextViewModeCaption.isEmpty()) { QAction *a = d->toggleViewModeActions.value(Kexi::TextViewMode); btn->setText(a->text()); } else { btn->setText(customTextViewModeCaption); } } toggleViewModeButtonBack(); } void KexiView::slotSwitchToDataViewModeInternal(bool) { slotSwitchToViewModeInternal(Kexi::DataViewMode); } void KexiView::slotSwitchToDesignViewModeInternal(bool) { slotSwitchToViewModeInternal(Kexi::DesignViewMode); } void KexiView::slotSwitchToTextViewModeInternal(bool) { slotSwitchToViewModeInternal(Kexi::TextViewMode); } void KexiView::slotSwitchToViewModeInternal(Kexi::ViewMode mode) { if (!d->slotSwitchToViewModeInternalEnabled) return; if (d->recentResultOfSwitchToViewModeInternal != true) d->recentResultOfSwitchToViewModeInternal = true; else d->recentResultOfSwitchToViewModeInternal = d->window->switchToViewModeInternal(mode); if (d->viewMode != mode) { //switch back visually KexiGroupButton *b = d->toggleViewModeButtons.value(mode); d->slotSwitchToViewModeInternalEnabled = false; b->setChecked(false); d->slotSwitchToViewModeInternalEnabled = true; } } void KexiView::initViewActions() { if (!d->topBarLyr) return; if (!d->viewActions.isEmpty() && d->saveDesignButton) { d->topBarLyr->addWidget(new KexiToolBarSeparator(d->topBarHWidget)); } foreach(QAction* action, d->viewActions) { if (action->isSeparator()) { d->topBarLyr->addWidget(new KexiToolBarSeparator(d->topBarHWidget)); } else { KexiSmallToolButton *btn = new KexiSmallToolButton(action, d->topBarHWidget); btn->setText(action->text()); btn->setToolTip(action->toolTip()); btn->setWhatsThis(action->whatsThis()); if (action->dynamicPropertyNames().contains("iconOnly") && action->property("iconOnly").toBool() ) { btn->setToolButtonStyle(Qt::ToolButtonIconOnly); } d->topBarLyr->addWidget(btn); } } } void KexiView::initMainMenuActions() { if (!d->topBarLyr) return; if (d->mainMenuActions.isEmpty()) { return; } d->mainMenu()->clear(); foreach(QAction* action, d->mainMenuActions) { d->mainMenu()->addAction(action); } } void KexiView::setSortedProperties(bool set) { d->sortedProperties = set; } bool KexiView::saveSettings() { return true; } QString KexiView::defaultIconName() const { return d->defaultIconName; } void KexiView::setDefaultIconName(const QString& iconName) { d->defaultIconName = iconName; } QList KexiView::currentParameters() const { return QList(); } #include "KexiView.moc" diff --git a/src/core/kexipartinfo.cpp b/src/core/kexipartinfo.cpp index f09390b67..f6a1bffc7 100644 --- a/src/core/kexipartinfo.cpp +++ b/src/core/kexipartinfo.cpp @@ -1,224 +1,224 @@ /* This file is part of the KDE project Copyright (C) 2003 Lucijan Busch Copyright (C) 2003-2016 Jarosław Staniek This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kexipartinfo_p.h" #include "kexipartmanager.h" #include "KexiMainWindowIface.h" -#include +#include #include #include #include #include #include #include using namespace KexiPart; static bool isTrue(KPluginMetaData *metaData, const char* fieldName, bool defaultValue = false) { QString s = metaData->value(QLatin1String(fieldName)); if (s.isEmpty()) { return defaultValue; } return 0 == s.compare(QLatin1String("true"), Qt::CaseInsensitive); } Info::Private::Private(Info *info, const QJsonObject &rootObject) : untranslatedGroupName(info->value("X-Kexi-GroupName")) , typeName(info->value("X-Kexi-TypeName")) , supportedViewModes(0) , supportedUserViewModes(0) , isVisibleInNavigator(isTrue(info, "X-Kexi-VisibleInProjectNavigator")) , isDataExportSupported(isTrue(info, "X-Kexi-SupportsDataExport")) , isPrintingSupported(isTrue(info, "X-Kexi-SupportsPrinting")) , isExecuteSupported(isTrue(info, "X-Kexi-SupportsExecution")) , isPropertyEditorAlwaysVisibleInDesignMode( isTrue(info, "X-Kexi-PropertyEditorAlwaysVisibleInDesignMode", true)) { groupName = info->readTranslatedString(rootObject, "X-Kexi-GroupName", untranslatedGroupName); const QStringList serviceTypes = info->serviceTypes(); if (serviceTypes.contains("Kexi/Viewer")) { supportedViewModes |= Kexi::DataViewMode; } if (serviceTypes.contains("Kexi/Designer")) { supportedViewModes |= Kexi::DesignViewMode; } if (serviceTypes.contains("Kexi/Editor")) { supportedViewModes |= Kexi::TextViewMode; } const QJsonArray userServiceTypes = rootObject.value("X-Kexi-ServiceTypesInUserMode").toArray(); if (userServiceTypes.contains(QJsonValue("Kexi/Viewer"))) { supportedUserViewModes |= Kexi::DataViewMode; } if (userServiceTypes.contains(QJsonValue("Kexi/Designer"))) { supportedUserViewModes |= Kexi::DesignViewMode; } if (userServiceTypes.contains(QJsonValue("Kexi/Editor"))) { supportedUserViewModes |= Kexi::TextViewMode; } } Info::Private::Private() : supportedViewModes(0) , supportedUserViewModes(0) , isVisibleInNavigator(false) , isDataExportSupported(false) , isPrintingSupported(false) , isExecuteSupported(false) , isPropertyEditorAlwaysVisibleInDesignMode(true) { } //------------------------------ /*! \return "create" QAction's name for part defined by \a info. The result is like "tablepart_create". */ static QString nameForCreateAction(const Info& info) { return info.id() + ".create"; } //------------------------------ KexiNewObjectAction::KexiNewObjectAction(Info* info, QObject *parent) : QAction(info->icon(), info->name() + "...", parent) , m_info(info) { setObjectName(nameForCreateAction(*m_info)); // default tooltip and what's this setToolTip(xi18nc("@info", "Create new object of type %1", m_info->name().toLower())); setWhatsThis(xi18nc("@info", "Creates new object of type %1", m_info->name().toLower())); connect(this, SIGNAL(triggered()), this, SLOT(slotTriggered())); connect(this, SIGNAL(newObjectRequested(KexiPart::Info*)), &Kexi::partManager(), SIGNAL(newObjectRequested(KexiPart::Info*))); } void KexiNewObjectAction::slotTriggered() { emit newObjectRequested(m_info); } //------------------------------ //Info::Info(const QString &id, const QString &iconName, // const QString &objectName) // : KPluginMetaData(), d(new Private) //{ // d->iconName = iconName; // d->objectName = objectName; //} Info::Info(const QPluginLoader &loader) : KexiPluginMetaData(loader), d(new Private(this, rootObject())) { } Info::~Info() { delete d; } QString Info::typeName() const { return d->typeName; } QString Info::groupName() const { return d->groupName; } QIcon Info::icon() const { return QIcon::fromTheme(iconName()); } QIcon Info::darkIcon() const { if (d->icon.isNull()) { d->icon = KexiStyle::darkIcon(iconName()); } return d->icon; } QString Info::untranslatedGroupName() const { return d->untranslatedGroupName; } Kexi::ViewModes Info::supportedViewModes() const { return d->supportedViewModes; } Kexi::ViewModes Info::supportedUserViewModes() const { return d->supportedUserViewModes; } bool Info::isVisibleInNavigator() const { return d->isVisibleInNavigator; } bool Info::isDataExportSupported() const { return d->isDataExportSupported; } bool Info::isPrintingSupported() const { return d->isPrintingSupported; } bool Info::isExecuteSupported() const { return d->isExecuteSupported; } bool Info::isPropertyEditorAlwaysVisibleInDesignMode() const { return d->isPropertyEditorAlwaysVisibleInDesignMode; } QAction* Info::newObjectAction() { if (!isVisibleInNavigator()) { return 0; } if (!KexiMainWindowIface::global() || !KexiMainWindowIface::global()->actionCollection()) { qWarning() << "Missing Kexi's global action collection"; return 0; } QAction *act = KexiMainWindowIface::global()->actionCollection()->action(nameForCreateAction(*this)); if (!act) { act = new KexiNewObjectAction(this, KexiMainWindowIface::global()->actionCollection()); KexiMainWindowIface::global()->actionCollection()->addAction(act->objectName(), act); } return act; } diff --git a/src/kexiutils/CMakeLists.txt b/src/kexiutils/CMakeLists.txt index 8176daa33..a91287e4d 100644 --- a/src/kexiutils/CMakeLists.txt +++ b/src/kexiutils/CMakeLists.txt @@ -1,96 +1,100 @@ add_definitions(-DKDE_DEFAULT_DEBUG_AREA=44024) include_directories(completer) set(kexiutils_LIB_SRCS utils.cpp FontSettings_p.cpp InternalPropertyMap.cpp SmallToolButton.cpp KexiCommandLinkButton.cpp FlowLayout.cpp transliteration_table.cpp kmessagewidget.cpp KexiContextMessage.cpp KexiTitleLabel.cpp KexiLinkWidget.cpp KexiLinkButton.cpp KexiCloseButton.cpp KexiAssistantPage.cpp KexiAssistantWidget.cpp KexiAnimatedLayout.cpp KexiCategorizedView.cpp KexiTester.cpp KexiJsonTrader.cpp KexiPushButton.cpp KexiFadeWidgetEffect.cpp KexiPluginMetaData.cpp - KexiStyle.cpp completer/KexiCompleter.cpp + + style/KexiStyle.cpp + style/KexiPropertyPaneLineEditStyle.cpp ) if (KEXI_MOBILE) else () if (KEXI_DEBUG_GUI) list(APPEND kexiutils_LIB_SRCS debuggui.cpp ) endif () endif () if(BUILD_TESTING) list(APPEND kexiutils_LIB_SRCS KexiTestHandler.cpp ) endif() kexi_add_library(kexiutils SHARED ${kexiutils_LIB_SRCS}) set(kexiutils_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR} ) target_include_directories(kexiutils PUBLIC "$" ) target_link_libraries(kexiutils PUBLIC KF5::KIOWidgets #for KRun... KF5::IconThemes KF5::WidgetsAddons KF5::ConfigWidgets # KStandardAction KColorScheme + KF5::GuiAddons # KColorUtils KF5::I18n KF5::ItemViews # KCategorizedView KCategoryDrawer KDb + KPropertyWidgets PRIVATE Qt5::Svg ) #target_link_libraries(kexiutils LINK_INTERFACE_LIBRARIES KF5::KIOWidgets) if(BUILD_TESTING) target_link_libraries(kexiutils PRIVATE Qt5::Test ) endif() generate_export_header(kexiutils) install(TARGETS kexiutils ${INSTALL_TARGETS_DEFAULT_ARGS}) if(FALSE) # TODO: install when we move to independent place install( FILES tristate.h utils.h kexiutils_export.h kexiutils_global.h InternalPropertyMap.h SmallToolButton.h FlowLayout.h kmessagewidget.h KexiContextMessage.h KexiTitleLabel.h KexiAssistantPage.h KexiAssistantWidget.h KexiAnimatedLayout.h DESTINATION ${INCLUDE_INSTALL_DIR}/kexiutils COMPONENT Devel) endif() if(BUILD_TESTING) add_subdirectory(tests) endif() diff --git a/src/kexiutils/style/KexiPropertyPaneLineEditStyle.cpp b/src/kexiutils/style/KexiPropertyPaneLineEditStyle.cpp new file mode 100644 index 000000000..96a774f01 --- /dev/null +++ b/src/kexiutils/style/KexiPropertyPaneLineEditStyle.cpp @@ -0,0 +1,200 @@ +/* This file is part of the KDE project + Copyright (C) 2016 Jarosław Staniek + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#include "KexiPropertyPaneLineEditStyle.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +static int MenuButton_IndicatorWidth = 20; + +//! A style modification for KexiPropertyPaneLineEdit to allow minimal size +class KexiPropertyPaneLineEditProxyStyle : public QProxyStyle +{ +public: + explicit KexiPropertyPaneLineEditProxyStyle(QStyle *s = nullptr) + : QProxyStyle(s) + , m_viewFocusBrush(KColorScheme::View, KColorScheme::FocusColor) + { + } + + int pixelMetric(PixelMetric metric, const QStyleOption* option, const QWidget* widget) const Q_DECL_OVERRIDE { + const QLineEdit* lineEdit = qobject_cast(widget); + if (lineEdit) { + switch( metric ) { + // frame width + case PM_DefaultFrameWidth: + return 0; + case PM_ComboBoxFrameWidth: + return 0; + default: + break; + } + } + return QProxyStyle::pixelMetric(metric, option, widget); + } + + QRect subControlRect(ComplexControl cc, const QStyleOptionComplex *opt, SubControl sc, const QWidget *widget) const Q_DECL_OVERRIDE { + if (cc == CC_ComboBox) { + if (sc == SC_ComboBoxEditField) { + //qDebug() << opt->rect; + QRect r(opt->rect); + r.adjust(1, 0, -MenuButton_IndicatorWidth, 0); + return visualRect(opt->direction, opt->rect, r); + } + } + return QProxyStyle::subControlRect(cc, opt, sc, widget); + } + + QSize sizeFromContents(ContentsType type, const QStyleOption *option, const QSize &size, const QWidget *widget) const Q_DECL_OVERRIDE { + if (type == CT_ComboBox) { + return size; + } + else if (type == CT_LineEdit) { + return size - QSize(MenuButton_IndicatorWidth, 0); + } + return QProxyStyle::sizeFromContents(type, option, size, widget); + } + + void drawPrimitive(PrimitiveElement element, const QStyleOption* option, QPainter* painter, + const QWidget* widget ) const Q_DECL_OVERRIDE + { + switch (element) { + case PE_FrameLineEdit: { + //qDebug() << "**" << option->rect; + return; + } + case PE_PanelLineEdit: { + const bool enabled(option->state & State_Enabled); + const bool hasFocus(enabled && (option->state & State_HasFocus)); + const QComboBox* combo = qobject_cast(widget->parentWidget()); + const bool popupVisible = combo && combo->view()->isVisible(); + if (hasFocus || popupVisible) { + //normal: QColor outline(KColorUtils::mix(option->palette.color(QPalette::Window), + // option->palette.color( QPalette::WindowText ), 0.25)); + const QColor outline = m_viewFocusBrush.brush(option->palette).color(); + const QColor background(option->palette.color(QPalette::Base)); + QRectF frameRect(option->rect); + if (outline.isValid()) { + painter->setPen(outline); + frameRect.adjust(0.5, 0.5, -0.5, -0.5); + } else { + painter->setPen(Qt::NoPen); + } + if (combo) { + frameRect.adjust(-MenuButton_IndicatorWidth - 1, 0, MenuButton_IndicatorWidth - 1, 0); + } + painter->setBrush(background.isValid() ? background : Qt::NoBrush); + const qreal radius = 1.5; + painter->setRenderHint(QPainter::Antialiasing); + //painter->setClipRect(option->rect.adjusted(-MenuButton_IndicatorWidth, 0, MenuButton_IndicatorWidth, 0)); + painter->drawRoundedRect(frameRect, radius, radius); + return; + } + } + default: + break; + } + QProxyStyle::drawPrimitive(element, option, painter, widget); + } + + void drawControl(ControlElement element, const QStyleOption *opt, QPainter *p, const QWidget *widget) const { + if (element == CE_ComboBoxLabel) { + const QStyleOptionComboBox *cb = qstyleoption_cast(opt); + if (cb) { + QStyleOptionComboBox cbNew(*cb); + cbNew.palette.setBrush(QPalette::Base, QBrush()); + QProxyStyle::drawControl(element, &cbNew, p, widget); + return; + } + //return; + } + QProxyStyle::drawControl(element, opt, p, widget); + } + + void drawComplexControl(ComplexControl cc, const QStyleOptionComplex *option, QPainter *painter, + const QWidget *widget = nullptr) const Q_DECL_OVERRIDE + { + QProxyStyle::drawComplexControl(cc, option, painter, widget); + const bool enabled(option->state & State_Enabled); + const bool hasFocus(enabled && (option->state & State_HasFocus)); + const QComboBox* combo = qobject_cast(widget); + const bool popupVisible = combo && combo->view()->isVisible(); + if (cc == CC_ComboBox && (hasFocus || popupVisible)) { + const qreal radius = 1.5; + QRectF frameRect(option->rect); + const QColor outline = m_viewFocusBrush.brush(option->palette).color(); + if (outline.isValid()) { + painter->setPen(outline); + frameRect.adjust(0.5, 0.5, -0.5, -0.5); + } else { + painter->setPen(Qt::NoPen); + } + painter->setBrush(Qt::NoBrush); + //qDebug() << frameRect << "frameRect"; + painter->drawRoundedRect(frameRect, radius, radius); + } + } + +private: + KStatefulBrush m_viewFocusBrush; +}; + +class Filter : public QObject +{ +public: + Filter(QObject *parent) : QObject(parent) + { + parent->installEventFilter(this); + } + bool eventFilter(QObject *watched, QEvent *event) Q_DECL_OVERRIDE { + if (event->type() == QEvent::StyleChange) { + return true; + } + return QObject::eventFilter(watched, event); + } +}; + +class KexiPropertyPaneLineEditProxyStyleGlobal +{ +public: + void alterStyle(QWidget *w) { + KexiPropertyPaneLineEditProxyStyle *s = new KexiPropertyPaneLineEditProxyStyle(w->style()); + (void)new Filter(w); + w->setStyle(s); + } + QScopedPointer style; +}; + +Q_GLOBAL_STATIC(KexiPropertyPaneLineEditProxyStyleGlobal, s_style); + +void alterPropertyPaneLineEditProxyStyle(QWidget *w) +{ + if (w) { + s_style->alterStyle(w); + } +} diff --git a/src/kexiutils/style/KexiPropertyPaneLineEditStyle.h b/src/kexiutils/style/KexiPropertyPaneLineEditStyle.h new file mode 100644 index 000000000..763146937 --- /dev/null +++ b/src/kexiutils/style/KexiPropertyPaneLineEditStyle.h @@ -0,0 +1,34 @@ +/* This file is part of the KDE project + Copyright (C) 2016 Jarosław Staniek + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef KEXIPROPERTYPANELINEEDITSTYLE_H +#define KEXIPROPERTYPANELINEEDITSTYLE_H + +#include +#include + +#include +#include +#include +#include + +//! @internal sets style for KexiPropertyPaneLineEdit +void alterPropertyPaneLineEditProxyStyle(QWidget *w); + +#endif diff --git a/src/kexiutils/KexiStyle.cpp b/src/kexiutils/style/KexiStyle.cpp similarity index 59% rename from src/kexiutils/KexiStyle.cpp rename to src/kexiutils/style/KexiStyle.cpp index c7d265254..bf72fdc2f 100644 --- a/src/kexiutils/KexiStyle.cpp +++ b/src/kexiutils/style/KexiStyle.cpp @@ -1,242 +1,428 @@ /* This file is part of the KDE project Copyright (C) 2016 Jarosław Staniek This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KexiStyle.h" +#include "KexiPropertyPaneLineEditStyle.h" #include "utils.h" +#include + +#include +#include +#include +#include #include +#include +#include +#include +#include #include #include #include -#include -#include #include +#include namespace KexiStyle { KEXIUTILS_EXPORT void setupFrame(QFrame *frame) { if (frame) { frame->setFrameStyle(QFrame::NoFrame); } } KEXIUTILS_EXPORT void setupGlobalViewModeSelector(QFrame *selector) { KexiStyle::setupFrame(selector); selector->setFont(KexiUtils::smallestReadableFont()); QPalette p(selector->palette()); p.setColor(QPalette::Window, KexiUtils::shadeBlack()); p.setColor(QPalette::Base, KexiUtils::shadeBlack()); p.setColor(QPalette::Button, KexiUtils::shadeBlack()); p.setColor(QPalette::AlternateBase, KexiUtils::shadeBlackLighter()); p.setColor(QPalette::WindowText, KexiUtils::cardboardGrey()); p.setColor(QPalette::ButtonText, KexiUtils::cardboardGrey()); p.setColor(QPalette::Text, KexiUtils::cardboardGrey()); p.setBrush(QPalette::Disabled, QPalette::Text, KexiUtils::iconGrey()); // yes, modes can be disabled //generic?? KColorScheme(QPalette::Disabled, KColorScheme::View).foreground()); p.setColor(QPalette::Highlight, KexiUtils::cardboardGrey()); p.setColor(QPalette::Active, QPalette::Highlight, KexiUtils::plasmaBlue()); // unused anyway because mode selector has no focus p.setColor(QPalette::HighlightedText, KexiUtils::charcoalGrey()); selector->setPalette(p); } KEXIUTILS_EXPORT void overpaintGlobalViewModeSelector(QWidget *widget, QPainter *painter, const QRect &selectedRect, const QColor &arrowColor) { // draw gradient painter->save(); int w = widget->fontMetrics().height() * 3 / 2; painter->translate(widget->width() - w, 0); QLinearGradient grad(0, 0, w, 0); QColor c(widget->palette().base().color()); c.setAlpha(0); grad.setColorAt(0, c); c.setAlpha(15); grad.setColorAt(0.5, c); grad.setColorAt(1.0, QColor(0, 0, 0, 50)); painter->fillRect(0, 0, w, widget->height(), QBrush(grad)); painter->restore(); // draw arrow: /| // \| if (!selectedRect.isNull()) { painter->save(); w = selectedRect.height() / 10; if (w % 2 == 0) { ++w; } painter->translate(selectedRect.x() + selectedRect.width() - w, selectedRect.y() + (selectedRect.height() - w * 2) / 2 - 0.5); QPolygon polygon; polygon << QPoint(w, 0) << QPoint(w, w * 2) << QPoint(0, w); painter->setPen(QPen(Qt::NoPen)); painter->setBrush(arrowColor); painter->drawPolygon(polygon); painter->restore(); } } KEXIUTILS_EXPORT void overpaintModeSelectorItem(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) { Q_UNUSED(painter) Q_UNUSED(option) Q_UNUSED(index) } KEXIUTILS_EXPORT QPalette alternativePalette(const QPalette &palette) { QPalette p(palette); p.setColor(QPalette::Window, KexiUtils::charcoalGrey()); p.setColor(QPalette::Base, KexiUtils::shadeBlack()); + p.setColor(QPalette::Disabled, QPalette::Base, KexiUtils::charcoalGrey()); p.setColor(QPalette::Button, KexiUtils::shadeBlack()); p.setColor(QPalette::AlternateBase, KexiUtils::shadeBlackLighter()); p.setColor(QPalette::WindowText, KexiUtils::paperWhite()); + p.setColor(QPalette::Disabled, QPalette::WindowText, KexiUtils::iconGrey().lighter(125)); p.setColor(QPalette::ButtonText, KexiUtils::paperWhite()); + p.setColor(QPalette::Disabled, QPalette::ButtonText, KexiUtils::cardboardGreyAlternative()); p.setColor(QPalette::Text, KexiUtils::paperWhite()); + p.setColor(QPalette::Disabled, QPalette::Text, p.color(QPalette::Disabled, QPalette::WindowText)); p.setColor(QPalette::Highlight, KexiUtils::cardboardGrey()); p.setColor(QPalette::Active, QPalette::Highlight, KexiUtils::plasmaBlue()); p.setColor(QPalette::HighlightedText, KexiUtils::charcoalGrey()); p.setColor(QPalette::Active, QPalette::HighlightedText, KexiUtils::cardboardGrey()); return p; } KEXIUTILS_EXPORT QPalette sidebarsPalette(const QPalette &palette) { return alternativePalette(palette); } KEXIUTILS_EXPORT void setSidebarsPalette(QWidget *widget) { widget->setPalette(sidebarsPalette(widget->palette())); widget->setAutoFillBackground(true); } KEXIUTILS_EXPORT QFont titleFont(const QFont &font) { QFont newFont(font); newFont.setCapitalization(QFont::AllUppercase); return newFont; } static const QString g_contexts[] = { QLatin1String("actions"), // Any QLatin1String("actions"), QLatin1String("apps"), QLatin1String("devices"), QLatin1String("filesystems"), QLatin1String("mimetypes"), QLatin1String("animations"), QLatin1String("categories"), QLatin1String("emblems"), QLatin1String("emotes"), QLatin1String("intl"), QLatin1String("places"), QLatin1String("status") }; KEXIUTILS_EXPORT QIcon darkIcon(const QString &iconName, KIconLoader::Context iconContext) { Q_ASSERT(iconContext < (sizeof(g_contexts) / sizeof(g_contexts[0]))); static const QIcon::Mode modes[] = { QIcon::Normal }; //can be supported too: , QIcon::Selected }; const QString prefix(QLatin1String(":/icons/breeze/") + g_contexts[iconContext] + QLatin1Char('/')); const QString suffixes[] = { iconName + QLatin1String("@dark.svg"), iconName + QLatin1String(".svg") }; static const QString sizesStr[] = { QString::fromLatin1("32/"), // important: opposite direction QString::fromLatin1("22/"), QString::fromLatin1("16/") }; static const QSize sizes[] = { QSize(32, 32), QSize(22, 22), QSize(16, 16) }; // important: opposite direction QIcon icon; for (int mode = 0; mode < int(sizeof(modes) / sizeof(modes[0])); ++mode) { for (int size = 0; size < int(sizeof(sizes) / sizeof(sizes[0])); ++size) { //qDebug() << prefix + sizesStr[size] + suffixes[mode] << sizes[size] << modes[mode]; icon.addFile(prefix + sizesStr[size] + suffixes[mode], sizes[size], modes[mode], QIcon::Off); icon.addFile(prefix + sizesStr[size] + suffixes[mode], sizes[size], modes[mode], QIcon::On); } } return icon; } class IconEngine : public QIconEngine { public: IconEngine(const KexiStyledIconParameters ¶meters) : m_parameters(parameters) { } inline QIconEngine *clone() const Q_DECL_OVERRIDE { return new IconEngine(*this); } //! @todo add caching? QPixmap pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state) Q_DECL_OVERRIDE { Q_UNUSED(state) QFile f(QLatin1String(":/icons/breeze/") + g_contexts[m_parameters.context] + QChar('/') + QString::number(size.width()) + QChar('/') + m_parameters.name + ".svg"); if (!f.open(QIODevice::ReadOnly)) { return QPixmap(); } QByteArray svg(f.readAll()); QColor color; if (mode == QIcon::Selected && m_parameters.selectedColor.isValid()) { color = m_parameters.selectedColor; } else if (mode == QIcon::Disabled && m_parameters.disabledColor.isValid()) { color = m_parameters.disabledColor; //qDebug() << m_parameters.disabledColor; } else if (m_parameters.color.isValid()) { color = m_parameters.color; } if (color.isValid()) { svg.replace(KexiUtils::iconGrey().name().toLatin1(), color.name().toLatin1()); } QSvgRenderer renderer(svg); QPixmap pm(size); pm.fill(Qt::transparent); QPainter p(&pm); renderer.render(&p, pm.rect()); return pm; } //! Nothing to paint extra here void paint(QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state) Q_DECL_OVERRIDE { Q_UNUSED(painter) Q_UNUSED(rect) Q_UNUSED(mode) Q_UNUSED(state) } private: //! Needed for clone() IconEngine(const IconEngine &other) : m_parameters(other.m_parameters) {} const KexiStyledIconParameters m_parameters; }; KEXIUTILS_EXPORT QIcon icon(const KexiStyledIconParameters ¶meters) { return QIcon(new IconEngine(parameters)); } +const int propertyPaneMargin = 2; + +Q_GLOBAL_STATIC(PropertyPane, s_propertyPane) + +KEXIUTILS_EXPORT const PropertyPane& propertyPane() +{ + return *s_propertyPane; +} + +PropertyPane::PropertyPane() + : margins(propertyPaneMargin, propertyPaneMargin * 2, propertyPaneMargin, propertyPaneMargin) //left top right bottom + , verticalSpacing(5) + , sectionTitleIndent(8) + , horizontalSpacingAfterIcon(2) + , horizontalSpacingAfterLabel(5) +{ +} + +void PropertyPane::setupEditor(KPropertyEditorView *view) const +{ + view->setGridLineColor(QColor()); + view->setFrameShape(QFrame::NoFrame); +} + +QPalette PropertyPane::sectionTitlePalette(const QPalette &palette) const +{ + QPalette pal(palette); + pal.setColor(QPalette::WindowText, KexiUtils::cardboardGreyAlternative()); + return pal; +} + +QPalette PropertyPane::labelPalette(const QPalette &palette) const +{ + QPalette pal(palette); + pal.setColor(QPalette::WindowText, QColor(0x7f8c8d)); + return pal; +} + +QPalette PropertyPane::warningLabelPalette(const QPalette &palette) const +{ + QPalette pal(palette); + QColor c(0xffa92d); // orange + c.setAlpha(120); + pal.setColor(QPalette::WindowText, c); + return pal; +} + +QFont PropertyPane::font() const +{ + return KexiUtils::smallestReadableFont(); } + +void PropertyPane::alterLineEditStyle(QLineEdit *edit) const +{ + alterPropertyPaneLineEditProxyStyle(edit); +} + +void PropertyPane::alterComboBoxStyle(QComboBox *combo) const +{ + if (combo) { + combo->setContentsMargins(0, 0, 0, 0); + alterPropertyPaneLineEditProxyStyle(combo); + alterPropertyPaneLineEditProxyStyle(combo->lineEdit()); + } +} + +void PropertyPane::alterTitleFont(QWidget *widget) const +{ + QFont f(widget->font()); + f.setCapitalization(QFont::AllUppercase); + widget->setFont(f); +} + +QVBoxLayout* PropertyPane::createVLayout(QWidget *widget) const +{ + QVBoxLayout *lyr = new QVBoxLayout(widget); + lyr->setContentsMargins(0, 0, 0, 0); + lyr->setSpacing(0); + return lyr; +} + +QGridLayout* PropertyPane::createFormLayout(QVBoxLayout *parentLayout) const +{ + const PropertyPane &s = KexiStyle::propertyPane(); + QGridLayout *formLyr = new QGridLayout; + formLyr->setContentsMargins(0, 0, s.margins.right(), 0); + formLyr->setVerticalSpacing(0); + parentLayout->addLayout(formLyr); + return formLyr; +} + +static QLabel* createLabelInternal(const QString &labelText) +{ + QLabel *label = new QLabel(labelText); + label->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + label->setWordWrap(true); + label->setContentsMargins(0, 0, 0, 0); + return label; +} + +QLabel* PropertyPane::createLabel(const QString &labelText) const +{ + const PropertyPane &s = KexiStyle::propertyPane(); + QLabel* label = createLabelInternal(labelText); + label->setPalette(s.labelPalette(label->palette())); + return label; +} + +QLabel* PropertyPane::createWarningLabel(const QString &labelText) const +{ + const PropertyPane &s = KexiStyle::propertyPane(); + QLabel* label = createLabelInternal(labelText); + label->setPalette(s.warningLabelPalette(label->palette())); + return label; +} + +QLabel* PropertyPane::createTitleLabel(const QString &title, QVBoxLayout *layout) const +{ + const PropertyPane &s = KexiStyle::propertyPane(); + QLabel *lbl = new QLabel(title); + lbl->setIndent(s.sectionTitleIndent); + s.alterTitleFont(lbl); + lbl->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + lbl->setPalette(s.sectionTitlePalette(lbl->palette())); + layout->addWidget(lbl); + layout->addSpacing(verticalSpacing); + return lbl; +} + +void PropertyPane::addLabelAndWidget(const QString &labelText, QWidget *widget, + QGridLayout *formLayout) const +{ + const PropertyPane &s = KexiStyle::propertyPane(); + QLabel * label = new QLabel(labelText); + label->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum); + label->setContentsMargins(s.sectionTitleIndent * 2, 0, s.horizontalSpacingAfterLabel, 0); + label->setPalette(s.labelPalette(label->palette())); + + QComboBox* comboBox = qobject_cast(widget); + if (comboBox) { + s.alterComboBoxStyle(comboBox); + } + widget->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum); + label->setBuddy(widget); + formLayout->addWidget(label, formLayout->rowCount(), 0, Qt::AlignRight | Qt::AlignTop); + formLayout->addWidget(widget, formLayout->rowCount() - 1, 1); + formLayout->addItem(new QSpacerItem(1, verticalSpacing, QSizePolicy::Fixed, QSizePolicy::Fixed), formLayout->rowCount(), 1); +} + +void PropertyPane::setFormLabelAndWidgetVisible(QWidget *widget, QGridLayout *formLayout, bool set) const +{ + if (!widget) { + return; + } + const int index = formLayout->indexOf(widget); + if (index == -1) { + qWarning() << "No widget" << widget << "in form layout" << formLayout; + return; + } + int row, column, rowSpan, columnSpan; + formLayout->getItemPosition(index, &row, &column, &rowSpan, &columnSpan); + QLayoutItem *labelItem = formLayout->itemAtPosition(row, 0); + if (labelItem->widget()) { + labelItem->widget()->setVisible(set); + } + widget->setVisible(set); + QLayoutItem *spacerItem = formLayout->itemAtPosition(row + 1, 1); + if (spacerItem && spacerItem->spacerItem()) { + spacerItem->spacerItem()->changeSize(set ? 1 : 0, set ? verticalSpacing : 0); + } +} + +} // namespace KexiStyle diff --git a/src/kexiutils/KexiStyle.h b/src/kexiutils/style/KexiStyle.h similarity index 73% rename from src/kexiutils/KexiStyle.h rename to src/kexiutils/style/KexiStyle.h index 655acdb33..410977719 100644 --- a/src/kexiutils/KexiStyle.h +++ b/src/kexiutils/style/KexiStyle.h @@ -1,109 +1,147 @@ /* This file is part of the KDE project Copyright (C) 2016 Jarosław Staniek This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KEXISTYLE_H #define KEXISTYLE_H #include #include #include +#include +class KPropertyEditorView; +class QComboBox; class QFont; class QFrame; +class QGridLayout; class QIcon; +class QLabel; +class QLineEdit; class QModelIndex; class QPainter; class QPalette; class QRect; class QStyleOptionViewItem; +class QVBoxLayout; class QWidget; //! Styled icon parameters class KEXIUTILS_EXPORT KexiStyledIconParameters { public: explicit KexiStyledIconParameters(KIconLoader::Context c = KIconLoader::Action) : context(c) { } //! Icon name QString name; //! Icon color, when used for normal mode (QIcon::Normal). //! If the value is valid, default color will be replaced with it. QColor color; //! Icon color, when used for selected mode (QIcon::Selected). //! If the value is valid, default color will be replaced with it. QColor selectedColor; //! Icon color, when used for disabled palette group (QPalette::Disabled). //! If the value is valid, default color will be replaced with it. QColor disabledColor; //! Icon context such as "actions" KIconLoader::Context context; }; //! Application style. //! @todo make it configurable? namespace KexiStyle { //! Setup style for @a frame. By default flat style is set (QFrame::NoFrame). KEXIUTILS_EXPORT void setupFrame(QFrame *frame); //! Setup style for the global view mode selector widget (KexiGlobalViewModeSelector). //! By default setupFrame() is called to set flat style, minimal fonts are set //! and a bit darker version of alternativePalette(). KEXIUTILS_EXPORT void setupGlobalViewModeSelector(QFrame *selector); //! Overpaints entire global view mode selector. By default it paints a dark shadow an arrow //! for selected item. If @a selectedRect is not null, provides geometry of selected item. KEXIUTILS_EXPORT void overpaintGlobalViewModeSelector(QWidget *widget, QPainter *painter, const QRect &selectedRect, const QColor &arrowColor); //! Overpaints global view mode selector's item. By default does nothing. KEXIUTILS_EXPORT void overpaintModeSelectorItem(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index); //! @return alternative palette based on @a palette. //! By default it is dark one based on Breeze colors. KEXIUTILS_EXPORT QPalette alternativePalette(const QPalette &palette); //! @return palette dedicated for side bars, based on @a palette. //! By default it equal to alternativePalette(). KEXIUTILS_EXPORT QPalette sidebarsPalette(const QPalette &palette); //! Sets sidebarsPalette(). If it's nonstandard palette, //! QWidget::setAutoFillBackground(true) is also called for the widget. KEXIUTILS_EXPORT void setSidebarsPalette(QWidget *widget); //! @return font @a font adjusted to make it a title font. //! By default capitalization is set for the font. KEXIUTILS_EXPORT QFont titleFont(const QFont &font); //! @return styled dark icon @a iconName of context @a iconContext. Group can be "actions", etc. //! The same styled icon can be used with light and dark background. Filename for dark //! variants should have "@dark" suffix. //! KIconLoader::Any means KIconLoader::Action. KEXIUTILS_EXPORT QIcon darkIcon(const QString &iconName, KIconLoader::Context iconContext = KIconLoader::Action); KEXIUTILS_EXPORT QIcon icon(const KexiStyledIconParameters ¶meters); + + //! Style definition for property pane + class KEXIUTILS_EXPORT PropertyPane { + public: + PropertyPane(); + void setupEditor(KPropertyEditorView *view) const; + QPalette sectionTitlePalette(const QPalette &palette) const; + QPalette labelPalette(const QPalette &palette) const; + QPalette warningLabelPalette(const QPalette &palette) const; + QFont font() const; + void alterLineEditStyle(QLineEdit *edit) const; + void alterComboBoxStyle(QComboBox *combo) const; + void alterTitleFont(QWidget *widget) const; + Q_REQUIRED_RESULT QVBoxLayout* createVLayout(QWidget *widget) const; + Q_REQUIRED_RESULT QGridLayout* createFormLayout(QVBoxLayout *parentLayout) const; + Q_REQUIRED_RESULT QLabel* createLabel(const QString &labelText) const; + Q_REQUIRED_RESULT QLabel* createWarningLabel(const QString &labelText) const; + QLabel* createTitleLabel(const QString &title, QVBoxLayout *lyr) const; + void addLabelAndWidget(const QString &labelText, QWidget *widget, QGridLayout *formLayout) const; + void setFormLabelAndWidgetVisible(QWidget *widget, QGridLayout *formLayout, bool set) const; + + const QMargins margins; + const int verticalSpacing; + const int sectionTitleIndent; + const int horizontalSpacingAfterIcon; + const int horizontalSpacingAfterLabel; + private: + Q_DISABLE_COPY(PropertyPane) + }; + + KEXIUTILS_EXPORT const PropertyPane& propertyPane(); } #endif // KEXISTYLE_H diff --git a/src/main/CMakeLists.txt b/src/main/CMakeLists.txt index 3ca7abdef..6da65aa44 100644 --- a/src/main/CMakeLists.txt +++ b/src/main/CMakeLists.txt @@ -1,81 +1,82 @@ #TODO add_definitions(-DKDE_DEFAULT_DEBUG_AREA=44019) include_directories(${CMAKE_SOURCE_DIR}/src/core ${CMAKE_SOURCE_DIR}/src/kexiutils +${CMAKE_SOURCE_DIR}/src/kexiutils/style ${CMAKE_SOURCE_DIR}/src/main ${CMAKE_SOURCE_DIR}/src/main/startup ${CMAKE_SOURCE_DIR}/src/widget ${CMAKE_BINARY_DIR}/src/widget ${CMAKE_SOURCE_DIR}/src/widget/navigator ${CMAKE_SOURCE_DIR}/src/widget/properties ) set(QT_USE_QTUITOOLS true) set(keximain_LIB_SRCS KexiMainWindow.cpp KexiMainWindow_p.cpp KexiMenuWidget.cpp kexifinddialog.cpp KexiSearchLineEdit.cpp KexiUserFeedbackAgent.cpp KexiBugReportDialog.cpp KexiGlobalViewModeSelector.cpp KexiObjectViewWidget.cpp KexiObjectViewTabWidget.cpp KexiPropertyPaneWidget.cpp startup/KexiNewProjectAssistant.cpp startup/KexiOpenProjectAssistant.cpp startup/KexiWelcomeAssistant.cpp startup/KexiWelcomeStatusBar.cpp startup/KexiImportExportAssistant.cpp startup/KexiStartupDialog.cpp startup/KexiStartup.cpp startup/KexiTemplatesModel.cpp startup/KexiRecentProjectsModel.cpp startup/KexiAssistantMessageHandler.cpp startup/KexiPasswordPage.cpp #todo printing/kexisimpleprintingengine.cpp #todo printing/kexisimpleprintingpagesetup.cpp #todo printing/kexisimpleprintingpart.cpp #todo printing/kexisimpleprintpreviewwindow.cpp ) ki18n_wrap_ui(keximain_LIB_SRCS kexifinddialog.ui startup/KexiProjectStorageTypeSelectionPage.ui startup/KexiServerDBNamePage.ui startup/KexiMainImportExportPage.ui ) kexi_add_library(keximain SHARED ${keximain_LIB_SRCS}) generate_export_header(keximain) target_link_libraries(keximain PUBLIC kexicore PRIVATE kexiextendedwidgets kexiguiutils KF5::GuiAddons Qt5::UiTools ) if(HAVE_KCRASH) target_link_libraries(keximain PRIVATE KF5::Crash ) endif() target_compile_definitions(keximain PRIVATE CMAKE_CURRENT_SOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}") install(TARGETS keximain ${INSTALL_TARGETS_DEFAULT_ARGS}) add_subdirectory(status) if (BUILD_TESTING) add_subdirectory(autotests) endif() diff --git a/src/main/KexiGlobalViewModeSelector.cpp b/src/main/KexiGlobalViewModeSelector.cpp index fbaa7fcdb..a139efd23 100644 --- a/src/main/KexiGlobalViewModeSelector.cpp +++ b/src/main/KexiGlobalViewModeSelector.cpp @@ -1,211 +1,211 @@ /* This file is part of the KDE project Copyright (C) 2016 Jarosław Staniek This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KexiGlobalViewModeSelector.h" #include "KexiGlobalViewModeSelector_p.h" -#include +#include #include #include #include #include KexiGlobalViewModeSelectorModel::KexiGlobalViewModeSelectorModel(QObject *parent) : QAbstractListModel(parent) { } KexiGlobalViewModeSelectorModel::~KexiGlobalViewModeSelectorModel() { qDeleteAll(modes); } int KexiGlobalViewModeSelectorModel::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent); return modes.count(); } QVariant KexiGlobalViewModeSelectorModel::data(const QModelIndex &index, int role) const { if (!index.isValid() || index.row() >= modes.size()) return QVariant(); KexiGlobalViewModeItem *mode = static_cast(index.internalPointer()); switch (role) { case Qt::DisplayRole: return mode->name; case Qt::DecorationRole: return mode->icon; default:; } return QVariant(); } Qt::ItemFlags KexiGlobalViewModeSelectorModel::flags(const QModelIndex &index) const { Qt::ItemFlags flags = QAbstractListModel::flags(index); KexiGlobalViewModeItem *mode = static_cast(index.internalPointer()); if (!mode->enabled) { flags &= ~(Qt::ItemIsEnabled | Qt::ItemIsSelectable); } return flags; } QModelIndex KexiGlobalViewModeSelectorModel::index(int row, int column, const QModelIndex& parent) const { Q_UNUSED(parent); if (row < 0 || row >= modes.count()) { return QModelIndex(); } return createIndex(row, column, modes.at(row)); } // ---- KexiGlobalViewModeSelectorDelegate::KexiGlobalViewModeSelectorDelegate(QObject *parent) : KexiListViewDelegate(parent) { } void KexiGlobalViewModeSelectorDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { KexiListViewDelegate::paint(painter, option, index); KexiStyle::overpaintModeSelectorItem(painter, option, index); } // ---- class KexiGlobalViewModeSelector::Private { public: Private() {} KexiGlobalViewModeSelectorModel model; QColor arrowColor; }; KexiGlobalViewModeSelector::KexiGlobalViewModeSelector(QWidget *parent) : KexiListView(DontUseDelegate, parent), d(new Private) { KexiStyle::setupGlobalViewModeSelector(this); setSpacing(0); setContentsMargins(0, 0, 0, 0); setFocusPolicy(Qt::NoFocus); setEditTriggers(NoEditTriggers); setDropIndicatorShown(false); setSelectionBehavior(SelectRows); setSelectionRectVisible(false); setUniformItemSizes(true); setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); setItemDelegate(new KexiGlobalViewModeSelectorDelegate(this)); KexiStyledIconParameters param; param.color = palette().color(QPalette::Text); param.selectedColor = palette().color(QPalette::HighlightedText); param.disabledColor = palette().color(QPalette::Disabled, QPalette::Text); { KexiGlobalViewModeItem *welcomeMode = new KexiGlobalViewModeItem; welcomeMode->name = xi18nc("Welcome global view mode", "Welcome"); param.context = KIconLoader::Action; param.name = "mode-selector-welcome"; welcomeMode->icon = KexiStyle::icon(param); d->model.modes << welcomeMode; } { KexiGlobalViewModeItem *projectMode = new KexiGlobalViewModeItem; projectMode->enabled = false; projectMode->name = xi18nc("Project global view mode (noun)", "Project"); param.context = KIconLoader::Action; param.name = "mode-selector-project"; projectMode->icon = KexiStyle::icon(param); d->model.modes << projectMode; } { KexiGlobalViewModeItem *dataMode = new KexiGlobalViewModeItem; dataMode->name = xi18nc("Edit global view mode (verb)", "Edit"); param.context = KIconLoader::Action; param.name = "mode-selector-edit"; dataMode->icon = KexiStyle::icon(param); d->model.modes << dataMode; } { KexiGlobalViewModeItem *designMode = new KexiGlobalViewModeItem; designMode->name = xi18nc("Design global view mode (verb)", "Design"); param.context = KIconLoader::Action; param.name = "mode-selector-design"; designMode->icon = KexiStyle::icon(param); d->model.modes << designMode; } { KexiGlobalViewModeItem *helpMode = new KexiGlobalViewModeItem; helpMode->name = xi18nc("Help global view mode (noun)", "Help"); param.context = KIconLoader::Action; param.name = "mode-selector-help"; helpMode->icon = KexiStyle::icon(param); d->model.modes << helpMode; } setModel(&d->model); setCurrentMode(Kexi::WelcomeGlobalMode); } KexiGlobalViewModeSelector::~KexiGlobalViewModeSelector() { } void KexiGlobalViewModeSelector::paintEvent(QPaintEvent *event) { KexiListView::paintEvent(event); QRect selectedRect; if (!selectedIndexes().isEmpty()) { selectedRect = visualRect(selectedIndexes().first()); } QPainter painter(viewport()); KexiStyle::overpaintGlobalViewModeSelector(this, &painter, selectedRect, d->arrowColor); } Kexi::GlobalViewMode KexiGlobalViewModeSelector::currentMode() const { int index = currentIndex().row(); if (index < 0 || index > Kexi::LastGlobalMode) { index = 0; } return static_cast(index); } void KexiGlobalViewModeSelector::setCurrentMode(Kexi::GlobalViewMode mode) { setCurrentIndex(model()->index(int(mode), 0)); } void KexiGlobalViewModeSelector::currentChanged(const QModelIndex ¤t, const QModelIndex &previous) { KexiListView::currentChanged(current, previous); emit currentModeChanged(); } void KexiGlobalViewModeSelector::setArrowColor(const QColor &color) { d->arrowColor = color; update(currentIndex()); } diff --git a/src/main/KexiMainWindow.cpp b/src/main/KexiMainWindow.cpp index 7c5b65a92..00c1054b8 100644 --- a/src/main/KexiMainWindow.cpp +++ b/src/main/KexiMainWindow.cpp @@ -1,4496 +1,4496 @@ /* This file is part of the KDE project Copyright (C) 2003 Lucijan Busch Copyright (C) 2003-2017 Jarosław Staniek This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KexiMainWindow.h" #include "KexiMainWindow_p.h" #include "kexiactionproxy.h" #include "kexipartmanager.h" #include "kexipart.h" #include "kexipartinfo.h" #include "kexipartguiclient.h" #include "kexiproject.h" #include "kexiprojectdata.h" #include "kexi.h" #include "kexiinternalpart.h" #include "kexiactioncategories.h" #include "kexifinddialog.h" #include "kexisearchandreplaceiface.h" #include "KexiBugReportDialog.h" #define KEXI_SKIP_REGISTERICONSRESOURCE #define KEXI_SKIP_SETUPPRIVATEICONSRESOURCE #include "KexiRegisterResource_p.h" #include "KexiObjectViewWidget.h" #include "KexiObjectViewTabWidget.h" #include "KexiPropertyPaneWidget.h" #include -#include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "startup/KexiStartup.h" #include "startup/KexiNewProjectAssistant.h" #include "startup/KexiOpenProjectAssistant.h" #include "startup/KexiWelcomeAssistant.h" #include "startup/KexiImportExportAssistant.h" #include "startup/KexiStartupDialog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if !defined(KexiVDebug) # define KexiVDebug if (0) qDebug() #endif #ifdef HAVE_KCRASH #include //! @todo else, add Breakpad? https://phabricator.kde.org/T1642 #endif KexiDockWidgetStyle::KexiDockWidgetStyle(const QString &baseStyleName) : QProxyStyle(baseStyleName) { } KexiDockWidgetStyle::~KexiDockWidgetStyle() { } void KexiDockWidgetStyle::polish(QWidget* widget) { baseStyle()->polish(widget); widget->setContentsMargins(0, 0, 0, 0); } class Q_DECL_HIDDEN KexiDockWidget::Private { public: Private() {} QSize hint; }; KexiDockWidget::KexiDockWidget(const QString &_tabText, QWidget *parent) : QDockWidget(parent), tabText(_tabText), d(new Private) { // No floatable dockers, Dolphin had problems, we don't want the same... // https://bugs.kde.org/show_bug.cgi?id=288629 // https://bugs.kde.org/show_bug.cgi?id=322299 setFeatures(QDockWidget::NoDockWidgetFeatures);//DockWidgetClosable); setAllowedAreas(Qt::LeftDockWidgetArea|Qt::RightDockWidgetArea); setFocusPolicy(Qt::NoFocus); if (style()->objectName().compare("windowsvista", Qt::CaseInsensitive) == 0) { // windowsvista style has broken accelerator visualization support KAcceleratorManager::setNoAccel(this); } KexiDockWidgetStyle *customStyle = new KexiDockWidgetStyle(style()->objectName()); customStyle->setParent(this); setStyle(customStyle); setTitleBarWidget(new QWidget(this)); // hide the title layout()->setContentsMargins(0, 0, 0, 0); layout()->setSpacing(0); } KexiDockWidget::~KexiDockWidget() { delete d; } void KexiDockWidget::paintEvent(QPaintEvent *pe) { Q_UNUSED(pe); QStylePainter p(this); if (isFloating()) { QStyleOptionFrame framOpt; framOpt.initFrom(this); p.drawPrimitive(QStyle::PE_FrameDockWidget, framOpt); } // Title must be painted after the frame, since the areas overlap, and // the title may wish to extend out to all sides (eg. XP style) QStyleOptionDockWidget titleOpt; initStyleOption(&titleOpt); p.drawControl(QStyle::CE_DockWidgetTitle, titleOpt); } void KexiDockWidget::setSizeHint(const QSize& hint) { d->hint = hint; } QSize KexiDockWidget::sizeHint() const { return d->hint.isValid() ? d->hint : QDockWidget::sizeHint(); } //------------------------------------------------- static bool setupIconTheme(KLocalizedString *errorMessage, KLocalizedString *detailsErrorMessage) { // Register kexi resource first to have priority over the standard breeze theme. // For example "table" icon exists in both resources. if (!registerResource("icons/kexi_breeze.rcc", QStandardPaths::AppDataLocation, QString(), QString(), errorMessage, detailsErrorMessage) || !registerGlobalBreezeIconsResource(errorMessage, detailsErrorMessage)) { return false; } setupBreezeIconTheme(); // tell KIconLoader an co. about the theme KConfigGroup cg(KSharedConfig::openConfig(), "Icons"); cg.writeEntry("Theme", "breeze"); cg.sync(); return true; } //! @todo 3.1 replace with KexiStyle bool setupApplication() { #if defined Q_OS_WIN || defined Q_OS_MACOS // Only this style matches current Kexi theme and can be supported/tested const char *name = "breeze"; QScopedPointer style(QStyleFactory::create(name)); if (!style || style->objectName() != name) { qWarning() << qPrintable(QString("Could not find application style %1. " "Kexi will not start. Please check if Kexi is properly installed. ") .arg(name)); return false; } qApp->setStyle(style.take()); #endif return true; } //static int KexiMainWindow::create(const QStringList &arguments, const QString &componentName, const QList &extraOptions) { qApp->setQuitOnLastWindowClosed(false); KLocalizedString::setApplicationDomain("kexi"); //! @todo KEXI3 app->setAttribute(Qt::AA_UseHighDpiPixmaps, true); KexiAboutData aboutData; if (!componentName.isEmpty()) { aboutData.setComponentName(componentName); } KAboutData::setApplicationData(aboutData); if (!setupApplication()) { return 1; } #ifdef HAVE_KCRASH KCrash::initialize(); #endif KLocalizedString errorMessage; KLocalizedString detailsErrorMessage; if (!setupIconTheme(&errorMessage, &detailsErrorMessage)) { if (detailsErrorMessage.isEmpty()) { KMessageBox::error(nullptr, errorMessage.toString()); } else { KMessageBox::detailedError(nullptr, errorMessage.toString(), detailsErrorMessage.toString()); } qWarning() << qPrintable(errorMessage.toString(Kuit::PlainText)); return 1; } QApplication::setWindowIcon(koIcon("kexi")); const tristate res = KexiStartupHandler::global()->init(arguments, extraOptions); if (!res || ~res) { return (~res) ? 0 : 1; } //qDebug() << "startupActions OK"; /* Exit requested, e.g. after database removing. */ if (KexiStartupHandler::global()->action() == KexiStartupData::Exit) { return 0; } KexiMainWindow *win = new KexiMainWindow(); #ifdef KEXI_DEBUG_GUI QWidget* debugWindow = 0; KConfigGroup generalGroup = KSharedConfig::openConfig()->group("General"); if (generalGroup.readEntry("ShowInternalDebugger", false)) { debugWindow = KexiUtils::createDebugWindow(win); debugWindow->show(); } #endif if (true != win->startup()) { delete win; return 1; } win->restoreSettings(); win->show(); #ifdef KEXI_DEBUG_GUI win->raise(); static_cast(win)->activateWindow(); #endif /*foreach (QWidget *widget, QApplication::topLevelWidgets()) { qDebug() << widget; }*/ return 0; } //------------------------------------------------- KexiMainMenuActionShortcut::KexiMainMenuActionShortcut(const QKeySequence& key, QAction *action, QWidget *parent) : QShortcut(key, parent) , m_action(action) { connect(this, SIGNAL(activated()), this, SLOT(slotActivated())); } KexiMainMenuActionShortcut::~KexiMainMenuActionShortcut() { } void KexiMainMenuActionShortcut::slotActivated() { if (!m_action->isEnabled()) { return; } m_action->trigger(); } //------------------------------------------------- KexiMainWindow::KexiMainWindow(QWidget *parent) : QMainWindow(parent) , KexiMainWindowIface() , KexiGUIMessageHandler(this) , d(new KexiMainWindow::Private(this)) { setObjectName("KexiMainWindow"); setAttribute(Qt::WA_DeleteOnClose); kexiTester() << KexiTestObject(this); if (d->userMode) qDebug() << "starting up in the User Mode"; setAsDefaultHost(); //this is default host now. //get informed connect(&Kexi::partManager(), SIGNAL(partLoaded(KexiPart::Part*)), this, SLOT(slotPartLoaded(KexiPart::Part*))); connect(&Kexi::partManager(), SIGNAL(newObjectRequested(KexiPart::Info*)), this, SLOT(newObject(KexiPart::Info*))); setAcceptDrops(true); setupActions(); setupMainWidget(); setupMainMenu(); updateAppCaption(); if (!d->userMode) { setupContextHelp(); //setupPropertyEditor(); } invalidateActions(); d->timer.singleShot(0, this, SLOT(slotLastActions())); if (KexiStartupHandler::global()->forcedFullScreen()) { toggleFullScreen(true); } // --- global config //! @todo move to specialized KexiConfig class KConfigGroup tablesGroup(d->config->group("Tables")); const int defaultMaxLengthForTextFields = tablesGroup.readEntry("DefaultMaxLengthForTextFields", int(-1)); if (defaultMaxLengthForTextFields >= 0) { KDbField::setDefaultMaxLength(defaultMaxLengthForTextFields); } // --- /global config } KexiMainWindow::~KexiMainWindow() { d->forceWindowClosing = true; closeProject(); delete d; Kexi::deleteGlobalObjects(); } KexiProject *KexiMainWindow::project() { return d->prj; } QList KexiMainWindow::allActions() const { return actionCollection()->actions(); } KActionCollection *KexiMainWindow::actionCollection() const { return d->actionCollection; } KexiWindow* KexiMainWindow::currentWindow() const { if (!d->objectViewWidget || !d->objectViewWidget->tabWidget()) { return 0; } return windowForTab(d->objectViewWidget->tabWidget()->currentIndex()); } KexiWindow* KexiMainWindow::windowForTab(int tabIndex) const { if (!d->objectViewWidget || !d->objectViewWidget->tabWidget()) return 0; return d->objectViewWidget->tabWidget()->window(tabIndex); } void KexiMainWindow::setupMainMenuActionShortcut(QAction * action) { if (!action->shortcut().isEmpty()) { //foreach(const QKeySequence &shortcut, action->shortcuts()) { //(void)new KexiMainMenuActionShortcut(shortcut, action, this); //} } } static void addThreeDotsToActionText(QAction* action) { action->setText(xi18nc("Action name with three dots...", "%1...", action->text())); } QAction * KexiMainWindow::addAction(const char *name, const QIcon &icon, const QString& text, const char *shortcut) { QAction *action = icon.isNull() ? new QAction(text, this) : new QAction(icon, text, this); actionCollection()->addAction(name, action); if (shortcut) { action->setShortcut(QKeySequence(shortcut)); action->setShortcutContext(Qt::ApplicationShortcut); //QShortcut *s = new QShortcut(action->shortcut(), this); //connect(s, SIGNAL(activated()), action, SLOT(trigger())); } return action; } QAction * KexiMainWindow::addAction(const char *name, const QString& text, const char *shortcut) { return addAction(name, QIcon(), text, shortcut); } void KexiMainWindow::setupActions() { KActionCollection *ac = actionCollection(); // PROJECT MENU QAction *action; ac->addAction("project_new", action = new KexiMenuWidgetAction(KStandardAction::New, this)); action->setText(xi18n("&New Project...")); action->setShortcuts(KStandardShortcut::openNew()); action->setToolTip(xi18n("Create a new project")); action->setWhatsThis( xi18n("Creates a new project. Currently opened project is not affected.")); connect(action, SIGNAL(triggered()), this, SLOT(slotProjectNew())); setupMainMenuActionShortcut(action); ac->addAction("project_open", action = new KexiMenuWidgetAction(KStandardAction::Open, this)); action->setText(xi18n("&Open Project...")); action->setIcon(koIcon("project-open")); action->setToolTip(xi18n("Open an existing project")); action->setWhatsThis( xi18n("Opens an existing project. Currently opened project is not affected.")); connect(action, SIGNAL(triggered()), this, SLOT(slotProjectOpen())); setupMainMenuActionShortcut(action); ac->addAction("project_open_recent", action = KStandardAction::openRecent(this, SLOT(slotProjectOpen()), this)); action->setToolTip(xi18n("Open a project that was recently opened.")); action->setWhatsThis(xi18n("Opens a project that was recently opened.")); { ac->addAction("project_welcome", action = d->action_project_welcome = new KexiMenuWidgetAction( QIcon(), xi18n("Welcome"), this)); addThreeDotsToActionText(action); connect(action, SIGNAL(triggered()), this, SLOT(slotProjectWelcome())); setupMainMenuActionShortcut(action); action->setToolTip(xi18n("Show Welcome page")); action->setWhatsThis( xi18n("Shows Welcome page with list of recently opened projects and other information. ")); } ac->addAction("project_save", d->action_save = KStandardAction::save(this, SLOT(slotProjectSave()), this)); d->action_save->setToolTip(xi18n("Save object changes")); d->action_save->setWhatsThis(xi18n("Saves object changes from currently selected window.")); setupMainMenuActionShortcut(d->action_save); d->action_save_as = addAction("project_saveas", koIcon("document-save-as"), xi18n("Save &As...")); d->action_save_as->setToolTip(xi18n("Save object as")); d->action_save_as->setWhatsThis( xi18n("Saves object from currently selected window under a new name (within the same project).")); connect(d->action_save_as, SIGNAL(triggered()), this, SLOT(slotProjectSaveAs())); #ifdef KEXI_SHOW_UNIMPLEMENTED ac->addAction("project_properties", action = d->action_project_properties = new KexiMenuWidgetAction( koIcon("document-properties"), futureI18n("Project Properties"), this)); connect(action, SIGNAL(triggered()), this, SLOT(slotProjectProperties())); setupMainMenuActionShortcut(action); #else d->action_project_properties = d->dummy_action; #endif //! @todo replace document-import icon with something other ac->addAction("project_import_export_send", action = d->action_project_import_export_send = new KexiMenuWidgetAction( koIcon("document-import"), xi18n("&Import, Export or Send..."), this)); action->setToolTip(xi18n("Import, export or send project")); action->setWhatsThis( xi18n("Imports, exports or sends project.")); connect(action, SIGNAL(triggered()), this, SLOT(slotProjectImportExportOrSend())); setupMainMenuActionShortcut(action); ac->addAction("project_close", action = d->action_close = new KexiMenuWidgetAction( koIcon("project-development-close"), xi18n("&Close Project"), this)); action->setToolTip(xi18n("Close the current project")); action->setWhatsThis(xi18n("Closes the current project.")); connect(action, SIGNAL(triggered()), this, SLOT(slotProjectClose())); setupMainMenuActionShortcut(action); ac->addAction("quit", action = new KexiMenuWidgetAction(KStandardAction::Quit, this)); connect(action, SIGNAL(triggered()), this, SLOT(slotProjectQuit())); action->setWhatsThis(xi18n("Quits Kexi application.")); setupMainMenuActionShortcut(action); #ifdef KEXI_SHOW_UNIMPLEMENTED d->action_project_relations = addAction("project_relations", KexiIcon("database-relations"), futureI18n("&Relationships...")); d->action_project_relations->setToolTip(futureI18n("Project relationships")); d->action_project_relations->setWhatsThis(futureI18n("Shows project relationships.")); connect(d->action_project_relations, SIGNAL(triggered()), this, SLOT(slotProjectRelations())); #else d->action_project_relations = d->dummy_action; #endif d->action_tools_import_project = addAction("tools_import_project", KexiIcon("database-import"), xi18n("&Import Database...")); d->action_tools_import_project->setToolTip(xi18n("Import entire database as a Kexi project")); d->action_tools_import_project->setWhatsThis( xi18n("Imports entire database as a Kexi project.")); connect(d->action_tools_import_project, SIGNAL(triggered()), this, SLOT(slotToolsImportProject())); d->action_tools_data_import = addAction("tools_import_tables", koIcon("document-import"), xi18n("Import Tables...")); d->action_tools_data_import->setToolTip(xi18n("Import data from an external source into this project")); d->action_tools_data_import->setWhatsThis(xi18n("Imports data from an external source into this project.")); connect(d->action_tools_data_import, SIGNAL(triggered()), this, SLOT(slotToolsImportTables())); d->action_tools_compact_database = addAction("tools_compact_database", //! @todo icon koIcon("application-x-compress"), xi18n("&Compact Database...")); d->action_tools_compact_database->setToolTip(xi18n("Compact the current database project")); d->action_tools_compact_database->setWhatsThis( xi18n("Compacts the current database project, so it will take less space and work faster.")); connect(d->action_tools_compact_database, SIGNAL(triggered()), this, SLOT(slotToolsCompactDatabase())); if (d->userMode) d->action_project_import_data_table = 0; else { d->action_project_import_data_table = addAction("project_import_data_table", KexiIcon("document-empty"), /*! @todo: change to "file_import" with a table or so */ xi18nc("Import->Table Data From File...", "Import Data From &File...")); d->action_project_import_data_table->setToolTip(xi18n("Import table data from a file")); d->action_project_import_data_table->setWhatsThis(xi18n("Imports table data from a file.")); connect(d->action_project_import_data_table, SIGNAL(triggered()), this, SLOT(slotProjectImportDataTable())); } d->action_project_export_data_table = addAction("project_export_data_table", KexiIcon("table"), /*! @todo: change to "file_export" with a table or so */ xi18nc("Export->Table or Query Data to File...", "Export Data to &File...")); d->action_project_export_data_table->setToolTip( xi18n("Export data from the active table or query to a file")); d->action_project_export_data_table->setWhatsThis( xi18n("Exports data from the active table or query to a file.")); connect(d->action_project_export_data_table, SIGNAL(triggered()), this, SLOT(slotProjectExportDataTable())); //! @todo new QAction(xi18n("From File..."), "document-open", 0, //! this, SLOT(slotImportFile()), actionCollection(), "project_import_file"); //! @todo new QAction(xi18n("From Server..."), "network-server-database", 0, //! this, SLOT(slotImportServer()), actionCollection(), "project_import_server"); #ifdef KEXI_SHOW_UNIMPLEMENTED ac->addAction("project_print", d->action_project_print = KStandardAction::print(this, SLOT(slotProjectPrint()), this)); d->action_project_print->setToolTip(futureI18n("Print data from the active table or query")); d->action_project_print->setWhatsThis(futureI18n("Prints data from the active table or query.")); ac->addAction("project_print_preview", d->action_project_print_preview = KStandardAction::printPreview( this, SLOT(slotProjectPrintPreview()), this)); d->action_project_print_preview->setToolTip( futureI18n("Show print preview for the active table or query")); d->action_project_print_preview->setWhatsThis( futureI18n("Shows print preview for the active table or query.")); d->action_project_print_setup = addAction("project_print_setup", koIcon("configure"), futureI18n("Print Set&up...")); //!< @todo document-page-setup could be a better icon d->action_project_print_setup->setToolTip( futureI18n("Show print setup for the active table or query")); d->action_project_print_setup->setWhatsThis( futureI18n("Shows print setup for the active table or query.")); connect(d->action_project_print_setup, SIGNAL(triggered()), this, SLOT(slotProjectPageSetup())); #else d->action_project_print = d->dummy_action; d->action_project_print_preview = d->dummy_action; d->action_project_print_setup = d->dummy_action; #endif //EDIT MENU d->action_edit_cut = createSharedAction(KStandardAction::Cut); d->action_edit_copy = createSharedAction(KStandardAction::Copy); d->action_edit_paste = createSharedAction(KStandardAction::Paste); if (d->userMode) d->action_edit_paste_special_data_table = 0; else { d->action_edit_paste_special_data_table = addAction( "edit_paste_special_data_table", d->action_edit_paste->icon(), xi18nc("Paste Special->As Data &Table...", "Paste Special...")); d->action_edit_paste_special_data_table->setToolTip( xi18n("Paste clipboard data as a table")); d->action_edit_paste_special_data_table->setWhatsThis( xi18n("Pastes clipboard data as a table.")); connect(d->action_edit_paste_special_data_table, SIGNAL(triggered()), this, SLOT(slotEditPasteSpecialDataTable())); } d->action_edit_copy_special_data_table = addAction( "edit_copy_special_data_table", KexiIcon("table"), xi18nc("Copy Special->Table or Query Data...", "Copy Special...")); d->action_edit_copy_special_data_table->setToolTip( xi18n("Copy selected table or query data to clipboard")); d->action_edit_copy_special_data_table->setWhatsThis( xi18n("Copies selected table or query data to clipboard.")); connect(d->action_edit_copy_special_data_table, SIGNAL(triggered()), this, SLOT(slotEditCopySpecialDataTable())); d->action_edit_undo = createSharedAction(KStandardAction::Undo); d->action_edit_undo->setWhatsThis(xi18n("Reverts the most recent editing action.")); d->action_edit_redo = createSharedAction(KStandardAction::Redo); d->action_edit_redo->setWhatsThis(xi18n("Reverts the most recent undo action.")); ac->addAction("edit_find", d->action_edit_find = KStandardAction::find( this, SLOT(slotEditFind()), this)); d->action_edit_find->setToolTip(xi18n("Find text")); d->action_edit_find->setWhatsThis(xi18n("Looks up the first occurrence of a piece of text.")); ac->addAction("edit_findnext", d->action_edit_findnext = KStandardAction::findNext( this, SLOT(slotEditFindNext()), this)); ac->addAction("edit_findprevious", d->action_edit_findprev = KStandardAction::findPrev( this, SLOT(slotEditFindPrevious()), this)); ac->addAction("edit_replace", d->action_edit_replace = KStandardAction::replace( this, SLOT(slotEditReplace()), this)); d->action_edit_replace_all = addAction("edit_replace_all", xi18n("Replace All")); d->action_edit_select_all = createSharedAction(KStandardAction::SelectAll); d->action_edit_delete = createSharedAction(xi18n("&Delete"), koIconName("edit-delete"), QKeySequence(), "edit_delete"); d->action_edit_delete->setToolTip(xi18n("Delete selected object")); d->action_edit_delete->setWhatsThis(xi18n("Deletes currently selected object.")); d->action_edit_delete_row = createSharedAction(xi18n("Delete Record"), KexiIconName("edit-table-delete-row"), QKeySequence(Qt::CTRL + Qt::Key_Delete), "edit_delete_row"); d->action_edit_delete_row->setToolTip(xi18n("Delete the current record")); d->action_edit_delete_row->setWhatsThis(xi18n("Deletes the current record.")); d->action_edit_clear_table = createSharedAction(xi18n("Clear Table Contents..."), KexiIconName("edit-table-clear"), QKeySequence(), "edit_clear_table"); d->action_edit_clear_table->setToolTip(xi18n("Clear table contents")); d->action_edit_clear_table->setWhatsThis(xi18n("Clears table contents.")); setActionVolatile(d->action_edit_clear_table, true); d->action_edit_edititem = createSharedAction(xi18n("Edit Item"), QString(), QKeySequence(), /* CONFLICT in TV: Qt::Key_F2, */ "edit_edititem"); d->action_edit_edititem->setToolTip(xi18n("Edit currently selected item")); d->action_edit_edititem->setWhatsThis(xi18n("Edits currently selected item.")); d->action_edit_insert_empty_row = createSharedAction(xi18n("&Insert Empty Row"), KexiIconName("edit-table-insert-row"), QKeySequence(Qt::SHIFT | Qt::CTRL | Qt::Key_Insert), "edit_insert_empty_row"); setActionVolatile(d->action_edit_insert_empty_row, true); d->action_edit_insert_empty_row->setToolTip(xi18n("Insert one empty row above")); d->action_edit_insert_empty_row->setWhatsThis( xi18n("Inserts one empty row above currently selected table row.")); //VIEW MENU /* UNUSED, see KexiToggleViewModeAction if (!d->userMode) { d->action_view_mode = new QActionGroup(this); ac->addAction( "view_data_mode", d->action_view_data_mode = new KToggleAction( KexiIcon("data-view"), xi18n("&Data View"), d->action_view_mode) ); // d->action_view_data_mode->setObjectName("view_data_mode"); d->action_view_data_mode->setShortcut(QKeySequence("F6")); //d->action_view_data_mode->setExclusiveGroup("view_mode"); d->action_view_data_mode->setToolTip(xi18n("Switch to data view")); d->action_view_data_mode->setWhatsThis(xi18n("Switches to data view.")); d->actions_for_view_modes.insert( Kexi::DataViewMode, d->action_view_data_mode ); connect(d->action_view_data_mode, SIGNAL(triggered()), this, SLOT(slotViewDataMode())); } else { d->action_view_mode = 0; d->action_view_data_mode = 0; } if (!d->userMode) { ac->addAction( "view_design_mode", d->action_view_design_mode = new KToggleAction( KexiIcon("design-view"), xi18n("D&esign View"), d->action_view_mode) ); // d->action_view_design_mode->setObjectName("view_design_mode"); d->action_view_design_mode->setShortcut(QKeySequence("F7")); //d->action_view_design_mode->setExclusiveGroup("view_mode"); d->action_view_design_mode->setToolTip(xi18n("Switch to design view")); d->action_view_design_mode->setWhatsThis(xi18n("Switches to design view.")); d->actions_for_view_modes.insert( Kexi::DesignViewMode, d->action_view_design_mode ); connect(d->action_view_design_mode, SIGNAL(triggered()), this, SLOT(slotViewDesignMode())); } else d->action_view_design_mode = 0; if (!d->userMode) { ac->addAction( "view_text_mode", d->action_view_text_mode = new KToggleAction( KexiIcon("sql-view"), xi18n("&Text View"), d->action_view_mode) ); d->action_view_text_mode->setObjectName("view_text_mode"); d->action_view_text_mode->setShortcut(QKeySequence("F8")); //d->action_view_text_mode->setExclusiveGroup("view_mode"); d->action_view_text_mode->setToolTip(xi18n("Switch to text view")); d->action_view_text_mode->setWhatsThis(xi18n("Switches to text view.")); d->actions_for_view_modes.insert( Kexi::TextViewMode, d->action_view_text_mode ); connect(d->action_view_text_mode, SIGNAL(triggered()), this, SLOT(slotViewTextMode())); } else d->action_view_text_mode = 0; */ if (d->isProjectNavigatorVisible) { ac->addAction("view_navigator", d->action_show_nav = new KToggleAction(xi18n("Show Project Navigator"), this)); d->action_show_nav->setChecked(true); d->action_show_nav->setShortcut(QKeySequence("Alt+0")); d->action_show_nav->setToolTip(xi18n("Show the Project Navigator pane")); d->action_show_nav->setWhatsThis(xi18n("Shows the Project Navigator pane.")); connect(d->action_show_nav, SIGNAL(triggered()), this, SLOT(slotToggleProjectNavigator())); } else { d->action_show_nav = 0; } if (d->isProjectNavigatorVisible) { // Shortcut taken from "Activate Projects pane" http://doc.qt.io/qtcreator/creator-keyboard-shortcuts.html d->action_activate_nav = addAction("activate_navigator", xi18n("Activate Project Navigator"), "Alt+X"); d->action_activate_nav->setToolTip(xi18n("Activate the Project Navigator pane")); d->action_activate_nav->setWhatsThis(xi18n("Activates the Project Navigator pane. If it is hidden, shows it first.")); connect(d->action_activate_nav, SIGNAL(triggered()), this, SLOT(slotActivateNavigator())); } else { d->action_activate_nav = 0; } d->action_activate_mainarea = addAction("activate_mainarea", xi18n("Activate main area") // , "Alt+2"? //! @todo activate_mainarea: pressing Esc in project nav or propeditor should move back to the main area ); d->action_activate_mainarea->setToolTip(xi18n("Activate the main area")); d->action_activate_mainarea->setWhatsThis(xi18n("Activates the main area.")); connect(d->action_activate_mainarea, SIGNAL(triggered()), this, SLOT(slotActivateMainArea())); //! @todo windows with "_3" prefix have conflicting auto shortcut set to Alt+3 -> remove that! if (!d->userMode) { ac->addAction("view_propeditor", d->action_show_propeditor = new KToggleAction(xi18n("Show Property Pane"), this)); d->action_show_propeditor->setShortcut(QKeySequence("Alt+3")); d->action_show_propeditor->setToolTip(xi18n("Show the Property pane")); d->action_show_propeditor->setWhatsThis(xi18n("Shows the Property pane.")); connect(d->action_show_propeditor, SIGNAL(triggered()), this, SLOT(slotTogglePropertyEditor())); } else { d->action_show_propeditor = 0; } if (!d->userMode) { d->action_activate_propeditor = addAction("activate_propeditor", xi18n("Activate Property Pane"), "Alt+-"); d->action_activate_propeditor->setToolTip(xi18n("Activate the Property pane")); d->action_activate_propeditor->setWhatsThis(xi18n("Activates the Property pane. If it is hidden, shows it first.")); connect(d->action_activate_propeditor, SIGNAL(triggered()), this, SLOT(slotActivatePropertyPane())); } else { d->action_activate_propeditor = 0; } d->action_tools_locate = addAction("tools_locate", xi18n("Locate..."), "Ctrl+K"); d->action_tools_locate->setToolTip(xi18n("Switch to Global Locate box")); d->action_tools_locate->setWhatsThis(xi18n("Switches to Global Locate box.")); // (connection is added elsewhere) //DATA MENU d->action_data_save_row = createSharedAction(xi18n("&Save Record"), koIconName("dialog-ok"), QKeySequence(Qt::SHIFT + Qt::Key_Return), "data_save_row"); d->action_data_save_row->setToolTip(xi18n("Save changes made to the current record")); d->action_data_save_row->setWhatsThis(xi18n("Saves changes made to the current record.")); //temp. disable because of problems with volatile actions setActionVolatile( d->action_data_save_row, true ); d->action_data_cancel_row_changes = createSharedAction(xi18n("&Cancel Record Changes"), koIconName("dialog-cancel"), QKeySequence(Qt::Key_Escape), "data_cancel_row_changes"); d->action_data_cancel_row_changes->setToolTip( xi18n("Cancel changes made to the current record")); d->action_data_cancel_row_changes->setWhatsThis( xi18n("Cancels changes made to the current record.")); //temp. disable because of problems with volatile actions setActionVolatile( d->action_data_cancel_row_changes, true ); d->action_data_execute = createSharedAction( xi18n("&Execute"), koIconName("media-playback-start"), QKeySequence(), "data_execute"); //! @todo d->action_data_execute->setToolTip(xi18n("")); //! @todo d->action_data_execute->setWhatsThis(xi18n("")); #ifdef KEXI_SHOW_UNIMPLEMENTED action = createSharedAction(futureI18n("&Filter"), koIconName("view-filter"), QKeySequence(), "data_filter"); setActionVolatile(action, true); #endif //! @todo action->setToolTip(xi18n("")); //! @todo action->setWhatsThis(xi18n("")); // - record-navigation related actions createSharedAction(KexiRecordNavigator::Actions::moveToFirstRecord(), QKeySequence(), "data_go_to_first_record"); createSharedAction(KexiRecordNavigator::Actions::moveToPreviousRecord(), QKeySequence(), "data_go_to_previous_record"); createSharedAction(KexiRecordNavigator::Actions::moveToNextRecord(), QKeySequence(), "data_go_to_next_record"); createSharedAction(KexiRecordNavigator::Actions::moveToLastRecord(), QKeySequence(), "data_go_to_last_record"); createSharedAction(KexiRecordNavigator::Actions::moveToNewRecord(), QKeySequence(), "data_go_to_new_record"); //FORMAT MENU #ifdef KEXI_SHOW_UNIMPLEMENTED d->action_format_font = createSharedAction(xi18n("&Font..."), koIconName("fonts-package"), QKeySequence(), "format_font"); d->action_format_font->setToolTip(xi18n("Change font for selected object")); d->action_format_font->setWhatsThis(xi18n("Changes font for selected object.")); #else d->action_format_font = d->dummy_action; #endif //TOOLS MENU //WINDOW MENU d->action_close_tab = addAction("close_tab", koIcon("tab-close"), xi18n("&Close Tab"), "Ctrl+W"); d->action_close_tab->setToolTip(xi18n("Close the current tab")); d->action_close_tab->setWhatsThis(xi18n("Closes the current tab.")); connect(d->action_close_tab, SIGNAL(triggered()), this, SLOT(closeCurrentWindow())); d->action_close_all_tabs = addAction("close_all_tabs", QIcon(), xi18n("Cl&ose All Tabs")); d->action_close_all_tabs->setToolTip(xi18n("Close all tabs")); d->action_close_all_tabs->setWhatsThis(xi18n("Closes all tabs.")); connect(d->action_close_all_tabs, SIGNAL(triggered()), this, SLOT(closeAllWindows())); d->action_next_tab = addAction("next_tab", koIcon("go-next"), KStandardShortcut::label(KStandardShortcut::TabNext)); d->action_next_tab->setWhatsThis(KStandardShortcut::whatsThis(KStandardShortcut::TabNext)); d->action_next_tab->setShortcuts(KStandardShortcut::shortcut(KStandardShortcut::TabNext)); connect(d->action_next_tab, SIGNAL(triggered()), this, SLOT(activateNextTab())); d->action_previous_tab = addAction("previous_tab", koIcon("go-previous"), KStandardShortcut::label(KStandardShortcut::TabPrev)); d->action_previous_tab->setWhatsThis(KStandardShortcut::whatsThis(KStandardShortcut::TabPrev)); d->action_previous_tab->setShortcuts(KStandardShortcut::shortcut(KStandardShortcut::TabPrev)); connect(d->action_previous_tab, SIGNAL(triggered()), this, SLOT(activatePreviousTab())); d->action_window_fullscreen = KStandardAction::fullScreen(this, SLOT(toggleFullScreen(bool)), this, ac); ac->addAction("full_screen", d->action_window_fullscreen); //SETTINGS MENU //! @todo put 'configure keys' into settings view #ifdef KEXI_SHOW_UNIMPLEMENTED //! @todo toolbars configuration will be handled in a special way #endif #ifdef KEXI_MACROS_SUPPORT Kexi::tempShowMacros() = true; #else Kexi::tempShowMacros() = false; #endif #ifdef KEXI_SCRIPTS_SUPPORT Kexi::tempShowScripts() = true; #else Kexi::tempShowScripts() = false; #endif #ifdef KEXI_SHOW_UNIMPLEMENTED //! @todo implement settings window in a specific way ac->addAction("settings", action = d->action_settings = new KexiMenuWidgetAction( KStandardAction::Preferences, this)); action->setObjectName("settings"); //action->setText(futureI18n("Settings...")); action->setToolTip(futureI18n("Show Kexi settings")); action->setWhatsThis(futureI18n("Shows Kexi settings.")); connect(action, SIGNAL(triggered()), this, SLOT(slotSettings())); setupMainMenuActionShortcut(action); #else d->action_settings = d->dummy_action; #endif //! @todo reenable 'tip of the day' later #if 0 KStandardAction::tipOfDay(this, SLOT(slotTipOfTheDayAction()), actionCollection()) ->setWhatsThis(xi18n("This shows useful tips on the use of this application.")); #endif // ----- declare action categories, so form's "assign action to button" // (and macros in the future) will be able to recognize category // of actions and filter them ----------------------------------- //! @todo shouldn't we move this to core? Kexi::ActionCategories *acat = Kexi::actionCategories(); acat->addAction("data_execute", Kexi::PartItemActionCategory); //! @todo unused for now acat->addWindowAction("data_filter", KexiPart::TableObjectType, KexiPart::QueryObjectType, KexiPart::FormObjectType); acat->addWindowAction("data_save_row", KexiPart::TableObjectType, KexiPart::QueryObjectType, KexiPart::FormObjectType); acat->addWindowAction("data_cancel_row_changes", KexiPart::TableObjectType, KexiPart::QueryObjectType, KexiPart::FormObjectType); acat->addWindowAction("delete_table_row", KexiPart::TableObjectType, KexiPart::QueryObjectType, KexiPart::FormObjectType); //! @todo support this in KexiPart::FormObjectType as well acat->addWindowAction("data_sort_az", KexiPart::TableObjectType, KexiPart::QueryObjectType); //! @todo support this in KexiPart::FormObjectType as well acat->addWindowAction("data_sort_za", KexiPart::TableObjectType, KexiPart::QueryObjectType); //! @todo support this in KexiPart::FormObjectType as well acat->addWindowAction("edit_clear_table", KexiPart::TableObjectType, KexiPart::QueryObjectType); //! @todo support this in KexiPart::FormObjectType as well acat->addWindowAction("edit_copy_special_data_table", KexiPart::TableObjectType, KexiPart::QueryObjectType); //! @todo support this in FormObjectType as well acat->addWindowAction("project_export_data_table", KexiPart::TableObjectType, KexiPart::QueryObjectType); // GlobalActions, etc. acat->addAction("edit_copy", Kexi::GlobalActionCategory | Kexi::PartItemActionCategory); acat->addAction("edit_cut", Kexi::GlobalActionCategory | Kexi::PartItemActionCategory); acat->addAction("edit_paste", Kexi::GlobalActionCategory | Kexi::PartItemActionCategory); acat->addAction("edit_delete", Kexi::GlobalActionCategory | Kexi::PartItemActionCategory | Kexi::WindowActionCategory, KexiPart::TableObjectType, KexiPart::QueryObjectType, KexiPart::FormObjectType); acat->addAction("edit_delete_row", Kexi::GlobalActionCategory | Kexi::WindowActionCategory, KexiPart::TableObjectType, KexiPart::QueryObjectType, KexiPart::FormObjectType); acat->addAction("edit_edititem", Kexi::PartItemActionCategory | Kexi::WindowActionCategory, KexiPart::TableObjectType, KexiPart::QueryObjectType); acat->addAction("edit_find", Kexi::GlobalActionCategory | Kexi::WindowActionCategory, KexiPart::TableObjectType, KexiPart::QueryObjectType, KexiPart::FormObjectType); acat->addAction("edit_findnext", Kexi::GlobalActionCategory | Kexi::WindowActionCategory, KexiPart::TableObjectType, KexiPart::QueryObjectType, KexiPart::FormObjectType); acat->addAction("edit_findprevious", Kexi::GlobalActionCategory | Kexi::WindowActionCategory, KexiPart::TableObjectType, KexiPart::QueryObjectType, KexiPart::FormObjectType); acat->addAction("edit_replace", Kexi::GlobalActionCategory | Kexi::WindowActionCategory, KexiPart::TableObjectType, KexiPart::QueryObjectType, KexiPart::FormObjectType); acat->addAction("edit_paste_special_data_table", Kexi::GlobalActionCategory); acat->addAction("help_about_app", Kexi::GlobalActionCategory); acat->addAction("help_about_kde", Kexi::GlobalActionCategory); acat->addAction("help_contents", Kexi::GlobalActionCategory); acat->addAction("help_report_bug", Kexi::GlobalActionCategory); acat->addAction("help_whats_this", Kexi::GlobalActionCategory); acat->addAction("help_donate", Kexi::GlobalActionCategory); // disabled for now acat->addAction("switch_application_language", Kexi::GlobalActionCategory); acat->addAction("options_configure_keybinding", Kexi::GlobalActionCategory); acat->addAction("project_close", Kexi::GlobalActionCategory); acat->addAction("project_import_data_table", Kexi::GlobalActionCategory); acat->addAction("project_new", Kexi::GlobalActionCategory); acat->addAction("project_open", Kexi::GlobalActionCategory); #ifdef KEXI_QUICK_PRINTING_SUPPORT //! @todo support this in FormObjectType, ReportObjectType as well as others acat->addAction("project_print", Kexi::WindowActionCategory, KexiPart::TableObjectType, KexiPart::QueryObjectType); //! @todo support this in FormObjectType, ReportObjectType as well as others acat->addAction("project_print_preview", Kexi::WindowActionCategory, KexiPart::TableObjectType, KexiPart::QueryObjectType); //! @todo support this in FormObjectType, ReportObjectType as well as others acat->addAction("project_print_setup", Kexi::WindowActionCategory, KexiPart::TableObjectType, KexiPart::QueryObjectType); #endif acat->addAction("quit", Kexi::GlobalActionCategory); acat->addAction("tools_compact_database", Kexi::GlobalActionCategory); acat->addAction("tools_import_project", Kexi::GlobalActionCategory); acat->addAction("tools_import_tables", Kexi::GlobalActionCategory); acat->addAction("view_data_mode", Kexi::GlobalActionCategory); acat->addAction("view_design_mode", Kexi::GlobalActionCategory); acat->addAction("view_text_mode", Kexi::GlobalActionCategory); acat->addAction("view_mainarea", Kexi::GlobalActionCategory); acat->addAction("view_navigator", Kexi::GlobalActionCategory); acat->addAction("activate_navigator", Kexi::GlobalActionCategory); acat->addAction("view_propeditor", Kexi::GlobalActionCategory); acat->addAction("activate_mainarea", Kexi::GlobalActionCategory); acat->addAction("activate_propeditor", Kexi::GlobalActionCategory); acat->addAction("close_tab", Kexi::GlobalActionCategory | Kexi::WindowActionCategory); acat->setAllObjectTypesSupported("close_tab", true); acat->addAction("close_all_tabs", Kexi::GlobalActionCategory | Kexi::WindowActionCategory); acat->setAllObjectTypesSupported("close_all_tabs", true); acat->addAction("next_tab", Kexi::GlobalActionCategory); acat->addAction("previous_tab", Kexi::GlobalActionCategory); acat->addAction("full_screen", Kexi::GlobalActionCategory); //skipped - design view only acat->addAction("format_font", Kexi::NoActionCategory); acat->addAction("project_save", Kexi::NoActionCategory); acat->addAction("edit_insert_empty_row", Kexi::NoActionCategory); //! @todo support this in KexiPart::TableObjectType, KexiPart::QueryObjectType later acat->addAction("edit_select_all", Kexi::NoActionCategory); //! @todo support this in KexiPart::TableObjectType, KexiPart::QueryObjectType, KexiPart::FormObjectType later acat->addAction("edit_redo", Kexi::NoActionCategory); //! @todo support this in KexiPart::TableObjectType, KexiPart::QueryObjectType, KexiPart::FormObjectType later acat->addAction("edit_undo", Kexi::NoActionCategory); //record-navigation related actions acat->addAction("data_go_to_first_record", Kexi::WindowActionCategory, KexiPart::TableObjectType, KexiPart::QueryObjectType, KexiPart::FormObjectType); acat->addAction("data_go_to_previous_record", Kexi::WindowActionCategory, KexiPart::TableObjectType, KexiPart::QueryObjectType, KexiPart::FormObjectType); acat->addAction("data_go_to_next_record", Kexi::WindowActionCategory, KexiPart::TableObjectType, KexiPart::QueryObjectType, KexiPart::FormObjectType); acat->addAction("data_go_to_last_record", Kexi::WindowActionCategory, KexiPart::TableObjectType, KexiPart::QueryObjectType, KexiPart::FormObjectType); acat->addAction("data_go_to_new_record", Kexi::WindowActionCategory, KexiPart::TableObjectType, KexiPart::QueryObjectType, KexiPart::FormObjectType); //skipped - internal: acat->addAction("tablepart_create", Kexi::NoActionCategory); acat->addAction("querypart_create", Kexi::NoActionCategory); acat->addAction("formpart_create", Kexi::NoActionCategory); acat->addAction("reportpart_create", Kexi::NoActionCategory); acat->addAction("macropart_create", Kexi::NoActionCategory); acat->addAction("scriptpart_create", Kexi::NoActionCategory); } void KexiMainWindow::setupMainMenu() { KActionCollection *ac = actionCollection(); QMenuBar *menu = menuBar(); { QMenu *fileMenu = menu->addMenu(xi18n("&File")); if (!d->userMode) { d->addAction(fileMenu, "project_new"); fileMenu->addSeparator(); d->addAction(fileMenu, "project_open"); d->addAction(fileMenu, "project_open_recent"); fileMenu->addSeparator(); } fileMenu->addAction(d->action_save); if (!d->userMode) { fileMenu->addAction(d->action_save_as); } fileMenu->addSeparator(); if (!d->userMode) { fileMenu->addAction(d->action_tools_import_project); fileMenu->addSeparator(); } #ifdef KEXI_SHOW_UNIMPLEMENTED fileMenu->addAction(d->action_project_print); fileMenu->addAction(d->action_project_print_preview); fileMenu->addAction(d->action_project_print_setup); fileMenu->addSeparator(); #endif #ifdef KEXI_SHOW_UNIMPLEMENTED fileMenu->addAction(d->action_project_properties); fileMenu->addSeparator(); #endif fileMenu->addAction(d->action_close_tab); fileMenu->addAction(d->action_close_all_tabs); if (!d->userMode) { fileMenu->addAction(d->action_close); } fileMenu->addSeparator(); d->addAction(fileMenu, "quit"); } { QMenu *editMenu = menu->addMenu(xi18n("&Edit")); editMenu->addAction(d->action_edit_undo); editMenu->addAction(d->action_edit_redo); editMenu->addSeparator(); editMenu->addAction(d->action_edit_cut); editMenu->addAction(d->action_edit_copy); editMenu->addAction(d->action_edit_copy_special_data_table); editMenu->addAction(d->action_edit_paste); if (!d->userMode) { editMenu->addAction(d->action_edit_paste_special_data_table); } editMenu->addSeparator(); editMenu->addAction(d->action_edit_select_all); editMenu->addSeparator(); { QMenu *findReplaceMenu = editMenu->addMenu(xi18n("&Find/Replace")); findReplaceMenu->addAction(d->action_edit_find); findReplaceMenu->addAction(d->action_edit_findnext); findReplaceMenu->addAction(d->action_edit_findprev); #ifdef KEXI_SHOW_UNIMPLEMENTED findReplaceMenu->addSeparator(); findReplaceMenu->addAction(d->action_edit_replace); findReplaceMenu->addAction(d->action_edit_replace_all); #endif } editMenu->addSeparator(); editMenu->addAction(d->action_edit_delete); // in local toolbar already: editMenu->addAction(d->action_data_save_row); // in local toolbar already: editMenu->addAction(d->action_data_cancel_row_changes); //TODO move to local menu: editMenu->addAction(d->action_edit_edititem); //TODO move to local menu: editMenu->addAction(d->action_edit_insert_empty_row); //TODO move to local menu: editMenu->addAction(d->action_data_execute); //editMenu->addSeparator(); //TODO move to local menu: editMenu->addAction(d->action_edit_delete); //TODO move to local menu: editMenu->addAction(d->action_edit_delete_row); //in local menu already editMenu->addAction(d->action_edit_clear_table); } #ifdef KEXI_SHOW_UNIMPLEMENTED { QMenu *formatMenu = menu->addMenu(xi18n("F&ormat")); formatMenu->addAction(d->action_format_font); } #endif { QMenu *toolsMenu = menu->addMenu(xi18n("&Tools")); toolsMenu->addAction(d->action_tools_locate); toolsMenu->addSeparator(); #ifdef KEXI_SHOW_UNIMPLEMENTED toolsMenu->addAction(d->action_project_relations); #endif toolsMenu->addAction(d->action_tools_compact_database); } { QMenu *dataMenu = menu->addMenu(xi18n("&Data")); if (!d->userMode) { dataMenu->addAction(d->action_project_import_data_table); dataMenu->addAction(d->action_tools_data_import); dataMenu->addSeparator(); } dataMenu->addAction(d->action_project_export_data_table); } { QMenu *windowMenu = menu->addMenu(xi18n("&Window")); windowMenu->addAction(d->action_next_tab); windowMenu->addAction(d->action_previous_tab); windowMenu->addSeparator(); windowMenu->addAction(d->action_show_nav); windowMenu->addAction(d->action_show_propeditor); } { QMenu *settingsMenu = menu->addMenu(xi18n("&Settings")); settingsMenu->addAction(d->action_window_fullscreen); settingsMenu->addSeparator(); #ifdef KEXI_SHOW_UNIMPLEMENTED settingsMenu->addAction(d->action_settings); #endif } { // add help menu actions... (KexiTabbedToolBar depends on them) KHelpMenu *helpMenu = new KHelpMenu(this, KAboutData::applicationData(), true/*showWhatsThis*/); QAction* help_report_bug_action = helpMenu->action(KHelpMenu::menuReportBug); ac->addAction(help_report_bug_action->objectName(), help_report_bug_action); QObject::disconnect(help_report_bug_action, 0, 0, 0); QObject::connect(help_report_bug_action, &QAction::triggered, this, &KexiMainWindow::slotReportBug); help_report_bug_action->setText(xi18nc("Report a bug or wish for Kexi application", "Report a &Bug or Wish...")); help_report_bug_action->setIcon(koIcon("tools-report-bug")); // good icon for toolbar help_report_bug_action->setWhatsThis(xi18n("Files a bug or wish for Kexi application.")); QAction* help_whats_this_action = helpMenu->action(KHelpMenu::menuWhatsThis); ac->addAction(help_whats_this_action->objectName(), help_whats_this_action); help_whats_this_action->setWhatsThis(xi18n("Activates a \"What's This?\" tool.")); QAction* help_contents_action = helpMenu->action(KHelpMenu::menuHelpContents); ac->addAction(help_contents_action->objectName(), help_contents_action); help_contents_action->setText(xi18n("Help")); help_contents_action->setWhatsThis(xi18n("Shows Kexi Handbook.")); QAction* help_about_app_action = helpMenu->action(KHelpMenu::menuAboutApp); ac->addAction(help_about_app_action->objectName(), help_about_app_action); help_about_app_action->setWhatsThis(xi18n("Shows information about Kexi application.")); QAction* help_about_kde_action = helpMenu->action(KHelpMenu::menuAboutKDE); ac->addAction(help_about_kde_action->objectName(), help_about_kde_action); help_about_kde_action->setWhatsThis(xi18n("Shows information about KDE.")); menu->addMenu(helpMenu->menu()); } } void KexiMainWindow::invalidateActions() { invalidateProjectWideActions(); invalidateSharedActions(); } void KexiMainWindow::invalidateSharedActions(QObject *o) { //! @todo enabling is more complex... /* d->action_edit_cut->setEnabled(true); d->action_edit_copy->setEnabled(true); d->action_edit_paste->setEnabled(true);*/ if (!o) o = focusWindow(); KexiSharedActionHost::invalidateSharedActions(o); } void KexiMainWindow::invalidateSharedActions() { invalidateSharedActions(0); } // unused, I think void KexiMainWindow::invalidateSharedActionsLater() { QTimer::singleShot(1, this, SLOT(invalidateSharedActions())); } void KexiMainWindow::invalidateProjectWideActions() { const bool has_window = currentWindow(); const bool window_dirty = currentWindow() && currentWindow()->isDirty(); const bool readOnly = d->prj && d->prj->dbConnection() && d->prj->dbConnection()->options()->isReadOnly(); //PROJECT MENU d->action_save->setEnabled(has_window && window_dirty && !readOnly); d->action_save_as->setEnabled(has_window && !readOnly); d->action_project_properties->setEnabled(d->prj); d->action_close->setEnabled(d->prj); d->action_project_relations->setEnabled(d->prj); if (d->objectViewWidget) { d->action_close_tab->setEnabled(has_window); d->action_close_all_tabs->setEnabled(has_window); } //DATA MENU if (d->action_project_import_data_table) d->action_project_import_data_table->setEnabled(d->prj && !readOnly); if (d->action_tools_data_import) d->action_tools_data_import->setEnabled(d->prj && !readOnly); d->action_project_export_data_table->setEnabled( currentWindow() && currentWindow()->part()->info()->isDataExportSupported()); if (d->action_edit_paste_special_data_table) d->action_edit_paste_special_data_table->setEnabled(d->prj && !readOnly); #ifdef KEXI_SHOW_UNIMPLEMENTED const bool printingActionsEnabled = currentWindow() && currentWindow()->part()->info()->isPrintingSupported() && !currentWindow()->neverSaved(); d->action_project_print->setEnabled(printingActionsEnabled); d->action_project_print_preview->setEnabled(printingActionsEnabled); d->action_project_print_setup->setEnabled(printingActionsEnabled); #endif //EDIT MENU //! @todo "copy special" is currently enabled only for data view mode; //! what about allowing it to enable in design view for "kexi/table" ? if (currentWindow() && currentWindow()->currentViewMode() == Kexi::DataViewMode) { KexiPart::Info *activePartInfo = currentWindow()->part()->info(); d->action_edit_copy_special_data_table->setEnabled( activePartInfo ? activePartInfo->isDataExportSupported() : false); } else { d->action_edit_copy_special_data_table->setEnabled(false); } d->action_edit_find->setEnabled(d->prj); //VIEW MENU if (d->action_show_nav) d->action_show_nav->setEnabled(d->prj); d->action_activate_mainarea->setEnabled(d->prj); //CREATE MENU if (d->tabbedToolBar && d->tabbedToolBar->createWidgetToolBar()) d->tabbedToolBar->createWidgetToolBar()->setEnabled(d->prj); // DATA MENU // WINDOW MENU if (d->objectViewWidget) { d->action_next_tab->setEnabled(d->objectViewWidget->tabWidget()->count() > 1); d->action_previous_tab->setEnabled(d->objectViewWidget->tabWidget()->count() > 1); } //TOOLS MENU // "compact db" supported if there's no db or the current db supports compacting and is opened r/w: d->action_tools_compact_database->setEnabled( !d->prj || (!readOnly && d->prj && d->prj->dbConnection() && (d->prj->dbConnection()->driver()->features() & KDbDriver::CompactingDatabaseSupported)) ); //DOCKS if (d->objectViewWidget && d->objectViewWidget->projectNavigator()) { d->objectViewWidget->projectNavigator()->setEnabled(d->prj); } if (d->objectViewWidget && d->objectViewWidget->propertyPane()) { d->objectViewWidget->propertyPane()->setEnabled(d->prj); } } tristate KexiMainWindow::startup() { tristate result = true; switch (KexiStartupHandler::global()->action()) { case KexiStartupHandler::CreateBlankProject: d->updatePropEditorVisibility(Kexi::NoViewMode); break; #ifdef KEXI_PROJECT_TEMPLATES case KexiStartupHandler::CreateFromTemplate: result = createProjectFromTemplate(*KexiStartupHandler::global()->projectData()); break; #endif case KexiStartupHandler::OpenProject: result = openProject(*KexiStartupHandler::global()->projectData()); break; case KexiStartupHandler::ImportProject: result = showProjectMigrationWizard( KexiStartupHandler::global()->importActionData().mimeType, KexiStartupHandler::global()->importActionData().fileName ); break; case KexiStartupHandler::ShowWelcomeScreen: //! @todo show welcome screen as soon as is available QTimer::singleShot(100, this, SLOT(slotProjectWelcome())); break; default: d->updatePropEditorVisibility(Kexi::NoViewMode); } return result; } static QString internalReason(const KDbResult &result) { const QString msg = result.message(); if (msg.isEmpty()) { return QString(); } return xi18n("
(reason: %1)", msg); } tristate KexiMainWindow::openProject(const KexiProjectData& projectData) { //qDebug() << projectData; QScopedPointer prj(createKexiProjectObject(projectData)); if (~KexiDBPasswordDialog::getPasswordIfNeeded(prj->data()->connectionData(), this)) { return cancelled; } bool incompatibleWithKexi; tristate res = prj->open(&incompatibleWithKexi); if (prj->data()->connectionData()->isPasswordNeeded()) { // password was supplied in this session, and shouldn't be stored or reused afterwards, // so let's remove it prj->data()->connectionData()->setPassword(QString()); } if (~res) { return cancelled; } else if (!res) { if (incompatibleWithKexi) { if (KMessageBox::Yes == KMessageBox::questionYesNo(this, xi18nc("@info (don't add tags around %1, it's done already)", "Database project %1 does not appear to have been created using Kexi." "Do you want to import it as a new Kexi project?", projectData.infoString()), QString(), KGuiItem(xi18nc("@action:button Import Database", "&Import..."), KexiIconName("database-import")), KStandardGuiItem::cancel())) { const bool anotherProjectAlreadyOpened = prj; tristate res = showProjectMigrationWizard("application/x-kexi-connectiondata", projectData.databaseName(), *projectData.connectionData()); if (!anotherProjectAlreadyOpened) //the project could have been opened within this instance return res; //always return cancelled because even if migration succeeded, new Kexi instance //will be started if user wanted to open the imported db return cancelled; } return cancelled; } return false; } // success d->prj = prj.take(); d->modeSelector->setCurrentMode(Kexi::EditGlobalMode); d->prj->data()->setLastOpened(QDateTime::currentDateTime()); Kexi::recentProjects()->addProjectData(*d->prj->data()); updateReadOnlyState(); invalidateActions(); setMessagesEnabled(false); QTimer::singleShot(1, this, SLOT(slotAutoOpenObjectsLater())); if (d->tabbedToolBar) { d->tabbedToolBar->showTab("create");// not needed since create toolbar already shows toolbar! move when kexi starts d->tabbedToolBar->hideTab("form");//temporalily until createToolbar is split d->tabbedToolBar->hideTab("report");//temporalily until createToolbar is split // make sure any tab is activated d->tabbedToolBar->setCurrentTab(0); } return true; } tristate KexiMainWindow::openProject(const KexiProjectData& data, const QString& shortcutPath, bool *opened) { if (!shortcutPath.isEmpty() && d->prj) { const tristate result = openProjectInExternalKexiInstance( shortcutPath, QString(), QString()); if (result == true) { *opened = true; } return result; } return openProject(data); } tristate KexiMainWindow::createProjectFromTemplate(const KexiProjectData& projectData) { Q_UNUSED(projectData); #ifdef KEXI_PROJECT_TEMPLATES QStringList mimetypes; mimetypes.append(KDb::defaultFileBasedDriverMimeType()); QString fname; //! @todo KEXI3 add equivalent of kfiledialog:/// const QString startDir("kfiledialog:///OpenExistingOrCreateNewProject"/*as in KexiNewProjectWizard*/); const QString caption(xi18nc("@window:title", "Select New Project's Location")); while (true) { if (fname.isEmpty() && !projectData.connectionData()->databaseName().isEmpty()) { //propose filename from db template name fname = projectData.connectionData()->databaseName(); } const bool specialDir = fname.isEmpty(); qDebug() << fname << "............."; QFileDialog dlg(specialDir ? QUrl(startDir) : QUrl(), QString(), this); dlg.setModal(true); dlg.setMimeFilter(mimetypes); if (!specialDir) dlg.selectUrl(QUrl::fromLocalFile(fname); // may also be a filename dlg.setFileMode(QFileDialog::ExistingFile); dlg.setFileMode(QFileDialog::AcceptOpen); dlg.setWindowTitle(caption); if (QDialog::Accepted != dlg.exec()) { return cancelled; } if (dlg.selectedFiles().isEmpty() { return cancelled; } fname = dlg.selectedFiles().first(); if (fname.isEmpty()) { return cancelled; } if (KexiUtils::askForFileOverwriting(fname, this)) { break; } } QFile sourceFile(projectData.connectionData()->fileName()); if (!sourceFile.copy(fname)) { //! @todo show error from with QFile::FileError return false; } return openProject(fname, 0, QString(), projectData.autoopenObjects/*copy*/); #else return false; #endif } void KexiMainWindow::updateReadOnlyState() { const bool readOnly = d->prj && d->prj->dbConnection() && d->prj->dbConnection()->options()->isReadOnly(); //! @todo KEXI3 show read-only flag in the GUI because we have no statusbar if (d->objectViewWidget && d->objectViewWidget->projectNavigator()) { d->objectViewWidget->projectNavigator()->setReadOnly(readOnly); } // update "insert ....." actions for every part KexiPart::PartInfoList *plist = Kexi::partManager().infoList(); if (plist) { foreach(KexiPart::Info *info, *plist) { QAction *a = info->newObjectAction(); if (a) a->setEnabled(!readOnly); } } } void KexiMainWindow::slotAutoOpenObjectsLater() { QString not_found_msg; bool openingCancelled; //ok, now open "autoopen: objects if (d->prj) { foreach(KexiProjectData::ObjectInfo* info, d->prj->data()->autoopenObjects) { KexiPart::Info *i = Kexi::partManager().infoForPluginId(info->value("type")); if (!i) { not_found_msg += "
  • "; if (!info->value("name").isEmpty()) not_found_msg += (QString("\"") + info->value("name") + "\" - "); if (info->value("action") == "new") not_found_msg += xi18n("cannot create object - unknown object type \"%1\"", info->value("type")); else not_found_msg += xi18n("unknown object type \"%1\"", info->value("type")); not_found_msg += internalReason(Kexi::partManager().result()) + "
  • "; continue; } // * NEW if (info->value("action") == "new") { if (!newObject(i, &openingCancelled) && !openingCancelled) { not_found_msg += "
  • "; not_found_msg += (xi18n("cannot create object of type \"%1\"", info->value("type")) + internalReason(d->prj->result()) + "
  • "); } else d->wasAutoOpen = true; continue; } KexiPart::Item *item = d->prj->item(i, info->value("name")); if (!item) { QString taskName; if (info->value("action") == "execute") taskName = xi18nc("\"executing object\" action", "executing"); #ifdef KEXI_QUICK_PRINTING_SUPPORT else if (info->value("action") == "print-preview") taskName = futureI18n("making print preview for"); else if (info->value("action") == "print") taskName = futureI18n("printing"); #endif else taskName = xi18n("opening"); not_found_msg += (QString("
  • ") + taskName + " \"" + info->value("name") + "\" - "); if ("table" == info->value("type").toLower()) not_found_msg += xi18n("table not found"); else if ("query" == info->value("type").toLower()) not_found_msg += xi18n("query not found"); else if ("macro" == info->value("type").toLower()) not_found_msg += xi18n("macro not found"); else if ("script" == info->value("type").toLower()) not_found_msg += xi18n("script not found"); else not_found_msg += xi18n("object not found"); not_found_msg += (internalReason(d->prj->result()) + "
  • "); continue; } // * EXECUTE, PRINT, PRINT PREVIEW if (info->value("action") == "execute") { tristate res = executeItem(item); if (false == res) { not_found_msg += (QString("
  • \"") + info->value("name") + "\" - " + xi18n("cannot execute object") + internalReason(d->prj->result()) + "
  • "); } continue; } #ifdef KEXI_QUICK_PRINTING_SUPPORT else if (info->value("action") == "print") { tristate res = printItem(item); if (false == res) { not_found_msg += (QString("
  • \"") + info->value("name") + "\" - " + futureI18n("cannot print object") + internalReason(d->prj->result()) + "
  • "); } continue; } else if (info->value("action") == "print-preview") { tristate res = printPreviewForItem(item); if (false == res) { not_found_msg += (QString("
  • \"") + info->value("name") + "\" - " + futureI18n("cannot make print preview of object") + internalReason(d->prj->result()) + "
  • "); } continue; } #endif Kexi::ViewMode viewMode; if (info->value("action") == "open") viewMode = Kexi::DataViewMode; else if (info->value("action") == "design") viewMode = Kexi::DesignViewMode; else if (info->value("action") == "edittext") viewMode = Kexi::TextViewMode; else continue; //sanity QString openObjectMessage; if (!openObject(item, viewMode, &openingCancelled, 0, &openObjectMessage) && (!openingCancelled || !openObjectMessage.isEmpty())) { not_found_msg += (QString("
  • \"") + info->value("name") + "\" - "); if (openObjectMessage.isEmpty()) not_found_msg += xi18n("cannot open object"); else not_found_msg += openObjectMessage; not_found_msg += internalReason(d->prj->result()) + "
  • "; continue; } else { d->wasAutoOpen = true; } } } setMessagesEnabled(true); if (!not_found_msg.isEmpty()) showErrorMessage(xi18n("You have requested selected objects to be automatically opened " "or processed on startup. Several objects cannot be opened or processed."), QString("
      %1
    ").arg(not_found_msg)); d->updatePropEditorVisibility(currentWindow() ? currentWindow()->currentViewMode() : Kexi::NoViewMode); #if defined(KDOCKWIDGET_P) if (d->propEditor) { KDockWidget *dw = (KDockWidget *)d->propEditorTabWidget->parentWidget(); KDockSplitter *ds = (KDockSplitter *)dw->parentWidget(); if (ds) ds->setSeparatorPosInPercent(d->config->readEntry("RightDockPosition", 80/* % */)); } #endif updateAppCaption(); if (d->tabbedToolBar) { d->tabbedToolBar->hideMainMenu(); } qApp->processEvents(); emit projectOpened(); } tristate KexiMainWindow::closeProject() { if (d->tabbedToolBar) d->tabbedToolBar->hideMainMenu(); #ifndef KEXI_NO_PENDING_DIALOGS if (d->pendingWindowsExist()) { qDebug() << "pendingWindowsExist..."; d->actionToExecuteWhenPendingJobsAreFinished = Private::CloseProjectAction; return cancelled; } #endif //only save nav. visibility setting if there is project opened d->saveSettingsForShowProjectNavigator = d->prj && d->isProjectNavigatorVisible; if (!d->prj) return true; { // make sure the project can be closed bool cancel = false; emit acceptProjectClosingRequested(&cancel); if (cancel) return cancelled; } d->windowExistedBeforeCloseProject = currentWindow(); #if defined(KDOCKWIDGET_P) //remember docks position - will be used on storeSettings() if (d->propEditor) { KDockWidget *dw = (KDockWidget *)d->propEditorTabWidget->parentWidget(); KDockSplitter *ds = (KDockSplitter *)dw->parentWidget(); if (ds) d->propEditorDockSeparatorPos = ds->separatorPosInPercent(); } if (d->nav) { if (d->propEditor) { //! @todo KEXI3 if (d->openedWindowsCount() == 0) //! @todo KEXI3 makeWidgetDockVisible(d->propEditorTabWidget); KDockWidget *dw = (KDockWidget *)d->propEditorTabWidget->parentWidget(); KDockSplitter *ds = (KDockSplitter *)dw->parentWidget(); if (ds) ds->setSeparatorPosInPercent(80); } KDockWidget *dw = (KDockWidget *)d->nav->parentWidget(); KDockSplitter *ds = (KDockSplitter *)dw->parentWidget(); int dwWidth = dw->width(); if (ds) { if (d->openedWindowsCount() != 0 && d->propEditorTabWidget && d->propEditorTabWidget->isVisible()) { d->navDockSeparatorPos = ds->separatorPosInPercent(); } else { d->navDockSeparatorPos = (100 * dwWidth) / width(); } } } #endif //close each window, optionally asking if user wants to close (if data changed) while (currentWindow()) { tristate res = closeWindow(currentWindow()); if (!res || ~res) return res; } // now we will close for sure emit beforeProjectClosing(); if (!d->prj->closeConnection()) return false; if (d->objectViewWidget && d->objectViewWidget->projectNavigator()) { d->navWasVisibleBeforeProjectClosing = d->objectViewWidget->projectNavigator()->isVisible(); d->setProjectNavigatorVisible(false); d->objectViewWidget->projectNavigator()->setProject(0); //slotProjectNavigatorVisibilityChanged(true); // hide side tab } if (d->objectViewWidget && d->objectViewWidget->propertyPane()) { d->objectViewWidget->propertyPane()->hide(); d->action_show_propeditor->setChecked(false); } d->clearWindows(); //sanity! delete d->prj; d->prj = 0; updateReadOnlyState(); invalidateActions(); updateAppCaption(); if (d->userMode) { d->modeSelector->setCurrentMode(Kexi::WelcomeGlobalMode); } emit projectClosed(); return true; } void KexiMainWindow::setupContextHelp() { #ifdef KEXI_SHOW_CONTEXT_HELP d->ctxHelp = new KexiContextHelp(d->mainWidget, this); //! @todo /* d->ctxHelp->setContextHelp(xi18n("Welcome"),xi18n("The KEXI team wishes you a lot of productive work, " "with this product.


    If you have found a bug or have a feature request, please don't " "hesitate to report it at our issue " "tracking system .


    If you would like to join our effort, the development documentation " "at www.kexi-project.org is a good starting point."),0); */ addToolWindow(d->ctxHelp, KDockWidget::DockBottom | KDockWidget::DockLeft, getMainDockWidget(), 20); #endif } void KexiMainWindow::setupMainWidget() { QWidget *centralWidget = new QWidget; setCentralWidget(centralWidget); QVBoxLayout *vlyr = new QVBoxLayout(centralWidget); vlyr->setContentsMargins(0, 0, 0, 0); vlyr->setSpacing(0); if (d->isMainMenuVisible) { QWidget *tabbedToolBarContainer = new QWidget(this); vlyr->addWidget(tabbedToolBarContainer); QVBoxLayout *tabbedToolBarContainerLyr = new QVBoxLayout(tabbedToolBarContainer); tabbedToolBarContainerLyr->setSpacing(0); tabbedToolBarContainerLyr->setContentsMargins( KexiUtils::marginHint() / 2, KexiUtils::marginHint() / 2, KexiUtils::marginHint() / 2, KexiUtils::marginHint() / 2); d->tabbedToolBar = new KexiTabbedToolBar(tabbedToolBarContainer); Q_ASSERT(d->action_tools_locate); connect(d->action_tools_locate, SIGNAL(triggered()), d->tabbedToolBar, SLOT(activateSearchLineEdit())); tabbedToolBarContainerLyr->addWidget(d->tabbedToolBar); d->tabbedToolBar->hideTab("form"); //temporarily until createToolbar is split d->tabbedToolBar->hideTab("report"); //temporarily until createToolbar is split } else { d->tabbedToolBar = 0; } QWidget *mainWidgetContainer = new QWidget(); vlyr->addWidget(mainWidgetContainer, 1); QHBoxLayout *mainWidgetContainerLyr = new QHBoxLayout(mainWidgetContainer); mainWidgetContainerLyr->setContentsMargins(0, 0, 0, 0); mainWidgetContainerLyr->setSpacing(0); d->modeSelector = new KexiGlobalViewModeSelector; connect(d->modeSelector, &KexiGlobalViewModeSelector::currentModeChanged, this, &KexiMainWindow::slotCurrentModeChanged); mainWidgetContainerLyr->addWidget(d->modeSelector); if (d->userMode) { d->modeSelector->hide(); } d->globalViewStack = new QStackedWidget; mainWidgetContainerLyr->addWidget(d->globalViewStack, 1); } //void KexiMainWindow::slotSetProjectNavigatorVisible(bool set) //{ // if (d->navDockWidget) // d->navDockWidget->setVisible(set); //} //void KexiMainWindow::slotSetPropertyEditorVisible(bool set) //{ // if (d->propEditorDockWidget) // d->propEditorDockWidget->setVisible(set); //} //void KexiMainWindow::slotProjectNavigatorVisibilityChanged(bool visible) //{ // d->setTabBarVisible(KMultiTabBar::Left, PROJECT_NAVIGATOR_TABBAR_ID, // d->navDockWidget, !visible); //} //void KexiMainWindow::slotPropertyEditorVisibilityChanged(bool visible) //{ // if (!d->enable_slotPropertyEditorVisibilityChanged) // return; // d->setPropertyEditorTabBarVisible(!visible); // if (!visible) // d->propertyEditorCollapsed = true; //} /* void KexiMainWindow::slotMultiTabBarTabClicked(int id) { if (id == PROJECT_NAVIGATOR_TABBAR_ID) { slotProjectNavigatorVisibilityChanged(true); d->navDockWidget->show(); } else if (id == PROPERTY_EDITOR_TABBAR_ID) { slotPropertyEditorVisibilityChanged(true); d->propEditorDockWidget->show(); d->propertyEditorCollapsed = false; } } static Qt::DockWidgetArea applyRightToLeftToDockArea(Qt::DockWidgetArea area) { if (QApplication::layoutDirection() == Qt::RightToLeft) { if (area == Qt::LeftDockWidgetArea) { return Qt::RightDockWidgetArea; } else if (area == Qt::RightDockWidgetArea) { return Qt::LeftDockWidgetArea; } } return area; }*/ void KexiMainWindow::setupObjectView() { if (d->objectViewWidget) { return; } KexiObjectViewWidget::Flags flags; if (d->isProjectNavigatorVisible) { flags |= KexiObjectViewWidget::ProjectNavigatorEnabled; } if (!d->userMode) { flags |= KexiObjectViewWidget::PropertyPaneEnabled; } d->objectViewWidget = new KexiObjectViewWidget(flags); connect(d->objectViewWidget, &KexiObjectViewWidget::activeWindowChanged, this, &KexiMainWindow::activeWindowChanged); connect(d->objectViewWidget, &KexiObjectViewWidget::closeWindowRequested, this, &KexiMainWindow::closeWindowForTab); connect(d->objectViewWidget, &KexiObjectViewWidget::closeAllWindowsRequested, this, &KexiMainWindow::closeAllWindows); connect(d->objectViewWidget, &KexiObjectViewWidget::projectNavigatorAnimationFinished, this, &KexiMainWindow::slotProjectNavigatorVisibilityChanged); slotProjectNavigatorVisibilityChanged(d->objectViewWidget->projectNavigator()); // Restore settings //! @todo FIX LAYOUT PROBLEMS KConfigGroup propertyEditorGroup(d->config->group("PropertyEditor")); - QFont f(KexiUtils::smallestReadableFont()); + QFont f(KexiStyle::propertyPane().font()); const qreal pointSizeF = propertyEditorGroup.readEntry("FontPointSize", -1.0f); // points are more accurate if (pointSizeF > 0.0) { f.setPointSizeF(pointSizeF); } else { const int pixelSize = propertyEditorGroup.readEntry("FontSize", -1); // compatibility with Kexi 2.x if (pixelSize > 0) { f.setPixelSize(pixelSize); } } if (d->objectViewWidget->propertyPane()) { d->objectViewWidget->propertyPane()->setFont(f); KConfigGroup mainWindowGroup(d->config->group("MainWindow")); const QSize projectNavigatorSize = mainWindowGroup.readEntry("ProjectNavigatorSize", QSize()); const QSize propertyEditorSize = mainWindowGroup.readEntry("PropertyEditorSize", QSize()); d->objectViewWidget->setSidebarWidths(projectNavigatorSize.isValid() ? projectNavigatorSize.width() : -1, propertyEditorSize.isValid() ? propertyEditorSize.width() : -1); } d->globalViewStack->addWidget(d->objectViewWidget); KexiProjectNavigator* navigator = d->objectViewWidget->projectNavigator(); if (navigator) { //connect(d->navDockWidget, SIGNAL(visibilityChanged(bool)), // this, SLOT(slotProjectNavigatorVisibilityChanged(bool))); //Nav2 Signals connect(navigator, SIGNAL(openItem(KexiPart::Item*,Kexi::ViewMode)), this, SLOT(openObject(KexiPart::Item*,Kexi::ViewMode))); connect(navigator, SIGNAL(openOrActivateItem(KexiPart::Item*,Kexi::ViewMode)), this, SLOT(openObjectFromNavigator(KexiPart::Item*,Kexi::ViewMode))); connect(navigator, SIGNAL(newItem(KexiPart::Info*)), this, SLOT(newObject(KexiPart::Info*))); connect(navigator, SIGNAL(removeItem(KexiPart::Item*)), this, SLOT(removeObject(KexiPart::Item*))); connect(navigator->model(), SIGNAL(renameItem(KexiPart::Item*,QString,bool*)), this, SLOT(renameObject(KexiPart::Item*,QString,bool*))); connect(navigator->model(), SIGNAL(changeItemCaption(KexiPart::Item*,QString,bool*)), this, SLOT(setObjectCaption(KexiPart::Item*,QString,bool*))); connect(navigator, SIGNAL(executeItem(KexiPart::Item*)), this, SLOT(executeItem(KexiPart::Item*))); connect(navigator, SIGNAL(exportItemToClipboardAsDataTable(KexiPart::Item*)), this, SLOT(copyItemToClipboardAsDataTable(KexiPart::Item*))); connect(navigator, SIGNAL(exportItemToFileAsDataTable(KexiPart::Item*)), this, SLOT(exportItemAsDataTable(KexiPart::Item*))); #ifdef KEXI_QUICK_PRINTING_SUPPORT connect(navigator, SIGNAL(printItem(KexiPart::Item*)), this, SLOT(printItem(KexiPart::Item*))); connect(navigator, SIGNAL(pageSetupForItem(KexiPart::Item*)), this, SLOT(showPageSetupForItem(KexiPart::Item*))); #endif connect(navigator, SIGNAL(selectionChanged(KexiPart::Item*)), this, SLOT(slotPartItemSelectedInNavigator(KexiPart::Item*))); } if (navigator) { connect(d->prj, SIGNAL(newItemStored(KexiPart::Item*)), navigator->model(), SLOT(slotAddItem(KexiPart::Item*))); connect(d->prj, SIGNAL(itemRemoved(KexiPart::Item)), navigator->model(), SLOT(slotRemoveItem(KexiPart::Item))); navigator->setFocus(); /*if (d->forceShowProjectNavigatorOnCreation) { slotShowNavigator(); d->forceShowProjectNavigatorOnCreation = false; } else if (d->forceHideProjectNavigatorOnCreation) { d->forceHideProjectNavigatorOnCreation = false; }*/ } invalidateActions(); } void KexiMainWindow::updateObjectView() { setupObjectView(); if (d->prj && d->prj->isConnected()) { KexiProjectNavigator* navigator = d->objectViewWidget->projectNavigator(); if (navigator && !navigator->model()->project()) { QString partManagerErrorMessages; navigator->setProject(d->prj, QString()/*all classes*/, &partManagerErrorMessages); if (partManagerErrorMessages.isEmpty()) { d->setProjectNavigatorVisible(true); } else { showWarningContinueMessage(partManagerErrorMessages, QString(), "ShowWarningsRelatedToPluginsLoading"); } } } } void KexiMainWindow::slotLastActions() { } void KexiMainWindow::slotPartLoaded(KexiPart::Part* p) { if (!p) return; p->createGUIClients(); } void KexiMainWindow::updateAppCaption() { //! @todo allow to set custom "static" app caption d->appCaptionPrefix.clear(); if (d->prj && d->prj->data()) {//add project name d->appCaptionPrefix = d->prj->data()->caption(); if (d->appCaptionPrefix.isEmpty()) { d->appCaptionPrefix = d->prj->data()->databaseName(); } if (d->prj->dbConnection()->options()->isReadOnly()) { d->appCaptionPrefix = xi18nc(" (read only)", "%1 (read only)", d->appCaptionPrefix); } } setWindowTitle(d->appCaptionPrefix); } bool KexiMainWindow::queryClose() { #ifndef KEXI_NO_PENDING_DIALOGS if (d->pendingWindowsExist()) { qDebug() << "pendingWindowsExist..."; d->actionToExecuteWhenPendingJobsAreFinished = Private::QuitAction; return false; } #endif const tristate res = closeProject(); if (~res) return false; if (res == true) storeSettings(); if (! ~res) { Kexi::deleteGlobalObjects(); qApp->quit(); } return ! ~res; } void KexiMainWindow::closeEvent(QCloseEvent *ev) { if (queryClose()) { ev->accept(); } else { ev->ignore(); } } void KexiMainWindow::resizeEvent(QResizeEvent *e) { QMainWindow::resizeEvent(e); qDebug() << "===" << e->size() << size() << isVisible(); } static const QSize KEXI_MIN_WINDOW_SIZE(1024, 768); void KexiMainWindow::restoreSettings() { KConfigGroup mainWindowGroup(d->config->group("MainWindow")); const bool maximize = mainWindowGroup.readEntry("Maximized", false); const QRect geometry(mainWindowGroup.readEntry("Geometry", QRect())); if (geometry.isValid()) setGeometry(geometry); else if (maximize) setWindowState(windowState() | Qt::WindowMaximized); else { QRect desk = QApplication::desktop()->screenGeometry( QApplication::desktop()->screenNumber(this)); if (desk.width() <= KEXI_MIN_WINDOW_SIZE.width() || desk.height() <= KEXI_MIN_WINDOW_SIZE.height()) { setWindowState(windowState() | Qt::WindowMaximized); } else { resize(KEXI_MIN_WINDOW_SIZE); } } } void KexiMainWindow::storeSettings() { //qDebug(); KConfigGroup mainWindowGroup(d->config->group("MainWindow")); if (isMaximized()) { mainWindowGroup.writeEntry("Maximized", true); mainWindowGroup.deleteEntry("Geometry"); } else { mainWindowGroup.deleteEntry("Maximized"); mainWindowGroup.writeEntry("Geometry", geometry()); } if (d->objectViewWidget) { int projectNavigatorWidth; int propertyEditorWidth; d->objectViewWidget->getSidebarWidths(&projectNavigatorWidth, &propertyEditorWidth); if (projectNavigatorWidth > 0) { mainWindowGroup.writeEntry("ProjectNavigatorSize", QSize(projectNavigatorWidth, 1)); } if (propertyEditorWidth > 0) { mainWindowGroup.writeEntry("PropertyEditorSize", QSize(propertyEditorWidth, 1)); } } d->config->sync(); } void KexiMainWindow::registerChild(KexiWindow *window) { //qDebug(); connect(window, SIGNAL(dirtyChanged(KexiWindow*)), this, SLOT(slotDirtyFlagChanged(KexiWindow*))); if (window->id() != -1) { d->insertWindow(window); } //qDebug() << "ID=" << window->id(); } void KexiMainWindow::updateCustomPropertyPanelTabs(KexiWindow *prevWindow, Kexi::ViewMode prevViewMode) { updateCustomPropertyPanelTabs( prevWindow ? prevWindow->part() : 0, prevWindow ? prevWindow->currentViewMode() : prevViewMode, currentWindow() ? currentWindow()->part() : 0, currentWindow() ? currentWindow()->currentViewMode() : Kexi::NoViewMode ); } void KexiMainWindow::updateCustomPropertyPanelTabs( KexiPart::Part *prevWindowPart, Kexi::ViewMode prevViewMode, KexiPart::Part *curWindowPart, Kexi::ViewMode curViewMode) { if (!d->objectViewWidget || !d->objectViewWidget->propertyPane()) return; if ( !curWindowPart || (/*prevWindowPart &&*/ curWindowPart && (prevWindowPart != curWindowPart || prevViewMode != curViewMode) ) ) { #warning TODO KexiMainWindow::updateCustomPropertyPanelTabs() #if 0 if (d->partForPreviouslySetupPropertyPanelTabs) { //remember current page number for this part if (( prevViewMode == Kexi::DesignViewMode && static_cast(d->partForPreviouslySetupPropertyPanelTabs) != curWindowPart) //part changed || curViewMode != Kexi::DesignViewMode) { //..or switching to other view mode d->recentlySelectedPropertyPanelPages.insert( d->partForPreviouslySetupPropertyPanelTabs, d->objectViewWidget->propertyPane()->currentIndex()); } } //delete old custom tabs (other than 'property' tab) const int count = d->objectViewWidget->propertyEditorTabWidget()->count(); for (int i = 1; i < count; i++) d->objectViewWidget->propertyEditorTabWidget()->removeTab(1); #endif } //don't change anything if part is not switched nor view mode changed if ((!prevWindowPart && !curWindowPart) || (prevWindowPart == curWindowPart && prevViewMode == curViewMode) || (curWindowPart && curViewMode != Kexi::DesignViewMode)) { //new part for 'previously setup tabs' d->partForPreviouslySetupPropertyPanelTabs = curWindowPart; return; } if (curWindowPart) { //recreate custom tabs curWindowPart->setupPropertyPane(d->objectViewWidget->propertyPane()->toolBox()); #warning TODO KexiMainWindow::updateCustomPropertyPanelTabs() #if 0 //restore current page number for this part if (d->recentlySelectedPropertyPanelPages.contains(curWindowPart)) { d->objectViewWidget->propertyEditorTabWidget()->setCurrentIndex( d->recentlySelectedPropertyPanelPages[ curWindowPart ] ); } #endif } //#endif //new part for 'previously setup tabs' d->partForPreviouslySetupPropertyPanelTabs = curWindowPart; } void KexiMainWindow::activeWindowChanged(KexiWindow *window, KexiWindow *prevWindow) { //qDebug() << "to=" << (window ? window->windowTitle() : ""); bool windowChanged = prevWindow != window; if (windowChanged) { if (prevWindow) { //inform previously activated dialog about deactivation prevWindow->deactivate(); } } updateCustomPropertyPanelTabs(prevWindow, prevWindow ? prevWindow->currentViewMode() : Kexi::NoViewMode); // inform the current view of the new dialog about property switching // (this will also call KexiMainWindow::propertySetSwitched() to update the current property editor's set if (windowChanged && currentWindow()) currentWindow()->selectedView()->propertySetSwitched(); if (windowChanged) { if (currentWindow() && currentWindow()->currentViewMode() != 0 && window) { //on opening new dialog it can be 0; we don't want this d->updatePropEditorVisibility(currentWindow()->currentViewMode()); restoreDesignTabIfNeeded(window->partItem()->pluginId(), window->currentViewMode(), prevWindow ? prevWindow->partItem()->identifier() : 0); activateDesignTabIfNeeded(window->partItem()->pluginId(), window->currentViewMode()); } } invalidateActions(); d->updateFindDialogContents(); if (window) window->setFocus(); } bool KexiMainWindow::activateWindow(int id) { qDebug(); #ifndef KEXI_NO_PENDING_DIALOGS Private::PendingJobType pendingType; return activateWindow(*d->openedWindowFor(id, pendingType)); #else return activateWindow(*d->openedWindowFor(id)); #endif } bool KexiMainWindow::activateWindow(KexiWindow& window) { //qDebug(); d->focus_before_popup = &window; d->objectViewWidget->tabWidget()->setCurrentWidget(window.parentWidget()/*container*/); window.activate(); return true; } void KexiMainWindow::activateNextTab() { int index = d->objectViewWidget->tabWidget()->currentIndex() + 1; if (index >= d->objectViewWidget->tabWidget()->count()) { index = 0; } d->objectViewWidget->tabWidget()->setCurrentIndex(index); } void KexiMainWindow::activatePreviousTab() { int index = d->objectViewWidget->tabWidget()->currentIndex() - 1; if (index < 0) { index = d->objectViewWidget->tabWidget()->count() - 1; } d->objectViewWidget->tabWidget()->setCurrentIndex(index); } void KexiMainWindow::slotSettings() { if (d->tabbedToolBar) { d->tabbedToolBar->showMainMenu("settings"); // dummy QLabel *dummy = KEXI_UNFINISHED_LABEL(actionCollection()->action("settings")->text()); d->tabbedToolBar->setMainMenuContent(dummy); } } void KexiMainWindow::slotConfigureKeys() { KShortcutsDialog::configure(actionCollection(), KShortcutsEditor::LetterShortcutsDisallowed, this); } void KexiMainWindow::slotConfigureToolbars() { KEditToolBar edit(actionCollection()); (void) edit.exec(); } void KexiMainWindow::slotProjectNew() { createNewProject(); } KexiProject* KexiMainWindow::createKexiProjectObject(const KexiProjectData &data) { KexiProject *prj = new KexiProject(data, this); connect(prj, SIGNAL(itemRenamed(KexiPart::Item,QString)), this, SLOT(slotObjectRenamed(KexiPart::Item,QString))); if (d->objectViewWidget && d->objectViewWidget->projectNavigator()){ connect(prj, SIGNAL(itemRemoved(KexiPart::Item)), d->objectViewWidget->projectNavigator()->model(), SLOT(slotRemoveItem(KexiPart::Item))); } return prj; } void KexiMainWindow::createNewProject() { if (!d->tabbedToolBar) return; d->tabbedToolBar->showMainMenu("project_new"); KexiNewProjectAssistant* assistant = new KexiNewProjectAssistant; connect(assistant, SIGNAL(createProject(KexiProjectData)), this, SLOT(createNewProject(KexiProjectData))); d->tabbedToolBar->setMainMenuContent(assistant); } tristate KexiMainWindow::createNewProject(const KexiProjectData &projectData) { QScopedPointer prj(createKexiProjectObject(projectData)); tristate res = prj->create(true /*overwrite*/); if (res != true) { return res; } //qDebug() << "new project created ---"; if (d->prj) { res = openProjectInExternalKexiInstance( prj->data()->connectionData()->databaseName(), prj->data()->connectionData(), prj->data()->databaseName()); Kexi::recentProjects()->addProjectData(*prj->data()); if (d->tabbedToolBar) { d->tabbedToolBar->hideMainMenu(); } return res; } if (d->tabbedToolBar) { d->tabbedToolBar->hideMainMenu(); } d->prj = prj.take(); d->prj->data()->setLastOpened(QDateTime::currentDateTime()); Kexi::recentProjects()->addProjectData(*d->prj->data()); d->modeSelector->setCurrentMode(Kexi::EditGlobalMode); invalidateActions(); updateAppCaption(); return true; } void KexiMainWindow::slotProjectOpen() { if (!d->tabbedToolBar) return; d->tabbedToolBar->showMainMenu("project_open"); KexiOpenProjectAssistant* assistant = new KexiOpenProjectAssistant; connect(assistant, SIGNAL(openProject(KexiProjectData)), this, SLOT(openProject(KexiProjectData))); connect(assistant, SIGNAL(openProject(QString)), this, SLOT(openProject(QString))); d->tabbedToolBar->setMainMenuContent(assistant); } tristate KexiMainWindow::openProject(const QString& aFileName) { return openProject(aFileName, QString(), QString()); } tristate KexiMainWindow::openProject(const QString& aFileName, const QString& fileNameForConnectionData, const QString& dbName) { if (d->prj) return openProjectInExternalKexiInstance(aFileName, fileNameForConnectionData, dbName); KDbConnectionData *cdata = 0; if (!fileNameForConnectionData.isEmpty()) { cdata = Kexi::connset().connectionDataForFileName(fileNameForConnectionData); if (!cdata) { qWarning() << "cdata?"; return false; } } return openProject(aFileName, cdata, dbName); } tristate KexiMainWindow::openProject(const QString& aFileName, KDbConnectionData *cdata, const QString& dbName, const KexiProjectData::AutoOpenObjects& autoopenObjects) { if (d->prj) { return openProjectInExternalKexiInstance(aFileName, cdata, dbName); } KexiProjectData* projectData = 0; const KexiStartupHandler *h = KexiStartupHandler::global(); bool readOnly = h->isSet(h->options().readOnly); bool deleteAfterOpen = false; if (cdata) { //server-based project if (dbName.isEmpty()) {//no database name given, ask user bool cancel; projectData = KexiStartupHandler::global()->selectProject(cdata, &cancel, this); if (cancel) return cancelled; } else { //! @todo caption arg? projectData = new KexiProjectData(*cdata, dbName); deleteAfterOpen = true; } } else { if (aFileName.isEmpty()) { qWarning() << "aFileName.isEmpty()"; return false; } //file-based project qDebug() << "Project File: " << aFileName; KDbConnectionData fileConnData; fileConnData.setDatabaseName(aFileName); QString detectedDriverId; int detectOptions = 0; if (readOnly) { detectOptions |= KexiStartupHandler::OpenReadOnly; } KexiStartupData::Import importActionData; bool forceReadOnly; const tristate res = KexiStartupHandler::detectActionForFile( &importActionData, &detectedDriverId, fileConnData.driverId(), aFileName, this, detectOptions, &forceReadOnly); if (forceReadOnly) { readOnly = true; } if (true != res) return res; if (importActionData) { //importing requested return showProjectMigrationWizard(importActionData.mimeType, importActionData.fileName); } fileConnData.setDriverId(detectedDriverId); if (fileConnData.driverId().isEmpty()) return false; //opening requested projectData = new KexiProjectData(fileConnData); deleteAfterOpen = true; } if (!projectData) return false; projectData->setReadOnly(readOnly); projectData->autoopenObjects = autoopenObjects; const tristate res = openProject(*projectData); if (deleteAfterOpen) //projectData object has been copied delete projectData; return res; } tristate KexiMainWindow::openProjectInExternalKexiInstance(const QString& aFileName, KDbConnectionData *cdata, const QString& dbName) { QString fileNameForConnectionData; if (aFileName.isEmpty()) { //try .kexic file if (cdata) fileNameForConnectionData = Kexi::connset().fileNameForConnectionData(*cdata); } return openProjectInExternalKexiInstance(aFileName, fileNameForConnectionData, dbName); } tristate KexiMainWindow::openProjectInExternalKexiInstance(const QString& aFileName, const QString& fileNameForConnectionData, const QString& dbName) { QString fileName(aFileName); QStringList args; // open a file-based project or a server connection provided as a .kexic file // (we have no other simple way to provide the startup data to a new process) if (fileName.isEmpty()) { //try .kexic file if (!fileNameForConnectionData.isEmpty()) args << "--skip-conn-dialog"; //user does not expect conn. dialog to be shown here if (dbName.isEmpty()) { //use 'kexi --skip-conn-dialog file.kexic' fileName = fileNameForConnectionData; } else { //use 'kexi --skip-conn-dialog --connection file.kexic dbName' if (fileNameForConnectionData.isEmpty()) { qWarning() << "fileNameForConnectionData?"; return false; } args << "--connection" << fileNameForConnectionData; fileName = dbName; } } if (fileName.isEmpty()) { qWarning() << "fileName?"; return false; } //! @todo use KRun //! @todo untested //Can arguments be supplied to KRun like is used here? AP args << fileName; const bool ok = QProcess::startDetached( qApp->applicationFilePath(), args, QFileInfo(fileName).absoluteDir().absolutePath()); if (!ok) { d->showStartProcessMsg(args); } if (d->tabbedToolBar) { d->tabbedToolBar->hideMainMenu(); } return ok; } void KexiMainWindow::slotProjectWelcome() { if (!d->tabbedToolBar) return; d->tabbedToolBar->showMainMenu("project_welcome"); KexiWelcomeAssistant* assistant = new KexiWelcomeAssistant( Kexi::recentProjects(), this); connect(assistant, SIGNAL(openProject(KexiProjectData,QString,bool*)), this, SLOT(openProject(KexiProjectData,QString,bool*))); d->tabbedToolBar->setMainMenuContent(assistant); } void KexiMainWindow::slotProjectSave() { if (!currentWindow() || currentWindow()->currentViewMode() == Kexi::DataViewMode) { return; } saveObject(currentWindow()); updateAppCaption(); invalidateActions(); } void KexiMainWindow::slotProjectSaveAs() { if (!currentWindow() || currentWindow()->currentViewMode() == Kexi::DataViewMode) { return; } saveObject(currentWindow(), QString(), SaveObjectAs); updateAppCaption(); invalidateActions(); } void KexiMainWindow::slotProjectPrint() { #ifdef KEXI_QUICK_PRINTING_SUPPORT if (currentWindow() && currentWindow()->partItem()) printItem(currentWindow()->partItem()); #endif } void KexiMainWindow::slotProjectPrintPreview() { #ifdef KEXI_QUICK_PRINTING_SUPPORT if (currentWindow() && currentWindow()->partItem()) printPreviewForItem(currentWindow()->partItem()); #endif } void KexiMainWindow::slotProjectPageSetup() { #ifdef KEXI_QUICK_PRINTING_SUPPORT if (currentWindow() && currentWindow()->partItem()) showPageSetupForItem(currentWindow()->partItem()); #endif } void KexiMainWindow::slotProjectExportDataTable() { if (currentWindow() && currentWindow()->partItem()) exportItemAsDataTable(currentWindow()->partItem()); } void KexiMainWindow::slotProjectProperties() { if (!d->tabbedToolBar) return; d->tabbedToolBar->showMainMenu("project_properties"); // dummy QLabel *dummy = KEXI_UNFINISHED_LABEL(actionCollection()->action("project_properties")->text()); d->tabbedToolBar->setMainMenuContent(dummy); //! @todo load the implementation not the ui :) // ProjectSettingsUI u(this); // u.exec(); } void KexiMainWindow::slotProjectImportExportOrSend() { if (!d->tabbedToolBar) return; d->tabbedToolBar->showMainMenu("project_import_export_send"); KexiImportExportAssistant* assistant = new KexiImportExportAssistant( d->action_project_import_export_send, d->action_tools_import_project); connect(assistant, SIGNAL(importProject()), this, SLOT(slotToolsImportProject())); d->tabbedToolBar->setMainMenuContent(assistant); } void KexiMainWindow::slotProjectClose() { closeProject(); } void KexiMainWindow::slotProjectRelations() { if (!d->prj) return; KexiWindow *w = KexiInternalPart::createKexiWindowInstance("org.kexi-project.relations", this); if (w) { activateWindow(*w); } } void KexiMainWindow::slotImportFile() { KEXI_UNFINISHED("Import: " + xi18n("From File...")); } void KexiMainWindow::slotImportServer() { KEXI_UNFINISHED("Import: " + xi18n("From Server...")); } void KexiMainWindow::slotProjectQuit() { if (~ closeProject()) return; close(); } void KexiMainWindow::slotActivateNavigator() { if (!d->objectViewWidget || !d->objectViewWidget->projectNavigator()) { return; } d->objectViewWidget->projectNavigator()->setFocus(); } void KexiMainWindow::slotActivateMainArea() { if (currentWindow()) currentWindow()->setFocus(); } void KexiMainWindow::slotActivatePropertyPane() { if (!d->objectViewWidget || !d->objectViewWidget->propertyPane()) { return; } d->objectViewWidget->propertyPane()->setFocus(); // if (d->objectViewWidget->propertyPane()->currentWidget()) { // d->objectViewWidget->propertyPane()->currentWidget()->setFocus(); // } } void KexiMainWindow::slotToggleProjectNavigator() { if (d->objectViewWidget && d->objectViewWidget->projectNavigator()) { d->setProjectNavigatorVisible(!d->objectViewWidget->projectNavigator()->isVisible(), Private::ShowAnimated); } } void KexiMainWindow::slotTogglePropertyEditor() { if (d->objectViewWidget && d->objectViewWidget->propertyPane()) { d->objectViewWidget->setPropertyPaneVisible(!d->objectViewWidget->propertyPane()->isVisible()); } } tristate KexiMainWindow::switchToViewMode(KexiWindow& window, Kexi::ViewMode viewMode) { const Kexi::ViewMode prevViewMode = currentWindow()->currentViewMode(); if (prevViewMode == viewMode) return true; if (!activateWindow(window)) return false; if (!currentWindow()) { return false; } if (&window != currentWindow()) return false; if (!currentWindow()->supportsViewMode(viewMode)) { showErrorMessage(xi18nc("@info", "Selected view is not supported for %1 object.", currentWindow()->partItem()->name()), xi18nc("@info", "Selected view (%1) is not supported by this object type (%2).", Kexi::nameForViewMode(viewMode), currentWindow()->part()->info()->name())); return false; } updateCustomPropertyPanelTabs(currentWindow()->part(), prevViewMode, currentWindow()->part(), viewMode); tristate res = currentWindow()->switchToViewMode(viewMode); if (!res) { updateCustomPropertyPanelTabs(0, Kexi::NoViewMode); //revert showErrorMessage(xi18n("Switching to other view failed (%1).", Kexi::nameForViewMode(viewMode)), currentWindow()); return false; } if (~res) { updateCustomPropertyPanelTabs(0, Kexi::NoViewMode); //revert return cancelled; } activateWindow(window); invalidateSharedActions(); invalidateProjectWideActions(); d->updateFindDialogContents(); d->updatePropEditorVisibility(viewMode); QString origTabToActivate; if (viewMode == Kexi::DesignViewMode) { // Save the orig tab: we want to back to design tab // when user moved to data view and then immediately to design view. origTabToActivate = d->tabsToActivateOnShow.value(currentWindow()->partItem()->identifier()); } restoreDesignTabIfNeeded(currentWindow()->partItem()->pluginId(), viewMode, currentWindow()->partItem()->identifier()); if (viewMode == Kexi::DesignViewMode) { activateDesignTab(currentWindow()->partItem()->pluginId()); // Restore the saved tab to the orig one. restoreDesignTabIfNeeded() saved tools tab probably. d->tabsToActivateOnShow.insert(currentWindow()->partItem()->identifier(), origTabToActivate); } return true; } void KexiMainWindow::slotViewDataMode() { if (currentWindow()) switchToViewMode(*currentWindow(), Kexi::DataViewMode); } void KexiMainWindow::slotViewDesignMode() { if (currentWindow()) switchToViewMode(*currentWindow(), Kexi::DesignViewMode); } void KexiMainWindow::slotViewTextMode() { if (currentWindow()) switchToViewMode(*currentWindow(), Kexi::TextViewMode); } //! Used to control if we're not Saving-As object under the original name class SaveAsObjectNameValidator : public KexiNameDialogValidator { public: SaveAsObjectNameValidator(const QString &originalObjectName) : m_originalObjectName(originalObjectName) { } virtual bool validate(KexiNameDialog *dialog) const { if (dialog->widget()->nameText() == m_originalObjectName) { KMessageBox::information(dialog, xi18nc("Could not save object under the original name.", "Could not save under the original name.")); return false; } return true; } private: QString m_originalObjectName; }; tristate KexiMainWindow::getNewObjectInfo( KexiPart::Item *partItem, const QString &originalName, KexiPart::Part *part, bool allowOverwriting, bool *overwriteNeeded, const QString& messageWhenAskingForName) { //data was never saved in the past -we need to create a new object at the backend KexiPart::Info *info = part->info(); if (!d->nameDialog) { d->nameDialog = new KexiNameDialog( messageWhenAskingForName, this); //check if that name is allowed d->nameDialog->widget()->addNameSubvalidator( new KDbObjectNameValidator(project()->dbConnection()->driver())); d->nameDialog->buttonBox()->button(QDialogButtonBox::Ok)->setText(xi18nc("@action:button Save object", "Save")); } else { d->nameDialog->widget()->setMessageText(messageWhenAskingForName); } d->nameDialog->widget()->setCaptionText(partItem->caption()); d->nameDialog->widget()->setNameText(partItem->name()); d->nameDialog->setWindowTitle(xi18nc("@title:window", "Save Object As")); d->nameDialog->setDialogIcon(info->iconName()); d->nameDialog->setAllowOverwriting(allowOverwriting); if (!originalName.isEmpty()) { d->nameDialog->setValidator(new SaveAsObjectNameValidator(originalName)); } if (d->nameDialog->execAndCheckIfObjectExists(*project(), *part, overwriteNeeded) != QDialog::Accepted) { return cancelled; } // close window of object that will be overwritten if (*overwriteNeeded) { KexiPart::Item* overwrittenItem = project()->item(info, d->nameDialog->widget()->nameText()); if (overwrittenItem) { KexiWindow * openedWindow = d->openedWindowFor(overwrittenItem->identifier()); if (openedWindow) { const tristate res = closeWindow(openedWindow); if (res != true) { return res; } } } } //update name and caption partItem->setName(d->nameDialog->widget()->nameText()); partItem->setCaption(d->nameDialog->widget()->captionText()); return true; } //! Used to delete part item on exit from block class PartItemDeleter : public QScopedPointer { public: explicit PartItemDeleter(KexiProject *prj) : m_prj(prj) {} ~PartItemDeleter() { if (!isNull()) { m_prj->deleteUnstoredItem(take()); } } private: KexiProject *m_prj; }; static void showSavingObjectFailedErrorMessage(KexiMainWindow *wnd, KexiPart::Item *item) { wnd->showErrorMessage( xi18nc("@info Saving object failed", "Saving %1 object failed.", item->name()), wnd->currentWindow()); } tristate KexiMainWindow::saveObject(KexiWindow *window, const QString& messageWhenAskingForName, SaveObjectOptions options) { tristate res; bool saveAs = options & SaveObjectAs; if (!saveAs && !window->neverSaved()) { //data was saved in the past -just save again res = window->storeData(options & DoNotAsk); if (!res) { showSavingObjectFailedErrorMessage(this, window->partItem()); } return res; } if (saveAs && window->neverSaved()) { //if never saved, saveAs == save saveAs = false; } const int oldItemID = window->partItem()->identifier(); KexiPart::Item *partItem; KexiView::StoreNewDataOptions storeNewDataOptions; PartItemDeleter itemDeleter(d->prj); if (saveAs) { partItem = d->prj->createPartItem(window->part()); if (!partItem) { //! @todo error return false; } itemDeleter.reset(partItem); } else { partItem = window->partItem(); } bool overwriteNeeded; res = getNewObjectInfo(partItem, saveAs ? window->partItem()->name() : QString(), window->part(), true /*allowOverwriting*/, &overwriteNeeded, messageWhenAskingForName); if (res != true) return res; if (overwriteNeeded) { storeNewDataOptions |= KexiView::OverwriteExistingData; } if (saveAs) { res = window->storeDataAs(partItem, storeNewDataOptions); } else { res = window->storeNewData(storeNewDataOptions); } if (~res) return cancelled; if (!res) { showSavingObjectFailedErrorMessage(this, partItem); return false; } d->updateWindowId(window, oldItemID); invalidateProjectWideActions(); itemDeleter.take(); return true; } tristate KexiMainWindow::closeWindow(KexiWindow *window) { return closeWindow(window ? window : currentWindow(), true); } tristate KexiMainWindow::closeCurrentWindow() { return closeWindow(0); } tristate KexiMainWindow::closeWindowForTab(int tabIndex) { KexiWindow* window = windowForTab(tabIndex); if (!window) return false; return closeWindow(window); } tristate KexiMainWindow::closeAllWindows() { if (!d->objectViewWidget || !d->objectViewWidget->tabWidget()) return true; QList windowList; for (int i = 0; i < d->objectViewWidget->tabWidget()->count(); ++i) { KexiWindow *window = windowForTab(i); if (window) { windowList.append(window); } } tristate alternateResult = true; for (KexiWindow *window : windowList) { const tristate result = closeWindow(window); if (~result) { return result; } if (result == false) { alternateResult = false; } } return alternateResult; } tristate KexiMainWindow::closeWindow(KexiWindow *window, bool layoutTaskBar, bool doNotSaveChanges) { //! @todo KEXI3 KexiMainWindow::closeWindow() ///@note Q_UNUSED layoutTaskBar Q_UNUSED(layoutTaskBar); if (!window) return true; if (d->insideCloseWindow) return true; const int previousItemId = window->partItem()->identifier(); #ifndef KEXI_NO_PENDING_DIALOGS d->addItemToPendingWindows(window->partItem(), Private::WindowClosingJob); #endif d->insideCloseWindow = true; if (window == currentWindow() && !window->isAttached()) { if (d->propertyEditor()) { // ah, closing detached window - better switch off property buffer right now... d->propertySet = 0; d->propertyEditor()->editor()->changeSet(0); } } bool remove_on_closing = window->partItem() ? window->partItem()->neverSaved() : false; if (window->isDirty() && !d->forceWindowClosing && !doNotSaveChanges) { //more accurate tool tips and what's this KGuiItem saveChanges(KStandardGuiItem::save()); saveChanges.setToolTip(xi18n("Save changes")); saveChanges.setWhatsThis( xi18nc("@info", "Saves all recent changes made in %1 object.", window->partItem()->name())); KGuiItem discardChanges(KStandardGuiItem::discard()); discardChanges.setWhatsThis( xi18nc("@info", "Discards all recent changes made in %1 object.", window->partItem()->name())); //dialog's data is dirty: //--adidional message, e.g. table designer will return // "Note: This table is already filled with data which will be removed." // if the window is in design view mode. const KLocalizedString additionalMessage( window->part()->i18nMessage(":additional message before saving design", window)); QString additionalMessageString; if (!additionalMessage.isEmpty()) additionalMessageString = additionalMessage.toString(); if (additionalMessageString.startsWith(':')) additionalMessageString.clear(); if (!additionalMessageString.isEmpty()) additionalMessageString = "

    " + additionalMessageString + "

    "; const KMessageBox::ButtonCode questionRes = KMessageBox::warningYesNoCancel(this, "

    " + window->part()->i18nMessage("Design of object %1 has been modified.", window) .subs(window->partItem()->name()).toString() + "

    " + xi18n("Do you want to save changes?") + "

    " + additionalMessageString /*may be empty*/, QString(), saveChanges, discardChanges); if (questionRes == KMessageBox::Cancel) { #ifndef KEXI_NO_PENDING_DIALOGS d->removePendingWindow(window->id()); #endif d->insideCloseWindow = false; d->windowsToClose.clear(); //give up with 'close all' return cancelled; } if (questionRes == KMessageBox::Yes) { //save it tristate res = saveObject(window, QString(), DoNotAsk); if (!res || ~res) { //! @todo show error info; (retry/ignore/cancel) #ifndef KEXI_NO_PENDING_DIALOGS d->removePendingWindow(window->id()); #endif d->insideCloseWindow = false; d->windowsToClose.clear(); //give up with 'close all' return res; } remove_on_closing = false; } } const int window_id = window->id(); //remember now, because removeObject() can destruct partitem object if (remove_on_closing) { //we won't save this object, and it was never saved -remove it if (!removeObject(window->partItem(), true)) { #ifndef KEXI_NO_PENDING_DIALOGS d->removePendingWindow(window->id()); #endif //msg? //! @todo ask if we'd continue and return true/false d->insideCloseWindow = false; d->windowsToClose.clear(); //give up with 'close all' return false; } } else { //not dirty now if (d->objectViewWidget && d->objectViewWidget->projectNavigator()) { d->objectViewWidget->projectNavigator()->updateItemName(*window->partItem(), false); } } hideDesignTab(previousItemId, QString()); d->removeWindow(window_id); d->setWindowContainerExistsFor(window->partItem()->identifier(), false); QWidget *windowContainer = window->parentWidget(); d->objectViewWidget->tabWidget()->removeTab( d->objectViewWidget->tabWidget()->indexOf(windowContainer)); #ifdef KEXI_QUICK_PRINTING_SUPPORT //also remove from 'print setup dialogs' cache, if needed int printedObjectID = 0; if (d->pageSetupWindowItemID2dataItemID_map.contains(window_id)) printedObjectID = d->pageSetupWindowItemID2dataItemID_map[ window_id ]; d->pageSetupWindows.remove(printedObjectID); #endif delete windowContainer; //focus navigator if nothing else available if (d->openedWindowsCount() == 0) { if (d->objectViewWidget && d->objectViewWidget->projectNavigator()) { d->objectViewWidget->projectNavigator()->setFocus(); } d->updatePropEditorVisibility(Kexi::NoViewMode); } invalidateActions(); d->insideCloseWindow = false; if (!d->windowsToClose.isEmpty()) {//continue 'close all' KexiWindow* w = d->windowsToClose.takeAt(0); closeWindow(w, true); } #ifndef KEXI_NO_PENDING_DIALOGS d->removePendingWindow(window_id); //perform pending global action that was suspended: if (!d->pendingWindowsExist()) { d->executeActionWhenPendingJobsAreFinished(); } #endif //d->objectViewWidget->slotCurrentTabIndexChanged(d->objectViewWidget->tabWidget()->currentIndex()); showDesignTabIfNeeded(0); if (currentWindow()) { restoreDesignTabIfNeeded(currentWindow()->partItem()->pluginId(), currentWindow()->currentViewMode(), 0); } d->tabsToActivateOnShow.remove(previousItemId); return true; } QWidget* KexiMainWindow::findWindow(QWidget *w) { while (w && !acceptsSharedActions(w)) { if (w == d->objectViewWidget->propertyPane()) { return currentWindow(); } w = w->parentWidget(); } return w; } KexiWindow* KexiMainWindow::openedWindowFor(int identifier) { return d->openedWindowFor(identifier); } KexiWindow* KexiMainWindow::openedWindowFor(const KexiPart::Item* item) { return item ? openedWindowFor(item->identifier()) : 0; } KDbQuerySchema* KexiMainWindow::unsavedQuery(int queryId) { KexiWindow * queryWindow = openedWindowFor(queryId); if (!queryWindow || !queryWindow->isDirty()) { return 0; } return queryWindow->part()->currentQuery(queryWindow->viewForMode(Kexi::DataViewMode)); } QList KexiMainWindow::currentParametersForQuery(int queryId) const { KexiWindow *queryWindow = d->openedWindowFor(queryId); if (!queryWindow) { return QList(); } KexiView *view = queryWindow->viewForMode(Kexi::DataViewMode); if (!view) { return QList(); } return view->currentParameters(); } bool KexiMainWindow::acceptsSharedActions(QObject *w) { return w->inherits("KexiWindow") || w->inherits("KexiView"); } bool KexiMainWindow::openingAllowed(KexiPart::Item* item, Kexi::ViewMode viewMode, QString* errorMessage) { //qDebug() << viewMode; //! @todo this can be more complex once we deliver ACLs... if (!d->userMode) return true; KexiPart::Part * part = Kexi::partManager().partForPluginId(item->pluginId()); if (!part) { if (errorMessage) { *errorMessage = Kexi::partManager().result().message(); } } //qDebug() << part << item->pluginId(); //if (part) // qDebug() << item->pluginId() << part->info()->supportedUserViewModes(); return part && (part->info()->supportedUserViewModes() & viewMode); } KexiWindow * KexiMainWindow::openObject(const QString& pluginId, const QString& name, Kexi::ViewMode viewMode, bool *openingCancelled, QMap* staticObjectArgs) { KexiPart::Item *item = d->prj->itemForPluginId(pluginId, name); if (!item) return 0; return openObject(item, viewMode, openingCancelled, staticObjectArgs); } KexiWindow * KexiMainWindow::openObject(KexiPart::Item* item, Kexi::ViewMode viewMode, bool *openingCancelled, QMap* staticObjectArgs, QString* errorMessage) { Q_ASSERT(openingCancelled); if (!d->prj || !item) { return 0; } if (!openingAllowed(item, viewMode, errorMessage)) { if (errorMessage) *errorMessage = xi18nc( "opening is not allowed in \"data view/design view/text view\" mode", "opening is not allowed in \"%1\" mode", Kexi::nameForViewMode(viewMode)); *openingCancelled = true; return 0; } //qDebug() << d->prj << item; KexiWindow *prevWindow = currentWindow(); KexiUtils::WaitCursor wait; #ifndef KEXI_NO_PENDING_DIALOGS Private::PendingJobType pendingType; KexiWindow *window = d->openedWindowFor(item, pendingType); if (pendingType != Private::NoJob) { *openingCancelled = true; return 0; } #else KexiWindow *window = openedWindowFor(item); #endif int previousItemId = currentWindow() ? currentWindow()->partItem()->identifier() : 0; *openingCancelled = false; bool alreadyOpened = false; QWidget *windowContainer = 0; if (window) { if (viewMode != window->currentViewMode()) { if (true != switchToViewMode(*window, viewMode)) return 0; } else activateWindow(*window); alreadyOpened = true; } else { if (d->windowContainerExistsFor(item->identifier())) { // window not yet present but window container exists: return 0 and wait return 0; } KexiPart::Part *part = Kexi::partManager().partForPluginId(item->pluginId()); d->updatePropEditorVisibility(viewMode, part ? part->info() : 0); //update tabs before opening updateCustomPropertyPanelTabs(currentWindow() ? currentWindow()->part() : 0, currentWindow() ? currentWindow()->currentViewMode() : Kexi::NoViewMode, part, viewMode); const int tabIndex = d->objectViewWidget->tabWidget()->addEmptyContainerTab( part ? part->info()->icon() : koIcon("object"), KexiWindow::windowTitleForItem(*item)); // open new tab earlier d->setWindowContainerExistsFor(item->identifier(), true); d->objectViewWidget->tabWidget()->setTabToolTip(tabIndex, KexiPart::fullCaptionForItem(item, part)); QString whatsThisText; if (part) { whatsThisText = xi18nc("@info", "Tab for %1 (%2).", item->captionOrName(), part->info()->name()); } else { whatsThisText = xi18nc("@info", "Tab for %1.", item->captionOrName()); } d->objectViewWidget->tabWidget()->setTabWhatsThis(tabIndex, whatsThisText); d->objectViewWidget->tabWidget()->setCurrentIndex(tabIndex); #ifndef KEXI_NO_PENDING_DIALOGS d->addItemToPendingWindows(item, Private::WindowOpeningJob); #endif windowContainer = d->objectViewWidget->tabWidget()->widget(tabIndex); window = d->prj->openObject(windowContainer, item, viewMode, staticObjectArgs); if (window) { d->objectViewWidget->tabWidget()->setWindowForTab(tabIndex, window); // update text and icon d->objectViewWidget->tabWidget()->setTabText(tabIndex, window->windowTitle()); d->objectViewWidget->tabWidget()->setTabIcon(tabIndex, window->windowIcon()); } } if (!window || !activateWindow(*window)) { #ifndef KEXI_NO_PENDING_DIALOGS d->removePendingWindow(item->identifier()); #endif d->setWindowContainerExistsFor(item->identifier(), false); d->objectViewWidget->tabWidget()->removeTab( d->objectViewWidget->tabWidget()->indexOf(windowContainer)); delete windowContainer; updateCustomPropertyPanelTabs(0, Kexi::NoViewMode); //revert //! @todo add error msg... return 0; } if (viewMode != window->currentViewMode()) invalidateSharedActions(); #ifndef KEXI_NO_PENDING_DIALOGS d->removePendingWindow(window->id()); //perform pending global action that was suspended: if (!d->pendingWindowsExist()) { d->executeActionWhenPendingJobsAreFinished(); } #endif if (window && !alreadyOpened) { // Call switchToViewMode() and propertySetSwitched() again here because // this is the time when then new window is the current one - previous call did nothing. switchToViewMode(*window, window->currentViewMode()); currentWindow()->selectedView()->propertySetSwitched(); } invalidateProjectWideActions(); restoreDesignTabIfNeeded(item->pluginId(), viewMode, previousItemId); activateDesignTabIfNeeded(item->pluginId(), viewMode); QString origTabToActivate; if (prevWindow) { // Save the orig tab for prevWindow that was stored in the restoreDesignTabIfNeeded() call above origTabToActivate = d->tabsToActivateOnShow.value(prevWindow->partItem()->identifier()); } activeWindowChanged(window, prevWindow); if (prevWindow) { // Restore the orig tab d->tabsToActivateOnShow.insert(prevWindow->partItem()->identifier(), origTabToActivate); } return window; } KexiWindow * KexiMainWindow::openObjectFromNavigator(KexiPart::Item* item, Kexi::ViewMode viewMode) { bool openingCancelled; return openObjectFromNavigator(item, viewMode, &openingCancelled); } KexiWindow * KexiMainWindow::openObjectFromNavigator(KexiPart::Item* item, Kexi::ViewMode viewMode, bool *openingCancelled) { Q_ASSERT(openingCancelled); if (!openingAllowed(item, viewMode)) { *openingCancelled = true; return 0; } if (!d->prj || !item) return 0; #ifndef KEXI_NO_PENDING_DIALOGS Private::PendingJobType pendingType; KexiWindow *window = d->openedWindowFor(item, pendingType); if (pendingType != Private::NoJob) { *openingCancelled = true; return 0; } #else KexiWindow *window = openedWindowFor(item); #endif *openingCancelled = false; if (window) { if (activateWindow(*window)) { return window; } } //if DataViewMode is not supported, try Design, then Text mode (currently useful for script part) KexiPart::Part *part = Kexi::partManager().partForPluginId(item->pluginId()); if (!part) return 0; if (viewMode == Kexi::DataViewMode && !(part->info()->supportedViewModes() & Kexi::DataViewMode)) { if (part->info()->supportedViewModes() & Kexi::DesignViewMode) return openObjectFromNavigator(item, Kexi::DesignViewMode, openingCancelled); else if (part->info()->supportedViewModes() & Kexi::TextViewMode) return openObjectFromNavigator(item, Kexi::TextViewMode, openingCancelled); } //do the same as in openObject() return openObject(item, viewMode, openingCancelled); } tristate KexiMainWindow::closeObject(KexiPart::Item* item) { #ifndef KEXI_NO_PENDING_DIALOGS Private::PendingJobType pendingType; KexiWindow *window = d->openedWindowFor(item, pendingType); if (pendingType == Private::WindowClosingJob) return true; else if (pendingType == Private::WindowOpeningJob) return cancelled; #else KexiWindow *window = openedWindowFor(item); #endif if (!window) return cancelled; return closeWindow(window); } bool KexiMainWindow::newObject(KexiPart::Info *info, bool* openingCancelled) { Q_ASSERT(openingCancelled); if (d->userMode) { *openingCancelled = true; return false; } *openingCancelled = false; if (!d->prj || !info) return false; KexiPart::Part *part = Kexi::partManager().part(info); if (!part) return false; KexiPart::Item *it = d->prj->createPartItem(info); if (!it) { //! @todo error return false; } if (!it->neverSaved()) { //only add stored objects to the browser d->objectViewWidget->projectNavigator()->model()->slotAddItem(it); } return openObject(it, Kexi::DesignViewMode, openingCancelled); } tristate KexiMainWindow::removeObject(KexiPart::Item *item, bool dontAsk) { if (d->userMode) return cancelled; if (!d->prj || !item) return false; KexiPart::Part *part = Kexi::partManager().partForPluginId(item->pluginId()); if (!part) return false; if (!dontAsk) { if (KMessageBox::No == KMessageBox::questionYesNo(this, xi18nc("@info Remove ?", "Do you want to permanently delete the following object?" "%1 %2" "If you click Delete, " "you will not be able to undo the deletion.", part->info()->name(), item->name()), xi18nc("@title:window Delete Object %1.", "Delete %1?", item->name()), KStandardGuiItem::del(), KStandardGuiItem::no(), QString(), KMessageBox::Notify | KMessageBox::Dangerous)) { return cancelled; } } tristate res = true; #ifdef KEXI_QUICK_PRINTING_SUPPORT //also close 'print setup' dialog for this item, if any KexiWindow * pageSetupWindow = d->pageSetupWindows[ item->identifier()]; const bool oldInsideCloseWindow = d->insideCloseWindow; { d->insideCloseWindow = false; if (pageSetupWindow) res = closeWindow(pageSetupWindow); } d->insideCloseWindow = oldInsideCloseWindow; if (!res || ~res) { return res; } #endif #ifndef KEXI_NO_PENDING_DIALOGS Private::PendingJobType pendingType; KexiWindow *window = d->openedWindowFor(item, pendingType); if (pendingType != Private::NoJob) { return cancelled; } #else KexiWindow *window = openedWindowFor(item); #endif if (window) {//close existing window const bool tmp = d->forceWindowClosing; d->forceWindowClosing = true; res = closeWindow(window); d->forceWindowClosing = tmp; //restore if (!res || ~res) { return res; } } #ifdef KEXI_QUICK_PRINTING_SUPPORT //in case the dialog is a 'print setup' dialog, also update d->pageSetupWindows int dataItemID = d->pageSetupWindowItemID2dataItemID_map[item->identifier()]; d->pageSetupWindowItemID2dataItemID_map.remove(item->identifier()); d->pageSetupWindows.remove(dataItemID); #endif if (!d->prj->removeObject(item)) { //! @todo better msg showSorryMessage(xi18n("Could not remove object.")); return false; } return true; } void KexiMainWindow::renameObject(KexiPart::Item *item, const QString& _newName, bool *success) { Q_ASSERT(success); if (d->userMode) { *success = false; return; } QString newName = _newName.trimmed(); if (newName.isEmpty()) { showSorryMessage(xi18n("Could not set empty name for this object.")); *success = false; return; } KexiWindow *window = openedWindowFor(item); if (window) { QString msg = xi18nc("@info", "Before renaming object %1 it should be closed." "Do you want to close it?", item->name()); int r = KMessageBox::questionYesNo(this, msg, QString(), KStandardGuiItem::closeWindow(), KStandardGuiItem::cancel()); if (r != KMessageBox::Yes) { *success = false; return; } } setMessagesEnabled(false); //to avoid double messages const bool res = d->prj->renameObject(item, newName); setMessagesEnabled(true); if (!res) { showErrorMessage(xi18nc("@info", "Renaming object %1 failed.", newName), d->prj); *success = false; return; } } void KexiMainWindow::setObjectCaption(KexiPart::Item *item, const QString& _newCaption, bool *success) { Q_ASSERT(success); if (d->userMode) { *success = false; return; } QString newCaption = _newCaption.trimmed(); setMessagesEnabled(false); //to avoid double messages const bool res = d->prj->setObjectCaption(item, newCaption); setMessagesEnabled(true); if (!res) { showErrorMessage(xi18nc("@info", "Setting caption for object %1 failed.", newCaption), d->prj); *success = false; return; } } void KexiMainWindow::slotObjectRenamed(const KexiPart::Item &item, const QString& oldName) { Q_UNUSED(oldName); #ifndef KEXI_NO_PENDING_DIALOGS Private::PendingJobType pendingType; KexiWindow *window = d->openedWindowFor(&item, pendingType); if (pendingType != Private::NoJob) return; #else KexiWindow *window = openedWindowFor(&item); #endif if (!window) return; //change item window->updateCaption(); if (static_cast(currentWindow()) == window)//optionally, update app. caption updateAppCaption(); } void KexiMainWindow::acceptPropertySetEditing() { if (d->propertyEditor()) { d->propertyEditor()->editor()->acceptInput(); } } void KexiMainWindow::propertySetSwitched(KexiWindow *window, bool force, bool preservePrevSelection, bool sortedProperties, const QByteArray& propertyToSelect) { KexiWindow* _currentWindow = currentWindow(); //qDebug() << "currentWindow(): " // << (_currentWindow ? _currentWindow->windowTitle() : QString("NULL")) // << " window: " << (window ? window->windowTitle() : QString("NULL")); if (_currentWindow && _currentWindow != window) { d->propertySet = 0; //we'll need to move to another prop. set return; } if (d->propertyEditor()) { KPropertySet *newSet = _currentWindow ? _currentWindow->propertySet() : 0; if (!newSet || (force || static_cast(d->propertySet) != newSet)) { d->propertySet = newSet; if (preservePrevSelection || force) { KPropertyEditorView::SetOptions options; if (preservePrevSelection) { options |= KPropertyEditorView::SetOption::PreservePreviousSelection; } if (sortedProperties) { options |= KPropertyEditorView::SetOption::AlphabeticalOrder; } if (propertyToSelect.isEmpty()) { d->propertyEditor()->editor()->changeSet(d->propertySet, options); } else { d->propertyEditor()->editor()->changeSet(d->propertySet, propertyToSelect, options); } } } } } void KexiMainWindow::slotDirtyFlagChanged(KexiWindow* window) { KexiPart::Item *item = window->partItem(); //update text in navigator and app. caption if (!d->userMode) { d->objectViewWidget->projectNavigator()->updateItemName(*item, window->isDirty()); } invalidateActions(); updateAppCaption(); d->objectViewWidget->tabWidget()->setTabText( d->objectViewWidget->tabWidget()->indexOf(window->parentWidget()), window->windowTitle()); } void KexiMainWindow::slotTipOfTheDay() { //! @todo } void KexiMainWindow::slotReportBug() { KexiBugReportDialog bugReport(this); bugReport.exec(); } bool KexiMainWindow::userMode() const { return d->userMode; } void KexiMainWindow::setupUserActions() { } void KexiMainWindow::slotToolsImportProject() { if (d->tabbedToolBar) d->tabbedToolBar->hideMainMenu(); showProjectMigrationWizard(QString(), QString()); } void KexiMainWindow::slotToolsImportTables() { if (project()) { QMap args; QDialog *dlg = KexiInternalPart::createModalDialogInstance("org.kexi-project.migration", "importtable", this, 0, &args); if (!dlg) return; //error msg has been shown by KexiInternalPart const int result = dlg->exec(); delete dlg; if (result != QDialog::Accepted) return; QString destinationTableName(args["destinationTableName"]); if (!destinationTableName.isEmpty()) { QString pluginId = "org.kexi-project.table"; bool openingCancelled; KexiMainWindow::openObject(pluginId, destinationTableName, Kexi::DataViewMode, &openingCancelled); } } } void KexiMainWindow::slotToolsCompactDatabase() { KexiProjectData *data = 0; KDbDriver *drv = 0; const bool projectWasOpened = d->prj; if (!d->prj) { KexiStartupDialog dlg( KexiStartupDialog::OpenExisting, 0, Kexi::connset(), this); if (dlg.exec() != QDialog::Accepted) return; if (dlg.selectedFileName().isEmpty()) { //! @todo add support for server based if needed? return; } KDbConnectionData cdata; cdata.setDatabaseName(dlg.selectedFileName()); //detect driver name for the selected file KexiStartupData::Import detectedImportAction; QString detectedDriverId; tristate res = KexiStartupHandler::detectActionForFile( &detectedImportAction, &detectedDriverId, QString() /*suggestedDriverId*/, cdata.databaseName(), 0, KexiStartupHandler::SkipMessages | KexiStartupHandler::ThisIsAProjectFile | KexiStartupHandler::DontConvert); if (true == res && !detectedImportAction) { cdata.setDriverId(detectedDriverId); drv = Kexi::driverManager().driver(cdata.driverId()); } if (!drv || !(drv->features() & KDbDriver::CompactingDatabaseSupported)) { KMessageBox::information(this, xi18n("Compacting database file %1 is not supported.", QDir::toNativeSeparators(cdata.databaseName()))); return; } data = new KexiProjectData(cdata); } else { //sanity if (!(d->prj && d->prj->dbConnection() && (d->prj->dbConnection()->driver()->features() & KDbDriver::CompactingDatabaseSupported))) return; KGuiItem yesItem(KStandardGuiItem::cont()); yesItem.setText(xi18nc("@action:button Compact database", "Compact")); if (KMessageBox::Yes != KMessageBox::questionYesNo(this, xi18n("The current project has to be closed before compacting the database. " "It will be open again after compacting.\n\nDo you want to continue?"), QString(), yesItem, KStandardGuiItem::cancel())) { return; } data = new KexiProjectData(*d->prj->data()); // a copy drv = d->prj->dbConnection()->driver(); const tristate res = closeProject(); if (~res || !res) { delete data; return; } } if (!drv->adminTools().vacuum(*data->connectionData(), data->databaseName())) { showErrorMessage(QString(), &drv->adminTools()); } if (projectWasOpened) openProject(*data); delete data; } tristate KexiMainWindow::showProjectMigrationWizard(const QString& mimeType, const QString& databaseName) { return d->showProjectMigrationWizard(mimeType, databaseName, 0); } tristate KexiMainWindow::showProjectMigrationWizard( const QString& mimeType, const QString& databaseName, const KDbConnectionData &cdata) { return d->showProjectMigrationWizard(mimeType, databaseName, &cdata); } tristate KexiMainWindow::executeItem(KexiPart::Item* item) { KexiPart::Info *info = item ? Kexi::partManager().infoForPluginId(item->pluginId()) : 0; if ((! info) || (! info->isExecuteSupported())) return false; KexiPart::Part *part = Kexi::partManager().part(info); if (!part) return false; return part->execute(item); } void KexiMainWindow::slotProjectImportDataTable() { //! @todo allow data appending (it is not possible now) if (d->userMode) return; QMap args; args.insert("sourceType", "file"); QDialog *dlg = KexiInternalPart::createModalDialogInstance( "org.kexi-project.importexport.csv", "KexiCSVImportDialog", this, 0, &args); if (!dlg) return; //error msg has been shown by KexiInternalPart dlg->exec(); delete dlg; } tristate KexiMainWindow::executeCustomActionForObject(KexiPart::Item* item, const QString& actionName) { if (actionName == "exportToCSV") return exportItemAsDataTable(item); else if (actionName == "copyToClipboardAsCSV") return copyItemToClipboardAsDataTable(item); qWarning() << "no such action:" << actionName; return false; } tristate KexiMainWindow::exportItemAsDataTable(KexiPart::Item* item) { if (!item) return false; QMap args; if (!checkForDirtyFlagOnExport(item, &args)) { return false; } //! @todo: accept record changes... args.insert("destinationType", "file"); args.insert("itemId", QString::number(item->identifier())); QDialog *dlg = KexiInternalPart::createModalDialogInstance( "org.kexi-project.importexport.csv", "KexiCSVExportWizard", this, 0, &args); if (!dlg) return false; //error msg has been shown by KexiInternalPart int result = dlg->exec(); delete dlg; return result == QDialog::Rejected ? tristate(cancelled) : tristate(true); } bool KexiMainWindow::checkForDirtyFlagOnExport(KexiPart::Item *item, QMap *args) { //! @todo: handle tables if (item->pluginId() != "org.kexi-project.query") { return true; } KexiWindow * itemWindow = openedWindowFor(item); if (itemWindow && itemWindow->isDirty()) { tristate result; if (item->neverSaved()) { result = true; } else { int prevWindowId = 0; if (!itemWindow->isVisible()) { prevWindowId = currentWindow()->id(); activateWindow(itemWindow->id()); } result = askOnExportingChangedQuery(item); if (prevWindowId != 0) { activateWindow(prevWindowId); } } if (~result) { return false; } else if (true == result) { args->insert("useTempQuery","1"); } } return true; } tristate KexiMainWindow::askOnExportingChangedQuery(KexiPart::Item *item) const { const KMessageBox::ButtonCode result = KMessageBox::warningYesNoCancel(const_cast(this), xi18nc("@info", "Design of query %1 that you want to export data" " from is changed and has not yet been saved. Do you want to use data" " from the changed query for exporting or from its original (saved)" " version?", item->captionOrName()), QString(), KGuiItem(xi18nc("@action:button Export query data", "Use the Changed Query")), KGuiItem(xi18nc("@action:button Export query data", "Use the Original Query")), KStandardGuiItem::cancel(), QString(), KMessageBox::Notify | KMessageBox::Dangerous); if (result == KMessageBox::Yes) { return true; } else if (result == KMessageBox::No) { return false; } return cancelled; } bool KexiMainWindow::printItem(KexiPart::Item* item, const QString& titleText) { //! @todo printItem(item, KexiSimplePrintingSettings::load(), titleText); Q_UNUSED(item) Q_UNUSED(titleText) return false; } tristate KexiMainWindow::printItem(KexiPart::Item* item) { return printItem(item, QString()); } bool KexiMainWindow::printPreviewForItem(KexiPart::Item* item, const QString& titleText, bool reload) { //! @todo printPreviewForItem(item, KexiSimplePrintingSettings::load(), titleText, reload); Q_UNUSED(item) Q_UNUSED(titleText) Q_UNUSED(reload) return false; } tristate KexiMainWindow::printPreviewForItem(KexiPart::Item* item) { return printPreviewForItem(item, QString(), //! @todo store cached record data? true/*reload*/); } tristate KexiMainWindow::showPageSetupForItem(KexiPart::Item* item) { Q_UNUSED(item) //! @todo check if changes to this object's design are saved, if not: ask for saving //! @todo accept record changes... //! @todo printActionForItem(item, PageSetupForItem); return false; } //! @todo reenable printItem() when ported #if 0 bool KexiMainWindow::printItem(KexiPart::Item* item, const KexiSimplePrintingSettings& settings, const QString& titleText) { //! @todo: check if changes to this object's design are saved, if not: ask for saving //! @todo: accept record changes... KexiSimplePrintingCommand cmd(this, item->identifier()); //modal return cmd.print(settings, titleText); } bool KexiMainWindow::printPreviewForItem(KexiPart::Item* item, const KexiSimplePrintingSettings& settings, const QString& titleText, bool reload) { //! @todo: check if changes to this object's design are saved, if not: ask for saving //! @todo: accept record changes... KexiSimplePrintingCommand* cmd = d->openedCustomObjectsForItem( item, "KexiSimplePrintingCommand"); if (!cmd) { d->addOpenedCustomObjectForItem( item, cmd = new KexiSimplePrintingCommand(this, item->identifier()), "KexiSimplePrintingCommand" ); } return cmd->showPrintPreview(settings, titleText, reload); } tristate KexiMainWindow::printActionForItem(KexiPart::Item* item, PrintActionType action) { if (!item) return false; KexiPart::Info *info = Kexi::partManager().infoForPluginId(item->pluginId()); if (!info->isPrintingSupported()) return false; KexiWindow *printingWindow = d->pageSetupWindows[ item->identifier()]; if (printingWindow) { if (!activateWindow(*printingWindow)) return false; if (action == PreviewItem || action == PrintItem) { QTimer::singleShot(0, printingWindow->selectedView(), (action == PreviewItem) ? SLOT(printPreview()) : SLOT(print())); } return true; } #ifndef KEXI_NO_PENDING_DIALOGS Private::PendingJobType pendingType; KexiWindow *window = d->openedWindowFor(item, pendingType); if (pendingType != Private::NoJob) return cancelled; #else KexiWindow *window = openedWindowFor(item); #endif if (window) { // accept record changes QWidget *prevFocusWidget = focusWidget(); window->setFocus(); d->action_data_save_row->activate(QAction::Trigger); if (prevFocusWidget) prevFocusWidget->setFocus(); // opened: check if changes made to this dialog are saved, if not: ask for saving if (window->neverSaved()) //sanity check return false; if (window->isDirty()) { KGuiItem saveChanges(KStandardGuiItem::save()); saveChanges.setToolTip(futureI18n("Save changes")); saveChanges.setWhatsThis( futureI18n("Pressing this button will save all recent changes made in \"%1\" object.", item->name())); KGuiItem doNotSave(KStandardGuiItem::no()); doNotSave.setWhatsThis( futureI18n("Pressing this button will ignore all unsaved changes made in \"%1\" object.", window->partItem()->name())); QString question; if (action == PrintItem) question = futureI18n("Do you want to save changes before printing?"); else if (action == PreviewItem) question = futureI18n("Do you want to save changes before making print preview?"); else if (action == PageSetupForItem) question = futureI18n("Do you want to save changes before showing page setup?"); else return false; const KMessageBox::ButtonCode questionRes = KMessageBox::warningYesNoCancel(this, "

    " + window->part()->i18nMessage("Design of object %1 has been modified.", window) .subs(item->name()) + "

    " + question + "

    ", QString(), saveChanges, doNotSave); if (KMessageBox::Cancel == questionRes) return cancelled; if (KMessageBox::Yes == questionRes) { tristate savingRes = saveObject(window, QString(), DoNotAsk); if (true != savingRes) return savingRes; } } } KexiPart::Part * printingPart = Kexi::partManager().partForClass("org.kexi-project.simpleprinting"); if (!printingPart) printingPart = new KexiSimplePrintingPart(); //hardcoded as there're no .desktop file KexiPart::Item* printingPartItem = d->prj->createPartItem( printingPart, item->name() //<-- this will look like "table1 : printing" on the window list ); QMap staticObjectArgs; staticObjectArgs["identifier"] = QString::number(item->identifier()); if (action == PrintItem) staticObjectArgs["action"] = "print"; else if (action == PreviewItem) staticObjectArgs["action"] = "printPreview"; else if (action == PageSetupForItem) staticObjectArgs["action"] = "pageSetup"; else return false; bool openingCancelled; printingWindow = openObject(printingPartItem, Kexi::DesignViewMode, &openingCancelled, &staticObjectArgs); if (openingCancelled) return cancelled; if (!printingWindow) //sanity return false; d->pageSetupWindows.insert(item->identifier(), printingWindow); d->pageSetupWindowItemID2dataItemID_map.insert( printingWindow->partItem()->identifier(), item->identifier()); return true; } #endif void KexiMainWindow::slotEditCopySpecialDataTable() { KexiPart::Item* item = d->objectViewWidget->projectNavigator()->selectedPartItem(); if (item) copyItemToClipboardAsDataTable(item); } tristate KexiMainWindow::copyItemToClipboardAsDataTable(KexiPart::Item* item) { if (!item) return false; QMap args; if (!checkForDirtyFlagOnExport(item, &args)) { return false; } args.insert("destinationType", "clipboard"); args.insert("itemId", QString::number(item->identifier())); QDialog *dlg = KexiInternalPart::createModalDialogInstance( "org.kexi-project.importexport.csv", "KexiCSVExportWizard", this, 0, &args); if (!dlg) return false; //error msg has been shown by KexiInternalPart const int result = dlg->exec(); delete dlg; return result == QDialog::Rejected ? tristate(cancelled) : tristate(true); } void KexiMainWindow::slotEditPasteSpecialDataTable() { //! @todo allow data appending (it is not possible now) if (d->userMode) return; QMap args; args.insert("sourceType", "clipboard"); QDialog *dlg = KexiInternalPart::createModalDialogInstance( "org.kexi-project.importexport.csv", "KexiCSVImportDialog", this, 0, &args); if (!dlg) return; //error msg has been shown by KexiInternalPart dlg->exec(); delete dlg; } void KexiMainWindow::slotEditFind() { KexiSearchAndReplaceViewInterface* iface = d->currentViewSupportingSearchAndReplaceInterface(); if (!iface) return; d->updateFindDialogContents(true/*create if does not exist*/); d->findDialog()->setReplaceMode(false); d->findDialog()->show(); d->findDialog()->activateWindow(); d->findDialog()->raise(); } void KexiMainWindow::slotEditFind(bool next) { KexiSearchAndReplaceViewInterface* iface = d->currentViewSupportingSearchAndReplaceInterface(); if (!iface) return; tristate res = iface->find( d->findDialog()->valueToFind(), d->findDialog()->options(), next); if (~res) return; d->findDialog()->updateMessage(true == res); //! @todo result } void KexiMainWindow::slotEditFindNext() { slotEditFind(true); } void KexiMainWindow::slotEditFindPrevious() { slotEditFind(false); } void KexiMainWindow::slotEditReplace() { KexiSearchAndReplaceViewInterface* iface = d->currentViewSupportingSearchAndReplaceInterface(); if (!iface) return; d->updateFindDialogContents(true/*create if does not exist*/); d->findDialog()->setReplaceMode(true); //! @todo slotEditReplace() d->findDialog()->show(); d->findDialog()->activateWindow(); } void KexiMainWindow::slotEditReplaceNext() { slotEditReplace(false); } void KexiMainWindow::slotEditReplace(bool all) { KexiSearchAndReplaceViewInterface* iface = d->currentViewSupportingSearchAndReplaceInterface(); if (!iface) return; //! @todo add question: "Do you want to replace every occurrence of \"%1\" with \"%2\"? //! You won't be able to undo this." + "Do not ask again". tristate res = iface->findNextAndReplace( d->findDialog()->valueToFind(), d->findDialog()->valueToReplaceWith(), d->findDialog()->options(), all); d->findDialog()->updateMessage(true == res); //! @todo result } void KexiMainWindow::slotEditReplaceAll() { slotEditReplace(true); } void KexiMainWindow::highlightObject(const QString& pluginId, const QString& name) { if (!d->prj) return; KexiPart::Item *item = d->prj->itemForPluginId(pluginId, name); if (!item) return; if (d->objectViewWidget && d->objectViewWidget->projectNavigator()) { d->setProjectNavigatorVisible(true, Private::ShowAnimated); d->objectViewWidget->projectNavigator()->selectItem(*item); } } void KexiMainWindow::slotPartItemSelectedInNavigator(KexiPart::Item* item) { Q_UNUSED(item); } KToolBar *KexiMainWindow::toolBar(const QString& name) const { return d->tabbedToolBar ? d->tabbedToolBar->toolBar(name) : 0; } void KexiMainWindow::appendWidgetToToolbar(const QString& name, QWidget* widget) { if (d->tabbedToolBar) d->tabbedToolBar->appendWidgetToToolbar(name, widget); } void KexiMainWindow::setWidgetVisibleInToolbar(QWidget* widget, bool visible) { if (d->tabbedToolBar) d->tabbedToolBar->setWidgetVisibleInToolbar(widget, visible); } void KexiMainWindow::addToolBarAction(const QString& toolBarName, QAction *action) { if (d->tabbedToolBar) d->tabbedToolBar->addAction(toolBarName, action); } void KexiMainWindow::updatePropertyEditorInfoLabel(const QString& textToDisplayForNullSet) { d->propertyEditor()->updateInfoLabelForPropertySet( d->propertySet, textToDisplayForNullSet); } void KexiMainWindow::addSearchableModel(KexiSearchableModel *model) { if (d->tabbedToolBar) { d->tabbedToolBar->addSearchableModel(model); } } void KexiMainWindow::setReasonableDialogSize(QDialog *dialog) { dialog->setMinimumSize(600, 400); dialog->resize(size() * 0.8); } void KexiMainWindow::restoreDesignTabAndActivateIfNeeded(const QString &tabName) { if (!d->tabbedToolBar) { return; } d->tabbedToolBar->showTab(tabName); if (currentWindow() && currentWindow()->partItem() && currentWindow()->partItem()->identifier() != 0) // for unstored items id can be < 0 { const QString tabToActivate = d->tabsToActivateOnShow.value( currentWindow()->partItem()->identifier()); //qDebug() << "tabToActivate:" << tabToActivate << "tabName:" << tabName; if (tabToActivate == tabName) { d->tabbedToolBar->setCurrentTab(tabToActivate); } } } void KexiMainWindow::restoreDesignTabIfNeeded(const QString &pluginId, Kexi::ViewMode viewMode, int previousItemId) { //qDebug() << pluginId << viewMode << previousItemId; if (viewMode == Kexi::DesignViewMode) { switch (d->prj->typeIdForPluginId(pluginId)) { case KexiPart::FormObjectType: { hideDesignTab(previousItemId, "org.kexi-project.report"); restoreDesignTabAndActivateIfNeeded("form"); break; } case KexiPart::ReportObjectType: { hideDesignTab(previousItemId, "org.kexi-project.form"); restoreDesignTabAndActivateIfNeeded("report"); break; } default: hideDesignTab(previousItemId); } } else { hideDesignTab(previousItemId); } } void KexiMainWindow::activateDesignTab(const QString &pluginId) { if (!d->tabbedToolBar) { return; } switch (d->prj->typeIdForPluginId(pluginId)) { case KexiPart::FormObjectType: d->tabbedToolBar->setCurrentTab("form"); break; case KexiPart::ReportObjectType: d->tabbedToolBar->setCurrentTab("report"); break; default:; } } void KexiMainWindow::activateDesignTabIfNeeded(const QString &pluginId, Kexi::ViewMode viewMode) { if (!d->tabbedToolBar) { return; } const QString tabToActivate = d->tabsToActivateOnShow.value(currentWindow()->partItem()->identifier()); //qDebug() << pluginId << viewMode << tabToActivate; if (viewMode == Kexi::DesignViewMode && tabToActivate.isEmpty()) { activateDesignTab(pluginId); } else { d->tabbedToolBar->setCurrentTab(tabToActivate); } } void KexiMainWindow::hideDesignTab(int itemId, const QString &pluginId) { if (!d->tabbedToolBar) { return; } //qDebug() << itemId << pluginId; if ( itemId > 0 && d->tabbedToolBar->currentWidget()) { const QString currentTab = d->tabbedToolBar->currentWidget()->objectName(); //qDebug() << "d->tabsToActivateOnShow.insert" << itemId << currentTab; d->tabsToActivateOnShow.insert(itemId, currentTab); } switch (d->prj->typeIdForPluginId(pluginId)) { case KexiPart::FormObjectType: d->tabbedToolBar->hideTab("form"); break; case KexiPart::ReportObjectType: d->tabbedToolBar->hideTab("report"); break; default: d->tabbedToolBar->hideTab("form"); d->tabbedToolBar->hideTab("report"); } } void KexiMainWindow::showDesignTabIfNeeded(int previousItemId) { if (d->insideCloseWindow && d->tabbedToolBar) return; if (currentWindow()) { restoreDesignTabIfNeeded(currentWindow()->partItem()->pluginId(), currentWindow()->currentViewMode(), previousItemId); } else { hideDesignTab(previousItemId); } } KexiUserFeedbackAgent* KexiMainWindow::userFeedbackAgent() const { return &d->userFeedback; } KexiMigrateManagerInterface* KexiMainWindow::migrateManager() { if (!d->migrateManager) { d->migrateManager = dynamic_cast( KexiInternalPart::createObjectInstance( "org.kexi-project.migration", "manager", this, this, nullptr)); } return d->migrateManager; } void KexiMainWindow::toggleFullScreen(bool isFullScreen) { static bool isTabbarRolledDown; if (d->tabbedToolBar) { if (isFullScreen) { isTabbarRolledDown = !d->tabbedToolBar->isRolledUp(); if (isTabbarRolledDown) { d->tabbedToolBar->toggleRollDown(); } } else { if (isTabbarRolledDown && d->tabbedToolBar->isRolledUp()) { d->tabbedToolBar->toggleRollDown(); } } } const Qt::WindowStates s = windowState() & Qt::WindowMaximized; if (isFullScreen) { setWindowState(windowState() | Qt::WindowFullScreen | s); } else { setWindowState((windowState() & ~Qt::WindowFullScreen)); showMaximized(); } } Kexi::GlobalViewMode KexiMainWindow::currentMode() const { return d->modeSelector->currentMode(); } void KexiMainWindow::setCurrentMode(Kexi::GlobalViewMode mode) { d->modeSelector->setCurrentMode(mode); } void KexiMainWindow::slotCurrentModeChanged() { switch (d->modeSelector->currentMode()) { case Kexi::WelcomeGlobalMode: break; case Kexi::ProjectGlobalMode: break; case Kexi::EditGlobalMode: updateObjectView(); d->globalViewStack->setCurrentWidget(d->objectViewWidget); break; case Kexi::DesignGlobalMode: break; case Kexi::HelpGlobalMode: break; } } void KexiMainWindow::slotProjectNavigatorVisibilityChanged(bool visible) { if (d->objectViewWidget && d->objectViewWidget->projectNavigator()) { d->modeSelector->setArrowColor(visible ? d->objectViewWidget->projectNavigator()->palette().color(QPalette::Window) : d->objectViewWidget->tabWidget()->palette().color(QPalette::Window)); } } diff --git a/src/plugins/forms/CMakeLists.txt b/src/plugins/forms/CMakeLists.txt index db691d0b7..5f83eed1e 100644 --- a/src/plugins/forms/CMakeLists.txt +++ b/src/plugins/forms/CMakeLists.txt @@ -1,87 +1,92 @@ -include_directories( ${CMAKE_SOURCE_DIR}/src/core - ${CMAKE_SOURCE_DIR}/src/widget/utils ${CMAKE_SOURCE_DIR}/src/widget - ${CMAKE_SOURCE_DIR}/src/formeditor) +include_directories( + ${CMAKE_SOURCE_DIR}/src/core + ${CMAKE_SOURCE_DIR}/src/kexiutils/style + ${CMAKE_SOURCE_DIR}/src/widget/utils + ${CMAKE_SOURCE_DIR}/src/widget + ${CMAKE_SOURCE_DIR}/src/widget/properties + ${CMAKE_SOURCE_DIR}/src/formeditor +) # the form plugin set(kexi_formplugin_SRCS kexiforms.cpp) add_library(kexi_formplugin MODULE ${kexi_formplugin_SRCS}) kcoreaddons_desktop_to_json(kexi_formplugin kexi_formplugin.desktop) target_link_libraries(kexi_formplugin kexicore kexiguiutils kexidatatable kexiextendedwidgets kformdesigner kexiformutils KPropertyWidgets ) install(TARGETS kexi_formplugin DESTINATION ${KEXI_PLUGIN_INSTALL_DIR}) # the form utils lib set(kexiformutils_LIB_SRCS # kexiformdataiteminterface.cpp kexidataawarewidgetinfo.cpp KexiFormScrollAreaWidget.cpp kexiformscrollview.cpp kexidbtextwidgetinterface.cpp kexiformmanager.cpp kexidatasourcepage.cpp kexiformpart.cpp kexiformview.cpp kexidbfactorybase.cpp widgets/kexidbutils.cpp widgets/kexidbautofield.cpp widgets/kexidbform.cpp widgets/kexidblabel.cpp widgets/kexidbimagebox.cpp widgets/KexiDBPushButton.cpp widgets/kexiframe.cpp widgets/kexidblineedit.cpp widgets/kexidbcheckbox.cpp widgets/kexidbtextedit.cpp widgets/kexidbcombobox.cpp widgets/kexidbcommandlinkbutton.cpp widgets/kexidbslider.cpp widgets/kexidbprogressbar.cpp widgets/kexidbdatepicker.cpp ) #obsolete widgets/kexidbdoublespinbox.cpp #obsolete widgets/kexidbintspinbox.cpp kexi_add_library(kexiformutils SHARED ${kexiformutils_LIB_SRCS}) generate_export_header(kexiformutils) target_link_libraries(kexiformutils PRIVATE kexicore kexiextendedwidgets kformdesigner kexiutils kexidataviewcommon kexidatatable kexiguiutils KDb KPropertyWidgets Qt5::Gui Qt5::Xml ) set(kexiformutils_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/src/plugins/forms ${CMAKE_SOURCE_DIR}/src/plugins/forms/widgets ) target_include_directories(kexiformutils PUBLIC "$" ) install(TARGETS kexiformutils ${INSTALL_TARGETS_DEFAULT_ARGS}) add_subdirectory(widgets) diff --git a/src/plugins/forms/kexidatasourcepage.cpp b/src/plugins/forms/kexidatasourcepage.cpp index 159cba8b5..a0a38eca3 100644 --- a/src/plugins/forms/kexidatasourcepage.cpp +++ b/src/plugins/forms/kexidatasourcepage.cpp @@ -1,454 +1,455 @@ /* This file is part of the KDE project Copyright (C) 2005-2009 Jarosław Staniek This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kexidatasourcepage.h" #include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include KexiDataSourcePage::KexiDataSourcePage(QWidget *parent) : KexiPropertyPaneViewBase(parent) , m_noDataSourceAvailableSingleText( xi18n("No data source could be assigned for this widget.") ) , m_noDataSourceAvailableMultiText( xi18n("No data source could be assigned for multiple widgets.") ) , m_insideClearFormDataSourceSelection(false) #ifndef KEXI_AUTOFIELD_FORM_WIDGET_SUPPORT , m_tableOrQuerySchema(0) #endif { infoLabel()->setContentsMargins(0, 0, 0, spacing()); m_noDataSourceAvailableLabel = new QLabel(m_noDataSourceAvailableSingleText, this); m_noDataSourceAvailableLabel->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum); m_noDataSourceAvailableLabel->setContentsMargins(0, 0, 0, spacing()); m_noDataSourceAvailableLabel->setAlignment(Qt::AlignBottom | Qt::AlignLeft); m_noDataSourceAvailableLabel->setWordWrap(true); mainLayout()->addWidget(m_noDataSourceAvailableLabel); //-Widget's Data Source QHBoxLayout *hlyr = new QHBoxLayout(); mainLayout()->addLayout(hlyr); #if 0 //! @todo unhide this when expression work // m_widgetDSLabel = new QLabel(futureI18nc("Table Field, Query Field or Expression", "Source field or expression"), this); #else m_widgetDSLabel = new QLabel( xi18nc("Table Field or Query Field", "Widget's data source:"), this); #endif m_widgetDSLabel->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); m_widgetDSLabel->setAlignment(Qt::AlignLeft | Qt::AlignBottom); hlyr->addWidget(m_widgetDSLabel); mainLayout()->addSpacing(KexiUtils::spacingHint()); // needed because unlike m_dataSourceLabel we have no button in hlyr #if 0 m_clearWidgetDSButton = new KexiSmallToolButton( koIcon("edit-clear-locationbar-rtl"), QString(), this); m_clearWidgetDSButton->setObjectName("clearWidgetDSButton"); m_clearWidgetDSButton->setMinimumHeight(m_widgetDSLabel->minimumHeight()); m_clearWidgetDSButton->setToolTip(futureI18n("Clear widget's data source")); hlyr->addWidget(m_clearWidgetDSButton); connect(m_clearWidgetDSButton, SIGNAL(clicked()), this, SLOT(clearWidgetDataSourceSelection())); #endif m_widgetDataSourceCombo = new KexiFieldComboBox(this); m_widgetDataSourceCombo->setObjectName("sourceFieldCombo"); m_widgetDataSourceCombo->setContentsMargins(0, 0, 0, 0); m_widgetDSLabel->setBuddy(m_widgetDataSourceCombo); connect(m_widgetDataSourceCombo, SIGNAL(editTextChanged(QString)), this, SLOT(slotWidgetDataSourceTextChanged(QString))); mainLayout()->addWidget(m_widgetDataSourceCombo); m_widgetDataSourceComboSpacer = addWidgetSpacer(); //- Form's Data Source hlyr = new QHBoxLayout(); hlyr->setContentsMargins(0, 0, 0, 0); mainLayout()->addLayout(hlyr); m_dataSourceLabel = new QLabel(xi18n("Form's data source:"), this); m_dataSourceLabel->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); m_dataSourceLabel->setAlignment(Qt::AlignLeft | Qt::AlignBottom); hlyr->addWidget(m_dataSourceLabel); m_gotoButton = new KexiSmallToolButton( koIcon("go-jump"), QString(), this); m_gotoButton->setObjectName("gotoButton"); m_gotoButton->setToolTip(xi18n("Go to selected form's data source")); m_gotoButton->setWhatsThis(xi18n("Goes to selected form's data source")); hlyr->addWidget(m_gotoButton); connect(m_gotoButton, SIGNAL(clicked()), this, SLOT(slotGotoSelected())); #if 0 m_clearDSButton = new KexiSmallToolButton( koIcon("edit-clear-locationbar-rtl"), QString(), this); m_clearDSButton->setObjectName("clearDSButton"); m_clearDSButton->setMinimumHeight(m_dataSourceLabel->minimumHeight()); m_clearDSButton->setToolTip(futureI18n("Clear form's data source")); hlyr->addWidget(m_clearDSButton); connect(m_clearDSButton, SIGNAL(clicked()), this, SLOT(clearFormDataSourceSelection())); #endif m_formDataSourceCombo = new KexiDataSourceComboBox(this); m_formDataSourceCombo->setObjectName("dataSourceCombo"); m_formDataSourceCombo->setContentsMargins(0, 0, 0, 0); m_dataSourceLabel->setBuddy(m_formDataSourceCombo); mainLayout()->addWidget(m_formDataSourceCombo); m_formDataSourceComboSpacer = addWidgetSpacer(); #ifndef KEXI_AUTOFIELD_FORM_WIDGET_SUPPORT mainLayout()->addStretch(); #else //2. Inserting fields //helper info //! @todo allow to hide such helpers by adding global option hlyr = new QHBoxLayout(); hlyr->setContentsMargins(0, 0, 0, 0); mainLayout()->addLayout(hlyr); m_mousePointerLabel = new QLabel(this); hlyr->addWidget(m_mousePointerLabel); m_mousePointerLabel->setPixmap(koIcon("tool-pointer")); m_mousePointerLabel->setFixedWidth(m_mousePointerLabel->pixmap() ? m_mousePointerLabel->pixmap()->width() : 0); m_availableFieldsDescriptionLabel = new QLabel( futureI18n("Select fields from the list below and drag them onto" " a form or click the Insert button"), this); m_availableFieldsDescriptionLabel->setAlignment(Qt::AlignLeft); m_availableFieldsDescriptionLabel->setWordWrap(true); hlyr->addWidget(m_availableFieldsDescriptionLabel); //Available Fields hlyr = new QHBoxLayout(); hlyr->setContentsMargins(0, 0, 0, 0); mainLayout()->addLayout(hlyr); m_availableFieldsLabel = new QLabel(futureI18n("Available fields"), this); m_availableFieldsLabel->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); hlyr->addWidget(m_availableFieldsLabel); m_addField = new KexiSmallToolButton( KexiIcon("add-field"), futureI18nc("Insert selected field into form", "Insert"), this); m_addField->setObjectName("addFieldButton"); m_addField->setFocusPolicy(Qt::StrongFocus); m_addField->setToolTip(futureI18n("Insert selected fields into form")); m_addField->setWhatsThis(futureI18n("Inserts selected fields into form")); hlyr->addWidget(m_addField); connect(m_addField, SIGNAL(clicked()), this, SLOT(slotInsertSelectedFields())); m_fieldListView = new KexiFieldListView(this, KexiFieldListView::ShowDataTypes | KexiFieldListView::AllowMultiSelection); m_fieldListView->setObjectName("fieldListView"); m_fieldListView->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding)); m_availableFieldsLabel->setBuddy(m_fieldListView); mainLayout()->addWidget(m_fieldListView, 1); connect(m_fieldListView, SIGNAL(selectionChanged()), this, SLOT(slotFieldListViewSelectionChanged())); connect(m_fieldListView, SIGNAL(fieldDoubleClicked(QString,QString,QString)), this, SLOT(slotFieldDoubleClicked(QString,QString,QString))); #endif mainLayout()->addStretch(1); connect(m_formDataSourceCombo, SIGNAL(editTextChanged(QString)), this, SLOT(slotFormDataSourceTextChanged(QString))); connect(m_formDataSourceCombo, SIGNAL(dataSourceChanged()), this, SLOT(slotFormDataSourceChanged())); connect(m_widgetDataSourceCombo, SIGNAL(selected()), this, SLOT(slotFieldSelected())); clearFormDataSourceSelection(); slotFieldListViewSelectionChanged(); } KexiDataSourcePage::~KexiDataSourcePage() { #ifndef KEXI_AUTOFIELD_FORM_WIDGET_SUPPORT delete m_tableOrQuerySchema; #endif } void KexiDataSourcePage::setProject(KexiProject *prj) { m_widgetDataSourceCombo->setProject(prj); m_formDataSourceCombo->setProject(prj); } void KexiDataSourcePage::clearFormDataSourceSelection(bool alsoClearComboBox) { if (m_insideClearFormDataSourceSelection) return; m_insideClearFormDataSourceSelection = true; if (alsoClearComboBox && !m_formDataSourceCombo->selectedName().isEmpty()) m_formDataSourceCombo->setDataSource(QString(), QString()); m_gotoButton->setEnabled(false); m_widgetDataSourceCombo->setFieldOrExpression(QString()); #ifdef KEXI_AUTOFIELD_FORM_WIDGET_SUPPORT m_addField->setEnabled(false); m_fieldListView->clear(); #endif m_insideClearFormDataSourceSelection = false; } void KexiDataSourcePage::slotWidgetDataSourceTextChanged(const QString &text) { if (text.isEmpty()) { clearWidgetDataSourceSelection(); } } void KexiDataSourcePage::clearWidgetDataSourceSelection() { m_widgetDataSourceCombo->setFieldOrExpression(QString()); slotFieldSelected(); } void KexiDataSourcePage::slotGotoSelected() { const QString pluginId(m_formDataSourceCombo->selectedPluginId()); if (pluginId == "org.kexi-project.table" || pluginId == "org.kexi-project.query") { if (m_formDataSourceCombo->isSelectionValid()) emit jumpToObjectRequested(pluginId, m_formDataSourceCombo->selectedName()); } } void KexiDataSourcePage::slotInsertSelectedFields() { #ifdef KEXI_AUTOFIELD_FORM_WIDGET_SUPPORT QStringList selectedFieldNames(m_fieldListView->selectedFieldNames()); if (selectedFieldNames.isEmpty()) return; emit insertAutoFields(m_fieldListView->schema()->table() ? "org.kexi-project.table" : "org.kexi-project.query", m_fieldListView->schema()->name(), selectedFieldNames); #endif } void KexiDataSourcePage::slotFieldDoubleClicked(const QString& sourcePluginId, const QString& sourceName, const QString& fieldName) { #ifdef KEXI_AUTOFIELD_FORM_WIDGET_SUPPORT QStringList selectedFields; selectedFields.append(fieldName); emit insertAutoFields(sourcePluginId, sourceName, selectedFields); #else Q_UNUSED(sourcePluginId); Q_UNUSED(sourceName); Q_UNUSED(fieldName); #endif } void KexiDataSourcePage::slotFormDataSourceTextChanged(const QString &text) { const bool enable = m_formDataSourceCombo->isSelectionValid(); if (text.isEmpty()) { clearFormDataSourceSelection(); } else if (!enable) { clearFormDataSourceSelection(m_formDataSourceCombo->selectedName().isEmpty()/*alsoClearComboBox*/); } updateSourceFieldWidgetsAvailability(); } void KexiDataSourcePage::slotFormDataSourceChanged() { if (!m_formDataSourceCombo->project()) return; const QString pluginId(m_formDataSourceCombo->selectedPluginId()); bool dataSourceFound = false; QString name(m_formDataSourceCombo->selectedName()); const bool isIdAcceptable = pluginId == QLatin1String("org.kexi-project.table") || pluginId == QLatin1String("org.kexi-project.query"); if (isIdAcceptable && m_formDataSourceCombo->isSelectionValid()) { KDbTableOrQuerySchema *tableOrQuery = new KDbTableOrQuerySchema( m_formDataSourceCombo->project()->dbConnection(), name.toLatin1(), pluginId == "org.kexi-project.table"); if (tableOrQuery->table() || tableOrQuery->query()) { #ifdef KEXI_AUTOFIELD_FORM_WIDGET_SUPPORT m_fieldListView->setSchema(tableOrQuery); #else m_tableOrQuerySchema = tableOrQuery; #endif dataSourceFound = true; m_widgetDataSourceCombo->setTableOrQuery(name, pluginId == "org.kexi-project.table"); } else { delete tableOrQuery; } } if (!dataSourceFound) { m_widgetDataSourceCombo->setTableOrQuery(QString(), true); } m_gotoButton->setEnabled(dataSourceFound); if (dataSourceFound) { slotFieldListViewSelectionChanged(); } else { #ifdef KEXI_AUTOFIELD_FORM_WIDGET_SUPPORT m_addField->setEnabled(false); #endif } updateSourceFieldWidgetsAvailability(); emit formDataSourceChanged(pluginId, name); } void KexiDataSourcePage::slotFieldSelected() { KDbField::Type dataType = KDbField::InvalidType; #ifdef KEXI_AUTOFIELD_FORM_WIDGET_SUPPORT //! @todo this should also work for expressions KDbField *field = m_fieldListView->schema()->field( m_widgetDataSourceCombo->fieldOrExpression()); #else KDbField *field = m_tableOrQuerySchema->field( m_widgetDataSourceCombo->fieldOrExpression()); //temp #endif if (field) dataType = field->type(); emit dataSourceFieldOrExpressionChanged( m_widgetDataSourceCombo->fieldOrExpression(), m_widgetDataSourceCombo->fieldOrExpressionCaption(), dataType ); } void KexiDataSourcePage::setFormDataSource(const QString& pluginId, const QString& name) { m_formDataSourceCombo->setDataSource(pluginId, name); } #define KexiDataSourcePage_FADE 1 void KexiDataSourcePage::assignPropertySet(KPropertySet* propertySet) { QString objectName; if (propertySet) objectName = propertySet->propertyValue("objectName").toString(); if (!objectName.isEmpty() && objectName == m_currentObjectName) return; //the same object m_currentObjectName = objectName; //! @todo #if KexiDataSourcePage_FADE KexiFadeWidgetEffect *animation = 0; if (isVisible()) animation = new KexiFadeWidgetEffect(this); #endif QString objectClassName; if (propertySet) { objectClassName = propertySet->propertyValue("this:className").toString(); } updateInfoLabelForPropertySet(propertySet); const bool isForm = objectClassName == "KexiDBForm"; const bool multipleSelection = objectClassName == "special:multiple"; const bool hasDataSourceProperty = propertySet && propertySet->contains("dataSource") && !multipleSelection; if (!isForm) { //this is a widget QString dataSource; if (hasDataSourceProperty) { if (propertySet) { dataSource = (*propertySet)["dataSource"].value().toString(); } m_noDataSourceAvailableLabel->hide(); m_widgetDataSourceCombo->setFieldOrExpression(dataSource); m_widgetDataSourceCombo->setEnabled(true); m_widgetDSLabel->show(); m_widgetDataSourceCombo->show(); m_widgetDataSourceComboSpacer->show(); updateSourceFieldWidgetsAvailability(); } } if (isForm) { m_noDataSourceAvailableLabel->hide(); } else if (!hasDataSourceProperty) { if (multipleSelection) { m_noDataSourceAvailableLabel->setText(m_noDataSourceAvailableMultiText); } else { m_noDataSourceAvailableLabel->setText(m_noDataSourceAvailableSingleText); } m_noDataSourceAvailableLabel->show(); m_widgetDataSourceCombo->setEditText(QString()); } if (isForm || !hasDataSourceProperty) { //no source field can be set m_widgetDSLabel->hide(); m_widgetDataSourceCombo->hide(); m_widgetDataSourceComboSpacer->hide(); } //! @todo #if KexiDataSourcePage_FADE if (animation) animation->start(100); #endif } void KexiDataSourcePage::slotFieldListViewSelectionChanged() { #ifdef KEXI_AUTOFIELD_FORM_WIDGET_SUPPORT //update "add field" button's state for (Q3ListViewItemIterator it(m_fieldListView); it.current(); ++it) { if (it.current()->isSelected()) { m_addField->setEnabled(true); return; } } m_addField->setEnabled(false); #endif } void KexiDataSourcePage::updateSourceFieldWidgetsAvailability() { const bool hasDataSource = m_formDataSourceCombo->isSelectionValid(); m_widgetDataSourceCombo->setEnabled(hasDataSource); m_widgetDSLabel->setEnabled(hasDataSource); #ifdef KEXI_AUTOFIELD_FORM_WIDGET_SUPPORT m_fieldListView->setEnabled(hasDataSource); m_availableFieldsLabel->setEnabled(hasDataSource); m_mousePointerLabel->setEnabled(hasDataSource); m_availableFieldsDescriptionLabel->setEnabled(hasDataSource); #endif } diff --git a/src/plugins/queries/CMakeLists.txt b/src/plugins/queries/CMakeLists.txt index f0ab1d440..bcd4d426d 100644 --- a/src/plugins/queries/CMakeLists.txt +++ b/src/plugins/queries/CMakeLists.txt @@ -1,31 +1,35 @@ -include_directories(${CMAKE_SOURCE_DIR}/src/core - ${CMAKE_SOURCE_DIR}/src/widget ${CMAKE_SOURCE_DIR}/src/widget/tableview) +include_directories( + ${CMAKE_SOURCE_DIR}/src/core + ${CMAKE_SOURCE_DIR}/src/widget + ${CMAKE_SOURCE_DIR}/src/widget/tableview + ${CMAKE_SOURCE_DIR}/src/kexiutils/style +) # the main plugin set(kexi_queryplugin_SRCS kexiquerypart.cpp kexiquerydesignersql.cpp kexiquerydesignerguieditor.cpp kexiqueryview.cpp ) add_library(kexi_queryplugin MODULE ${kexi_queryplugin_SRCS}) kcoreaddons_desktop_to_json(kexi_queryplugin kexi_queryplugin.desktop) target_link_libraries(kexi_queryplugin PRIVATE kexicore kexiextendedwidgets kexidataviewcommon kexidatatable kexirelationsview kexiutils KDb KPropertyWidgets Qt5::Gui Qt5::Xml ) install(TARGETS kexi_queryplugin DESTINATION ${KEXI_PLUGIN_INSTALL_DIR}) diff --git a/src/plugins/queries/kexiquerydesignerguieditor.cpp b/src/plugins/queries/kexiquerydesignerguieditor.cpp index fa3c583d8..cd9b8a812 100644 --- a/src/plugins/queries/kexiquerydesignerguieditor.cpp +++ b/src/plugins/queries/kexiquerydesignerguieditor.cpp @@ -1,1934 +1,1934 @@ /* This file is part of the KDE project Copyright (C) 2004 Lucijan Busch Copyright (C) 2004-2016 Jarosław Staniek This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kexiquerydesignerguieditor.h" #include -#include +#include #include #include #include #include #include #include #include #include #include #include #include #include "kexiquerypart.h" #include "kexiqueryview.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //! @todo remove KEXI_NO_QUERY_TOTALS later #define KEXI_NO_QUERY_TOTALS //! indices for table columns #define COLUMN_ID_COLUMN 0 #define COLUMN_ID_TABLE 1 #define COLUMN_ID_VISIBLE 2 #ifdef KEXI_NO_QUERY_TOTALS # define COLUMN_ID_SORTING 3 # define COLUMN_ID_CRITERIA 4 #else # define COLUMN_ID_TOTALS 3 # define COLUMN_ID_SORTING 4 # define COLUMN_ID_CRITERIA 5 #endif /*! @internal */ class Q_DECL_HIDDEN KexiQueryDesignerGuiEditor::Private { public: Private(KexiQueryDesignerGuiEditor *p) : q(p) , conn(0) { droppedNewRecord = 0; slotTableAdded_enabled = true; sortColumnPreferredWidth = 0; } bool changeSingleCellValue(KDbRecordData *recordData, int columnNumber, const QVariant& value, KDbResultInfo* result) { data->clearRecordEditBuffer(); if (!data->updateRecordEditBuffer(recordData, columnNumber, value) || !data->saveRecordChanges(recordData, true)) { if (result) *result = data->result(); return false; } return true; } KexiQueryDesignerGuiEditor *q; KDbTableViewData *data; KexiDataTableView *dataTable; //! @todo KEXI3 use equivalent of QPointer KDbConnection *conn; KexiRelationsView *relations; KexiSectionHeader *head; QSplitter *spl; /*! Used to remember in slotDroppedAtRow() what data was dropped, so we can create appropriate prop. set in slotRecordInserted() This information is cached and entirely refreshed on updateColumnsData(). */ KDbTableViewData *fieldColumnData, *tablesColumnData; /*! Collects identifiers selected in 1st (field) column, so we're able to distinguish between table identifiers selected from the dropdown list, and strings (e.g. expressions) entered by hand. This information is cached and entirely refreshed on updateColumnsData(). The dict is filled with (char*)1 values (doesn't matter what it is); */ QSet fieldColumnIdentifiers; void addFieldColumnIdentifier(const QString& id) { fieldColumnIdentifiers.insert(id.toLower()); } int comboArrowWidth; int sortColumnPreferredWidth; void initSortColumnPreferredWidth(const QVector &items) { int maxw = -1; for (int i=0; i < items.size(); ++i) { maxw = qMax(maxw, q->fontMetrics().width(items[i] + QLatin1String(" "))); } sortColumnPreferredWidth = maxw + KexiUtils::comboBoxArrowSize(q->style()).width(); } KexiDataAwarePropertySet* sets; KDbRecordData *droppedNewRecord; QString droppedNewTable, droppedNewField; bool slotTableAdded_enabled; }; static bool isAsterisk(const QString& tableName, const QString& fieldName) { return tableName == "*" || fieldName.endsWith('*'); } //! @internal \return true if sorting is allowed for \a fieldName and \a tableName static bool sortingAllowed(const QString& fieldName, const QString& tableName) { return !(fieldName == "*" || (fieldName.isEmpty() && tableName == "*")); } //========================================================= KexiQueryDesignerGuiEditor::KexiQueryDesignerGuiEditor( QWidget *parent) : KexiView(parent) , d(new Private(this)) { d->conn = KexiMainWindowIface::global()->project()->dbConnection(); d->spl = new QSplitter(Qt::Vertical, this); d->spl->setChildrenCollapsible(false); d->relations = new KexiRelationsView(d->spl); d->spl->addWidget(d->relations); d->relations->setObjectName("relations"); connect(d->relations, SIGNAL(tableAdded(KDbTableSchema*)), this, SLOT(slotTableAdded(KDbTableSchema*))); connect(d->relations, SIGNAL(tableHidden(KDbTableSchema*)), this, SLOT(slotTableHidden(KDbTableSchema*))); connect(d->relations, SIGNAL(appendFields(KDbTableOrQuerySchema&,QStringList)), this, SLOT(slotAppendFields(KDbTableOrQuerySchema&,QStringList))); d->head = new KexiSectionHeader(xi18n("Query Columns"), Qt::Vertical, d->spl); d->spl->addWidget(d->head); d->dataTable = new KexiDataTableView(d->head, false); d->head->setWidget(d->dataTable); d->dataTable->setObjectName("guieditor_dataTable"); d->dataTable->dataAwareObject()->setSpreadSheetMode(true); d->data = new KDbTableViewData(); //just empty data d->sets = new KexiDataAwarePropertySet(this, d->dataTable->dataAwareObject()); connect(d->sets, SIGNAL(propertyChanged(KPropertySet&,KProperty&)), this, SLOT(slotPropertyChanged(KPropertySet&,KProperty&))); initTableColumns(); initTableRows(); QList c; c << COLUMN_ID_COLUMN << COLUMN_ID_TABLE << COLUMN_ID_CRITERIA; if (d->dataTable->tableView()/*sanity*/) { KexiStyle::setupFrame(d->dataTable->tableView()); d->dataTable->tableView()->adjustColumnWidthToContents(COLUMN_ID_VISIBLE); d->dataTable->tableView()->setColumnWidth(COLUMN_ID_SORTING, d->sortColumnPreferredWidth); d->dataTable->tableView()->setStretchLastColumn(true); d->dataTable->tableView()->maximizeColumnsWidth(c); d->dataTable->tableView()->setDropsAtRecordEnabled(true); connect(d->dataTable->tableView(), SIGNAL(dragOverRecord(KDbRecordData*,int,QDragMoveEvent*)), this, SLOT(slotDragOverTableRecord(KDbRecordData*,int,QDragMoveEvent*))); connect(d->dataTable->tableView(), SIGNAL(droppedAtRecord(KDbRecordData*,int,QDropEvent*,KDbRecordData*&)), this, SLOT(slotDroppedAtRecord(KDbRecordData*,int,QDropEvent*,KDbRecordData*&))); connect(d->dataTable->tableView(), SIGNAL(newItemAppendedForAfterDeletingInSpreadSheetMode()), this, SLOT(slotNewItemAppendedForAfterDeletingInSpreadSheetMode())); } connect(d->data, SIGNAL(aboutToChangeCell(KDbRecordData*,int,QVariant*,KDbResultInfo*)), this, SLOT(slotBeforeCellChanged(KDbRecordData*,int,QVariant*,KDbResultInfo*))); connect(d->data, SIGNAL(recordInserted(KDbRecordData*,int,bool)), this, SLOT(slotRecordInserted(KDbRecordData*,int,bool))); connect(d->relations, SIGNAL(tablePositionChanged(KexiRelationsTableContainer*)), this, SLOT(slotTablePositionChanged(KexiRelationsTableContainer*))); connect(d->relations, SIGNAL(aboutConnectionRemove(KexiRelationsConnection*)), this, SLOT(slotAboutConnectionRemove(KexiRelationsConnection*))); addChildView(d->relations); addChildView(d->dataTable); setViewWidget(d->spl, false/* no focus proxy*/); setFocusProxy(d->dataTable); d->relations->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); d->head->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); updateGeometry(); d->spl->setSizes(QList() << 800 << 400); } KexiQueryDesignerGuiEditor::~KexiQueryDesignerGuiEditor() { delete d; } void KexiQueryDesignerGuiEditor::initTableColumns() { KDbTableViewColumn *col1 = new KDbTableViewColumn("column", KDbField::Enum, xi18n("Column"), xi18n("Describes field name or expression for the designed query.")); col1->setRelatedDataEditable(true); d->fieldColumnData = new KDbTableViewData(KDbField::Text, KDbField::Text); col1->setRelatedData(d->fieldColumnData); d->data->addColumn(col1); KDbTableViewColumn *col2 = new KDbTableViewColumn("table", KDbField::Enum, xi18n("Table"), xi18n("Describes table for a given field. Can be empty.")); d->tablesColumnData = new KDbTableViewData(KDbField::Text, KDbField::Text); col2->setRelatedData(d->tablesColumnData); d->data->addColumn(col2); KDbTableViewColumn *col3 = new KDbTableViewColumn("visible", KDbField::Boolean, xi18n("Visible"), xi18n("Describes visibility for a given field or expression.")); col3->field()->setDefaultValue(QVariant(false)); col3->field()->setNotNull(true); d->data->addColumn(col3); #ifndef KEXI_NO_QUERY_TOTALS KDbTableViewColumn *col4 = new KDbTableViewColumn("totals", KDbField::Enum, futureI18n("Totals"), futureI18n("Describes a way of computing totals for a given field or expression.")); QVector totalsTypes; totalsTypes.append(futureI18n("Group by")); totalsTypes.append(futureI18n("Sum")); totalsTypes.append(futureI18n("Average")); totalsTypes.append(futureI18n("Min")); totalsTypes.append(futureI18n("Max")); //! @todo more like this col4->field()->setEnumHints(totalsTypes); d->data->addColumn(col4); #endif KDbTableViewColumn *col5 = new KDbTableViewColumn("sort", KDbField::Enum, xi18n("Sorting"), xi18n("Describes a way of sorting for a given field.")); QVector sortTypes; sortTypes.append(""); sortTypes.append(xi18n("Ascending")); sortTypes.append(xi18n("Descending")); col5->field()->setEnumHints(sortTypes); d->data->addColumn(col5); d->initSortColumnPreferredWidth(sortTypes); KDbTableViewColumn *col6 = new KDbTableViewColumn("criteria", KDbField::Text, xi18n("Criteria"), xi18n("Describes the criteria for a given field or expression.")); d->data->addColumn(col6); } void KexiQueryDesignerGuiEditor::initTableRows() { d->data->deleteAllRecords(); for (int i = 0; i < (int)d->sets->size(); i++) { KDbRecordData* data = d->data->createItem(); d->data->append(data); (*data)[COLUMN_ID_VISIBLE] = QVariant(false); } d->dataTable->dataAwareObject()->setData(d->data); updateColumnsData(); } void KexiQueryDesignerGuiEditor::updateColumnsData() { d->dataTable->dataAwareObject()->acceptRecordEditing(); QStringList sortedTableNames; foreach(KexiRelationsTableContainer* cont, *d->relations->tables()) { sortedTableNames += cont->schema()->name(); } qSort(sortedTableNames); //several tables can be hidden now, so remove rows for these tables QList recordsToDelete; for (int r = 0; r < (int)d->sets->size(); r++) { KPropertySet *set = d->sets->at(r); if (set) { QString tableName = (*set)["table"].value().toString(); //QString fieldName = (*set)["field"].value().toString(); const bool allTablesAsterisk = tableName == "*" && d->relations->tables()->isEmpty(); const bool fieldNotFound = tableName != "*" && !(*set)["isExpression"].value().toBool() && sortedTableNames.end() == qFind(sortedTableNames.begin(), sortedTableNames.end(), tableName); if (allTablesAsterisk || fieldNotFound) { //table not found: mark this line for later removal recordsToDelete += r; } } } d->data->deleteRecords(recordsToDelete); //update 'table' and 'field' columns d->tablesColumnData->deleteAllRecords(); d->fieldColumnData->deleteAllRecords(); d->fieldColumnIdentifiers.clear(); KDbRecordData *data = d->fieldColumnData->createItem(); (*data)[COLUMN_ID_COLUMN] = "*"; (*data)[COLUMN_ID_TABLE] = "*"; d->fieldColumnData->append(data); d->addFieldColumnIdentifier((*data)[COLUMN_ID_COLUMN].toString()); //cache tempData()->unregisterForTablesSchemaChanges(); foreach(const QString& tableName, sortedTableNames) { //table /*! @todo what about query? */ const KDbTableSchema *table = d->relations->tables()->value(tableName)->schema()->table(); KDbTableSchemaChangeListener::registerForChanges(d->conn, tempData(), table); //this table will be used data = d->tablesColumnData->createItem(); (*data)[COLUMN_ID_COLUMN] = table->name(); (*data)[COLUMN_ID_TABLE] = (*data)[COLUMN_ID_COLUMN]; d->tablesColumnData->append(data); //fields data = d->fieldColumnData->createItem(); (*data)[COLUMN_ID_COLUMN] = QString(table->name() + ".*"); (*data)[COLUMN_ID_TABLE] = (*data)[COLUMN_ID_COLUMN]; d->fieldColumnData->append(data); d->addFieldColumnIdentifier((*data)[COLUMN_ID_COLUMN].toString()); //cache foreach(KDbField *field, *table->fields()) { data = d->fieldColumnData->createItem(); (*data)[COLUMN_ID_COLUMN] = QString(table->name() + '.' + field->name()); (*data)[COLUMN_ID_TABLE] = QString(" " + field->name()); d->fieldColumnData->append(data); d->addFieldColumnIdentifier((*data)[COLUMN_ID_COLUMN].toString()); //cache } } //! @todo } KexiRelationsView *KexiQueryDesignerGuiEditor::relationsView() const { return d->relations; } KexiQueryPartTempData * KexiQueryDesignerGuiEditor::tempData() const { return static_cast(window()->data()); } static QString msgCannotSwitch_EmptyDesign() { return xi18n("Cannot switch to data view, because query design is empty.\n" "First, please create your design."); } bool KexiQueryDesignerGuiEditor::buildSchema(QString *errMsg) { //build query schema KexiQueryPartTempData * temp = tempData(); if (temp->query()) { KexiQueryView *queryDataView = dynamic_cast(window()->viewForMode(Kexi::DataViewMode)); if (queryDataView) { queryDataView->setData(0); } temp->clearQuery(); } else { temp->setQuery(new KDbQuerySchema()); } //add tables foreach(KexiRelationsTableContainer* cont, *d->relations->tables()) { /*! @todo what about query? */ temp->query()->addTable(cont->schema()->table()); } //add fields, also build: // -WHERE expression // -ORDER BY list KDbExpression whereExpr; const int count = qMin(d->data->count(), d->sets->size()); bool fieldsFound = false; KDbTableViewDataConstIterator it(d->data->constBegin()); for (int i = 0; i < count && it != d->data->constEnd(); ++it, i++) { if (!(**it)[COLUMN_ID_TABLE].isNull() && (**it)[COLUMN_ID_COLUMN].isNull()) { //show message about missing field name, and set focus to that cell qDebug() << "no field provided!"; d->dataTable->dataAwareObject()->setCursorPosition(i, 0); if (errMsg) *errMsg = xi18nc("@info", "Select column for table %1", (**it)[COLUMN_ID_TABLE].toString()); return false; } KPropertySet *set = d->sets->at(i); if (set) { QString tableName = (*set)["table"].value().toString().trimmed(); QString fieldName = (*set)["field"].value().toString(); QString fieldAndTableName = fieldName; KDbField *currentField = 0; // will be set if this column is a single field if (!tableName.isEmpty()) fieldAndTableName.prepend(tableName + "."); const bool fieldVisible = (*set)["visible"].value().toBool(); QString criteriaStr = (*set)["criteria"].value().toString(); QByteArray alias((*set)["alias"].value().toByteArray()); if (!criteriaStr.isEmpty()) { KDbToken token; KDbExpression criteriaExpr = parseExpressionString(criteriaStr, &token, true/*allowRelationalOperator*/); if (criteriaExpr.isValid()) {//for sanity if (errMsg) *errMsg = xi18nc("@info", "Invalid criteria %1", criteriaStr); return false; } //build relational expression for column variable KDbVariableExpression varExpr(fieldAndTableName); criteriaExpr = KDbBinaryExpression(varExpr, token, criteriaExpr); //critera ok: add it to WHERE section if (whereExpr.isValid()) whereExpr = KDbBinaryExpression(whereExpr, KDbToken::AND, criteriaExpr); else //first expr. whereExpr = criteriaExpr; } if (tableName.isEmpty()) { if ((*set)["isExpression"].value().toBool() == true) { //add expression column KDbToken dummyToken; KDbExpression columnExpr = parseExpressionString(fieldName, &dummyToken, false/*!allowRelationalOperator*/); if (!columnExpr.isValid()) { if (errMsg) *errMsg = xi18nc("@info", "Invalid expression %1", fieldName); return false; } if (fieldVisible) { if (!temp->query()->addExpression(columnExpr)) { if (errMsg) *errMsg = xi18nc("@info", "Invalid expression %1", fieldName); return false; } fieldsFound = true; } else { if (!temp->query()->addInvisibleExpression(columnExpr)) { if (errMsg) *errMsg = xi18nc("@info", "Invalid expression %1", fieldName); return false; } } if (!alias.isEmpty()) temp->query()->setColumnAlias(temp->query()->fieldCount() - 1, alias); } //! @todo } else if (tableName == "*") { //all tables asterisk if (fieldVisible) { if (!temp->query()->addAsterisk(new KDbQueryAsterisk(temp->query()))) { return false; } fieldsFound = true; } else { if (!temp->query()->addInvisibleAsterisk(new KDbQueryAsterisk(temp->query()))) { return false; } } continue; } else { KDbTableSchema *t = d->conn->tableSchema(tableName); if (fieldName == "*") { //single-table asterisk: + ".*" + number if (fieldVisible) { if (!t || !temp->query()->addAsterisk(new KDbQueryAsterisk(temp->query(), *t))) { return false; } fieldsFound = true; } else { if (!t || !temp->query()->addInvisibleAsterisk(new KDbQueryAsterisk(temp->query(), *t))) { return false; } } } else { if (!t) { qWarning() << "query designer: NO TABLE '" << (*set)["table"].value().toString() << "'"; continue; } currentField = t->field(fieldName); if (!currentField) { qWarning() << "query designer: NO FIELD '" << fieldName << "'"; continue; } if (!fieldVisible && criteriaStr.isEmpty() && set->contains("isExpression") && (*set)["sorting"].value().toString() != "nosorting") { qDebug() << "invisible field with sorting: do not add it to the fields list"; continue; } const int tablePosition = temp->query()->tablePosition(t->name()); if (fieldVisible) { if (!temp->query()->addField(currentField, tablePosition)) { return false; } fieldsFound = true; } else { if (!temp->query()->addInvisibleField(currentField, tablePosition)) { return false; } } if (!alias.isEmpty()) temp->query()->setColumnAlias(temp->query()->fieldCount() - 1, alias); } } } else {//!set //qDebug() << (**it)[COLUMN_ID_TABLE].toString(); } } if (!fieldsFound) { if (errMsg) *errMsg = msgCannotSwitch_EmptyDesign(); return false; } if (whereExpr.isValid()) { qDebug() << "setting CRITERIA:" << whereExpr; } //set always, because if whereExpr==NULL, //this will clear prev. expr temp->query()->setWhereExpression(whereExpr); //add relations (looking for connections) foreach(KexiRelationsConnection* conn, *d->relations->relationsConnections()) { KexiRelationsTableContainer *masterTable = conn->masterTable(); KexiRelationsTableContainer *detailsTable = conn->detailsTable(); /*! @todo what about query? */ temp->query()->addRelationship( masterTable->schema()->table()->field(conn->masterField()), detailsTable->schema()->table()->field(conn->detailsField())); } // Add sorting information (ORDER BY) - we can do that only now // after all KDbQueryColumnInfo items are instantiated KDbOrderByColumnList orderByColumns; it = d->data->constBegin(); int fieldNumber = -1; //field number (empty rows are omitted) for (int i = 0/*row number*/; i < count && it != d->data->constEnd(); ++it, i++) { KPropertySet *set = d->sets->at(i); if (!set) continue; fieldNumber++; KDbField *currentField = 0; KDbQueryColumnInfo *currentColumn = 0; const QString sortingString((*set)["sorting"].value().toString()); if (sortingString != "ascending" && sortingString != "descending") { continue; } const KDbOrderByColumn::SortOrder sortOrder = sortingString == QLatin1String("ascending") ? KDbOrderByColumn::SortOrder::Ascending : KDbOrderByColumn::SortOrder::Descending; if (!(*set)["visible"].value().toBool()) { // this row defines invisible field but contains sorting information, // what means KDbField should be used as a reference for this sorting // Note1: alias is not supported here. // Try to find a field (not mentioned after SELECT): currentField = temp->query()->findTableField((*set)["field"].value().toString()); if (!currentField) { qWarning() << "NO FIELD" << (*set)["field"].value().toString() << "available for sorting"; continue; } orderByColumns.appendField(currentField, sortOrder); continue; } currentField = temp->query()->field(fieldNumber); if (!currentField || currentField->isExpression() || currentField->isQueryAsterisk()) //! @todo support expressions here continue; //! @todo ok, but not for expressions QString aliasString((*set)["alias"].value().toString()); currentColumn = temp->query()->columnInfo( (*set)["table"].value().toString() + "." + (aliasString.isEmpty() ? currentField->name() : aliasString)); if (currentField && currentColumn) { if (currentColumn->isVisible()) orderByColumns.appendColumn(currentColumn, sortOrder); else if (currentColumn->field()) orderByColumns.appendField(currentColumn->field(), sortOrder); } } temp->query()->setOrderByColumnList(orderByColumns); qDebug() << *temp->query(); temp->registerTableSchemaChanges(temp->query()); //! @todo ? return true; } tristate KexiQueryDesignerGuiEditor::beforeSwitchTo(Kexi::ViewMode mode, bool *dontStore) { Q_ASSERT(dontStore); qDebug() << mode; if (!d->dataTable->dataAwareObject()->acceptRecordEditing()) return cancelled; qDebug() << "queryChangedInView:" << tempData()->queryChangedInView(); if (mode == Kexi::DesignViewMode) { return true; } else if (mode == Kexi::DataViewMode) { if (!isDirty() && window()->neverSaved()) { KMessageBox::information(this, msgCannotSwitch_EmptyDesign()); return cancelled; } if (tempData()->queryChangedInView() != Kexi::NoViewMode || !tempData()->query()) { //remember current design in a temporary structure QString errMsg; //build schema; problems are not allowed if (!buildSchema(&errMsg)) { KMessageBox::sorry(this, errMsg); return cancelled; } } *dontStore = true; //! @todo return true; } else if (mode == Kexi::TextViewMode) { *dontStore = true; if (tempData()->queryChangedInView() != Kexi::NoViewMode || !tempData()->query()) { //remember current design in a temporary structure //build schema; ignore problems buildSchema(); } /* if (tempData()->query && tempData()->query->fieldCount()==0) { //no fields selected: let's add "*" (all-tables asterisk), // otherwise SQL statement will be invalid tempData()->query->addAsterisk( new KDbQueryAsterisk( tempData()->query ) ); }*/ //! @todo return true; } return false; } tristate KexiQueryDesignerGuiEditor::afterSwitchFrom(Kexi::ViewMode mode) { if (!d->relations->setConnection(d->conn)) { window()->setStatus(d->conn); return false; } if (mode == Kexi::NoViewMode || (mode == Kexi::DataViewMode && !tempData()->query())) { //this is not a SWITCH but a fresh opening in this view mode if (!window()->neverSaved()) { if (!loadLayout()) { //err msg window()->setStatus(d->conn, xi18n("Query definition loading failed."), xi18n("Query design may be corrupted so it could not be opened even in text view.\n" "You can delete the query and create it again.")); return false; } // Invalid queries case: // KexiWindow::switchToViewMode() first opens DesignViewMode, // and then KexiQueryPart::loadSchemaObject() doesn't allocate KDbQuerySchema object // do we're carefully looking at window()->schemaObject() KDbQuerySchema * q = dynamic_cast(window()->schemaObject()); if (q) { KDbResultInfo result; showFieldsForQuery(q, result); if (!result.success) { window()->setStatus(&result, xi18n("Query definition loading failed.")); tempData()->proposeOpeningInTextViewModeBecauseOfProblems = true; return false; } } //! @todo load global query properties } } else if (mode == Kexi::TextViewMode || mode == Kexi::DataViewMode) { // Switch from text or data view. In the second case, the design could be changed as well // because there could be changes made in the text view before switching to the data view. if (tempData()->queryChangedInView() == Kexi::TextViewMode) { //SQL view changed the query design //-clear and regenerate GUI items initTableRows(); //! @todo if (tempData()->query()) { //there is a query schema to show showTablesForQuery(tempData()->query()); //-show fields KDbResultInfo result; showFieldsAndRelationsForQuery(tempData()->query(), result); if (!result.success) { window()->setStatus(&result, xi18n("Query definition loading failed.")); return false; } } else { d->relations->clear(); } } //! @todo load global query properties } if (mode == Kexi::DataViewMode) { //this is just a SWITCH from data view //set cursor if needed: if (d->dataTable->dataAwareObject()->currentRecord() < 0 || d->dataTable->dataAwareObject()->currentColumn() < 0) { d->dataTable->dataAwareObject()->ensureCellVisible(0, 0); d->dataTable->dataAwareObject()->setCursorPosition(0, 0); } } if (d->sets->size() > 0) { d->dataTable->tableView()->adjustColumnWidthToContents(COLUMN_ID_COLUMN); d->dataTable->tableView()->adjustColumnWidthToContents(COLUMN_ID_TABLE); } tempData()->setQueryChangedInView(false); setFocus(); //to allow shared actions proper update return true; } KDbObject* KexiQueryDesignerGuiEditor::storeNewData(const KDbObject& object, KexiView::StoreNewDataOptions options, bool *cancel) { Q_ASSERT(cancel); Q_UNUSED(options); if (!d->dataTable->dataAwareObject()->acceptRecordEditing()) { *cancel = true; return 0; } QString errMsg; KexiQueryPartTempData * temp = tempData(); if (!temp->query() || !(viewMode() == Kexi::DesignViewMode && temp->queryChangedInView() == Kexi::NoViewMode)) { //only rebuild schema if it has not been rebuilt previously if (!buildSchema(&errMsg)) { KMessageBox::sorry(this, errMsg); *cancel = true; return 0; } } (KDbObject&)*temp->query() = object; //copy main attributes bool ok = d->conn->storeNewObjectData(temp->query()); if (ok) { ok = KexiMainWindowIface::global()->project()->removeUserDataBlock(temp->query()->id()); // for sanity } window()->setId(temp->query()->id()); if (ok) ok = storeLayout(); if (!ok) { temp->setQuery(0); return 0; } return temp->takeQuery(); //will be returned, so: don't keep it in temp } tristate KexiQueryDesignerGuiEditor::storeData(bool dontAsk) { if (!d->dataTable->dataAwareObject()->acceptRecordEditing()) return cancelled; const bool was_dirty = isDirty(); tristate res = KexiView::storeData(dontAsk); //this clears dirty flag if (true == res) res = buildSchema(); if (true == res) res = storeLayout(); if (true != res) { if (was_dirty) setDirty(true); } return res; } void KexiQueryDesignerGuiEditor::showTablesForQuery(KDbQuerySchema *query) { // instead of hiding all tables and showing some tables, // show only these new and hide these unncecessary; the same for connections) d->slotTableAdded_enabled = false; //speedup d->relations->removeAllConnections(); //connections will be recreated d->relations->hideAllTablesExcept(query->tables()); foreach(KDbTableSchema* table, *query->tables()) { d->relations->addTable(table); } d->slotTableAdded_enabled = true; updateColumnsData(); } void KexiQueryDesignerGuiEditor::addConnection( KDbField *masterField, KDbField *detailsField) { SourceConnection conn; conn.masterTable = masterField->table()->name(); //<<name(); conn.detailsTable = detailsField->table()->name(); conn.detailsField = detailsField->name(); d->relations->addConnection(conn); } void KexiQueryDesignerGuiEditor::showFieldsForQuery(KDbQuerySchema *query, KDbResultInfo& result) { showFieldsOrRelationsForQueryInternal(query, true, false, result); } void KexiQueryDesignerGuiEditor::showRelationsForQuery(KDbQuerySchema *query, KDbResultInfo& result) { showFieldsOrRelationsForQueryInternal(query, false, true, result); } void KexiQueryDesignerGuiEditor::showFieldsAndRelationsForQuery(KDbQuerySchema *query, KDbResultInfo& result) { showFieldsOrRelationsForQueryInternal(query, true, true, result); } void KexiQueryDesignerGuiEditor::showFieldsOrRelationsForQueryInternal( KDbQuerySchema *query, bool showFields, bool showRelations, KDbResultInfo& result) { result.clear(); const bool was_dirty = isDirty(); //1. Show explicitly declared relations: if (showRelations) { foreach(KDbRelationship *rel, *query->relationships()) { //! @todo: now only sigle-field relationships are implemented! KDbField *masterField = rel->masterIndex()->fields()->first(); KDbField *detailsField = rel->detailsIndex()->fields()->first(); addConnection(masterField, detailsField); } } //2. Collect information about criterias // --this must be top level chain of AND's // --this will also show joins as: [table1.]field1 = [table2.]field2 KDbUtils::CaseInsensitiveHash criterias; KDbExpression e = query->whereExpression(); KDbExpression eItem; while (e.isValid()) { //eat parentheses because the expression can be (....) AND (... AND ... ) while (e.isValid() && e.isUnary() && e.token() == '(') e = e.toUnary().arg(); if (e.isBinary() && e.token() == KDbToken::AND) { eItem = e.toBinary().left(); e = e.toBinary().right(); } else { eItem = e; e = KDbExpression(); } //eat parentheses while (eItem.isValid() && eItem.isUnary() && eItem.token() == '(') eItem = eItem.toUnary().arg(); if (!eItem.isValid()) continue; qDebug() << eItem; KDbBinaryExpression binary(eItem.toBinary()); if (binary.isValid() && eItem.expressionClass() == KDb::RelationalExpression) { KDbField *leftField = 0, *rightField = 0; if (eItem.token() == '=' && binary.left().isVariable() && binary.right().isVariable() && (leftField = query->findTableField(binary.left().toString(0).toString())) && (rightField = query->findTableField(binary.right().toString(0).toString()))) { //! @todo move this check to parser on KDbQuerySchema creation //! or to KDbQuerySchema creation (WHERE expression should be then simplified //! by removing joins //this is relationship defined as following JOIN: [table1.]field1 = [table2.]field2 if (showRelations) { //! @todo testing primary key here is too simplified; maybe look ar isForeignKey() or indices.. //! @todo what about multifield joins? if (leftField->isPrimaryKey()) addConnection(leftField /*master*/, rightField /*details*/); else addConnection(rightField /*master*/, leftField /*details*/); //! @todo addConnection() should have "bool oneToOne" arg, for 1-to-1 relations } } else if (binary.left().isVariable()) { //this is: variable , op , argument //store variable -> argument: criterias.insertMulti(binary.left().toVariable().name(), binary.right()); } else if (binary.right().isVariable()) { //this is: argument , op , variable //store variable -> argument: criterias.insertMulti(binary.right().toVariable().name(), binary.left()); } } } //while if (!showFields) return; //3. show fields (including * and table.*) int row_num = 0; QSet usedCriterias; // <-- used criterias will be saved here // so in step 4. we will be able to add // remaining invisible columns with criterias qDebug() << *query; foreach(KDbField* field, *query->fields()) { qDebug() << *field; } foreach(KDbField* field, *query->fields()) { //append a new row QString tableName, fieldName, columnAlias, criteriaString; KDbBinaryExpression criteriaExpr; KDbExpression criteriaArgument; if (field->isQueryAsterisk()) { if (field->table()) {//single-table asterisk tableName = field->table()->name(); fieldName = "*"; } else {//all-tables asterisk tableName = "*"; fieldName = ""; } } else { columnAlias = query->columnAlias(row_num); if (field->isExpression()) { //! @todo ok? perhaps do not allow to omit aliases? fieldName = field->expression().toString(0).toString(); } else { tableName = field->table()->name(); fieldName = field->name(); criteriaArgument = criterias.value(fieldName); if (!criteriaArgument.isValid()) {//try table.field criteriaArgument = criterias.value(tableName + "." + fieldName); } if (criteriaArgument.isValid()) {//criteria expression is just a parent of argument criteriaExpr = criteriaArgument.parent().toBinary(); usedCriterias.insert(criteriaArgument.toString(0).toString()); //save info. about used criteria } } } //create new row data KDbRecordData *newRecord = createNewRow(tableName, fieldName, true /* visible*/); if (criteriaExpr.isValid()) { //! @todo fix for !INFIX operators if (criteriaExpr.token() == '=') criteriaString = criteriaArgument.toString(0).toString(); else criteriaString = criteriaExpr.token().toString() + " " + criteriaArgument.toString(0).toString(); (*newRecord)[COLUMN_ID_CRITERIA] = criteriaString; } d->dataTable->dataAwareObject()->insertItem(newRecord, row_num); //OK, row inserted: create a new set for it KPropertySet &set = *createPropertySet(row_num, tableName, fieldName, true/*new one*/); if (!columnAlias.isEmpty()) set["alias"].setValue(columnAlias, KProperty::DefaultValueOptions ^ KProperty::ValueOption::RememberOld); if (!criteriaString.isEmpty()) set["criteria"].setValue(criteriaString, KProperty::DefaultValueOptions ^ KProperty::ValueOption::RememberOld); if (field->isExpression()) { if (!d->changeSingleCellValue(newRecord, COLUMN_ID_COLUMN, QVariant(columnAlias + ": " + field->expression().toString(0).toString()), &result)) return; //problems with setting column expression } row_num++; } //4. show ORDER BY information d->data->clearRecordEditBuffer(); const KDbOrderByColumnList* orderByColumns = query->orderByColumnList(); QHash columnsOrder( query->columnsOrder(KDbQuerySchema::UnexpandedListWithoutAsterisks)); for (auto orderByColumnIt(orderByColumns->constBegin()); orderByColumnIt != orderByColumns->constEnd(); ++orderByColumnIt) { KDbOrderByColumn* orderByColumn = *orderByColumnIt; KDbQueryColumnInfo *column = orderByColumn->column(); KDbRecordData *data = 0; KPropertySet *rowPropertySet = 0; if (column) { //sorting for visible column if (column->isVisible()) { if (columnsOrder.contains(column)) { const int columnPosition = columnsOrder.value(column); data = d->data->at(columnPosition); rowPropertySet = d->sets->at(columnPosition); qDebug() << "\tSetting \"" << *orderByColumn << "\" sorting for record #" << columnPosition; } } } else if (orderByColumn->field()) { //this will be presented as invisible field: create new row KDbField* field = orderByColumn->field(); QString tableName(field->table() ? field->table()->name() : QString()); data = createNewRow(tableName, field->name(), false /* !visible*/); d->dataTable->dataAwareObject()->insertItem(data, row_num); rowPropertySet = createPropertySet(row_num, tableName, field->name(), true /*newOne*/); propertySetSwitched(); qDebug() << "\tSetting \"" << *orderByColumn << "\" sorting for invisible field" << field->name() << ", table " << tableName << " -row #" << row_num; row_num++; } //alter sorting for either existing or new row if (data && rowPropertySet) { // this will automatically update "sorting" property d->data->updateRecordEditBuffer(data, COLUMN_ID_SORTING, orderByColumn->sortOrder() == KDbOrderByColumn::SortOrder::Ascending ? 1 : 2); // in slotBeforeCellChanged() d->data->saveRecordChanges(data, true); (*rowPropertySet)["sorting"].clearModifiedFlag(); // this property should look "fresh" if (!(*data)[COLUMN_ID_VISIBLE].toBool()) //update (*rowPropertySet)["visible"].setValue(QVariant(false), KProperty::DefaultValueOptions ^ KProperty::ValueOption::RememberOld); } } //5. Show fields for unused criterias (with "Visible" column set to false) foreach(const KDbExpression &criteriaArgument, criterias) { // <-- contains field or table.field if (usedCriterias.contains(criteriaArgument.toString(0).toString())) continue; //unused: append a new row KDbBinaryExpression criteriaExpr = criteriaArgument.parent().toBinary(); if (!criteriaExpr.isValid()) { qWarning() << "criteriaExpr is not a binary expr"; continue; } KDbVariableExpression columnNameArgument = criteriaExpr.left().toVariable(); //left or right if (!columnNameArgument.isValid()) { columnNameArgument = criteriaExpr.right().toVariable(); if (!columnNameArgument.isValid()) { qWarning() << "columnNameArgument is not a variable (table or table.field) expr"; continue; } } KDbField* field = 0; if (!columnNameArgument.name().contains('.') && query->tables()->count() == 1) { //extreme case: only field name provided for one-table query: field = query->tables()->first()->field(columnNameArgument.name()); } else { field = query->findTableField(columnNameArgument.name()); } if (!field) { qWarning() << "no columnInfo found in the query for name" << columnNameArgument.name(); continue; } QString tableName, fieldName, /*columnAlias,*/ criteriaString; //! @todo what about ALIAS? tableName = field->table()->name(); fieldName = field->name(); //create new row data KDbRecordData *newRecord = createNewRow(tableName, fieldName, false /* !visible*/); if (criteriaExpr.isValid()) { //! @todo fix for !INFIX operators if (criteriaExpr.token() == '=') criteriaString = criteriaArgument.toString(0).toString(); else criteriaString = criteriaExpr.token().toString() + " " + criteriaArgument.toString(0).toString(); (*newRecord)[COLUMN_ID_CRITERIA] = criteriaString; } d->dataTable->dataAwareObject()->insertItem(newRecord, row_num); //OK, row inserted: create a new set for it KPropertySet &set = *createPropertySet(row_num++, tableName, fieldName, true/*new one*/); //! @todo if (!columnAlias.isEmpty()) //! @todo set["alias"].setValue(columnAlias, false); //// if (!criteriaString.isEmpty()) set["criteria"].setValue(criteriaString, KProperty::DefaultValueOptions ^ KProperty::ValueOption::RememberOld); set["visible"].setValue(QVariant(false), KProperty::DefaultValueOptions ^ KProperty::ValueOption::RememberOld); } //current property set has most probably changed propertySetSwitched(); if (!was_dirty) setDirty(false); //move to 1st column, 1st row d->dataTable->dataAwareObject()->ensureCellVisible(0, 0); // tempData()->registerTableSchemaChanges(query); } bool KexiQueryDesignerGuiEditor::loadLayout() { QString xml; //! @todo errmsg if (!loadDataBlock(&xml, "query_layout") || xml.isEmpty()) { //in a case when query layout was not saved, build layout by hand // -- dynamic cast because of a need for handling invalid queries // (as in KexiQueryDesignerGuiEditor::afterSwitchFrom()): KDbQuerySchema * q = dynamic_cast(window()->schemaObject()); if (q) { showTablesForQuery(q); KDbResultInfo result; showRelationsForQuery(q, result); if (!result.success) { window()->setStatus(&result, xi18n("Query definition loading failed.")); return false; } } return true; } QDomDocument doc; doc.setContent(xml); QDomElement doc_el = doc.documentElement(), el; if (doc_el.tagName() != "query_layout") { //! @todo errmsg return false; } const bool was_dirty = isDirty(); //add tables and relations to the relation view for (el = doc_el.firstChild().toElement(); !el.isNull(); el = el.nextSibling().toElement()) { if (el.tagName() == "table") { KDbTableSchema *t = d->conn->tableSchema(el.attribute("name")); int x = el.attribute("x", "-1").toInt(); int y = el.attribute("y", "-1").toInt(); int width = el.attribute("width", "-1").toInt(); int height = el.attribute("height", "-1").toInt(); QRect rect; if (x != -1 || y != -1 || width != -1 || height != -1) rect = QRect(x, y, width, height); d->relations->addTable(t, rect); } else if (el.tagName() == "conn") { SourceConnection src_conn; src_conn.masterTable = el.attribute("mtable"); src_conn.masterField = el.attribute("mfield"); src_conn.detailsTable = el.attribute("dtable"); src_conn.detailsField = el.attribute("dfield"); d->relations->addConnection(src_conn); } } if (!was_dirty) setDirty(false); return true; } bool KexiQueryDesignerGuiEditor::storeLayout() { KexiQueryPartTempData * temp = tempData(); // Save SQL without driver-escaped keywords. if (window()->schemaObject()) //set this instance as obsolete (only if it's stored) d->conn->setQuerySchemaObsolete(window()->schemaObject()->name()); KDbSelectStatementOptions options; options.addVisibleLookupColumns = false; KDbNativeStatementBuilder builder; KDbEscapedString sql; if (!builder.generateSelectStatement(&sql, temp->query(), options)) { return false; } if (!storeDataBlock(sql.toString(), "sql")) { return false; } //serialize detailed XML query definition QString xml = "", tmp; foreach(KexiRelationsTableContainer* cont, *d->relations->tables()) { /*! @todo what about query? */ tmp = QString("schema()->name()) + "\" x=\"" + QString::number(cont->x()) + "\" y=\"" + QString::number(cont->y()) + "\" width=\"" + QString::number(cont->width()) + "\" height=\"" + QString::number(cont->height()) + "\"/>"; xml += tmp; } foreach(KexiRelationsConnection *conn, *d->relations->relationsConnections()) { tmp = QString("masterTable()->schema()->name()) + "\" mfield=\"" + conn->masterField() + "\" dtable=\"" + QString(conn->detailsTable()->schema()->name()) + "\" dfield=\"" + conn->detailsField() + "\"/>"; xml += tmp; } xml += ""; if (!storeDataBlock(xml, "query_layout")) { return false; } return true; } QSize KexiQueryDesignerGuiEditor::sizeHint() const { QSize s1 = d->relations->sizeHint(); QSize s2 = d->head->sizeHint(); return QSize(qMax(s1.width(), s2.width()), s1.height() + s2.height()); } KDbRecordData* KexiQueryDesignerGuiEditor::createNewRow(const QString& tableName, const QString& fieldName, bool visible) const { KDbRecordData *newRecord = d->data->createItem(); QString key; if (tableName == "*") key = "*"; else { if (!tableName.isEmpty()) key = (tableName + "."); key += fieldName; } (*newRecord)[COLUMN_ID_COLUMN] = key; (*newRecord)[COLUMN_ID_TABLE] = tableName; (*newRecord)[COLUMN_ID_VISIBLE] = QVariant(visible); #ifndef KEXI_NO_QUERY_TOTALS (*newRecord)[COLUMN_ID_TOTALS] = QVariant(0); #endif return newRecord; } void KexiQueryDesignerGuiEditor::slotDragOverTableRecord( KDbRecordData * /*data*/, int /*record*/, QDragMoveEvent* e) { if (e->mimeData()->hasFormat("kexi/field")) { e->setAccepted(true); } } void KexiQueryDesignerGuiEditor::slotDroppedAtRecord(KDbRecordData * /*data*/, int /*record*/, QDropEvent *ev, KDbRecordData*& newRecord) { QString sourcePartClass; QString srcTable; QStringList srcFields; if (!KexiFieldDrag::decode(ev, &sourcePartClass, &srcTable, &srcFields)) { return; } if (srcFields.count() != 1) { return; } //insert new row at specific place newRecord = createNewRow(srcTable, srcFields[0], true /* visible*/); d->droppedNewRecord = newRecord; d->droppedNewTable = srcTable; d->droppedNewField = srcFields[0]; //! @todo } void KexiQueryDesignerGuiEditor::slotNewItemAppendedForAfterDeletingInSpreadSheetMode() { KDbRecordData *data = d->data->last(); if (data) (*data)[COLUMN_ID_VISIBLE] = QVariant(false); //the same init as in initTableRows() } void KexiQueryDesignerGuiEditor::slotRecordInserted(KDbRecordData* data, int record, bool /*repaint*/) { if (d->droppedNewRecord && d->droppedNewRecord == data) { createPropertySet(record, d->droppedNewTable, d->droppedNewField, true); propertySetSwitched(); d->droppedNewRecord = 0; } tempData()->setQueryChangedInView(true); } void KexiQueryDesignerGuiEditor::slotTableAdded(KDbTableSchema* /*t*/) { if (!d->slotTableAdded_enabled) return; updateColumnsData(); setDirty(); tempData()->setQueryChangedInView(true); d->dataTable->setFocus(); } void KexiQueryDesignerGuiEditor::slotTableHidden(KDbTableSchema* /*t*/) { updateColumnsData(); setDirty(); tempData()->setQueryChangedInView(true); } QByteArray KexiQueryDesignerGuiEditor::generateUniqueAlias() const { //! @todo add option for using non-i18n'd "expr" prefix? const QByteArray expStr( xi18nc("short for 'expression' word (only latin letters, please)", "expr").toLatin1()); //! @todo optimization: cache it? QSet aliases; const int setsSize = d->sets->size(); for (int r = 0; r < setsSize; r++) { //! @todo use iterator here KPropertySet *set = d->sets->at(r); if (set) { const QByteArray a((*set)["alias"].value().toByteArray().toLower()); if (!a.isEmpty()) aliases.insert(a); } } int aliasNr = 1; for (;;aliasNr++) { if (!aliases.contains(expStr + QByteArray::number(aliasNr))) break; } return expStr + QByteArray::number(aliasNr); } //! @todo this is primitive, temporary: reuse SQL parser KDbExpression KexiQueryDesignerGuiEditor::parseExpressionString(const QString& fullString, KDbToken *token, bool allowRelationalOperator) { Q_ASSERT(token); QString str = fullString.trimmed(); int len = 0; //KDbExpression expr; //1. get token *token = KDbToken(); //2-char-long tokens if (str.startsWith(QLatin1String(">="))) { *token = KDbToken::GREATER_OR_EQUAL; len = 2; } else if (str.startsWith(QLatin1String("<="))) { *token = KDbToken::LESS_OR_EQUAL; len = 2; } else if (str.startsWith(QLatin1String("<>"))) { *token = KDbToken::NOT_EQUAL; len = 2; } else if (str.startsWith(QLatin1String("!="))) { *token = KDbToken::NOT_EQUAL2; len = 2; } else if (str.startsWith(QLatin1String("=="))) { *token = '='; len = 2; } else if (str.startsWith(QLatin1String("LIKE "), Qt::CaseInsensitive)) { *token = KDbToken::LIKE; len = 5; } else if (str.startsWith(QLatin1String("NOT "), Qt::CaseInsensitive)) { str = str.mid(4).trimmed(); if (str.startsWith(QLatin1String("LIKE "), Qt::CaseInsensitive)) { *token = KDbToken::NOT_LIKE; len = 5; } else { return KDbExpression(); } } else { if (str.startsWith(QLatin1Char('=')) //1-char-long tokens || str.startsWith(QLatin1Char('<')) || str.startsWith(QLatin1Char('>'))) { *token = str[0].toLatin1(); len = 1; } else { if (allowRelationalOperator) *token = '='; } } if (!allowRelationalOperator && token->isValid()) return KDbExpression(); //1. get expression after token if (len > 0) str = str.mid(len).trimmed(); if (str.isEmpty()) return KDbExpression(); KDbExpression valueExpr; QRegularExpressionMatch match; if (str.length() >= 2 && ( (str.startsWith(QLatin1Char('"')) && str.endsWith(QLatin1Char('"'))) || (str.startsWith(QLatin1Char('\'')) && str.endsWith(QLatin1Char('\'')))) ) { valueExpr = KDbConstExpression(KDbToken::CHARACTER_STRING_LITERAL, str.mid(1, str.length() - 2)); } else if (str.startsWith(QLatin1Char('[')) && str.endsWith(QLatin1Char(']'))) { valueExpr = KDbQueryParameterExpression(str.mid(1, str.length() - 2)); } else if ((match = QRegularExpression("^(\\d{1,4})-(\\d{1,2})-(\\d{1,2})$").match(str)).hasMatch()) { valueExpr = KDbConstExpression(KDbToken::DATE_CONST, QDate::fromString( match.captured(1).rightJustified(4, '0') + "-" + match.captured(2).rightJustified(2, '0') + "-" + match.captured(3).rightJustified(2, '0'), Qt::ISODate)); } else if ((match = QRegularExpression("^(\\d{1,2}):(\\d{1,2})$").match(str)).hasMatch() || (match = QRegularExpression("^(\\d{1,2}):(\\d{1,2}):(\\d{1,2})$").match(str)).hasMatch()) { QString res = match.captured(1).rightJustified(2, '0') + ":" + match.captured(2).rightJustified(2, '0') + ":" + match.captured(3).rightJustified(2, '0'); // qDebug() << res; valueExpr = KDbConstExpression(KDbToken::TIME_CONST, QTime::fromString(res, Qt::ISODate)); } else if ((match = QRegularExpression("^(\\d{1,4})-(\\d{1,2})-(\\d{1,2})\\s+(\\d{1,2}):(\\d{1,2})$").match(str)).hasMatch() || (match = QRegularExpression("^(\\d{1,4})-(\\d{1,2})-(\\d{1,2})\\s+(\\d{1,2}):(\\d{1,2}):(\\d{1,2})$").match(str)).hasMatch()) { QString res = match.captured(1).rightJustified(4, '0') + "-" + match.captured(2).rightJustified(2, '0') + "-" + match.captured(3).rightJustified(2, '0') + "T" + match.captured(4).rightJustified(2, '0') + ":" + match.captured(5).rightJustified(2, '0') + ":" + match.captured(6).rightJustified(2, '0'); // qDebug() << res; valueExpr = KDbConstExpression(KDbToken::DATETIME_CONST, QDateTime::fromString(res, Qt::ISODate)); } else if ((str[0] >= '0' && str[0] <= '9') || str[0] == '-' || str[0] == '+') { //number QLocale locale; const QChar decimalSym = locale.decimalPoint(); bool ok; int pos = str.indexOf('.'); if (pos == -1) {//second chance: local decimal symbol pos = str.indexOf(decimalSym); } if (pos >= 0) {//real const number const int left = str.leftRef(pos).toInt(&ok); if (!ok) return KDbExpression(); const int right = str.midRef(pos + 1).toInt(&ok); if (!ok) return KDbExpression(); valueExpr = KDbConstExpression(KDbToken::REAL_CONST, QPoint(left, right)); //decoded to QPoint } else { //integer const const qint64 val = str.toLongLong(&ok); if (!ok) return KDbExpression(); valueExpr = KDbConstExpression(KDbToken::INTEGER_CONST, val); } } else if (str.toLower() == "null") { valueExpr = KDbConstExpression(KDbToken::SQL_NULL, QVariant()); } else {//identfier if (!KDb::isIdentifier(str)) return KDbExpression(); valueExpr = KDbVariableExpression(str); // the default is 'fieldname' //find first matching field for name 'str': foreach(KexiRelationsTableContainer *cont, *d->relations->tables()) { /*! @todo what about query? */ if (cont->schema()->table() && cont->schema()->table()->field(str)) { valueExpr = KDbVariableExpression(cont->schema()->table()->name() + '.' + str); // the expression is now: tablename.fieldname //! @todo KEXI3 check this we're calling KDbQuerySchema::validate() instead of this: valueExpr.toVariable().field = cont->schema()->table()->field(str); break; } } } return valueExpr; } void KexiQueryDesignerGuiEditor::slotBeforeCellChanged(KDbRecordData *data, int colnum, QVariant* newValue, KDbResultInfo* result) { switch (colnum) { case COLUMN_ID_COLUMN: slotBeforeColumnCellChanged(data, *newValue, result); break; case COLUMN_ID_TABLE: slotBeforeTableCellChanged(data, *newValue, result); break; case COLUMN_ID_VISIBLE: slotBeforeVisibleCellChanged(data, *newValue, result); break; #ifndef KEXI_NO_QUERY_TOTALS case COLUMN_ID_TOTALS: slotBeforeTotalsCellChanged(data, newValue, result); break; #endif case COLUMN_ID_SORTING: slotBeforeSortingCellChanged(data, *newValue, result); break; case COLUMN_ID_CRITERIA: slotBeforeCriteriaCellChanged(data, *newValue, result); break; default: Q_ASSERT_X(false, "colnum", "unhandled value"); } } void KexiQueryDesignerGuiEditor::slotBeforeColumnCellChanged(KDbRecordData *data, QVariant& newValue, KDbResultInfo* result) { if (newValue.isNull()) { d->data->updateRecordEditBuffer(data, COLUMN_ID_TABLE, QVariant(), false/*!allowSignals*/); d->data->updateRecordEditBuffer(data, COLUMN_ID_VISIBLE, QVariant(false));//invisible d->data->updateRecordEditBuffer(data, COLUMN_ID_SORTING, QVariant()); #ifndef KEXI_NO_QUERY_TOTALS d->data->updateRecordEditBuffer(data, COLUMN_ID_TOTALS, QVariant());//remove totals #endif d->data->updateRecordEditBuffer(data, COLUMN_ID_CRITERIA, QVariant());//remove crit. d->sets->eraseCurrentPropertySet(); return; } //auto fill 'table' column QString fieldId(newValue.toString().trimmed()); //tmp, can look like "table.field" QString fieldName; //"field" part of "table.field" or expression string QString tableName; //empty for expressions QByteArray alias; const bool isExpression = !d->fieldColumnIdentifiers.contains(fieldId.toLower()); if (isExpression) { //this value is entered by hand and doesn't match //any value in the combo box -- we're assuming this is an expression //-table remains null //-find "alias" in something like "alias : expr" const int id = fieldId.indexOf(':'); if (id > 0) { alias = fieldId.left(id).trimmed().toLatin1(); if (!KDb::isIdentifier(alias)) { result->success = false; result->allowToDiscardChanges = true; result->column = COLUMN_ID_COLUMN; result->message = xi18nc("@info", "Entered column alias %1 is not a valid identifier.", QString::fromLatin1(alias)); result->description = xi18n("Identifiers should start with a letter or '_' character"); return; } } fieldName = fieldId.mid(id + 1).trimmed(); //check expr. KDbExpression e; KDbToken dummyToken; if ((e = parseExpressionString(fieldName, &dummyToken, false/*allowRelationalOperator*/)).isValid()) { fieldName = e.toString(0).toString(); //print it prettier //this is just checking: destroy expr. object } else { result->success = false; result->allowToDiscardChanges = true; result->column = COLUMN_ID_COLUMN; result->message = xi18nc("@info", "Invalid expression %1", fieldName); return; } } else {//not expr. //this value is properly selected from combo box list if (fieldId == "*") { tableName = "*"; } else { if (!KDb::splitToTableAndFieldParts( fieldId, &tableName, &fieldName, KDb::SetFieldNameIfNoTableName)) { qWarning() << "no 'field' or 'table.field'"; return; } } } KProperty::ValueOptions valueOptions = KProperty::DefaultValueOptions; KPropertySet *set = d->sets->findPropertySetForItem(*data); if (!set) { valueOptions ^= KProperty::ValueOption::RememberOld; // no old val. const int row = d->data->indexOf(data); if (row < 0) { result->success = false; return; } set = createPropertySet(row, tableName, fieldName, true); propertySetSwitched(); } d->data->updateRecordEditBuffer(data, COLUMN_ID_TABLE, QVariant(tableName), false/*!allowSignals*/); d->data->updateRecordEditBuffer(data, COLUMN_ID_VISIBLE, QVariant(true)); #ifndef KEXI_NO_QUERY_TOTALS d->data->updateRecordEditBuffer(data, COLUMN_ID_TOTALS, QVariant(0)); #endif if (!sortingAllowed(fieldName, tableName)) { // sorting is not available for "*" or "table.*" rows //! @todo what about expressions? d->data->updateRecordEditBuffer(data, COLUMN_ID_SORTING, QVariant()); } //update properties (*set)["field"].setValue(fieldName, valueOptions); if (isExpression) { //-no alias but it's needed: if (alias.isEmpty()) //-try oto get old alias alias = (*set)["alias"].value().toByteArray(); if (alias.isEmpty()) //-generate smallest unique alias alias = generateUniqueAlias(); } (*set)["isExpression"].setValue(QVariant(isExpression), valueOptions); if (!alias.isEmpty()) { (*set)["alias"].setValue(alias, valueOptions); //pretty printed "alias: expr" newValue = QString(QString(alias) + ": " + fieldName); } (*set)["caption"].setValue(QString(), valueOptions); (*set)["table"].setValue(tableName, valueOptions); updatePropertiesVisibility(*set); } void KexiQueryDesignerGuiEditor::slotBeforeTableCellChanged(KDbRecordData *data, QVariant& newValue, KDbResultInfo* result) { Q_UNUSED(result) if (newValue.isNull()) { if (!(*data)[COLUMN_ID_COLUMN].toString().isEmpty()) { d->data->updateRecordEditBuffer(data, COLUMN_ID_COLUMN, QVariant(), false/*!allowSignals*/); } d->data->updateRecordEditBuffer(data, COLUMN_ID_VISIBLE, QVariant(false));//invisible #ifndef KEXI_NO_QUERY_TOTALS d->data->updateRecordEditBuffer(data, COLUMN_ID_TOTALS, QVariant());//remove totals #endif d->data->updateRecordEditBuffer(data, COLUMN_ID_CRITERIA, QVariant());//remove crit. d->sets->eraseCurrentPropertySet(); } //update property KPropertySet *set = d->sets->findPropertySetForItem(*data); if (set) { if ((*set)["isExpression"].value().toBool() == false) { (*set)["table"] = newValue; (*set)["caption"] = QVariant(QString()); } else { //do not set table for expr. columns newValue = QVariant(); } updatePropertiesVisibility(*set); } } void KexiQueryDesignerGuiEditor::slotBeforeVisibleCellChanged(KDbRecordData *data, QVariant& newValue, KDbResultInfo* result) { Q_UNUSED(result) KProperty::ValueOptions valueOptions = KProperty::DefaultValueOptions; if (!propertySet()) { valueOptions ^= KProperty::ValueOption::RememberOld; // no old val. createPropertySet(d->dataTable->dataAwareObject()->currentRecord(), (*data)[COLUMN_ID_TABLE].toString(), (*data)[COLUMN_ID_COLUMN].toString(), true); #ifndef KEXI_NO_QUERY_TOTALS d->data->updateRecordEditBuffer(data, COLUMN_ID_TOTALS, QVariant(0));//totals #endif propertySetSwitched(); } KPropertySet &set = *propertySet(); set["visible"].setValue(newValue, valueOptions); } void KexiQueryDesignerGuiEditor::slotBeforeTotalsCellChanged(KDbRecordData *data, QVariant& newValue, KDbResultInfo* result) { #ifdef KEXI_NO_QUERY_TOTALS Q_UNUSED(data) Q_UNUSED(newValue) Q_UNUSED(result) #else //! @todo unused yet setDirty(true); tempData()->setQueryChangedInView(true); #endif } void KexiQueryDesignerGuiEditor::slotBeforeSortingCellChanged(KDbRecordData *data, QVariant& newValue, KDbResultInfo* result) { KProperty::ValueOptions valueOptions = KProperty::DefaultValueOptions; KPropertySet *set = d->sets->findPropertySetForItem(*data); if (!set) { valueOptions ^= KProperty::ValueOption::RememberOld; // no old val. set = createPropertySet(d->dataTable->dataAwareObject()->currentRecord(), (*data)[COLUMN_ID_TABLE].toString(), (*data)[COLUMN_ID_COLUMN].toString(), true); #ifndef KEXI_NO_QUERY_TOTALS d->data->updateRecordEditBuffer(data, COLUMN_ID_TOTALS, QVariant(0));//totals #endif propertySetSwitched(); } QString table(set->property("table").value().toString()); QString field(set->property("field").value().toString()); if (newValue.toInt() == 0 || sortingAllowed(field, table)) { KProperty &property = set->property("sorting"); QString key(property.listData()->keysAsStringList()[ newValue.toInt()]); qDebug() << "new key=" << key; property.setValue(key, valueOptions); } else { //show msg: sorting is not available result->success = false; result->allowToDiscardChanges = true; result->column = COLUMN_ID_SORTING; result->message = xi18n("Could not set sorting for multiple columns (%1)", table == "*" ? table : (table + ".*")); } } void KexiQueryDesignerGuiEditor::slotBeforeCriteriaCellChanged(KDbRecordData *data, QVariant& newValue, KDbResultInfo* result) { //! @todo this is primitive, temporary: reuse SQL parser //QString operatorStr, argStr; KDbExpression e; const QString str = newValue.toString().trimmed(); KDbToken token; QString field, table; KPropertySet *set = d->sets->findPropertySetForItem(*data); if (set) { field = (*set)["field"].value().toString(); table = (*set)["table"].value().toString(); } if (!str.isEmpty() && (!set || table == "*" || field.contains("*"))) { //asterisk found! criteria not allowed result->success = false; result->allowToDiscardChanges = true; result->column = COLUMN_ID_CRITERIA; if (propertySet()) result->message = xi18nc("@info", "Could not set criteria for %1", table == "*" ? table : field); else result->message = xi18n("Could not set criteria for empty record"); } else if (str.isEmpty() || (e = parseExpressionString(str, &token, true/*allowRelationalOperator*/)).isValid()) { if (e.isValid()) { QString tokenStr; if (token != '=') { tokenStr = token.toString() + " "; } if (set) { (*set)["criteria"] = QString(tokenStr + e.toString(0).toString()); //print it prettier } //this is just checking: destroy expr. object } else if (set && str.isEmpty()) { (*set)["criteria"] = QVariant(); //clear it } setDirty(true); tempData()->setQueryChangedInView(true); } else { result->success = false; result->allowToDiscardChanges = true; result->column = COLUMN_ID_CRITERIA; result->message = xi18nc("@info", "Invalid criteria %1", newValue.toString()); } } void KexiQueryDesignerGuiEditor::slotTablePositionChanged(KexiRelationsTableContainer*) { setDirty(true); // this is not needed here because only position has changed: tempData()->setQueryChangedInView(true); } void KexiQueryDesignerGuiEditor::slotAboutConnectionRemove(KexiRelationsConnection*) { setDirty(true); tempData()->setQueryChangedInView(true); } void KexiQueryDesignerGuiEditor::slotAppendFields( KDbTableOrQuerySchema& tableOrQuery, const QStringList& fieldNames) { //! @todo how about query columns and multiple fields? KDbTableSchema *table = tableOrQuery.table(); if (!table || fieldNames.isEmpty()) return; QString fieldName(fieldNames.first()); if (fieldName != "*" && !table->field(fieldName)) return; int row_num; //find last filled row in the GUI table for (row_num = d->sets->size() - 1; row_num >= 0 && !d->sets->at(row_num); row_num--) { } row_num++; //after //add row KDbRecordData *newRecord = createNewRow(table->name(), fieldName, true /* visible*/); d->dataTable->dataAwareObject()->insertItem(newRecord, row_num); d->dataTable->dataAwareObject()->setCursorPosition(row_num, 0); //create buffer createPropertySet(row_num, table->name(), fieldName, true/*new one*/); propertySetSwitched(); d->dataTable->setFocus(); } KPropertySet *KexiQueryDesignerGuiEditor::propertySet() { return d->sets->currentPropertySet(); } void KexiQueryDesignerGuiEditor::updatePropertiesVisibility(KPropertySet& set) { const bool asterisk = isAsterisk( set["table"].value().toString(), set["field"].value().toString() ); #ifdef KEXI_SHOW_UNFINISHED set["caption"].setVisible(!asterisk); #endif set["alias"].setVisible(!asterisk); /*always invisible #ifdef KEXI_SHOW_UNFINISHED set["sorting"].setVisible( !asterisk ); #endif*/ propertySetReloaded(true); } KPropertySet* KexiQueryDesignerGuiEditor::createPropertySet(int row, const QString& tableName, const QString& fieldName, bool newOne) { //const bool asterisk = isAsterisk(tableName, fieldName); KPropertySet *set = new KPropertySet(d->sets); KProperty *prop; //meta-info for property editor set->addProperty(prop = new KProperty("this:classString", xi18nc("Query column", "Column"))); prop->setVisible(false); //! \todo add table_field icon (add buff->addProperty(prop = new KexiProperty("this:iconName", KexiIconName("table_field")) ); // prop->setVisible(false); set->addProperty(prop = new KProperty("this:visibleObjectNameProperty", "visibleName")); prop->setVisible(false);//always hidden set->addProperty(prop = new KProperty("this:objectNameReadOnly", true)); prop->setVisible(false);//always hidden set->addProperty(prop = new KProperty("visibleName", QVariant(tableName + '.' + fieldName))); prop->setVisible(false);//always hidden set->addProperty(prop = new KProperty("table", QVariant(tableName))); prop->setVisible(false);//always hidden set->addProperty(prop = new KProperty("field", QVariant(fieldName))); prop->setVisible(false);//always hidden set->addProperty(prop = new KProperty("caption", QVariant(QString()), xi18n("Caption"))); #ifndef KEXI_SHOW_UNFINISHED prop->setVisible(false); #endif set->addProperty(prop = new KProperty("alias", QVariant(QString()), xi18n("Alias"))); set->addProperty(prop = new KProperty("visible", QVariant(true))); prop->setVisible(false); /*! @todo set->addProperty(prop = new KexiProperty("totals", QVariant(QString())) ); prop->setVisible(false);*/ //sorting QStringList slist, nlist; slist << "nosorting" << "ascending" << "descending"; nlist << xi18n("None") << xi18n("Ascending") << xi18n("Descending"); set->addProperty(prop = new KProperty("sorting", slist, nlist, slist[0], xi18n("Sorting"))); prop->setVisible(false); set->addProperty(prop = new KProperty("criteria", QVariant(QString()))); prop->setVisible(false); set->addProperty(prop = new KProperty("isExpression", QVariant(false))); prop->setVisible(false); d->sets->set(row, set, newOne); updatePropertiesVisibility(*set); return set; } void KexiQueryDesignerGuiEditor::setFocus() { d->dataTable->setFocus(); } void KexiQueryDesignerGuiEditor::slotPropertyChanged(KPropertySet& set, KProperty& property) { const QByteArray pname(property.name()); /*! @todo use KexiProperty::setValidator(QString) when implemented as described in TODO #60 */ if (pname == "alias" || pname == "name") { const QVariant& v = property.value(); if (!v.toString().trimmed().isEmpty() && !KDb::isIdentifier(v.toString())) { KMessageBox::sorry(this, KDb::identifierExpectedMessage(property.caption(), v.toString())); property.resetValue(); } if (pname == "alias") { if (set["isExpression"].value().toBool() == true) { //update value in column #1 d->dataTable->dataAwareObject()->acceptEditor(); d->data->updateRecordEditBuffer(d->dataTable->dataAwareObject()->selectedRecord(), 0, QVariant(set["alias"].value().toString() + ": " + set["field"].value().toString())); d->data->saveRecordChanges(d->dataTable->dataAwareObject()->selectedRecord(), true); } } } tempData()->setQueryChangedInView(true); } void KexiQueryDesignerGuiEditor::slotNewItemStored(KexiPart::Item* item) { d->relations->objectCreated(item->pluginId(), item->name()); } void KexiQueryDesignerGuiEditor::slotItemRemoved(const KexiPart::Item& item) { d->relations->objectDeleted(item.pluginId(), item.name()); } void KexiQueryDesignerGuiEditor::slotItemRenamed(const KexiPart::Item& item, const QString& oldName) { d->relations->objectRenamed(item.pluginId(), oldName, item.name()); } diff --git a/src/plugins/reports/CMakeLists.txt b/src/plugins/reports/CMakeLists.txt index 351db508e..454c2481b 100644 --- a/src/plugins/reports/CMakeLists.txt +++ b/src/plugins/reports/CMakeLists.txt @@ -1,52 +1,54 @@ include_directories( ${CMAKE_SOURCE_DIR}/src/core + ${CMAKE_SOURCE_DIR}/src/kexiutils/style ${CMAKE_SOURCE_DIR}/src/widget + ${CMAKE_SOURCE_DIR}/src/widget/properties ) # the main plugin set(kexi_reportplugin_SRCS kexireports.cpp kexireportpart.cpp kexireportview.cpp kexireportdesignview.cpp KexiDBReportDataSource.cpp kexisourceselector.cpp krscriptfunctions.cpp ) if (KEXI_MOBILE) else () #TODO KEXI3 # list(APPEND kexi_reportplugin_SRCS # keximigratereportdata.cpp # ) endif () #TODO KEXI3 qt5_wrap_cpp(kexi_reportplugin_SRCS ../scripting/kexiscripting/kexiscriptadaptor.h) add_library(kexi_reportplugin MODULE ${kexi_reportplugin_SRCS}) kcoreaddons_desktop_to_json(kexi_reportplugin kexi_reportplugin.desktop) target_link_libraries(kexi_reportplugin PRIVATE kexicore kexiguiutils kexiextendedwidgets KDb KReport KPropertyWidgets ) if (KEXI_MOBILE) else () target_link_libraries(kexi_reportplugin PRIVATE keximain #TODO KEXI3 keximigrate ) endif () install(TARGETS kexi_reportplugin DESTINATION ${KEXI_PLUGIN_INSTALL_DIR}) diff --git a/src/plugins/reports/kexireportview.cpp b/src/plugins/reports/kexireportview.cpp index 0517dd78e..1f063c32f 100644 --- a/src/plugins/reports/kexireportview.cpp +++ b/src/plugins/reports/kexireportview.cpp @@ -1,479 +1,479 @@ /* * Kexi Report Plugin * Copyright (C) 2007-2008 by Adam Pigg (adam@piggz.co.uk) Copyright (C) 2014 Jarosław Staniek * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #include "kexireportview.h" #include #include "KexiDBReportDataSource.h" #ifndef KEXI_MOBILE #include //! @todo KEXI3 #if 0 #include "keximigratereportdata.h" #endif #endif #include #include #include -#include +#include //! @todo KEXI3 #include "../scripting/kexiscripting/kexiscriptadaptor.h" #include #include #include #include #include "krscriptfunctions.h" #include #include #include #include #include #include #include #include #include #include #include #include KexiReportView::KexiReportView(QWidget *parent) : KexiView(parent), m_preRenderer(0), m_functions(0) //! @todo KEXI3, m_kexi(0) { setObjectName("KexiReportDesigner_DataView"); m_reportView = new KReportView(this); setViewWidget(m_reportView); KexiStyle::setupFrame(m_reportView->scrollArea()); #ifndef KEXI_MOBILE m_pageSelector = new KexiRecordNavigator(*m_reportView->scrollArea(), m_reportView); m_pageSelector->setInsertingButtonVisible(false); m_pageSelector->setInsertingEnabled(false); m_pageSelector->setLabelText(xi18nc("Page selector label", "Page:")); m_pageSelector->setButtonToolTipText(KexiRecordNavigator::ButtonFirst, xi18n("Go to first page")); m_pageSelector->setButtonWhatsThisText(KexiRecordNavigator::ButtonFirst, xi18n("Goes to first page")); m_pageSelector->setButtonToolTipText(KexiRecordNavigator::ButtonPrevious, xi18n("Go to previous page")); m_pageSelector->setButtonWhatsThisText(KexiRecordNavigator::ButtonPrevious, xi18n("Goes to previous page")); m_pageSelector->setButtonToolTipText(KexiRecordNavigator::ButtonNext, xi18n("Go to next page")); m_pageSelector->setButtonWhatsThisText(KexiRecordNavigator::ButtonNext, xi18n("Goes to next page")); m_pageSelector->setButtonToolTipText(KexiRecordNavigator::ButtonLast, xi18n("Go to last page")); m_pageSelector->setButtonWhatsThisText(KexiRecordNavigator::ButtonLast, xi18n("Goes to last page")); m_pageSelector->setNumberFieldToolTips(xi18n("Current page number"), xi18n("Number of pages")); m_pageSelector->setRecordHandler(this); #endif // -- setup local actions QList viewActions; QAction* a; #ifndef KEXI_MOBILE viewActions << (a = new QAction(koIcon("document-print"), xi18n("Print"), this)); a->setObjectName("print_report"); a->setToolTip(xi18n("Print report")); a->setWhatsThis(xi18n("Prints the current report.")); connect(a, SIGNAL(triggered()), this, SLOT(slotPrintReport())); KActionMenu *exportMenu = new KActionMenu(koIcon("document-export"), xi18nc("@title:menu","E&xport As"), this); exportMenu->setObjectName("report_export_as"); exportMenu->setDelayed(false); #endif #ifdef KEXI_MOBILE viewActions << (a = new QAction(xi18n("Export:"), this)); a->setEnabled(false); //!TODO this is a bit of a dirty way to add what looks like a label to the toolbar! // " ", not "", is said to be needed in maemo, the icon didn't display properly without it viewActions << (a = new QAction(koIcon("application-vnd.oasis.opendocument.text"), QLatin1String(" "), this)); #else exportMenu->addAction(a = new QAction(koIcon("application-vnd.oasis.opendocument.text"), xi18nc("open dialog to export as text document", "Text Document..."), this)); #endif a->setObjectName("export_as_text_document"); a->setToolTip(xi18n("Export the report as a text document (in OpenDocument Text format)")); a->setWhatsThis(xi18n("Exports the report as a text document (in OpenDocument Text format).")); a->setEnabled(true); connect(a, SIGNAL(triggered()), this, SLOT(slotExportAsTextDocument())); #ifdef KEXI_MOBILE viewActions << (a = new QAction(koIcon("application-pdf"), QLatin1String(" "), this)); #else exportMenu->addAction(a = new QAction(koIcon("application-pdf"), xi18nc("Portable Document Format...", "PDF..."), this)); #endif a->setObjectName("export_as_pdf"); a->setToolTip(xi18n("Export as PDF")); a->setWhatsThis(xi18n("Exports the current report as PDF.")); a->setEnabled(true); connect(a, SIGNAL(triggered()), this, SLOT(slotExportAsPdf())); #ifdef KEXI_MOBILE viewActions << (a = new QAction(koIcon("application-vnd.oasis.opendocument.spreadsheet"), QLatin1String(" "), this)); #else exportMenu->addAction(a = new QAction(koIcon("application-vnd.oasis.opendocument.spreadsheet"), xi18nc("open dialog to export as spreadsheet", "Spreadsheet..."), this)); #endif a->setObjectName("export_as_spreadsheet"); a->setToolTip(xi18n("Export the report as a spreadsheet (in OpenDocument Spreadsheet format)")); a->setWhatsThis(xi18n("Exports the report as a spreadsheet (in OpenDocument Spreadsheet format).")); a->setEnabled(true); connect(a, SIGNAL(triggered()), this, SLOT(slotExportAsSpreadsheet())); #ifdef KEXI_MOBILE viewActions << (a = new QAction(koIcon("text-html"), QLatin1String(" "), this)); #else exportMenu->addAction(a = new QAction(koIcon("text-html"), xi18nc("open dialog to export as web page", "Web Page..."), this)); #endif a->setObjectName("export_as_web_page"); a->setToolTip(xi18n("Export the report as a web page (in HTML format)")); a->setWhatsThis(xi18n("Exports the report as a web page (in HTML format).")); a->setEnabled(true); connect(a, SIGNAL(triggered()), this, SLOT(slotExportAsWebPage())); setViewActions(viewActions); #ifndef KEXI_MOBILE // setup main menu actions QList mainMenuActions; mainMenuActions << exportMenu; setMainMenuActions(mainMenuActions); #endif } KexiReportView::~KexiReportView() { qDebug(); delete m_preRenderer; } void KexiReportView::slotPrintReport() { QScopedPointer renderer(m_factory.createInstance("print")); if (!renderer) { return; } QPrinter printer(QPrinter::HighResolution); QPrintDialog dialog(&printer, this); if (dialog.exec() == QDialog::Accepted) { KReportRendererContext cxt; QPainter painter; cxt.setPrinter(&printer); cxt.setPainter(&painter); if (!renderer->render(cxt, m_preRenderer->document())) { KMessageBox::error(this, xi18n("Printing the report failed."), xi18n("Print Failed")); } } } void KexiReportView::slotExportAsPdf() { QScopedPointer renderer(m_factory.createInstance("print")); if (renderer) { KReportRendererContext cxt; cxt.setUrl(getExportUrl(QLatin1String("application/pdf"), xi18n("Export Report as PDF"), "kfiledialog:///LastVisitedPDFExportPath/", "pdf")); if (!cxt.url().isValid()) { return; } QPrinter printer; QPainter painter; printer.setOutputFileName(cxt.url().path()); printer.setOutputFormat(QPrinter::PdfFormat); printer.setColorMode(QPrinter::Color); painter.begin(&printer); cxt.setPrinter(&printer); cxt.setPainter(&painter); if (!renderer->render(cxt, m_preRenderer->document())) { KMessageBox::error(this, xi18n("Exporting the report as PDF to %1 failed.", cxt.url().toDisplayString()), xi18n("Export Failed")); } else { openExportedDocument(cxt.url()); } } } QUrl KexiReportView::getExportUrl(const QString &mimetype, const QString &caption, const QString &lastExportPath, const QString &extension) { QString defaultSavePath; QString recentDirClass; defaultSavePath = KFileWidget::getStartUrl(QUrl(lastExportPath), recentDirClass).toLocalFile() + '/' + window()->partItem()->captionOrName() + '.' + extension; // loop until an url has been chosen or the file selection has been cancelled const QMimeDatabase db; const QString filterString = db.mimeTypeForName(mimetype).filterString(); return QFileDialog::getSaveFileUrl(this, caption, QUrl(defaultSavePath), filterString); } void KexiReportView::openExportedDocument(const QUrl &destination) { const int answer = KMessageBox::questionYesNo( this, xi18n("Do you want to open exported document?"), QString(), KStandardGuiItem::open(), KStandardGuiItem::close()); if (answer == KMessageBox::Yes) { (void)new KRun(destination, this->topLevelWidget()); } } void KexiReportView::slotExportAsSpreadsheet() { QScopedPointer renderer(m_factory.createInstance("ods")); if (renderer) { KReportRendererContext cxt; cxt.setUrl(getExportUrl(QLatin1String("application/vnd.oasis.opendocument.spreadsheet"), xi18n("Export Report as Spreadsheet"), "kfiledialog:///LastVisitedODSExportPath/", "ods")); if (!cxt.url().isValid()) { return; } if (!renderer->render(cxt, m_preRenderer->document())) { KMessageBox::error(this, xi18n("Failed to export the report as spreadsheet to %1.", cxt.url().toDisplayString()), xi18n("Export Failed")); } else { openExportedDocument(cxt.url()); } } } void KexiReportView::slotExportAsTextDocument() { QScopedPointer renderer(m_factory.createInstance("odt")); //! @todo Show error or don't show the commands to the user if the plugin isn't available. //! The same for other createInstance() calls. if (renderer) { KReportRendererContext cxt; cxt.setUrl(getExportUrl(QLatin1String("application/vnd.oasis.opendocument.text"), xi18n("Export Report as Text Document"), "kfiledialog:///LastVisitedODTExportPath/", "odt")); if (!cxt.url().isValid()) { return; } if (!renderer->render(cxt, m_preRenderer->document())) { KMessageBox::error(this, xi18n("Exporting the report as text document to %1 failed.", cxt.url().toDisplayString()), xi18n("Export Failed")); } else { openExportedDocument(cxt.url()); } } } void KexiReportView::slotExportAsWebPage() { const QString dialogTitle = xi18n("Export Report as Web Page"); KReportRendererContext cxt; cxt.setUrl(getExportUrl(QLatin1String("text/html"), dialogTitle, "kfiledialog:///LastVisitedHTMLExportPath/", "html")); if (!cxt.url().isValid()) { return; } const int answer = KMessageBox::questionYesNo( this, xi18nc("@info", "Would you like to use Cascading Style Sheets (CSS) in the exported " "web page or use HTML tables?" "CSS give output closer to the original."), dialogTitle, KGuiItem(xi18nc("@action:button", "Use CSS")), KGuiItem(xi18nc("@action:button", "Use Table"))); QScopedPointer renderer( m_factory.createInstance(answer == KMessageBox::Yes ? "htmlcss" : "htmltable")); if (!renderer) { return; } if (!renderer->render(cxt, m_preRenderer->document())) { KMessageBox::error(this, xi18n("Exporting the report as web page to %1 failed.", cxt.url().toDisplayString()), xi18n("Export Failed")); } else { openExportedDocument(cxt.url()); } } tristate KexiReportView::beforeSwitchTo(Kexi::ViewMode mode, bool *dontStore) { Q_UNUSED(mode); Q_UNUSED(dontStore); return true; } tristate KexiReportView::afterSwitchFrom(Kexi::ViewMode mode) { Q_UNUSED(mode); if (tempData()->reportSchemaChangedInPreviousView) { tempData()->reportSchemaChangedInPreviousView = false; qDebug() << "Schema changed"; delete m_preRenderer; //qDebug() << tempData()->reportDefinition.tagName(); m_preRenderer = new KReportPreRenderer(tempData()->reportDefinition); if (m_preRenderer->isValid()) { KReportDataSource *reportData = 0; if (!tempData()->connectionDefinition.isNull()) { reportData = createSourceData(tempData()->connectionDefinition); } m_preRenderer->setSourceData(reportData); m_preRenderer->setName(window()->partItem()->name()); //Add a kexi object to provide kexidb and extra functionality //! @todo KEXI3 if we want this if(!m_kexi) { // m_kexi = new KexiScriptAdaptor(); // } // m_preRenderer->registerScriptObject(m_kexi, "Kexi"); //If using a kexidb source, add a functions scripting object if (tempData()->connectionDefinition.attribute("type") == "internal") { m_functions = new KRScriptFunctions(reportData, KexiMainWindowIface::global()->project()->dbConnection()); m_preRenderer->registerScriptObject(m_functions, "field"); connect(m_preRenderer, SIGNAL(groupChanged(QMap)), m_functions, SLOT(setGroupData(QMap))); } connect(m_preRenderer, SIGNAL(finishedAllASyncItems()), this, SLOT(finishedAllASyncItems())); if (!m_preRenderer->generateDocument()) { qWarning() << "Could not generate report document"; return false; } m_reportView->setDocument(m_preRenderer->document()); #ifndef KEXI_MOBILE m_pageSelector->setRecordCount(m_reportView->pageCount()); m_pageSelector->setCurrentRecordNumber(1); #endif } else { KMessageBox::error(this, xi18n("Report schema appears to be invalid or corrupt"), xi18n("Opening failed")); } } return true; } KReportDataSource* KexiReportView::createSourceData(QDomElement e) { KReportDataSource *kodata = 0; if (e.attribute("type") == "internal" && !e.attribute("source").isEmpty()) { kodata = new KexiDBReportDataSource(e.attribute("source"), KexiMainWindowIface::global()->project()->dbConnection()); } #ifndef KEXI_MOBILE //! @todo KEXI3 #if 0 if (e.attribute("type") == "external") { kodata = new KexiMigrateReportData(e.attribute("source")); } #endif #endif return kodata; } KexiReportPartTempData* KexiReportView::tempData() const { return static_cast(window()->data()); } void KexiReportView::addNewRecordRequested() { } void KexiReportView::moveToFirstRecordRequested() { m_reportView->moveToFirstPage(); #ifndef KEXI_MOBILE m_pageSelector->setCurrentRecordNumber(m_reportView->currentPage()); #endif } void KexiReportView::moveToLastRecordRequested() { m_reportView->moveToLastPage(); #ifndef KEXI_MOBILE m_pageSelector->setCurrentRecordNumber(m_reportView->currentPage()); #endif } void KexiReportView::moveToNextRecordRequested() { m_reportView->moveToNextPage(); #ifndef KEXI_MOBILE m_pageSelector->setCurrentRecordNumber(m_reportView->currentPage()); #endif } void KexiReportView::moveToPreviousRecordRequested() { m_reportView->moveToPreviousPage(); #ifndef KEXI_MOBILE m_pageSelector->setCurrentRecordNumber(m_reportView->currentPage()); #endif } void KexiReportView::moveToRecordRequested(int r) { Q_UNUSED(r); } int KexiReportView::currentRecord() const { return m_reportView->currentPage(); } int KexiReportView::recordCount() const { return m_reportView->pageCount(); } void KexiReportView::finishedAllASyncItems() { m_reportView->refreshCurrentPage(); } diff --git a/src/widget/CMakeLists.txt b/src/widget/CMakeLists.txt index 577ce408a..83560987b 100644 --- a/src/widget/CMakeLists.txt +++ b/src/widget/CMakeLists.txt @@ -1,106 +1,109 @@ add_subdirectory( dataviewcommon ) add_subdirectory( relations ) add_subdirectory( undo ) add_subdirectory( utils ) if(KEXI_MOBILE) else() add_subdirectory( tableview ) endif() add_definitions(-DKDE_DEFAULT_DEBUG_AREA=44023) -include_directories(${CMAKE_SOURCE_DIR}/src/widget/tableview ${CMAKE_SOURCE_DIR}/src/core) - +include_directories( + ${CMAKE_SOURCE_DIR}/src/widget/tableview + ${CMAKE_SOURCE_DIR}/src/core + ${CMAKE_SOURCE_DIR}/src/kexiutils/style +) ########### next target ############### set(kexiextendedwidgets_LIB_SRCS fields/KexiFieldComboBox.cpp fields/KexiFieldListModel.cpp fields/KexiFieldListModelItem.cpp fields/KexiFieldListView.cpp navigator/KexiProjectModel.cpp navigator/KexiProjectModelItem.cpp navigator/KexiProjectItemDelegate.cpp navigator/KexiProjectNavigator.cpp navigator/KexiProjectTreeView.cpp properties/KexiCustomPropertyFactory.cpp properties/KexiCustomPropertyFactory_p.cpp properties/KexiPropertyEditorView.cpp properties/KexiPropertyPaneViewBase.cpp kexiquerydesignersqleditor.cpp kexiqueryparameters.cpp kexisectionheader.cpp kexidbdrivercombobox.cpp kexieditor.cpp KexiDataSourceComboBox.cpp KexiObjectInfoLabel.cpp kexicharencodingcombobox.cpp KexiDBTitlePage.cpp KexiProjectSelectorWidget.cpp kexislider.cpp KexiServerDriverNotFoundMessage.cpp KexiNameWidget.cpp KexiNameDialog.cpp KexiStartupFileHandler.cpp KexiListView.cpp KexiWidgetWidthAnimator.cpp ) if (KEXI_MOBILE) else () list(APPEND kexiextendedwidgets_LIB_SRCS #navigator/KexiProjectListView.cpp #navigator/KexiProjectListViewItem.cpp kexidbconnectionwidget.cpp # TODO replace use of KexiProjectListView and KexiProjectListViewList (with KexiProjectNavigator) # in kexiactionselectiondialog and remove them kexiprjtypeselector.cpp KexiConnectionSelectorWidget.cpp KexiFileWidget.cpp KexiFileDialog.cpp KexiPasswordWidget.cpp KexiDBPasswordDialog.cpp ) ki18n_wrap_ui(kexiextendedwidgets_LIB_SRCS KexiConnectionSelector.ui kexidbconnectionwidget.ui kexidbconnectionwidgetdetails.ui kexiprjtypeselector.ui KexiPasswordWidget.ui ) endif () ki18n_wrap_ui(kexiextendedwidgets_LIB_SRCS KexiDBTitlePage.ui KexiProjectSelector.ui ) kexi_add_library(kexiextendedwidgets SHARED ${kexiextendedwidgets_LIB_SRCS}) generate_export_header(kexiextendedwidgets BASE_NAME kexiextwidgets) target_link_libraries(kexiextendedwidgets PRIVATE kexidataviewcommon kexiutils kexiguiutils kexicore KDb KPropertyWidgets KF5::TextWidgets # KTextEdit KF5::Codecs # KCharsets PUBLIC KF5::KIOFileWidgets # KFileWidget KF5::TextEditor ) install(TARGETS kexiextendedwidgets ${INSTALL_TARGETS_DEFAULT_ARGS}) diff --git a/src/widget/navigator/KexiProjectItemDelegate.cpp b/src/widget/navigator/KexiProjectItemDelegate.cpp index 47cfdacdf..124157898 100644 --- a/src/widget/navigator/KexiProjectItemDelegate.cpp +++ b/src/widget/navigator/KexiProjectItemDelegate.cpp @@ -1,117 +1,117 @@ /* This file is part of the KDE project Copyright (C) 2011-2016 Jarosław Staniek This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KexiProjectItemDelegate.h" #include "KexiProjectModel.h" #include "KexiProjectModelItem.h" #include -#include +#include #include #include #include static int paddingBeforeGroupItem(const QFontMetrics& fm) { return fm.lineSpacing() / 2; } class Q_DECL_HIDDEN KexiProjectItemDelegate::Private { public: Private() {} }; KexiProjectItemDelegate::KexiProjectItemDelegate(QObject *parent) : QStyledItemDelegate(parent), d(new Private) { } KexiProjectItemDelegate::~KexiProjectItemDelegate() { delete d; } void KexiProjectItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { const KexiProjectModel* model = qobject_cast(index.model()); QPersistentModelIndex highlighted = model->itemWithSearchHighlight(); QStyleOptionViewItem newOption(option); if (highlighted.isValid() && highlighted == index) { newOption.state |= QStyle::State_MouseOver; } KexiProjectModelItem *item = static_cast(index.internalPointer()); const bool isGroupItem = !item->partItem(); if (!isGroupItem) { QStyledItemDelegate::paint(painter, newOption, QModelIndex()); if (!(newOption.state & QStyle::State_Selected)) { newOption.state &= (0xffffffff ^ QStyle::State_MouseOver); // don't paint mouse highlight // twice because it's translucent } } newOption.rect.setLeft(newOption.rect.left() + newOption.decorationSize.width() / (isGroupItem ? 2 : 1)); if (isGroupItem) { if (item->childCount() == 0) { return; } newOption.palette = KexiUtils::paletteWithDimmedColor(newOption.palette, QPalette::Active, QPalette::Text); newOption.palette.setColor(QPalette::Disabled, QPalette::Text, newOption.palette.color(QPalette::Active, QPalette::Text)); newOption.displayAlignment = Qt::AlignLeft | Qt::AlignBottom; newOption.state &= (0xffffffff ^ QStyle::State_MouseOver); newOption.rect.setBottom(newOption.rect.bottom() - 3); newOption.font = KexiStyle::titleFont(newOption.font); } QStyledItemDelegate::paint(painter, newOption, index); } QWidget* KexiProjectItemDelegate::createEditor( QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const { QWidget *editor = QStyledItemDelegate::createEditor(parent, option, index); if (qobject_cast(editor)) { // sanity check KDbIdentifierValidator *validator = new KDbIdentifierValidator(editor); validator->setLowerCaseForced(true); qobject_cast(editor)->setValidator(validator); } return editor; } QSize KexiProjectItemDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const { QSize s = QStyledItemDelegate::sizeHint(option, index); KexiProjectModelItem *item = static_cast(index.internalPointer()); if (!item->partItem()) { // this is a group item: add padding before if (item->childCount() == 0) { return QSize(0, 0); } s.setHeight(s.height() + paddingBeforeGroupItem(option.fontMetrics)); } const int minHeight = option.fontMetrics.lineSpacing() + 2; if (s.height() < minHeight) { // make sure the item isn't too small (happens at least for Oxygen style) s.setHeight(minHeight); } return s; } diff --git a/src/widget/relations/CMakeLists.txt b/src/widget/relations/CMakeLists.txt index 65059bddb..67c3ae6cb 100644 --- a/src/widget/relations/CMakeLists.txt +++ b/src/widget/relations/CMakeLists.txt @@ -1,29 +1,32 @@ -include_directories(${CMAKE_SOURCE_DIR}/src/core) +include_directories( + ${CMAKE_SOURCE_DIR}/src/core + ${CMAKE_SOURCE_DIR}/src/kexiutils/style +) ########### next target ############### set(kexirelationsview_LIB_SRCS KexiRelationsScrollArea.cpp KexiRelationsConnection.cpp KexiRelationsTableContainer.cpp KexiRelationsTableContainer_p.cpp KexiRelationsView.cpp ) kexi_add_library(kexirelationsview SHARED ${kexirelationsview_LIB_SRCS}) generate_export_header(kexirelationsview) target_link_libraries( kexirelationsview kexicore kexiextendedwidgets kexiutils KDb Qt5::Core Qt5::Gui ) install(TARGETS kexirelationsview ${INSTALL_TARGETS_DEFAULT_ARGS}) diff --git a/src/widget/relations/KexiRelationsView.cpp b/src/widget/relations/KexiRelationsView.cpp index 2fc577291..5562db76f 100644 --- a/src/widget/relations/KexiRelationsView.cpp +++ b/src/widget/relations/KexiRelationsView.cpp @@ -1,483 +1,483 @@ /* This file is part of the KDE project Copyright (C) 2002 Lucijan Busch Copyright (C) 2003 Joseph Wenninger Copyright (C) 2003-2007 Jarosław Staniek This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KexiRelationsView.h" #include #include -#include +#include #include #include #include "KexiRelationsScrollArea.h" #include "KexiRelationsConnection.h" #include #include #include #include #include #include #include #include #include #include #include //! @internal class Q_DECL_HIDDEN KexiRelationsView::Private { public: Private() { } KComboBox *tableCombo; QPushButton *btnAdd; KexiRelationsScrollArea *scrollArea; KDbConnection *conn; QMenu *tableQueryPopup; //!< over table/query QMenu *connectionPopup; //!< over connection QMenu *areaPopup; //!< over outer area QAction *openSelectedTableAction, *designSelectedTableAction, *appendSelectedFieldAction, *appendSelectedFieldsAction, *hideTableAction; }; //--------------- KexiRelationsView::KexiRelationsView(QWidget *parent) : KexiView(parent) , d(new Private) { QWidget *mainWidget = new QWidget(this); QGridLayout *g = new QGridLayout(mainWidget); g->setContentsMargins(0, 0, 0, 0); g->setSpacing(KexiUtils::spacingHint()); QWidget *horWidget = new QWidget(mainWidget); QHBoxLayout *hlyr = new QHBoxLayout(horWidget); hlyr->setContentsMargins(0, 0, 0, 0); g->addWidget(horWidget, 0, 0); d->tableCombo = new KComboBox(horWidget); d->tableCombo->setObjectName("tables_combo"); d->tableCombo->setMinimumWidth(QFontMetrics(font()).width("w")*20); d->tableCombo->setInsertPolicy(QComboBox::NoInsert); d->tableCombo->setSizePolicy(QSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred)); QLabel *lbl = new QLabel(xi18n("Table:"), horWidget); lbl->setBuddy(d->tableCombo); lbl->setIndent(3); hlyr->addWidget(lbl); hlyr->addWidget(d->tableCombo); d->btnAdd = new QPushButton(xi18nc("Insert table/query into relations view", "&Insert"), horWidget); hlyr->addWidget(d->btnAdd); hlyr->addStretch(1); connect(d->btnAdd, SIGNAL(clicked()), this, SLOT(slotAddTable())); d->scrollArea = new KexiRelationsScrollArea(mainWidget); d->scrollArea->setObjectName("scroll_area"); KexiStyle::setupFrame(d->scrollArea); setViewWidget(mainWidget, false/* no focus proxy */); setFocusProxy(d->scrollArea); g->addWidget(d->scrollArea, 1, 0); //actions d->tableQueryPopup = new QMenu(this); d->tableQueryPopup->setObjectName("tableQueryPopup"); connect(d->tableQueryPopup, SIGNAL(aboutToShow()), this, SLOT(aboutToShowPopupMenu())); d->hideTableAction = plugSharedAction("edit_delete", xi18n("&Hide Table"), d->tableQueryPopup); if (d->hideTableAction) d->hideTableAction->setIcon(QIcon()); d->connectionPopup = new QMenu(this); d->connectionPopup->setObjectName("connectionPopup"); connect(d->connectionPopup, SIGNAL(aboutToShow()), this, SLOT(aboutToShowPopupMenu())); //! @todo areaPopup d->areaPopup = new QMenu(this); d->areaPopup->setObjectName("areaPopup"); d->appendSelectedFieldAction = new QAction(KexiIcon("add-field"), xi18n("&Append Field"), this); d->appendSelectedFieldAction->setObjectName("relationsview_appendField"); connect(d->appendSelectedFieldAction, SIGNAL(triggered()), this, SLOT(appendSelectedFields())); d->appendSelectedFieldsAction = new QAction(KexiIcon("add-field"), xi18n("&Append Fields"), this); d->appendSelectedFieldsAction->setObjectName("relationsview_appendFields"); connect(d->appendSelectedFieldsAction, SIGNAL(triggered()), this, SLOT(appendSelectedFields())); d->openSelectedTableAction = new QAction(koIcon("document-open"), xi18n("&Open Table"), this); d->openSelectedTableAction->setObjectName("relationsview_openTable"); connect(d->openSelectedTableAction, SIGNAL(triggered()), this, SLOT(openSelectedTable())); d->designSelectedTableAction = new QAction(koIcon("document-properties"), xi18n("&Design Table"), this); connect(d->designSelectedTableAction, SIGNAL(triggered()), this, SLOT(designSelectedTable())); d->designSelectedTableAction->setObjectName("relationsview_designTable"); plugSharedAction("edit_delete", this, SLOT(removeSelectedObject())); connect(d->scrollArea, SIGNAL(tableViewGotFocus()), this, SLOT(tableViewGotFocus())); connect(d->scrollArea, SIGNAL(connectionViewGotFocus()), this, SLOT(connectionViewGotFocus())); connect(d->scrollArea, SIGNAL(emptyAreaGotFocus()), this, SLOT(emptyAreaGotFocus())); connect(d->scrollArea, SIGNAL(tableContextMenuRequest(QPoint)), this, SLOT(tableContextMenuRequest(QPoint))); connect(d->scrollArea, SIGNAL(connectionContextMenuRequest(QPoint)), this, SLOT(connectionContextMenuRequest(QPoint))); connect(d->scrollArea, SIGNAL(tableHidden(KDbTableSchema*)), this, SLOT(slotTableHidden(KDbTableSchema*))); connect(d->scrollArea, SIGNAL(tablePositionChanged(KexiRelationsTableContainer*)), this, SIGNAL(tablePositionChanged(KexiRelationsTableContainer*))); connect(d->scrollArea, SIGNAL(aboutConnectionRemove(KexiRelationsConnection*)), this, SIGNAL(aboutConnectionRemove(KexiRelationsConnection*))); //! @todo #if 0 if (!embedd) { /*todo setContextHelp(xi18n("Relations"), xi18n("To create a relationship simply drag the source field onto the target field. " "An arrowhead is used to show which table is the parent (master) and which table is the child (slave) in the relationship."));*/ } #endif #ifdef TESTING_KexiRelationWidget for (int i = 0;i < (int)d->db->tableNames().count();i++) QTimer::singleShot(100, this, SLOT(slotAddTable())); #endif invalidateActions(); } KexiRelationsView::~KexiRelationsView() { delete d; } TablesHash* KexiRelationsView::tables() const { return d->scrollArea->tables(); } KexiRelationsTableContainer* KexiRelationsView::table(const QString& name) const { return d->scrollArea->tables()->value(name); } const QSet* KexiRelationsView::relationsConnections() const { return d->scrollArea->relationsConnections(); } void KexiRelationsView::slotAddTable() { if (d->tableCombo->currentIndex() == -1) return; const QString tname = d->tableCombo->itemText(d->tableCombo->currentIndex()); KDbTableSchema *t = d->conn->tableSchema(tname); addTable(t); } void KexiRelationsView::addTable(KDbTableSchema *t, const QRect &rect) { if (!t) return; if (!d->scrollArea->tableContainer(t)) { KexiRelationsTableContainer *c = d->scrollArea->addTableContainer(t, rect); qDebug() << "adding table" << t->name(); if (!c) return; connect(c, SIGNAL(fieldsDoubleClicked(KDbTableOrQuerySchema&,QStringList)), this, SIGNAL(appendFields(KDbTableOrQuerySchema&,QStringList))); } const QString tname = t->name().toLower(); const int count = d->tableCombo->count(); int i = 0; for (; i < count; i++) { if (d->tableCombo->itemText(i).toLower() == tname) break; } if (i < count) { int oi = d->tableCombo->currentIndex(); qDebug() << "removing a table from the combo box"; d->tableCombo->removeItem(i); if (d->tableCombo->count() > 0) { if (oi >= d->tableCombo->count()) { oi = d->tableCombo->count() - 1; } d->tableCombo->setCurrentIndex(oi); } else { d->tableCombo->setEnabled(false); d->btnAdd->setEnabled(false); } } emit tableAdded(t); } void KexiRelationsView::addConnection(const SourceConnection& conn) { d->scrollArea->addConnection(conn); } void KexiRelationsView::addTable(const QString& t) { for (int i = 0; i < d->tableCombo->count(); i++) { if (d->tableCombo->itemText(i) == t) { d->tableCombo->setCurrentIndex(i); slotAddTable(); } } } void KexiRelationsView::tableViewGotFocus() { invalidateActions(); } void KexiRelationsView::connectionViewGotFocus() { invalidateActions(); } void KexiRelationsView::emptyAreaGotFocus() { invalidateActions(); } void KexiRelationsView::tableContextMenuRequest(const QPoint& pos) { invalidateActions(); executePopup(pos); } void KexiRelationsView::connectionContextMenuRequest(const QPoint& pos) { invalidateActions(); executePopup(pos); } void KexiRelationsView::emptyAreaContextMenuRequest(const QPoint& /*pos*/) { invalidateActions(); //! @todo } void KexiRelationsView::invalidateActions() { setAvailable("edit_delete", d->scrollArea->selectedConnection() || d->scrollArea->focusedTableContainer()); } void KexiRelationsView::executePopup(QPoint pos) { if (pos == QPoint(-1, -1)) { pos = mapToGlobal( d->scrollArea->focusedTableContainer() ? d->scrollArea->focusedTableContainer()->pos() + d->scrollArea->focusedTableContainer()->rect().center() : rect().center()); } if (d->scrollArea->focusedTableContainer()) d->tableQueryPopup->exec(pos); else if (d->scrollArea->selectedConnection()) d->connectionPopup->exec(pos); } void KexiRelationsView::removeSelectedObject() { d->scrollArea->removeSelectedObject(); } void KexiRelationsView::appendSelectedFields() { KexiRelationsTableContainer* currentTableContainer = d->scrollArea->focusedTableContainer(); if (!currentTableContainer) return; emit appendFields(*currentTableContainer->schema(), currentTableContainer->selectedFieldNames()); } void KexiRelationsView::openSelectedTable() { /*! @todo what about query? */ if (!d->scrollArea->focusedTableContainer() || !d->scrollArea->focusedTableContainer()->schema()->table()) return; bool openingCancelled; KexiMainWindowIface::global()->openObject( "kexi/table", d->scrollArea->focusedTableContainer()->schema()->name(), Kexi::DataViewMode, &openingCancelled); } void KexiRelationsView::designSelectedTable() { /*! @todo what about query? */ if (!d->scrollArea->focusedTableContainer() || !d->scrollArea->focusedTableContainer()->schema()->table()) return; bool openingCancelled; KexiMainWindowIface::global()->openObject( "kexi/table", d->scrollArea->focusedTableContainer()->schema()->name(), Kexi::DesignViewMode, &openingCancelled); } QSize KexiRelationsView::sizeHint() const { return d->scrollArea->sizeHint(); } void KexiRelationsView::slotTableHidden(KDbTableSchema* table) { const QString &t = table->name().toLower(); int i; for (i = 0; i < d->tableCombo->count() && t > d->tableCombo->itemText(i).toLower(); i++) { } d->tableCombo->insertItem(i, table->name()); if (!d->tableCombo->isEnabled()) { d->tableCombo->setCurrentIndex(0); d->tableCombo->setEnabled(true); d->btnAdd->setEnabled(true); } emit tableHidden(table); } void KexiRelationsView::aboutToShowPopupMenu() { KexiRelationsTableContainer* currentTableContainer = d->scrollArea->focusedTableContainer(); if (currentTableContainer /*&& currentTableContainer->schema()->table()*/) { /*! @todo what about query? */ d->tableQueryPopup->clear(); d->tableQueryPopup->addSection(KexiIcon("table"), QString(d->scrollArea->focusedTableContainer()->schema()->name()) + " : " + xi18n("Table")); QStringList selectedFieldNames(currentTableContainer->selectedFieldNames()); if (currentTableContainer && !selectedFieldNames.isEmpty()) { if (selectedFieldNames.count() > 1 || selectedFieldNames.first() == "*") //multiple d->tableQueryPopup->addAction(d->appendSelectedFieldsAction); else d->tableQueryPopup->addAction(d->appendSelectedFieldAction); d->tableQueryPopup->addSeparator(); } d->tableQueryPopup->addAction(d->openSelectedTableAction); d->tableQueryPopup->addAction(d->designSelectedTableAction); d->tableQueryPopup->addSeparator(); d->tableQueryPopup->addAction(d->hideTableAction); } else if (d->scrollArea->selectedConnection()) { unplugSharedAction("edit_delete", d->connectionPopup); d->connectionPopup->clear(); d->connectionPopup->addSection(QIcon(), d->scrollArea->selectedConnection()->toString() + " : " + xi18n("Relationship")); plugSharedAction("edit_delete", d->connectionPopup); } } bool KexiRelationsView::clear() { d->scrollArea->clear(); return setConnection(d->conn); } /*! Removes all coonections from the view. */ void KexiRelationsView::removeAllConnections() { d->scrollArea->removeAllConnections(); } bool KexiRelationsView::setConnection(KDbConnection *conn) { d->tableCombo->clear(); d->conn = conn; if (conn) { bool ok; QStringList result = d->conn->tableNames(false/*no system tables*/, &ok); if (!ok) { return false; } result.sort(); d->tableCombo->addItems(result); } return true; } void KexiRelationsView::objectCreated(const QString &mime, const QString& name) { if (mime == "kexi/table" || mime == "kexi/query") { //! @todo query? const int count = d->tableCombo->count(); QString strName(name); int i = 0; for (; i < count && d->tableCombo->itemText(i) <= strName; i++) { } d->tableCombo->insertItem(i, name); } } void KexiRelationsView::objectDeleted(const QString &mime, const QString& name) { if (mime == "kexi/table" || mime == "kexi/query") { for (int i = 0; i < d->tableCombo->count(); i++) { //! @todo query? if (d->tableCombo->itemText(i) == name) { d->tableCombo->removeItem(i); if (d->tableCombo->currentIndex() == i) { if (i == (d->tableCombo->count() - 1)) d->tableCombo->setCurrentIndex(i - 1); else d->tableCombo->setCurrentIndex(i); } break; } } } } void KexiRelationsView::objectRenamed(const QString &mime, const QString& name, const QString& newName) { if (mime == "kexi/table" || mime == "kexi/query") { const int count = d->tableCombo->count(); for (int i = 0; i < count; i++) { //! @todo query? if (d->tableCombo->itemText(i) == name) { d->tableCombo->removeItem(i); int j = 0; for (; j < count && d->tableCombo->itemText(j) <= newName; j++) { } d->tableCombo->insertItem(j, newName); break; } } } } void KexiRelationsView::hideAllTablesExcept(QList* tables) { d->scrollArea->hideAllTablesExcept(tables); }