diff --git a/src/core/KexiMainWindowIface.h b/src/core/KexiMainWindowIface.h index fdf98cbd7..3477e8f16 100644 --- a/src/core/KexiMainWindowIface.h +++ b/src/core/KexiMainWindowIface.h @@ -1,330 +1,334 @@ /* 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. */ #ifndef KEXIMAINWINDOWIFACE_H #define KEXIMAINWINDOWIFACE_H //#define KEXI_IMPL_WARNINGS #include #include #include #include "KexiMigrateManagerInterface.h" #include "kexisharedactionhost.h" #include "kexi.h" class KDbQuerySchema; class KexiWindow; class KexiProject; class KActionCollection; class KexiSearchableModel; class KexiUserFeedbackAgent; class KexiMigrateManagerInterface; namespace KexiPart { class Item; class Info; } class KToolBar; namespace Kexi { //! Describes a global mode for the application /*! The following describes availability of modes. - Welcome mode: always ON - Help mode: always ON -# State: No project - Project mode: OFF - Edit mode: OFF - Design mode: OFF -# State: Project opened, no object opened - Project mode: ON - Edit mode: ON - Design mode: OFF -# State: Project opened, at least one object opened - Project mode: ON - Edit mode: ON - Design mode: ON (if the current object can be designed) */ /*! @todo What about custom modes? */ enum GlobalViewMode { WelcomeGlobalMode, ProjectGlobalMode, EditGlobalMode, DesignGlobalMode, HelpGlobalMode, LastGlobalMode = HelpGlobalMode }; } /** * @short Kexi's main window interface * This interface is implemented by KexiMainWindow class. * KexiMainWindow offers simple features what lowers cross-dependency (and also avoids * circular dependencies between Kexi modules). */ class KEXICORE_EXPORT KexiMainWindowIface : public KexiSharedActionHost { public: //! Used by printActionForItem() enum PrintActionType { PrintItem, PreviewItem, PageSetupForItem }; KexiMainWindowIface(); virtual ~KexiMainWindowIface(); //! \return KexiMainWindowImpl global singleton (if it is instantiated) static KexiMainWindowIface* global(); QWidget* thisWidget(); //! Project data of currently opened project or NULL if no project here yet. virtual KexiProject *project() = 0; //! @todo KEXI3 virtual KActionCollection* actionCollection() const = 0; virtual KActionCollection* actionCollection() const = 0; //! @todo KEXI3 virtual QWidget* focusWidget() const = 0; virtual QWidget* focusWidget() const = 0; /*! Registers window \a window for watching and adds it to the main window's stack. */ virtual void registerChild(KexiWindow *window) = 0; /*! \return a list of all actions defined by application. Not all of them are shared. Don't use plug these actions in your windows by hand but user methods from KexiView! */ virtual QList allActions() const = 0; /*! \return currently active window or 0 if there is no active window. */ virtual KexiWindow* currentWindow() const = 0; /*! Switches \a window to view \a mode. Activates the window if it is not the current window. */ virtual tristate switchToViewMode(KexiWindow& window, Kexi::ViewMode viewMode) = 0; //! @return current global mode virtual Kexi::GlobalViewMode currentMode() const = 0; /*! \return true if this window is in the User Mode. */ virtual bool userMode() const = 0; // Q_SIGNALS: //! Emitted to make sure the project can be close. //! Connect a slot here and set \a cancel to true to cancel the closing. virtual void acceptProjectClosingRequested(bool *cancel) = 0; //! Emitted before closing the project (and destroying all it's data members). //! You can do you cleanup of your structures here. virtual void beforeProjectClosing() = 0; //! Emitted after closing the project. virtual void projectClosed() = 0; // public Q_SLOTS: /*! Creates new object of type defined by \a info part info. \a openingCancelled is set to true if opening has been cancelled. \return true on success. */ virtual bool newObject(KexiPart::Info *info, bool *openingCancelled) = 0; //! Opens object pointed by \a item in a view \a viewMode virtual KexiWindow* openObject(KexiPart::Item *item, Kexi::ViewMode viewMode, bool *openingCancelled, QMap* staticObjectArgs = 0, QString* errorMessage = 0) = 0; //! For convenience virtual KexiWindow* openObject(const QString& mime, const QString& name, Kexi::ViewMode viewMode, bool *openingCancelled, QMap* staticObjectArgs = 0) = 0; /*! Closes the object for \a item. \return true on success (closing can be dealyed though), false on failure and cancelled if the object has "opening" job assigned. */ virtual tristate closeObject(KexiPart::Item* item) = 0; /*! Called to accept property butter editing. */ virtual void acceptPropertySetEditing() = 0; /*! Received information from active view that \a window has switched its property set, so property editor contents should be reloaded. If \a force is true, property editor's data is reloaded even if the currently pointed property set is the same as before. If \a preservePrevSelection is true and there was a property set set before call, previously selected item will be preselected in the editor (if found). */ virtual void propertySetSwitched(KexiWindow *window, bool force = false, bool preservePrevSelection = true, bool sortedProperties = false, const QByteArray& propertyToSelect = QByteArray()) = 0; + virtual void beginPropertyPaneUpdate() = 0; + + virtual void endPropertyPaneUpdate() = 0; + //! Options used in saveObject() enum SaveObjectOption { DoNotAsk = 1, //!< Do not ask for confirmation of overwriting SaveObjectAs = 2 //!< Saving object with a new name }; Q_DECLARE_FLAGS(SaveObjectOptions, SaveObjectOption) /*! Saves window's \a window data. If window's data is never saved, user is asked for name and title, before saving (see getNewObjectInfo()). \return true on successul saving or false on error. If saving was cancelled by user, cancelled is returned. \a messageWhenAskingForName is a i18n'ed text that will be visible within name/caption dialog (see KexiNameDialog), which is popped up for never saved objects. Saving object with a new name is also supported here, to do so SaveObjectOption::SaveObjectAs should be added to @a options. */ virtual tristate saveObject(KexiWindow *window, const QString& messageWhenAskingForName = QString(), SaveObjectOptions options = 0) = 0; /*! Closes window \a window. If window's data (see KexiWindow::isDirty()) is unsaved, used will be asked if saving should be perforemed. \return true on successull closing or false on closing error. If closing was cancelled by user, cancelled is returned. If \a window is 0, the current one will be closed. */ virtual tristate closeWindow(KexiWindow *window) = 0; /*! Find window for a given \a identifier. \return 0 if no windows found. */ virtual KexiWindow *openedWindowFor(int identifier) = 0; /*! Find window for a given \a item. \return 0 if no windows found. */ virtual KexiWindow *openedWindowFor(const KexiPart::Item* item) = 0; /*! Parametrs for query with given id. */ virtual QList currentParametersForQuery(int queryId) const = 0; //! \return query schema currently unsaved (edited) in a window corresponding to Kexi object identified by \a identifier. /*! For implementation in plugins, default implementation returns 0. * In implementations 0 should be returned if there is no such Kexi object * in the current project or if the object's window is not opened or if * the window contains no edited query at the moment. * If the query is "unsaved" the window displaying the corresponding Kexi object is marked as "dirty". * Currently supported type of Kexi objects are only queries being in data view. * See KexiQueryPart::unsavedQuery(int) for this implementation. * The query schema returned by this method can be used for example by data * exporting routines so users can export result of running unsaved * query without prior saving its design. * The changes to design can be even discarded without consequences this way. @note Returned pointer leads to a temporary query schema object owned by the corresponding view, * so lifetime of the object is limited to the lifetime of the view and its window. * Do not store the pointer after the window is closed to avoid dangling pointers. \see KexiPart::Part::currentQuery(KexiView*) KexiWindow::isDirty() */ virtual KDbQuerySchema* unsavedQuery(int identifier) = 0; /*! Displays a dialog for entering object's name and title. Used on new object saving. \return true on successul closing or cancelled on cancel returned. It's unlikely to have false returned here. \a messageWhenAskingForName is a i18n'ed text that will be visible within name/caption dialog (see KexiNameDialog). If \a allowOverwriting is true, user will be asked for existing object's overwriting, else it will be impossible to enter a name of existing object. You can check \a overwriteNeeded after calling this method. If it's true, user agreed on overwriting, if it's false, user picked nonexisting name, so no overwrite will be needed. If \a originalName is not empty, the dialog will make sure the entered name is different, what is useful for "Saving As" objects. */ virtual tristate getNewObjectInfo(KexiPart::Item *partItem, const QString &originalName, KexiPart::Part *part, bool allowOverwriting, bool *overwriteNeeded, const QString& messageWhenAskingForName = QString()) = 0; /*! Highlights object of mime \a mime and name \a name. This can be done in the Project Navigator or so. If a window for the object is opened (in any mode), it should be raised. */ virtual void highlightObject(const QString& mime, const QString& name) = 0; //! Shows "print" dialog for \a item. //! \return true on success. virtual tristate printItem(KexiPart::Item* item) = 0; //! Shows "print preview" window. //! \return true on success. virtual tristate printPreviewForItem(KexiPart::Item* item) = 0; //! Shows "page setup" window for \a item. //! \return true on success and cancelled when the action was cancelled. virtual tristate showPageSetupForItem(KexiPart::Item* item) = 0; /*! Executes custom action for the main window, usually provided by a plugin. Also used by KexiFormEventAction. */ virtual tristate executeCustomActionForObject(KexiPart::Item* item, const QString& actionName) = 0; //! @todo temporary solution before the tabbed toolbar framework emerges /*! Appends widget @a widget to tabbed toolbar declared as @a name. @a widget will be reparented but the ownership is not taken. */ virtual void appendWidgetToToolbar(const QString& name, QWidget* widget) = 0; //! @todo temporary solution before the tabbed toolbar framework emerges /*! Shows or hides widget in the tabbed toolbar. */ virtual void setWidgetVisibleInToolbar(QWidget* widget, bool visible) = 0; //! @todo replace with the final Actions API virtual void addToolBarAction(const QString& toolBarName, QAction *action) = 0; //! @todo replace with the final Actions API virtual KToolBar *toolBar(const QString& name) const = 0; /*! Updates info label of the property editor by reusing properties provided by the current property set. Read documentation of KexiPropertyEditorView class for information about accepted properties. If the current property is 0 and @a textToDisplayForNullSet string is not empty, this string is displayed (without icon or any other additional part). If the current property is 0 and @a textToDisplayForNullSet string is empty, the info label widget becomes hidden. */ virtual void updatePropertyEditorInfoLabel(const QString& textToDisplayForNullSet = QString()) = 0; /*! Add searchable model to the main window. This extends search to a new area. One example is Project Navigator. */ virtual void addSearchableModel(KexiSearchableModel *model) = 0; virtual KexiUserFeedbackAgent* userFeedbackAgent() const = 0; //! Interface to the migrate manager virtual KexiMigrateManagerInterface* migrateManager() = 0; //! Sets reasonable dialog size based on main window size, that is 80% of its size. virtual void setReasonableDialogSize(QDialog *dialog) = 0; protected: //! Sets current global mode virtual void setCurrentMode(Kexi::GlobalViewMode mode) = 0; protected: // Q_SLOTS: virtual void slotObjectRenamed(const KexiPart::Item &item, const QString& oldName) = 0; }; Q_DECLARE_OPERATORS_FOR_FLAGS(KexiMainWindowIface::SaveObjectOptions) #endif diff --git a/src/formeditor/form.cpp b/src/formeditor/form.cpp index 24acedd5b..7e3620f84 100644 --- a/src/formeditor/form.cpp +++ b/src/formeditor/form.cpp @@ -1,2687 +1,2690 @@ /* This file is part of the KDE project Copyright (C) 2003 Lucijan Busch Copyright (C) 2004 Cedric Pasteur Copyright (C) 2004-2014 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 "form_p.h" #include "WidgetInfo.h" #include "FormWidget.h" #include "container.h" #include "objecttree.h" #include "formIO.h" #include "FormWidgetInterface.h" #include "widgetlibrary.h" #include "events.h" #include "utils.h" #include "widgetwithsubpropertiesinterface.h" #include "tabstopdialog.h" #include #include #include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace KFormDesigner; Form::Form(WidgetLibrary* library, Mode mode, KActionCollection &col, ActionGroup& group) : QObject(library) , d( new FormPrivate(this, library) ) { init(mode, col, group); } Form::Form(Form *parent) : QObject(parent->library()) , d( new FormPrivate(this, parent->library()) ) { init(parent->mode(), *parent->actionCollection(), *parent->widgetActionGroup()); } Form::~Form() { emit destroying(); delete d; } void Form::init(Mode mode, KActionCollection &col, KFormDesigner::ActionGroup &group) { d->mode = mode; d->features = 0; d->widgetActionGroup = &group; connect(&d->propertySet, SIGNAL(propertyChanged(KPropertySet&,KProperty&)), this, SLOT(slotPropertyChanged(KPropertySet&,KProperty&))); connect(&d->propertySet, SIGNAL(propertyReset(KPropertySet&,KProperty&)), this, SLOT(slotPropertyReset(KPropertySet&,KProperty&))); d->collection = &col; } WidgetLibrary* Form::library() const { return d->library; } KActionCollection *Form::actionCollection() const { return d->collection; } KFormDesigner::ActionGroup* Form::widgetActionGroup() const { return d->widgetActionGroup; } void Form::setFeatures(Features features) { d->features = features; } Form::Features Form::features() const { return d->features; } QWidget* Form::widget() const { if (d->topTree) return d->topTree->widget(); else if (d->toplevel) return d->toplevel->widget(); else // preview form return d->widget; } FormWidget* Form::formWidget() const { return d->formWidget; } ObjectTree* Form::objectTree() const { return d->topTree; } QWidgetList* Form::selectedWidgets() const { return &(d->selected); } QWidget* Form::selectedWidget() const { return d->selected.count() == 1 ? d->selected.first() : 0; } void Form::setInteractiveMode(bool interactive) { d->interactive = interactive; } bool Form::interactiveMode() const { return d->interactive; } Form::Mode Form::mode() const { return d->mode; } bool Form::isModified() const { return d->modified; } void Form::setModified(bool set) { d->modified = set; emit modified(set); } int Form::gridSize() const { return d->gridSize; } void Form::setGridSize(int gridSize) { d->gridSize = gridSize; } int Form::defaultMargin() const { return 11; } int Form::defaultSpacing() const { return 6; } QString Form::filename() const { return d->filename; } void Form::setFilename(const QString &file) { d->filename = file; } void Form::clearUndoStack() { d->undoStack.clear(); } void Form::setUndoStackClean() { d->undoStack.setClean(); } #ifdef KFD_SIGSLOTS ConnectionBuffer* Form::connectionBuffer() const { return d->connBuffer; } void Form::setConnectionBuffer(ConnectionBuffer *b) { if (b != d->connBuffer) { delete d->connBuffer; } d->connBuffer = b; } #endif #ifdef KEXI_PIXMAP_COLLECTIONS_SUPPORT PixmapCollection* Form::pixmapCollection() const { return d->pixcollection; } #endif void Form::setPixmapsStoredInline(bool set) { d->pixmapsStoredInline = set; } bool Form::pixmapsStoredInline() const { return d->pixmapsStoredInline; } ObjectTreeList* Form::tabStops() { return &(d->tabstops); } bool Form::autoTabStops() const { return d->autoTabstops; } void Form::setAutoTabStops(bool autoTab) { d->autoTabstops = autoTab; } QHash* Form::headerProperties() { return &d->headerProperties; } //////////////// Container -related functions /////////////////////// Container* Form::toplevelContainer() const { return d->toplevel; } void Form::createToplevel(QWidget *container, FormWidget *formWidget, const QByteArray &) { //qDebug() << "container= " << (container ? container->objectName() : "") // << " formWidget=" << formWidget; setFormWidget(formWidget); d->toplevel = new Container(0, container, this); d->toplevel->setObjectName(objectName()); d->topTree = new ObjectTree(xi18n("Form"), container->objectName(), container, d->toplevel); d->toplevel->setObjectTree(d->topTree); d->toplevel->setForm(this); //! @todo pixmapcollection #ifdef KEXI_PIXMAP_COLLECTIONS_SUPPORT d->pixcollection = new PixmapCollection(container->objectName(), this); #endif d->topTree->setWidget(container); //! @todo copy caption in Kexi from object's caption // d->topTree->addModifiedProperty("caption", name()); //d->topTree->addModifiedProperty("icon"); connect(container, SIGNAL(destroyed()), this, SLOT(formDeleted())); //qDebug() << "d->toplevel=" << d->toplevel; // alter the style delete d->designModeStyle; d->designModeStyle = 0; if (d->mode == DesignMode) { d->designModeStyle = new DesignModeStyle(d->topTree->widget()->style()->objectName()); d->designModeStyle->setParent(this); d->topTree->widget()->setStyle(d->designModeStyle); } } Container* Form::activeContainer() { if (d->selected.isEmpty()) return d->toplevel; ObjectTreeItem *it; if (d->selected.count() == 1) it = d->topTree->lookup(d->selected.last()->objectName()); else it = commonParentContainer(d->selected); if (!it) return 0; if (it->container()) return it->container(); else return it->parent()->container(); } ObjectTreeItem* Form::commonParentContainer(const QWidgetList& wlist) { // create a list of all widget parents QSet parents; foreach (QWidget *w, wlist) { parents.insert(w->parentWidget()); } QWidgetList parentsList(parents.toList()); removeChildrenFromList(parentsList); // one widget remains == the container we are looking for ObjectTreeItem *item; if (parentsList.count() == 1) { item = d->topTree->lookup(parentsList.first()->objectName()); } else { // we need to go one level up item = commonParentContainer(parentsList); } return item; } Container* Form::parentContainer(QWidget *w) const { if (!w) return 0; ObjectTreeItem *it = d->topTree->lookup(w->objectName()); if (!it || !it->parent()) return 0; if (it->parent()->container()) return it->parent()->container(); else return it->parent()->parent()->container(); } void Form::setMode(Mode mode) { d->mode = mode; if (d->mode == DesignMode) { d->designModeStyle = new DesignModeStyle(d->widget->style()->objectName()); d->designModeStyle->setParent(this); d->widget->setStyle(d->designModeStyle); return; } ObjectTreeHash hash(*(d->topTree->hash())); foreach (ObjectTreeItem *item, hash) { library()->previewWidget( item->widget()->metaObject()->className(), item->widget(), d->toplevel ); } d->widget = d->topTree->widget(); delete d->topTree; d->topTree = 0; delete d->toplevel; d->toplevel = 0; // alter the style delete d->designModeStyle; d->designModeStyle = 0; } ///////////////////////////// Selection stuff /////////////////////// void Form::selectWidget(QWidget *w, WidgetSelectionFlags flags) { if (!d->selectWidgetEnabled) return; d->selectWidgetEnabled = false; selectWidgetInternal(w, flags); d->selectWidgetEnabled = true; } void Form::selectWidgetInternal(QWidget *w, WidgetSelectionFlags flags) { if (!w) { selectWidget(widget()); return; } //qDebug() << "selected count=" << d->selected.count(); if (!d->selected.isEmpty()) { //qDebug() << "first=" << d->selected.first(); } //qDebug() << w; if (d->selected.count() == 1 && d->selected.first() == w) { return; } if (d->selected.isEmpty() || w == widget() || (d->selected.first() == widget())) { flags |= ReplacePreviousSelection; } //raise selected widget and all possible parents QWidget *wtmp = w; while (!(flags & DontRaise) && wtmp && wtmp->parentWidget() && (wtmp != widget())) { wtmp->raise(); if (d->resizeHandles.value( wtmp->objectName() )) d->resizeHandles.value( wtmp->objectName() )->raise(); wtmp = wtmp->parentWidget(); } if (wtmp) wtmp->setFocus(); if (flags & ReplacePreviousSelection) { d->selected.clear(); qDeleteAll(d->resizeHandles); d->resizeHandles.clear(); } d->selected.append(w); emitSelectionChanged(w, flags); emitActionSignals(); // WidgetStack and TabWidget pages widgets shouldn't have resize handles, but their parent //! @todo move special case to a factory? #if 0 if (!isTopLevelWidget(w) && w->parentWidget() && KexiUtils::objectIsA(w->parentWidget(), "QWidgetStack")) { w = w->parentWidget(); if (w->parentWidget() && w->parentWidget()->inherits("QTabWidget")) w = w->parentWidget(); } #endif if (w && w != widget()) { ResizeHandleSet *handles = new ResizeHandleSet(w, this); d->resizeHandles.insert(w->objectName(), handles); connect(handles, SIGNAL(geometryChangeStarted()), parentContainer(w), SLOT(startChangingGeometryPropertyForSelectedWidget())); connect(handles, SIGNAL(geometryChanged(QRect)), parentContainer(w), SLOT(setGeometryPropertyForSelectedWidget(QRect))); } } void Form::selectWidgets(const QList& widgets, WidgetSelectionFlags flags) { int i = 0; const int count = widgets.count(); foreach (QWidget* widget, widgets) { if (i == 1) { flags |= AddToPreviousSelection; } if (i == (count - 1)) { flags = LastSelection; } selectWidget(widget, flags); ++i; } } QList Form::widgetsForNames(const QList& names) const { QList widgets; foreach (const QByteArray& name, names) { ObjectTreeItem* item = objectTree()->lookup(name); if (item) { //we're checking for item!=0 because the name could be of a form widget widgets.append(item->widget()); } } return widgets; } void Form::selectWidgets(const QList& names, WidgetSelectionFlags flags) { selectWidgets(widgetsForNames(names), flags); } bool Form::isTopLevelWidget(QWidget *w) const { /* should not be used, just check w==formWidget() instead? */ ObjectTreeItem *item = objectTree()->lookup(w->objectName()); if (!item) return true; return !item->parent(); } ResizeHandleSet* Form::resizeHandlesForWidget(QWidget* w) { return d->resizeHandles.value(w->objectName()); } void Form::deselectWidget(QWidget *w) { d->selected.removeOne(w); ResizeHandleSet *set = d->resizeHandles.take(w->objectName()); delete set; } void Form::selectFormWidget() { selectWidget(widget()); } void Form::clearSelection() { d->selected.clear(); qDeleteAll(d->resizeHandles); d->resizeHandles.clear(); emitSelectionChanged(0, DefaultWidgetSelectionFlags); emitActionSignals(); } void Form::setInsertionPoint(const QPoint &p) { d->insertionPoint = p; } QAction* Form::action(const QString& name) { if (name == KStandardAction::name(KStandardAction::Undo)) { QAction *a = d->internalCollection.action( name ); if (!a) { a = d->undoStack.createUndoAction(&d->internalCollection); // connect this action to the form instead of stack disconnect(a, SIGNAL(triggered()), &d->undoStack, SLOT(undo())); connect(a, SIGNAL(triggered()), this, SLOT(undo())); } return a; } else if (name == KStandardAction::name(KStandardAction::Redo)) { QAction *a = d->internalCollection.action( name ); if (!a) { a = d->undoStack.createRedoAction(&d->internalCollection); // connect this action to the form instead of stack disconnect(a, SIGNAL(triggered()), &d->undoStack, SLOT(redo())); connect(a, SIGNAL(triggered()), this, SLOT(redo())); } return a; } return d->collection->action(name); } void Form::emitActionSignals() { // Update menu and toolbar items if (selectedWidget()) { if (widget() == selectedWidget()) emitFormWidgetSelected(); else emitWidgetSelected( false ); } else if (selectedWidgets()) { emitWidgetSelected( true ); } } void Form::emitUndoActionSignals() { //! @todo pixmapcollection #ifdef KEXI_PIXMAP_COLLECTIONS_SUPPORT QAction *undoAction = d->collection->action(QLatin1String("edit_undo")); if (undoAction) emitUndoEnabled(undoAction->isEnabled(), undoAction->text()); QAction *redoAction = d->collection->action(QLatin1String("edit_redo")); if (redoAction) emitRedoEnabled(redoAction->isEnabled(), redoAction->text()); #endif } void Form::emitSelectionSignals() { if (!selectedWidgets()->isEmpty()) { emitSelectionChanged(selectedWidgets()->first(), DefaultWidgetSelectionFlags); } foreach (QWidget *w, *selectedWidgets()) { emitSelectionChanged(w, LastSelection); } } void Form::emitWidgetSelected(bool multiple) { enableFormActions(); // Enable edit actions d->enableAction("edit_copy", true); d->enableAction("edit_cut", true); d->enableAction("edit_delete", true); d->enableAction("clear_contents", true); // 'Align Widgets' menu d->enableAction("align_menu", multiple); d->enableAction("align_to_left", multiple); d->enableAction("align_to_right", multiple); d->enableAction("align_to_top", multiple); d->enableAction("align_to_bottom", multiple); d->enableAction("adjust_size_menu", true); d->enableAction("adjust_width_small", multiple); d->enableAction("adjust_width_big", multiple); d->enableAction("adjust_height_small", multiple); d->enableAction("adjust_height_big", multiple); d->enableAction("format_raise", true); d->enableAction("format_lower", true); QWidgetList *wlist = selectedWidgets(); bool fontEnabled = false; foreach (QWidget* w, *wlist) { if (-1 != w->metaObject()->indexOfProperty("font")) { fontEnabled = true; break; } } d->enableAction("format_font", fontEnabled); // If the widgets selected is a container, we enable layout actions if (!multiple) { if (!wlist->isEmpty()) { objectTree()->lookup(wlist->first()->objectName()); } } emit widgetSelected(true); } void Form::emitFormWidgetSelected() { d->enableAction("edit_copy", false); d->enableAction("edit_cut", false); d->enableAction("edit_delete", false); d->enableAction("clear_contents", false); // Disable format functions d->enableAction("align_menu", false); d->enableAction("align_to_left", false); d->enableAction("align_to_right", false); d->enableAction("align_to_top", false); d->enableAction("align_to_bottom", false); d->enableAction("adjust_size_menu", false); d->enableAction("format_raise", false); d->enableAction("format_lower", false); d->enableAction("format_font", false); enableFormActions(); emit formWidgetSelected(); } void Form::emitNoFormSelected() { disableWidgetActions(); // Disable 'Tools' actions d->enableAction("pixmap_collection", false); #ifdef KFD_SIGSLOTS if (d->features & EnableConnections) { d->enableAction("form_connections", false); } #endif d->enableAction("taborder", false); d->enableAction("change_style", true); // Disable items in 'File' if (d->features & EnableFileActions) { d->enableAction("file_save", false); d->enableAction("file_save_as", false); d->enableAction("preview_form", false); } emit noFormSelected(); } void Form::enableFormActions() { // Enable 'Tools' actions d->enableAction("pixmap_collection", true); #ifdef KFD_SIGSLOTS if (d->features & EnableConnections) { d->enableAction("form_connections", true); } #endif d->enableAction("taborder", true); d->enableAction("change_style", true); // Enable items in 'File' if (d->features & EnableFileActions) { d->enableAction("file_save", true); d->enableAction("file_save_as", true); d->enableAction("preview_form", true); } d->enableAction("edit_paste", true); //?? isPasteEnabled()); d->enableAction("edit_select_all", true); } void Form::disableWidgetActions() { // Disable edit actions d->enableAction("edit_copy", false); d->enableAction("edit_cut", false); d->enableAction("edit_delete", false); d->enableAction("clear_contents", false); // Disable format functions d->enableAction("align_menu", false); d->enableAction("align_to_left", false); d->enableAction("align_to_right", false); d->enableAction("align_to_top", false); d->enableAction("align_to_bottom", false); d->enableAction("adjust_size_menu", false); d->enableAction("format_raise", false); d->enableAction("format_lower", false); } /////////////////////////// Various slots and signals ///////////////////// void Form::formDeleted() { d->selected.clear(); d->resizeHandles.clear(); deleteLater(); } void Form::changeName(const QByteArray &oldname, const QByteArray &newname) { if (oldname == newname) return; if (d->topTree->rename(oldname, newname)) { #ifdef KFD_SIGSLOTS d->connBuffer->fixName(oldname, newname); #endif ResizeHandleSet *temp = d->resizeHandles.take(oldname); d->resizeHandles.insert(newname, temp); } else { // rename failed KMessageBox::sorry(widget()->topLevelWidget(), xi18nc("@info", "Renaming widget %1 to %2 failed.", QString::fromLatin1(oldname), QString::fromLatin1(newname))); qWarning() << "widget" << newname << "already exists, reverting rename"; d->propertySet.changeProperty("objectName", oldname); } } void Form::emitChildAdded(ObjectTreeItem *item) { addWidgetToTabStops(item); emit childAdded(item); } void Form::emitChildRemoved(ObjectTreeItem *item) { d->tabstops.removeOne(item); #ifdef KFD_SIGSLOTS if (d->connBuffer) d->connBuffer->removeAllConnectionsForWidget(item->name()); #endif emit childRemoved(item); } bool Form::addCommand(Command *command, AddCommandOption option) { setModified(true); if (option == DontExecuteCommand) { command->blockRedoOnce(); } d->undoStack.push(command); //qDebug() << "ADDED:" << *command; return true; } void Form::emitUndoEnabled() { //! @todo pixmapcollection #ifdef KEXI_PIXMAP_COLLECTIONS_SUPPORT QAction *undoAction = d->collection->action(QLatin1String("edit_undo")); if (undoAction) emitUndoEnabled(undoAction->isEnabled(), undoAction->text()); #endif } void Form::emitRedoEnabled() { //! @todo pixmapcollection #ifdef KEXI_PIXMAP_COLLECTIONS_SUPPORT QAction *redoAction = d->collection->action(QLatin1String("edit_redo")); if (redoAction) emitRedoEnabled(redoAction->isEnabled(), redoAction->text()); #endif } void Form::slotFormRestored() { setModified(false); } /////////////////////////// Tab stops //////////////////////// void Form::addWidgetToTabStops(ObjectTreeItem *it) { QWidget *w = it->widget(); if (!w) return; if (!(w->focusPolicy() & Qt::TabFocus)) { // For composed widgets, we check if one of the child can have focus const QObjectList list(w->children()); foreach(const QObject *obj, list) { if (obj->isWidgetType()) {//QWidget::TabFocus flag will be checked later! if (!d->tabstops.contains(it)) { //qDebug() << "adding child of" << w << ":" << obj; d->tabstops.append(it); return; } } } } else if (!d->tabstops.contains(it)) { // not yet in the list //qDebug() << "adding" << w; d->tabstops.append(it); } } void Form::updateTabStopsOrder() { ObjectTreeList newList(d->tabstops); foreach (ObjectTreeItem *item, d->tabstops) { if (!(item->widget()->focusPolicy() & Qt::TabFocus)) { //qDebug() << "Widget removed because has no TabFocus:" //. << item->widget()->objectName(); newList.removeOne(item); } } d->tabstops = newList; } //! Collects all the containers reculsively. Used by Form::autoAssignTabStops(). static void collectContainers(ObjectTreeItem* item, QSet& containers) { if (!item->container()) return; if (!containers.contains(item->container())) { //qDebug() << item->container()->objectTree()->className() // << " " << item->container()->objectTree()->name(); containers.insert(item->container()); } foreach (ObjectTreeItem *child, *item->children()) { collectContainers(child, containers); } } void Form::autoAssignTabStops() { VerticalWidgetList list(toplevelContainer()->widget()); HorizontalWidgetList hlist(toplevelContainer()->widget()); // 1. Collect all the containers, as we'll be sorting widgets groupped by containers QSet containers; collectContainers(toplevelContainer()->objectTree(), containers); foreach (ObjectTreeItem *item, d->tabstops) { if (item->widget()) { //qDebug() << "Widget to sort: " << item->widget(); list.append(item->widget()); } } list.sort(); //foreach (QWidget *w, list) { // qDebug() << w->metaObject()->className() << w->objectName(); //} d->tabstops.clear(); /// We automatically sort widget from the top-left to bottom-right corner //! \todo Handle RTL layout (ie from top-right to bottom-left) for (QWidgetList::ConstIterator it(list.constBegin()); it!=list.constEnd(); ++it) { QWidget *w = *it; hlist.append(w); ++it; QWidget *nextw = it==list.constEnd() ? 0 : *it; Q_UNUSED(nextw); QObject *page_w = 0; KFormDesigner::TabWidget *tab_w = KFormDesigner::findParent( w, "KFormDesigner::TabWidget", page_w); for (; it!=list.constEnd(); ++it) { QWidget *nextw = *it; if (KDbUtils::hasParent(w, nextw)) // do not group (sort) widgets where one is a child of another break; if (nextw->y() >= (w->y() + 20)) break; if (tab_w) { QObject *page_nextw = 0; KFormDesigner::TabWidget *tab_nextw = KFormDesigner::findParent( nextw, "KFormDesigner::TabWidget", page_nextw); if (tab_w == tab_nextw) { if (page_w != page_nextw) // 'nextw' widget within different tab page break; } } hlist.append(nextw); } hlist.sort(); foreach (QWidget *w, hlist) { ObjectTreeItem *tree = d->topTree->lookup(w->objectName()); if (tree) { //qDebug() << "adding " << tree->name(); d->tabstops.append(tree); } } --it; hlist.clear(); } } QString Form::formatVersion() const { return d->formatVersion; } void Form::setFormatVersion(const QString &ver) { d->formatVersion = ver; } QString Form::originalFormatVersion() const { return d->originalFormatVersion; } void Form::setOriginalFormatVersion(const QString &ver) { d->originalFormatVersion = ver; } void Form::setFormWidget(FormWidget* w) { d->formWidget = w; if (!d->formWidget) return; d->formWidget->setForm(this); } void Form::enterWidgetInsertingState(const QByteArray &classname) { if (d->state != WidgetInserting) { enterWidgetSelectingState(); } d->state = WidgetInserting; if (toplevelContainer()) { widget()->setCursor(QCursor(Qt::CrossCursor)); } const QList list(widget()->findChildren()); foreach (QWidget *w, list) { d->cursors.insert(w, w->cursor()); w->setCursor(QCursor(Qt::CrossCursor)); } d->selectedClass = classname; QAction *pointer_action = d->collection->action(QLatin1String("edit_pointer")); if (pointer_action) { pointer_action->setChecked(false); } } QByteArray Form::selectedClass() const { return d->selectedClass; } void Form::abortWidgetInserting() { if (d->state != WidgetInserting) return; widget()->unsetCursor(); const QList list(widget()->findChildren()); foreach (QWidget *w, list) { w->unsetCursor(); } d->state = WidgetSelecting; QAction *pointer_action = d->widgetActionGroup->action(QLatin1String("edit_pointer")); if (pointer_action) { pointer_action->setChecked(true); } } void Form::enterWidgetSelectingState() { switch (d->state) { case WidgetInserting: abortWidgetInserting(); break; #ifdef KFD_SIGSLOTS case Connecting: abortCreatingConnection(); break; #endif default: break; } } #ifdef KFD_SIGSLOTS void Form::enterConnectingState() { if (!(d->features & EnableConnections)) return; enterWidgetSelectingState(); // We set a Pointing hand cursor while drawing the connection d->mouseTrackers = new QStringList(); if (toplevelContainer()) { widget()->setCursor(QCursor(Qt::PointingHandCursor)); widget()->setMouseTracking(true); } const QList list(widget()->findChildren()); foreach(QWidget *w, list) { d->cursors.insert(w, w->cursor()); w->setCursor(QCursor(Qt::PointingHandCursor)); if (w->hasMouseTracking()) d->mouseTrackers->append(w->objectName()); w->setMouseTracking(true); } delete m_connection; m_connection = new Connection(); m_drawingSlot = true; if (m_dragConnection) m_dragConnection->setChecked(true); } void Form::resetSelectedConnection() { //! @todo if (!(d->features & EnableConnections)) return; delete m_connection; m_connection = new Connection(); if (formWidget()) { formWidget()->clearForm(); } if (widget()) { widget()->repaint(); } } void Form::abortCreatingConnection() { //! @todo if (!(d->features & EnableConnections)) return; if (d->state != Connecting) return; if (formWidget()) { formWidget()->clearForm(); } widget()->unsetCursor(); widget()->setMouseTracking(false); const QList list(widget()->findChildren()); foreach (QWidget *w, list) { QHash::ConstIterator curIt(d->cursors.find(w)); if (curIt != d->cursors.constEnd()) w->setCursor(*curIt); w->setMouseTracking(d->mouseTrackers->contains(w->objectName())); } delete d->mouseTrackers; d->mouseTrackers = 0; if (m_connection->slot().isNull()) emit connectionAborted(this); delete m_connection; m_connection = 0; m_drawingSlot = false; QAction *pointer_action = d->widgetActionGroup->action(QLatin1String("edit_pointer")); if (pointer_action) { pointer_action->setChecked(true); } } #endif Form::State Form::state() const { return d->state; } void Form::addPropertyCommand(const QByteArray &wname, const QVariant &oldValue, const QVariant &value, const QByteArray &propertyName, AddCommandOption addOption, int idOfPropertyCommand) { QHash oldValues; oldValues.insert(wname, oldValue); addPropertyCommand(oldValues, value, propertyName, addOption, idOfPropertyCommand); } void Form::addPropertyCommand(const QHash &oldValues, const QVariant &value, const QByteArray &propertyName, AddCommandOption addOption, int idOfPropertyCommand) { //! @todo add to merge in PropertyCommand... #if 0 qDebug() << d->propertySet[propertyName]; qDebug() << "oldValue:" << oldValues << "value:" << value; qDebug() << "idOfPropertyCommand:" << idOfPropertyCommand; d->insideAddPropertyCommand = true; PropertyCommand *presentCommand = dynamic_cast( d->commandHistory->presentCommand() ); if ( presentCommand && d->lastCommand == presentCommand && idOfPropertyCommand > 0 && d->idOfPropertyCommand == idOfPropertyCommand) { d->lastCommand->setValue(value); // just change the value, // to avoid multiple PropertyCommands that only differ by value } else { d->lastCommand = new PropertyCommand(*this, oldValues, value, propertyName); if (!addCommand(d->lastCommand, execute)) { d->lastCommand = 0; } d->idOfPropertyCommand = idOfPropertyCommand; } d->insideAddPropertyCommand = false; #endif d->insideAddPropertyCommand = true; d->lastCommand = new PropertyCommand(*this, oldValues, value, propertyName); d->lastCommand->setUniqueId(idOfPropertyCommand); //qDebug() << "ADD:" << *d->lastCommand; if (!addCommand(d->lastCommand, addOption)) { d->lastCommand = 0; } d->insideAddPropertyCommand = false; } void Form::addPropertyCommandGroup(PropertyCommandGroup *commandGroup, AddCommandOption addOption, int idOfPropertyCommand) { //! @todo add to merge in PropertyCommand...? #if 0 if (!commandGroup || commandGroup->commands().isEmpty()) return; qDebug() << "count:" << commandGroup->commands().count(); qDebug() << "idOfPropertyCommand:" << idOfPropertyCommand; d->insideAddPropertyCommand = true; PropertyCommandGroup *presentCommand = dynamic_cast( d->commandHistory->presentCommand() ); if ( presentCommand && d->lastCommandGroup == presentCommand && idOfPropertyCommand > 0 && d->idOfPropertyCommand == idOfPropertyCommand) { presentCommand->copyPropertyValuesFrom(*commandGroup); // just change the values, // to avoid multiple CommandsGroups // that only differ by values delete commandGroup; } else { d->lastCommandGroup = commandGroup; addCommand(d->lastCommandGroup, execute); d->idOfPropertyCommand = idOfPropertyCommand; } #endif d->insideAddPropertyCommand = true; d->lastCommandGroup = commandGroup; if (!addCommand(d->lastCommandGroup, addOption)) { d->lastCommandGroup = 0; } d->idOfPropertyCommand = idOfPropertyCommand; d->insideAddPropertyCommand = false; } void Form::slotPropertyChanged(KPropertySet& set, KProperty& p) { Q_UNUSED(set); if (!d->slotPropertyChangedEnabled || !objectTree()) return; const QByteArray property( p.name() ); if (property.startsWith("this:")) return; //starts with magical prefix: it's a "meta" prop. const QVariant value( p.value() ); // check if the name is valid (ie is correct identifier) and there is no name conflict if (property == "objectName") { if (d->selected.count() != 1) { qWarning() << "changing objectName property only allowed for single selection"; return; } if (!isNameValid(value.toString())) return; } else if (property == "paletteBackgroundPixmap") { // a widget with a background pixmap should have its own origin // special types of properties handled separately } else if (property == "paletteBackgroundColor") { d->setColorProperty(p, &QWidget::backgroundRole, p.value()); return; } else if (property == "paletteForegroundColor") { d->setColorProperty(p, &QWidget::foregroundRole, p.value()); return; } else if (property == "autoFillBackground") { if (!p.value().toBool()) { // make background inherited d->setColorProperty(p, &QWidget::backgroundRole, QVariant()); } } else if (property == "hAlign" || property == "vAlign" || property == "wordbreak") { saveAlignProperty(property); return; } // make sure we are not already undoing -> avoid recursion if (d->isUndoing && !d->isRedoing) { return; } if (d->selected.count() == 1) { // one widget selected // If the last command is the same, we just change its value //! @todo add to merge in PropertyCommand if needed if (d->slotPropertyChanged_addCommandEnabled && !d->isRedoing) { addPropertyCommand(d->selected.first()->objectName().toLatin1(), p.oldValue(), value, property, DontExecuteCommand); } // If the property is changed, we add it in ObjectTreeItem modifProp ObjectTreeItem *tree = objectTree()->lookup(d->selected.first()->objectName()); if (tree && p.isModified()) { tree->addModifiedProperty(property, d->selected.first()->property(property)); } if (property == "objectName") { changeName(d->selected.first()->objectName().toLatin1(), p.value().toByteArray()); emit widgetNameChanged(d->selected.first()->objectName().toLatin1(), p.value().toByteArray()); } d->selected.first()->setProperty(property, value); handleWidgetPropertyChanged(d->selected.first(), property, value); } else { //! @todo add to merge in PropertyCommand if needed if (d->slotPropertyChanged_addCommandEnabled && !d->isRedoing) { // We store old values for each widget QHash oldValues; foreach(QWidget* widget, d->selected) { oldValues.insert(widget->objectName().toLatin1(), widget->property(property)); } addPropertyCommand(oldValues, value, property, DontExecuteCommand); } foreach(QWidget* widget, d->selected) { ObjectTreeItem *titem = objectTree()->lookup(widget->objectName()); if (titem && p.isModified()) titem->addModifiedProperty(property, widget->property(property)); widget->setProperty(property, value); handleWidgetPropertyChanged(widget, property, value); } } } void Form::slotPropertyReset(KPropertySet& set, KProperty& property) { Q_UNUSED(set); if (d->selected.count() < 2) return; // We use the old value in modifProp for each widget foreach(QWidget* widget, d->selected) { ObjectTreeItem *titem = objectTree()->lookup(widget->objectName()); if (titem && titem->modifiedProperties()->contains(property.name())) widget->setProperty( property.name(), titem->modifiedProperties()->find(property.name()).value()); } } bool Form::isNameValid(const QString &name) const { if (d->selected.isEmpty()) return false; //! @todo add to the undo buffer QWidget *w = d->selected.first(); //also update widget's name in QObject member if (!KDb::isIdentifier(name)) { KMessageBox::sorry(widget(), xi18nc("@info", "Could not rename widget %1 to " "%2 because " "%3 is not a valid name (identifier) for a widget.", w->objectName(), name, name)); d->slotPropertyChangedEnabled = false; d->propertySet["objectName"].resetValue(); d->slotPropertyChangedEnabled = true; return false; } if (objectTree()->lookup(name)) { KMessageBox::sorry(widget(), xi18nc("@info", "Could not rename widget %1 to %2 " "because a widget with the name %3 already exists.", w->objectName(), name, name)); d->slotPropertyChangedEnabled = false; d->propertySet["objectName"].resetValue(); d->slotPropertyChangedEnabled = true; return false; } return true; } void Form::undo() { if (!objectTree()) return; if (!d->undoStack.canUndo()) { qWarning() << "cannot redo"; return; } d->isUndoing = true; d->undoStack.undo(); d->isUndoing = false; } void Form::redo() { if (!objectTree()) return; if (!d->undoStack.canRedo()) { qWarning() << "cannot redo"; return; } d->isRedoing = true; d->undoStack.redo(); d->isRedoing = false; } bool Form::isRedoing() const { return d->isRedoing; } void Form::setUndoing(bool undoing) { d->isUndoing = undoing; } bool Form::isUndoing() const { return d->isUndoing; } int Form::commandsCount() const { return d->undoStack.count(); } const KUndo2Command* Form::command(int index) const { return d->undoStack.command(index); } bool Form::isPropertyVisible(const QByteArray &property, bool isTopLevel, const QByteArray &classname) const { const bool multiple = d->selected.count() >= 2; if (multiple && classname.isEmpty()) return false; QWidget *w = d->selected.first(); WidgetWithSubpropertiesInterface* subpropIface = dynamic_cast(w); QWidget *subwidget; if (subpropIface && subpropIface->findMetaSubproperty(property).isValid()) // special case - subproperty subwidget = subpropIface->subwidget(); else subwidget = w; return library()->isPropertyVisible( subwidget->metaObject()->className(), subwidget, property, multiple, isTopLevel); } void Form::addWidget(QWidget *w) { d->selected.append(w); // Reset some stuff d->lastCommand = 0; d->lastCommandGroup = 0; QByteArray classname; if (d->selected.first()->metaObject()->className() == w->metaObject()->className()) { classname = d->selected.first()->metaObject()->className(); } // show only properties shared by widget (properties chosen by factory) bool isTopLevel = isTopLevelWidget(w); for (KPropertySetIterator it(d->propertySet); it.current(); ++it) { //qDebug() << it.current(); if (!isPropertyVisible(it.current()->name(), isTopLevel, classname)) { it.current()->setVisible(false); } } if (d->selected.count() >= 2) { //second widget, update metainfo d->propertySet["this:className"].setValue("special:multiple"); d->propertySet["this:classString"].setValue( xi18n("Multiple Widgets (%1)", d->selected.count())); d->propertySet["this:iconName"].setValue(KexiIconName("multiple-objects")); //name doesn't make sense for now d->propertySet["objectName"].setValue(""); } } void Form::createPropertiesForWidget(QWidget *w) { d->propertySet.clear(); if (!objectTree()) { qWarning() << "no object tree!"; return; } ObjectTreeItem *tree = objectTree()->lookup(w->objectName()); if (!tree) return; const QHash* modifiedProperties = tree->modifiedProperties(); QHash::ConstIterator modifiedPropertiesIt; bool isTopLevel = isTopLevelWidget(w); KProperty *newProp = 0; WidgetInfo *winfo = library()->widgetInfoForClassName(w->metaObject()->className()); if (!winfo) { qWarning() << "no widget info for class" << w->metaObject()->className(); return; } //! @todo ineffective, get property names directly const QList propList( KexiUtils::propertiesForMetaObjectWithInherited(w->metaObject())); //qDebug() << "propList.count() ==" << propList.count(); QSet propNames; foreach(const QMetaProperty& mp, propList) { propNames.insert(mp.name()); } // add subproperties if available WidgetWithSubpropertiesInterface* subpropIface = dynamic_cast(w); if (subpropIface) { const QSet subproperties(subpropIface->subproperties()); foreach(const QByteArray& propName, subproperties) { propNames.insert(propName); //qDebug() << "Added subproperty: " << propName; } } // iterate over the property list, and create Property objects foreach(const QByteArray& propName, propNames) { //qDebug() << ">> " << propName; const QMetaProperty subMeta = // special case - subproperty subpropIface ? subpropIface->findMetaSubproperty(propName) : QMetaProperty(); const QMetaProperty meta = subMeta.isValid() ? subMeta : KexiUtils::findPropertyWithSuperclasses(w, propName.constData()); if (!meta.isValid()) { //qDebug() << "!meta.isValid()"; continue; } const char* propertyName = meta.name(); QWidget *subwidget = subMeta.isValid()//subpropIface ? (subpropIface ? subpropIface->subwidget() : 0) : w; WidgetInfo *subwinfo = subwidget ? library()->widgetInfoForClassName( subwidget->metaObject()->className()) : 0; // qDebug() << "$$$ " << subwidget->className(); if ( subwinfo && meta.isDesignable(subwidget) && meta.isWritable() && meta.isReadable() && !d->propertySet.contains(propertyName) ) { //! \todo add another list for property description QString desc(d->propCaption.value(meta.name())); //! \todo change i18n if (desc.isEmpty()) { //try to get property description from factory desc = library()->propertyDescForName(subwinfo, propertyName); } modifiedPropertiesIt = modifiedProperties->find(propertyName); const bool oldValueExists = modifiedPropertiesIt != modifiedProperties->constEnd(); if (meta.isEnumType()) { if (qstrcmp(propertyName, "alignment") == 0) { createAlignProperty(meta, w, subwidget); continue; } QStringList keys(KexiUtils::enumKeysForProperty(meta)); newProp = new KProperty( propertyName, d->createValueList(subwinfo, keys), // assign current or older value meta.enumerator().valueToKey( oldValueExists ? modifiedPropertiesIt.value().toInt() : subwidget->property(propertyName).toInt()), desc, desc); //now set current value, so the old one is stored as old if (oldValueExists) { newProp->setValue( meta.enumerator().valueToKey(subwidget->property(propertyName).toInt())); } } else { int realType = subwinfo->customTypeForProperty(propertyName); if (realType == KProperty::Invalid || realType == KProperty::Auto) { realType = int(meta.type()); } newProp = new KProperty( propertyName, // assign current or older value oldValueExists ? modifiedPropertiesIt.value() : subwidget->property(propertyName), desc, desc, realType ); //now set current value, so the old one is stored as old if (oldValueExists) { newProp->setValue(subwidget->property(propertyName)); } } if (!isPropertyVisible(propertyName, isTopLevel)) newProp->setVisible(false); //! @todo if (newProp->type() == KProperty::Invalid) { newProp->setType(KProperty::String); } d->propertySet.addProperty(newProp); } // update the Property.oldValue() and isModified() using the value stored in the ObjectTreeItem updatePropertyValue(tree, propertyName, meta); } const QString paletteBackgroundColorDesc(d->propCaption.value("paletteBackgroundColor")); newProp = new KProperty("paletteBackgroundColor", w->palette().color(w->backgroundRole()), paletteBackgroundColorDesc, paletteBackgroundColorDesc); const QString paletteForegroundColorDesc(d->propCaption.value("paletteForegroundColor")); d->propertySet.addProperty(newProp); newProp = new KProperty("paletteForegroundColor", w->palette().color(w->foregroundRole()), paletteForegroundColorDesc, paletteForegroundColorDesc); d->propertySet.addProperty(newProp); // name should be updated only when pressing Enter d->propertySet["objectName"].setValueSyncPolicy(KProperty::ValueSyncPolicy::FocusOut); if (winfo) { library()->setPropertyOptions(d->propertySet, *winfo, w); d->propertySet.addProperty(newProp = new KProperty("this:classString", winfo->name())); newProp->setVisible(false); d->propertySet.addProperty(newProp = new KProperty("this:iconName", winfo->iconName())); newProp->setVisible(false); } d->propertySet.addProperty(newProp = new KProperty("this:className", w->metaObject()->className())); newProp->setVisible(false); } void Form::updatePropertyValue(ObjectTreeItem *tree, const char *property, const QMetaProperty &meta) { Q_UNUSED(tree); Q_UNUSED(property); Q_UNUSED(meta); return; //! @todo ???? #if 0 const char *propertyName = meta.isValid() ? meta.name() : property; if (!d->propertySet.contains(propertyName)) return; KProperty &p = d->propertySet[propertyName]; //! \todo what about set properties, and lists properties const QHash::ConstIterator it(tree->modifiedProperties()->find(propertyName)); if (it != tree->modifiedProperties()->constEnd()) { blockSignals(true); if (meta.isValid() && meta.isEnumType()) { p.setValue(meta.enumerator().valueToKey(it.value().toInt()), false); } else { p.setValue(it.value(), false); } p.setValue(p.value(), true); blockSignals(false); } #endif } //! @todo what about 'forceReload' arg? It's not passed to updatePropertiesForSelection() now... void Form::emitSelectionChanged(QWidget *w, WidgetSelectionFlags flags) { updatePropertiesForSelection(w, flags); emit selectionChanged(w, flags); } void Form::updatePropertiesForSelection(QWidget *w, WidgetSelectionFlags flags) { if (!w) { //! @todo clearSet()? return; } + KexiMainWindowIface::global()->beginPropertyPaneUpdate(); // if our list is empty,don't use add parameter value if (d->selected.isEmpty() == 0) { flags |= ReplacePreviousSelection; } if (flags & ReplacePreviousSelection) { createPropertiesForWidget(w); w->installEventFilter(this); connect(w, SIGNAL(destroyed()), this, SLOT(widgetDestroyed())); } else { addWidget(w); } if (flags & LastSelection) { emit propertySetSwitched(); } - } + KexiMainWindowIface::global()->endPropertyPaneUpdate(); +} KPropertySet* Form::propertySet() { return &d->propertySet; } bool Form::isSnapToGridEnabled() const { return d->snapToGrid; } void Form::setSnapToGridEnabled(bool enabled) { d->snapToGrid = enabled; } void Form::createContextMenu(QWidget *w, Container *container, const QPoint& menuPos, ContextMenuTarget target) { if (!widget()) return; const bool toplevelWidgetSelected = widget() == w; const int widgetsCount = container->form()->selectedWidgets()->count(); const bool multiple = widgetsCount > 1; //set title QString n( container->form()->library()->displayName(w->metaObject()->className()) ); QIcon icon; QString titleText; if (!multiple) { if (w == container->form()->widget()) { icon = KexiIcon("form"); titleText = xi18n("%1 : Form", w->objectName()); } else { icon = QIcon::fromTheme( container->form()->library()->iconName(w->metaObject()->className())); titleText = QString(w->objectName()) + " : " + n; } } else { icon = KexiIcon("multiple-objects"); titleText = xi18n("Multiple Widgets (%1)", widgetsCount); } QMenu menu; menu.addSection(icon, titleText); QAction *a; #define PLUG_ACTION(_name, forceVisible) \ { a = d->collection->action(_name); \ if (a && (forceVisible || a->isEnabled())) { \ if (separatorNeeded) \ menu.addSeparator(); \ separatorNeeded = false; \ menu.addAction(a); \ } \ } bool separatorNeeded = false; PLUG_ACTION("edit_cut", !toplevelWidgetSelected); PLUG_ACTION("edit_copy", !toplevelWidgetSelected); PLUG_ACTION("edit_paste", true); PLUG_ACTION("edit_delete", !toplevelWidgetSelected); separatorNeeded = true; PLUG_ACTION("align_menu", !toplevelWidgetSelected); PLUG_ACTION("adjust_size_menu", !toplevelWidgetSelected); separatorNeeded = true; // We create the buddy menu QAction *noBuddyAction = 0; QLabel *buddyLabelWidget = 0; QList sortedItemNames; if (!multiple) { buddyLabelWidget = qobject_cast(w); if (buddyLabelWidget) { if (!buddyLabelWidget->text().contains("&") || buddyLabelWidget->textFormat() == Qt::RichText) { buddyLabelWidget = 0; } } } if (buddyLabelWidget) { // setup menu if (separatorNeeded) menu.addSeparator(); QMenu *sub = new QMenu(w); QWidget *buddy = buddyLabelWidget->buddy(); noBuddyAction = sub->addAction(xi18n("No Buddy")); if (!buddy) noBuddyAction->setChecked(true); sub->addSeparator(); // Add all the widgets that can have focus // 1. Sort by name QHash items; foreach (ObjectTreeItem *item, *container->form()->tabStops()) { items.insert(item->name().toLatin1(), item); } sortedItemNames = items.keys(); qSort(sortedItemNames); foreach (const QString& name, sortedItemNames) { ObjectTreeItem *item = items.value(name); QAction* action = sub->addAction( QIcon::fromTheme( container->form()->library()->iconName(item->className().toLatin1())), item->name() ); if (item->widget() == buddy) action->setChecked(true); } separatorNeeded = true; } #ifdef KFD_SIGSLOTS if (!multiple && (d->features & EnableEvents)) { if (separatorNeeded) menu.addSeparator(); // We create the signals menu QMenu *sigMenu = new QMenu(); const QList list( KexiUtils::methodsForMetaObjectWithParents(w->metaObject(), QMetaMethod::Signal, QMetaMethod::Public)); foreach(const QMetaMethod& m, list) { sigMenu->addAction(m.signature()); } QAction *eventsSubMenuAction = menu.addMenu(sigMenu); eventsSubMenuAction->setText(futureI18n("Events")); if (list.isEmpty()) eventsSubMenuAction->setEnabled(false); connect(sigMenu, SIGNAL(triggered(QAction*)), this, SLOT(menuSignalChosen(QAction*))); separatorNeeded = true; } #endif // Other items if (!multiple) { QAction* lastAction = 0; if (separatorNeeded) { lastAction = menu.addSeparator(); } const int oldIndex = menu.actions().count() - 1; container->form()->library() ->createMenuActions(w->metaObject()->className(), w, &menu, container); if (oldIndex == (menu.actions().count() - 1)) { //nothing added if (separatorNeeded) { menu.removeAction(lastAction); } } } //show the menu at the selected widget QPoint pos; switch (target) { case FormContextMenuTarget: { pos = w->mapToGlobal(menuPos); d->insertionPoint = menuPos; break; } case WidgetTreeContextMenuTarget: { pos = QCursor::pos(); d->insertionPoint = container->widget()->mapToGlobal(w->pos() + QPoint(10, 10)); // user may still want to paste break; } } //qDebug() << w << container->widget() << "menuPos=" << menuPos << "pos=" << pos; QAction *result = menu.exec(pos); if (!result) { // nothing to do } else if (noBuddyAction && buddyLabelWidget && result == noBuddyAction) { buddyLabelWidget->setBuddy(0); } else if (sortedItemNames.contains(result->text())) { ObjectTreeItem *item = objectTree()->lookup(result->text()); if (item && item->widget()) { buddyLabelWidget->setBuddy(item->widget()); } } d->insertionPoint = QPoint(); } void Form::deleteWidget() { if (!objectTree()) { return; } QWidgetList *list = selectedWidgets(); if (list->isEmpty()) { return; } if (widget() == list->first()) { //toplevel form is selected, cannot delete it return; } Command *com = new DeleteWidgetCommand(*this, *list); addCommand(com); } void Form::copyWidget() { if (!objectTree() || isFormWidgetSelected()) { return; } QWidgetList *list = selectedWidgets(); if (list->isEmpty()) { return; } QDomDocument doc; QHash containers; QHash parents; KFormDesigner::widgetsToXML(doc, containers, parents, *this, *list); KFormDesigner::copyToClipboard(doc.toString()); emitActionSignals(); // to update 'Paste' item state emitUndoActionSignals(); } bool Form::isFormWidgetSelected() const { return selectedWidget() && selectedWidget() == widget(); } void Form::cutWidget() { if (!objectTree() || isFormWidgetSelected()) { return; } QWidgetList *list = selectedWidgets(); if (list->isEmpty()) { return; } Command *com = new CutWidgetCommand(*this, *list); addCommand(com); } void Form::pasteWidget() { if (!objectTree()) { return; } const QMimeData *mimeData = QApplication::clipboard()->mimeData(); const bool mimeDataHasXmlUiFormat = mimeData->hasFormat( KFormDesigner::mimeType() ); if (!mimeDataHasXmlUiFormat && !mimeData->hasText()) { return; } QDomDocument doc; if (!doc.setContent( mimeDataHasXmlUiFormat ? QString::fromUtf8( mimeData->data(KFormDesigner::mimeType())) : mimeData->text() )) { return; } if (!doc.firstChildElement("UI").hasChildNodes()) { return; } Command *com = new PasteWidgetCommand(doc, *activeContainer(), d->insertionPoint); addCommand(com); } void Form::editTabOrder() { if (!objectTree()) { return; } QWidget *topLevel = widget()->topLevelWidget(); TabStopDialog dlg(topLevel); if (dlg.exec(this) == QDialog::Accepted) { d->propertySet.changePropertyIfExists("autoTabStops", dlg.autoTabStops()); //force set dirty setModified(true); } } void Form::editFormPixmapCollection() { if (!objectTree()) { return; } //! @todo pixmapcollection #ifdef KEXI_PIXMAP_COLLECTIONS_SUPPORT PixmapCollectionEditor dialog(pixmapCollection(), widget()->topLevelWidget()); dialog.exec(); #endif } void Form::editConnections() { #ifdef KFD_SIGSLOTS if (!(d->features & EnableConnections)) { return; } if (!objectTree()) { return; } ConnectionDialog dialog(this, widget()->topLevelWidget()); dialog.exec(); #endif } void Form::alignWidgets(WidgetAlignment alignment) { QWidgetList* selected = selectedWidgets(); if (!objectTree() || selected->count() < 2) { return; } QWidget *parentWidget = selected->first()->parentWidget(); foreach (QWidget *w, *selected) { if (w->parentWidget() != parentWidget) { //qDebug() << "alignment ==" << alignment << "widgets don't have the same parent widget"; return; } } Command *com = new AlignWidgetsCommand(*this, alignment, *selected); addCommand(com); } void Form::alignWidgetsToLeft() { alignWidgets(AlignToLeft); } void Form::alignWidgetsToRight() { alignWidgets(AlignToRight); } void Form::alignWidgetsToTop() { alignWidgets(AlignToTop); } void Form::alignWidgetsToBottom() { alignWidgets(AlignToBottom); } void Form::adjustWidgetSize() { if (!objectTree()) { return; } Command *com = new AdjustSizeCommand(*this, AdjustSizeCommand::SizeToFit, *selectedWidgets()); addCommand(com); } void Form::alignWidgetsToGrid() { if (!objectTree()) { return; } Command *com = new AlignWidgetsCommand(*this, AlignToGrid, *selectedWidgets()); addCommand(com); } void Form::adjustSizeToGrid() { if (!objectTree()) { return; } Command *com = new AdjustSizeCommand(*this, AdjustSizeCommand::SizeToGrid, *selectedWidgets()); addCommand(com); } void Form::adjustWidthToSmall() { if (!objectTree()) { return; } Command *com = new AdjustSizeCommand(*this, AdjustSizeCommand::SizeToSmallWidth, *selectedWidgets()); addCommand(com); } void Form::adjustWidthToBig() { if (!objectTree()) { return; } Command *com = new AdjustSizeCommand(*this, AdjustSizeCommand::SizeToBigWidth, *selectedWidgets()); addCommand(com); } void Form::adjustHeightToSmall() { if (!objectTree()) { return; } Command *com = new AdjustSizeCommand(*this, AdjustSizeCommand::SizeToSmallHeight, *selectedWidgets()); addCommand(com); } void Form::adjustHeightToBig() { if (!objectTree()) { return; } Command *com = new AdjustSizeCommand(*this, AdjustSizeCommand::SizeToBigHeight, *selectedWidgets()); addCommand(com); } void Form::bringWidgetToFront() { if (!objectTree()) { return; } foreach (QWidget *w, *selectedWidgets()) { w->raise(); } } void Form::sendWidgetToBack() { if (!objectTree()) { return; } foreach (QWidget *w, *selectedWidgets()) { w->lower(); } } void Form::selectAll() { if (!objectTree()) { return; } selectFormWidget(); int count = objectTree()->children()->count(); foreach (ObjectTreeItem *titem, *objectTree()->children()) { selectWidget( titem->widget(), AddToPreviousSelection | ((count > 1) ? MoreWillBeSelected : LastSelection) ); count--; } } void Form::clearWidgetContent() { if (!objectTree()) { return; } foreach (QWidget *w, *selectedWidgets()) { library()->clearWidgetContent(w->metaObject()->className(), w); } } // Alignment-related functions ///////////////////////////// void Form::createAlignProperty(const QMetaProperty& meta, QWidget *widget, QWidget *subwidget) { if (!objectTree()) return; const int alignment = subwidget->property("alignment").toInt(); const QList keys(meta.enumerator().valueToKeys(alignment).split('|')); //qDebug() << "keys:" << keys; const QStringList possibleValues(KexiUtils::enumKeysForProperty(meta)); //qDebug() << "possibleValues:" << possibleValues; ObjectTreeItem *tree = objectTree()->lookup(widget->objectName()); const bool isTopLevel = isTopLevelWidget(widget); if (possibleValues.contains("AlignHCenter")) { // Create the horizontal alignment property QString value; if (keys.contains("AlignHCenter") || keys.contains("AlignCenter")) value = "AlignHCenter"; else if (keys.contains("AlignRight")) value = "AlignRight"; else if (keys.contains("AlignLeft")) value = "AlignLeft"; else if (keys.contains("AlignJustify")) value = "AlignJustify"; else value = "AlignAuto"; QStringList list; list << "AlignAuto" << "AlignLeft" << "AlignRight" << "AlignHCenter" << "AlignJustify"; KProperty *p = new KProperty( "hAlign", d->createValueList(0, list), value, xi18nc("Translators: please keep this string short (less than 20 chars)", "Hor. Alignment"), xi18n("Horizontal Alignment")); d->propertySet.addProperty(p); if (!isPropertyVisible(p->name(), isTopLevel)) { p->setVisible(false); } updatePropertyValue(tree, "hAlign"); } if (possibleValues.contains("AlignTop")) { // Create the ver alignment property QString value; if (keys.contains("AlignTop")) value = "AlignTop"; else if (keys.contains("AlignBottom")) value = "AlignBottom"; else value = "AlignVCenter"; QStringList list; list << "AlignTop" << "AlignVCenter" << "AlignBottom"; KProperty *p = new KProperty( "vAlign", d->createValueList(0, list), value, xi18nc("Translators: please keep this string short (less than 20 chars)", "Ver. Alignment"), xi18n("Vertical Alignment")); d->propertySet.addProperty(p); if (!isPropertyVisible(p->name(), isTopLevel)) { p->setVisible(false); } updatePropertyValue(tree, "vAlign"); } if (possibleValues.contains("WordBreak")) { // Create the wordbreak property KProperty *p = new KProperty("wordbreak", QVariant((bool)(alignment & Qt::TextWordWrap)), xi18n("Word Break"), xi18n("Word Break")); d->propertySet.addProperty(p); updatePropertyValue(tree, "wordbreak"); if (!library()->isPropertyVisible( subwidget->metaObject()->className(), subwidget, p->name(), false/*multiple*/, isTopLevel)) { p->setVisible(false); } } } void Form::saveAlignProperty(const QString &property) { QStringList list; if (d->propertySet.contains("hAlign")) list.append(d->propertySet["hAlign"].value().toString()); if (d->propertySet.contains("vAlign")) list.append(d->propertySet["vAlign"].value().toString()); if (d->propertySet.contains("wordbreak") && d->propertySet["wordbreak"].value().toBool()) list.append("WordBreak"); WidgetWithSubpropertiesInterface* subpropIface = dynamic_cast(d->selected.first()); QWidget *subwidget = (subpropIface && subpropIface->subwidget()) ? subpropIface->subwidget() : (QWidget*)d->selected.first(); int count = subwidget->metaObject()->indexOfProperty("alignment"); const QMetaProperty meta( subwidget->metaObject()->property(count) ); const int valueForKeys = meta.enumerator().keysToValue(list.join("|").toLatin1()); subwidget->setProperty("alignment", valueForKeys); ObjectTreeItem *tree = objectTree()->lookup(d->selected.first()->objectName()); if (tree && d->propertySet[ property.toLatin1()].isModified()) { tree->addModifiedProperty( property.toLatin1(), d->propertySet[property.toLatin1()].oldValue()); } if (d->isUndoing) { return; } if (d->lastCommand && d->lastCommand->propertyName() == "alignment") { d->lastCommand->setValue(valueForKeys); } else { d->lastCommand = new PropertyCommand(*this, d->selected.first()->objectName().toLatin1(), subwidget->property("alignment"), valueForKeys, "alignment"); if (!addCommand(d->lastCommand, DontExecuteCommand)) { d->lastCommand = 0; } } } void Form::createPropertyCommandsInDesignMode(QWidget* widget, const QHash &propValues, Command *parentCommand, bool addToActiveForm) { if (!widget || propValues.isEmpty()) return; //is this widget selected? (if so, use property system) const bool widgetIsSelected = selectedWidget() == widget; d->slotPropertyChanged_addCommandEnabled = false; QHash::ConstIterator endIt = propValues.constEnd(); for (QHash::ConstIterator it = propValues.constBegin(); it != endIt; ++it) { if (!d->propertySet.contains(it.key())) { qWarning() << "\"" << it.key() << "\" property not found"; continue; } (void)new PropertyCommand(*this, widget->objectName().toLatin1(), widget->property(it.key()), it.value(), it.key(), parentCommand); if (widgetIsSelected) { d->propertySet.changeProperty(it.key(), it.value()); } else { WidgetWithSubpropertiesInterface* subpropIface = dynamic_cast(widget); QWidget *subwidget = (subpropIface && subpropIface->subwidget()) ? subpropIface->subwidget() : widget; if (subwidget && -1 != subwidget->metaObject()->indexOfProperty(it.key()) && subwidget->property(it.key()) != it.value()) { ObjectTreeItem *tree = objectTree()->lookup(widget->objectName()); if (tree) { tree->addModifiedProperty(it.key(), subwidget->property(it.key())); } subwidget->setProperty(it.key(), it.value()); handleWidgetPropertyChanged(widget, it.key(), it.value()); } } } d->lastCommand = 0; d->lastCommandGroup = 0; if (addToActiveForm) { addCommand(parentCommand, DontExecuteCommand); } d->slotPropertyChanged_addCommandEnabled = true; } void Form::handleWidgetPropertyChanged(QWidget *w, const QByteArray &name, const QVariant &value) { Q_UNUSED(w); if (name == "autoTabStops") { //update autoTabStops setting at KFD::Form level setAutoTabStops(value.toBool()); } else if (name == "geometry" && widget()) { //fall back to sizeInternal property.... d->propertySet.changePropertyIfExists("sizeInternal", value.toRect().size()); } } void Form::changeFont() { QWidgetList *wlist = selectedWidgets(); QWidgetList widgetsWithFontProperty; QFont font; bool oneFontSelected = true; foreach (QWidget* widget, *wlist) { if (library()->isPropertyVisible(widget->metaObject()->className(), widget, "font")) { widgetsWithFontProperty.append(widget); if (oneFontSelected) { if (widgetsWithFontProperty.count() == 1) font = widget->font(); else if (font != widget->font()) oneFontSelected = false; } } } if (widgetsWithFontProperty.isEmpty()) return; if (!oneFontSelected) //many different fonts selected: pick a font from toplevel conatiner font = widget()->font(); if (1 == widgetsWithFontProperty.count()) { //single widget's settings bool ok; font = QFontDialog::getFont(&ok, widget()); if (!ok) { return; } d->propertySet.changeProperty("font", font); return; } //multiple widgets //! @todo KEXI3 port KFontDialog::getFontDiff() #if 0 QFlags diffFlags = KFontChooser::NoFontDiffFlags; if (QDialog::Accepted != KFontDialog::getFontDiff( font, diffFlags, KFontChooser::NoDisplayFlags, widget()) || 0 == diffFlags) { return; } //update font foreach (QWidget* widget, widgetsWithFontProperty) { QFont prevFont(widget->font()); if (diffFlags & KFontChooser::FontDiffFamily) prevFont.setFamily(font.family()); if (diffFlags & KFontChooser::FontDiffStyle) { prevFont.setBold(font.bold()); prevFont.setItalic(font.italic()); } if (diffFlags & KFontChooser::FontDiffSize) { prevFont.setPointSize(font.pointSize()); } //! @todo this modification is not added to UNDO BUFFER: //! do it when KPropertySet supports multiple selections widget->setFont(prevFont); } //! @todo temporary fix for dirty flag setModified(true); #endif } void Form::setSlotPropertyChangedEnabled(bool set) { d->slotPropertyChangedEnabled = set; } void Form::createInlineEditor(const KFormDesigner::WidgetFactory::InlineEditorCreationArguments& args) { if (!args.execute) return; if (args.multiLine) { KTextEdit *textedit = new KTextEdit(args.widget->parentWidget()); textedit->setPlainText(args.text); textedit->setAlignment(args.alignment); if (qobject_cast(args.widget)) { textedit->setWordWrapMode(qobject_cast(args.widget)->wordWrapMode()); textedit->setLineWrapMode(qobject_cast(args.widget)->lineWrapMode()); } textedit->moveCursor(QTextCursor::End); textedit->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); textedit->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); //ok? textedit->setFrameShape(args.useFrame ? QFrame::StyledPanel : QFrame::NoFrame); textedit->show(); textedit->setFocus(); textedit->selectAll(); d->inlineEditor = textedit; connect(textedit, SIGNAL(textChanged()), this, SLOT(slotInlineTextChanged())); connect(args.widget, SIGNAL(destroyed()), this, SLOT(widgetDestroyed())); connect(textedit, SIGNAL(destroyed()), this, SLOT(inlineEditorDeleted())); } else { QLineEdit *editor = new QLineEdit(args.widget->parentWidget()); d->inlineEditor = editor; editor->setText(args.text); editor->setAlignment(args.alignment); editor->setFrame(args.useFrame); editor->show(); editor->setFocus(); editor->selectAll(); connect(editor, SIGNAL(textChanged(QString)), this, SLOT(changeInlineTextInternal(QString))); connect(args.widget, SIGNAL(destroyed()), this, SLOT(widgetDestroyed())); connect(editor, SIGNAL(destroyed()), this, SLOT(inlineEditorDeleted())); } d->inlineEditor->installEventFilter(this); d->inlineEditor->setFont(args.widget->font()); d->inlineEditor->setGeometry(args.geometry); // setup palette d->inlineEditor->setBackgroundRole(args.widget->backgroundRole()); QPalette pal(args.widget->palette()); QBrush baseBrush; if (args.transparentBackground) { baseBrush = QBrush(Qt::transparent); } else { baseBrush = pal.base(); QColor baseColor(baseBrush.color()); if (!args.widget->inherits("KexiCommandLinkButton")) { //! @todo HACK! any idea?? baseColor.setAlpha(120); } baseBrush.setColor(baseColor); } pal.setBrush(QPalette::Base, baseBrush); pal.setBrush(d->inlineEditor->backgroundRole(), pal.brush(args.widget->backgroundRole())); pal.setBrush(d->inlineEditor->foregroundRole(), pal.brush(args.widget->foregroundRole())); d->inlineEditor->setPalette(pal); //copy properties if available WidgetWithSubpropertiesInterface* subpropIface = dynamic_cast(args.widget); QWidget *subwidget = (subpropIface && subpropIface->subwidget()) ? subpropIface->subwidget() : args.widget; if ( -1 != d->inlineEditor->metaObject()->indexOfProperty("margin") && -1 != subwidget->metaObject()->indexOfProperty("margin")) { d->inlineEditor->setProperty("margin", subwidget->property("margin")); } ResizeHandleSet *handles = resizeHandlesForWidget(args.widget); if (handles) { handles->setEditingMode(true); handles->raise(); } ObjectTreeItem *tree = args.container->form()->objectTree()->lookup(args.widget->objectName()); if (!tree) return; tree->eventEater()->setContainer(this); d->inlineEditorContainer = args.container; d->editedWidgetClass = args.classname; d->originalInlineText = args.text; d->slotPropertyChangedEnabled = false; InlineTextEditingCommand command( // to update size of the widget *this, selectedWidget(), d->editedWidgetClass, args.text); command.execute(); d->slotPropertyChangedEnabled = true; } void Form::changeInlineTextInternal(const QString& text) { if (d->editedWidgetClass.isEmpty()) return; d->slotPropertyChangedEnabled = false; InlineTextEditingCommand *command = new InlineTextEditingCommand( *this, selectedWidget(), d->editedWidgetClass, text); addCommand(command); d->slotPropertyChangedEnabled = true; } bool Form::eventFilter(QObject *obj, QEvent *ev) { if ( (ev->type() == QEvent::Resize || ev->type() == QEvent::Move) && obj == selectedWidget() && d->inlineEditor) { // resize widget using resize handles WidgetInfo *winfo = library()->widgetInfoForClassName(obj->metaObject()->className()); if (winfo) { winfo->factory()->resizeEditor( d->inlineEditor, selectedWidget(), selectedWidget()->metaObject()->className()); } } else if ( ev->type() == QEvent::Paint && obj == selectedWidget() && d->inlineEditor && d->inlineEditorContainer) { // paint event for container edited (eg button group) return d->inlineEditorContainer->eventFilter(obj, ev); } else if ( ev->type() == QEvent::MouseButtonPress && obj == selectedWidget() && d->inlineEditor && d->inlineEditorContainer) { // click outside editor --> cancel editing resetInlineEditor(); return d->inlineEditorContainer->eventFilter(obj, ev); } if (ev->type() == QEvent::FocusOut && d->inlineEditor) { QWidget *w = d->inlineEditor; if (obj != w) return false; QWidget *focus = w->topLevelWidget()->focusWidget(); if ( focus && w != focus && !KexiUtils::findFirstChild(w, focus->objectName().toLatin1(), focus->metaObject()->className()) ) { resetInlineEditor(); } } else if (ev->type() == QEvent::KeyPress) { QWidget *w = d->inlineEditor; if (obj != w) return false; QKeyEvent *e = static_cast(ev); if ( (e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter) && e->modifiers() != Qt::AltModifier) { resetInlineEditor(); } if (e->key() == Qt::Key_Escape) { setInlineEditorText(d->originalInlineText); resetInlineEditor(); } } else if (ev->type() == QEvent::ContextMenu) { QWidget *w = d->inlineEditor; if (obj != w) return false; return true; } return false; } void Form::slotInlineTextChanged() { changeInlineTextInternal(inlineEditorText()); } QString Form::inlineEditorText() const { QWidget *ed = d->inlineEditor; if (!ed) return QString(); return qobject_cast(ed) ? qobject_cast(ed)->toPlainText() : qobject_cast(ed)->text(); } void Form::setInlineEditorText(const QString& text) { QWidget *ed = d->inlineEditor; if (!ed) return; if (qobject_cast(ed)) qobject_cast(ed)->setPlainText(text); else if (qobject_cast(ed)) qobject_cast(ed)->setText(text); else qWarning() << "Inline editor is neither KTextEdit nor QLineEdit"; } void Form::disableFilter(QWidget *w, Container *container) { Q_UNUSED(container); ObjectTreeItem *tree = objectTree()->lookup(w->objectName()); if (!tree) return; tree->eventEater()->setContainer(this); w->setFocus(); ResizeHandleSet *handles = resizeHandlesForWidget(w); if (handles) { handles->setEditingMode(true); handles->raise(); } d->inlineEditor = 0; d->inlineEditorContainer = 0; d->editedWidgetClass.clear(); if (!tree->isEnabled()) { //! @todo widget is disabled, so we re-enable it while editing } connect(w, SIGNAL(destroyed()), this, SLOT(widgetDestroyed())); } void Form::resetInlineEditor() { if (!d->inlineEditorContainer) { return; } d->inlineEditorContainer->stopInlineEditing(); QWidget *ed = d->inlineEditor; QWidget *widget = selectedWidget(); if (widget) { FormWidgetInterface* fwiface = dynamic_cast(widget); if (fwiface) fwiface->setEditingMode(false); ObjectTreeItem *tree = objectTree()->lookup(widget->objectName()); if (!tree) { qWarning() << "Cannot find tree item for widget" << widget->objectName(); return; } tree->eventEater()->setContainer(d->inlineEditorContainer); // "disable" the widget if needed if (!ed && !tree->isEnabled()) { widget->setPalette(KexiUtils::paletteForReadOnly(widget->palette())); } } if (ed) { d->slotPropertyChangedEnabled = false; InlineTextEditingCommand command( *this, selectedWidget(), d->editedWidgetClass, inlineEditorText()); command.execute(); d->slotPropertyChangedEnabled = true; } d->inlineEditor = 0; d->inlineEditorContainer = 0; if (ed) { disconnect(ed, 0, this, 0); ed->deleteLater(); } if (widget) { disconnect(widget, 0, this, 0); widget->update(); } ResizeHandleSet *handles = resizeHandlesForWidget(widget); if (handles) { handles->setEditingMode(false); } d->editedWidgetClass.clear(); } void Form::widgetDestroyed() { if (d->inlineEditor) { d->inlineEditor->deleteLater(); d->inlineEditor = 0; } ResizeHandleSet *handles = resizeHandlesForWidget(static_cast(sender())); if (handles) { handles->setEditingMode(false); } d->inlineEditorContainer = 0; d->editedWidgetClass.clear(); } void Form::inlineEditorDeleted() { ResizeHandleSet *handles = resizeHandlesForWidget(static_cast(sender())); if (handles) { handles->setEditingMode(false); } d->inlineEditor = 0; d->inlineEditorContainer = 0; d->editedWidgetClass.clear(); } QByteArray Form::editedWidgetClass() const { return d->editedWidgetClass; } diff --git a/src/kexiutils/KexiFadeWidgetEffect.cpp b/src/kexiutils/KexiFadeWidgetEffect.cpp index 7e792f93c..1d1fc0799 100644 --- a/src/kexiutils/KexiFadeWidgetEffect.cpp +++ b/src/kexiutils/KexiFadeWidgetEffect.cpp @@ -1,151 +1,159 @@ /* This file is part of the KDE project Copyright (C) 2008 Matthias Kretz Copyright (C) 2008 Rafael Fernández López 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) version 3. 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 "KexiFadeWidgetEffect.h" #include "KexiFadeWidgetEffect_p.h" -#include +#include #include #include #include KexiFadeWidgetEffectPrivate::KexiFadeWidgetEffectPrivate(QWidget *_destWidget) : destWidget(_destWidget), disabled(false) { } // Code from KFileItemDelegate (Author: Frederik Höglund) // Fast transitions. Read: // http://techbase.kde.org/Development/Tutorials/Graphics/Performance // for further information on why not use setOpacity. QPixmap KexiFadeWidgetEffectPrivate::transition(const QPixmap &from, const QPixmap &to, qreal amount) const { const int value = int(0xff * amount); if (value == 0) { return from; } if (value == 1) { return to; } QColor color; color.setAlphaF(amount); // If the native paint engine supports Porter/Duff compositing and CompositionMode_Plus if (from.paintEngine()->hasFeature(QPaintEngine::PorterDuff) && from.paintEngine()->hasFeature(QPaintEngine::BlendModes)) { QPixmap under = from; QPixmap over = to; QPainter p; p.begin(&over); p.setCompositionMode(QPainter::CompositionMode_DestinationIn); p.fillRect(over.rect(), color); p.end(); p.begin(&under); p.setCompositionMode(QPainter::CompositionMode_DestinationOut); p.fillRect(under.rect(), color); p.setCompositionMode(QPainter::CompositionMode_Plus); p.drawPixmap(0, 0, over); p.end(); return under; } else { // Fall back to using QRasterPaintEngine to do the transition. QImage under = from.toImage(); QImage over = to.toImage(); QPainter p; p.begin(&over); p.setCompositionMode(QPainter::CompositionMode_DestinationIn); p.fillRect(over.rect(), color); p.end(); p.begin(&under); p.setCompositionMode(QPainter::CompositionMode_DestinationOut); p.fillRect(under.rect(), color); p.setCompositionMode(QPainter::CompositionMode_Plus); p.drawImage(0, 0, over); p.end(); return QPixmap::fromImage(under); } } KexiFadeWidgetEffect::KexiFadeWidgetEffect(QWidget *destWidget, int defaultDuration) - : QWidget(destWidget ? destWidget->parentWidget() : 0), + : QWidget(destWidget), d(new KexiFadeWidgetEffectPrivate(destWidget)) { d->defaultDuration = defaultDuration; Q_ASSERT(destWidget && destWidget->parentWidget()); if (!destWidget || !destWidget->parentWidget() || !destWidget->isVisible() || !style()->styleHint(QStyle::SH_Widget_Animate, 0, this)) { d->disabled = true; hide(); return; } + destWidget->updateGeometry(); + destWidget->repaint(); setGeometry(QRect(destWidget->mapTo(parentWidget(), QPoint(0, 0)), destWidget->size())); d->oldPixmap = destWidget->grab(); d->timeLine.setFrameRange(0, 255); d->timeLine.setCurveShape(QTimeLine::EaseOutCurve); connect(&d->timeLine, SIGNAL(finished()), SLOT(finished())); connect(&d->timeLine, SIGNAL(frameChanged(int)), SLOT(repaint())); show(); } KexiFadeWidgetEffect::~KexiFadeWidgetEffect() { delete d; } void KexiFadeWidgetEffect::finished() { d->destWidget->setUpdatesEnabled(false); hide(); deleteLater(); d->destWidget->setUpdatesEnabled(true); } void KexiFadeWidgetEffect::start(int duration) { if (d->disabled) { deleteLater(); return; } + d->destWidget->updateGeometry(); + d->destWidget->update(); + qApp->processEvents(); + hide(); d->newPixmap = d->destWidget->grab(); d->timeLine.setDuration(duration); + show(); + raise(); d->timeLine.start(); } void KexiFadeWidgetEffect::start() { start(d->defaultDuration); } void KexiFadeWidgetEffect::paintEvent(QPaintEvent *) { QPainter p(this); p.drawPixmap(rect(), d->transition(d->oldPixmap, d->newPixmap, d->timeLine.currentValue())); p.end(); } diff --git a/src/main/KexiMainWindow.cpp b/src/main/KexiMainWindow.cpp index 00c1054b8..68dfdb22d 100644 --- a/src/main/KexiMainWindow.cpp +++ b/src/main/KexiMainWindow.cpp @@ -1,4496 +1,4513 @@ /* 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 "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(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); + d->objectViewWidget->propertyPane()->updateInfoLabelForPropertySet( + d->propertySet); +} + +void KexiMainWindow::beginPropertyPaneUpdate() +{ + if (d->propertyPaneAnimation) { + d->propertyPaneAnimation->hide(); + d->propertyPaneAnimation->deleteLater(); + } + d->propertyPaneAnimation = new KexiFadeWidgetEffect(d->objectViewWidget->propertyPane()); +} + +void KexiMainWindow::endPropertyPaneUpdate() +{ + if (d->propertyPaneAnimation) { + d->objectViewWidget->propertyPane()->repaint(); + d->propertyPaneAnimation->start(150); + } } 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/main/KexiMainWindow.h b/src/main/KexiMainWindow.h index 5415d05a6..101565dd2 100644 --- a/src/main/KexiMainWindow.h +++ b/src/main/KexiMainWindow.h @@ -1,651 +1,655 @@ /* 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. */ #ifndef KEXIMAINWINDOW_H #define KEXIMAINWINDOW_H //#define KEXI_IMPL_WARNINGS #include "keximain_export.h" #include #include #include #include #include class QPaintEvent; class KDbObject; class KDbConnectionData; class KexiProjectData; class KexiObjectViewWidget; namespace KexiPart { class Info; class Part; } /** * @short Kexi's main window implementation */ class KEXIMAIN_EXPORT KexiMainWindow : public QMainWindow, public KexiMainWindowIface, public KexiGUIMessageHandler { Q_OBJECT public: /*! Creates an empty mainwindow. */ explicit KexiMainWindow(QWidget *parent = 0); virtual ~KexiMainWindow(); virtual KActionCollection* actionCollection() const; //! @todo virtual QWidget* focusWidget() const; virtual QWidget* focusWidget() const { return QMainWindow::focusWidget(); } /*! Used by the main Kexi's routine. Creates a new Kexi main window. For Kexi applications @a arguments are equal to CoreApplication::arguments() but test applications alter the list. If @a componentName is provided, it is assigned to application's KAboutData::componentName. It's not used by Kexi itself but is useful for test application that are based on KexiMainWindow. @a extraOptions can be supplied to extend the list of supported options. @note Extra options must not override Kexi's built-in options. @return 0 on success (the result can be used as a result of main()) and other value on error */ static int create(const QStringList &arguments, const QString &componentName = QString(), const QList &extraOptions = QList()); //! Project data of currently opened project or NULL if no project here yet. virtual KexiProject *project(); /*! Registers window \a window for watching and adds it to the main window's stack. */ virtual void registerChild(KexiWindow *window); /*! Activates a window by it's document identifier. \return false if doc couldn't be raised or isn't opened. */ bool activateWindow(int id); /*! Like above, using \a window passed explicitly. Above method just calls this one. */ bool activateWindow(KexiWindow& window); /*! Performs startup actions. \return false if application should exit immediately with an error status. */ tristate startup(); /*! \return true if the application window is in the User Mode. */ virtual bool userMode() const; //! @return current global mode Kexi::GlobalViewMode currentMode() const Q_DECL_OVERRIDE; /*! \return true if opening of item \a item in \a viewMode mode is allowed. userMode() is taken into account as well as KexiPart::PartInfo::supportedUserViewModes() for \a item. */ bool openingAllowed(KexiPart::Item* item, Kexi::ViewMode viewMode, QString* errorMessage = 0); /*! Implemented for KexiMainWindow. */ virtual QList allActions() const; /*! \return currently active window or 0 if there is no active window. Implemented for KexiWindow. */ virtual KexiWindow* currentWindow() const; /*! @return window for tab @a tabIndex or 0 if there is no such tab. */ KexiWindow* windowForTab(int tabIndex) const; //! @todo temporary solution before the tabbed toolbar framework emerges // see KexiMainWindowIface virtual void appendWidgetToToolbar(const QString& name, QWidget* widget); //! @todo temporary solution before the tabbed toolbar framework emerges // see KexiMainWindowIface virtual void setWidgetVisibleInToolbar(QWidget* widget, bool visible); //! @todo replace with the final Actions API // see KexiMainWindowIface virtual void addToolBarAction(const QString& toolBarName, QAction *action); // see KexiMainWindowIface virtual KToolBar *toolBar(const QString& name) const; //! Shows design tab @a tabName again and activates it as current if it was hidden //! before for the same object. void restoreDesignTabAndActivateIfNeeded(const QString &tabName); //! Shows design tab again when switching between objects or views. void restoreDesignTabIfNeeded(const QString &pluginId, Kexi::ViewMode viewMode, int previousItemId); //! Sets currently visible design tab when switching to design view, according to object type opened. virtual void activateDesignTabIfNeeded(const QString &pluginId, Kexi::ViewMode viewMode); //! Hides design tabs when they are closed (depending on ID @a pluginId). //! If @a pluginId is empty, all tabs get hidden. virtual void hideDesignTab(int itemId, const QString &pluginId = QString()); /*! Implemented for KexiMainWindow */ virtual KexiUserFeedbackAgent* userFeedbackAgent() const; /*! Implemented for KexiMainWindow */ virtual KexiMigrateManagerInterface* migrateManager(); public Q_SLOTS: /*! Implemented for KexiMainWindow */ virtual tristate closeWindow(KexiWindow *window); /*! Closes the current window. */ tristate closeCurrentWindow(); /*! Closes window inside tab @a tabIndex. */ tristate closeWindowForTab(int tabIndex); /*! Closes all windows. */ tristate closeAllWindows(); /*! Internal implementation. If \a doNotSaveChanges is true, messages asking for saving the will be skipped and the changes will be dropped. This should not be usually used, maybe except for test suites (see kexi/tests/altertable/ directory). */ tristate closeWindow(KexiWindow *window, bool layoutTaskBar, bool doNotSaveChanges = false); /*! Activates next tab. */ void activateNextTab(); /*! Activates previous tab. */ void activatePreviousTab(); //! @todo move part of this to KexiProject, because currently KexiProject::openObject() allows multiple opens! /*! Opens object pointed by \a item in a view \a viewMode. \a staticObjectArgs can be passed for static object (only works when part for this item is of type KexiPart::StaticPart). \a openingCancelled is set to true if opening has been cancelled. \a errorMessage, if not 0, points to a string that can be set to error message if one encountered. @c nullptr can be returned if the KexiWindow object for @a item is not yet fully constructed but openObject() has been quickly called again for the same @a item. This can happen if user clicked multiple times on the same Project navigator's item. In this case @a openingCancelled is not set; the caller should not display error message but the opening should be silently abandoned. */ virtual KexiWindow* openObject(KexiPart::Item *item, Kexi::ViewMode viewMode, bool *openingCancelled, QMap* staticObjectArgs = 0, QString* errorMessage = 0); //! For convenience virtual KexiWindow* openObject(const QString& pluginId, const QString& name, Kexi::ViewMode viewMode, bool *openingCancelled, QMap* staticObjectArgs = 0); /*! Closes the object for \a item. \return true on success (closing can be dealyed though), false on failure and cancelled if the object has "opening" job assigned. */ virtual tristate closeObject(KexiPart::Item* item); /*! Implemented for KexiMainWindow */ virtual tristate saveObject(KexiWindow *window, const QString& messageWhenAskingForName = QString(), SaveObjectOptions options = 0); /*! Implemented for KexiMainWindowIface. */ virtual KexiWindow *openedWindowFor(int identifier); virtual KexiWindow *openedWindowFor(const KexiPart::Item *item); /*! Implemented for KexiMainWindowIface */ virtual QList currentParametersForQuery(int queryId) const; /*! Implemented for KexiMainWindowIface. */ virtual KDbQuerySchema *unsavedQuery(int queryId); /*! Implemented for KexiMainWindow */ virtual tristate getNewObjectInfo(KexiPart::Item *partItem, const QString &originalName, KexiPart::Part *part, bool allowOverwriting, bool *overwriteNeeded, const QString& messageWhenAskingForName = QString()); /*! Implemented for KexiMainWindow */ virtual void highlightObject(const QString& pluginId, const QString& name); /*! Opens project pointed by \a projectData. Application state (e.g. actions) is updated. \a projectData is copied into a project structures. \return true on success */ tristate openProject(const KexiProjectData& projectData); /*! Helper. Opens project pointed by \a aFileName. If \a aFileName is empty, a connection shortcut (.kexic file name) is obtained from global connection set using \a cdata (if present). In this case: * If connection shortcut has been found and \a dbName (a server database name) is provided 'kexi --skip-dialog --connection file.kexic dbName' is executed (or the project is opened directly if there's no project opened in the current Kexi main window. * If connection shortcut has been found and \a dbName is not provided, 'kexi --skip-dialog file.kexic' is executed (or the connection is opened directly if there's no porject opened in the current Kexi main window. */ tristate openProject(const QString& aFileName, KDbConnectionData *cdata, const QString& dbName = QString(), const KexiProjectData::AutoOpenObjects& autoopenObjects = KexiProjectData::AutoOpenObjects()); /*! Helper. Opens project pointed by \a aFileName. Like above but \a fileNameForConnectionData can be passed instead of a pointer to connection data itself. \return false if \a fileNameForConnectionData is not empty but there is no such connection in Kexi::connset() for this filename. \a fileNameForConnectionData can be empty. */ tristate openProject(const QString& aFileName, const QString& fileNameForConnectionData, const QString& dbName = QString()); /*! Helper. Opens project pointed by \a aFileName. */ tristate openProject(const QString& aFileName); /*! Opens project referenced by @a data. If @a shortcutPath is a empty .kexis filename and there is another project opened, a new instance of Kexi is started with the .kexis file as argument. Value pointed by @a opened is set to true if the database has been opened successfully. @return true on successful opening, cancelled if the operation was cancelled and false on failure.*/ tristate openProject(const KexiProjectData& data, const QString& shortcutPath, bool *opened); /*! Creates a new project usign template pointed by \a projectData. Application state (e.g. actions) is updated. New project data is copied into a project structures. \return true on success */ tristate createProjectFromTemplate(const KexiProjectData& projectData); /*! Closes current project, \return true on success. Application state (e.g. actions) is updated. \return true on success. If closing was cancelled by user, cancelled is returned. */ tristate closeProject(); //! Shows "print" dialog for \a item. //! \return true on success. virtual tristate printItem(KexiPart::Item* item); //! Shows "print preview" window. //! \return true on success. virtual tristate printPreviewForItem(KexiPart::Item* item); //! Shows "page setup" window for \a item. //! \return true on success and cancelled when the action was cancelled. virtual tristate showPageSetupForItem(KexiPart::Item* item); /*! Executes custom action for the main window, usually provided by a plugin. Also used by KexiFormEventAction. */ virtual tristate executeCustomActionForObject(KexiPart::Item* item, const QString& actionName); /*! Add searchable model to the main window. This extends search to a new area. One example is Project Navigator. @see KexiMainWindowIface */ virtual void addSearchableModel(KexiSearchableModel *model); //! Shows design tab when switching between objects or views. Depends on current window and view mode. void showDesignTabIfNeeded(int previousItemId); void toggleFullScreen(bool isFullScreen); /*! Implemented for KexiMainWindowIface. Sets reasonable dialog size based on main window size, that is 80% of its size. */ virtual void setReasonableDialogSize(QDialog *dialog); Q_SIGNALS: //! Emitted to make sure the project can be close. //! Connect a slot here and set \a cancel to true to cancel the closing. void acceptProjectClosingRequested(bool *cancel); //! Emitted before closing the project (and destroying all it's data members). //! You can do you cleanup of your structures here. void beforeProjectClosing(); //! Emitted after closing the project. void projectClosed(); //! Emitted after opening a project, even after slotAutoOpenObjectsLater(). void projectOpened(); protected: /*! Setups main menu with sub-menus */ void setupMainMenu(); /*! Setups main widget */ void setupMainWidget(); /*! Creates a view widget for object view, used in edit and design global view mode if it's not yet created. This includes project navigator and property editor. */ void setupObjectView(); //! Setups object view and assigns project to the project navigator. void updateObjectView(); void setupContextHelp(); void setupPropertyEditor(); void setupMainMenuActionShortcut(QAction * action); /*! Creates standard actions like new, open, save ... */ void setupActions(); /*! Creates user project-wide actions */ void setupUserActions(); /*! Sets up the window from user settings. */ void restoreSettings(); /*! Writes user settings back. */ void storeSettings(); /*! Invalidates availability of all actions for current application state. */ void invalidateActions(); /*! Invalidates action availability for current application state. These actions are dependent on active window. */ virtual void invalidateSharedActions(QObject *o); /*! Invalidates action availability for current application state. These actions only depend on project availability, not on curently active window. */ void invalidateProjectWideActions(); /*! Shows dialog for creating new project, and creates one. The dialog is not shown if option for automatic creation is checked or KexiStartupHandler::global()->projectData() was provided from command line. \a cancelled is set to true if creation has been cancelled (e.g. user answered no when asked for database overwriting, etc. \return true if database was created, false on error or when cancel was pressed */ void createNewProject(); /*! Shows dialog for creating new blank project, and return a data describing it. If the dialog was cancelled, \a cancelled will be set to true (false otherwise). \a shortcutFileName, if not 0, will be set to a shortcut filename (in case when server database project was selected). */ KexiProjectData* createBlankProjectData(bool *cancelled, bool confirmOverwrites = true, QString *shortcutFileName = 0); /*! Reimplemented from KexiSharedActionHost: accepts only KexiDockBase and KexiWindow subclasses. */ virtual bool acceptsSharedActions(QObject *w); /*! Performs lookup like in KexiSharedActionHost::focusWindow() but starting from \a w instead of a widget returned by QWidget::focusWidget(). \return NULL if no widget matches acceptsSharedActions() or if \a w is NULL. */ virtual QWidget* findWindow(QWidget *w); /*! Updates application's caption - also shows project's name. */ void updateAppCaption(); virtual void closeEvent(QCloseEvent *ev); void resizeEvent(QResizeEvent *e) Q_DECL_OVERRIDE; //! Called by KexiMainWidget::queryClose() bool queryClose(); /*! Implemented for KexiMainWindowIface. Switches \a window to view \a mode. Activates the window if it is not the current window. */ virtual tristate switchToViewMode(KexiWindow& window, Kexi::ViewMode viewMode); /*! Helper. Updates setup of property panel's tabs. Used when switching from \a prevWindow window to a current window. */ void updateCustomPropertyPanelTabs(KexiWindow *prevWindow, Kexi::ViewMode prevViewMode); /*! @overload void updateCustomPropertyPanelTabs(KexiWindow *prevWindow, Kexi::ViewMode prevViewMode) */ void updateCustomPropertyPanelTabs(KexiPart::Part *prevWindowPart, Kexi::ViewMode prevViewMode, KexiPart::Part *curWindowPart, Kexi::ViewMode curViewMode); /*! Used in openProject when running another Kexi process is required. */ tristate openProjectInExternalKexiInstance(const QString& aFileName, KDbConnectionData *cdata, const QString& dbName); /*! Used in openProject when running another Kexi process is required. */ tristate openProjectInExternalKexiInstance(const QString& aFileName, const QString& fileNameForConnectionData, const QString& dbName); /*! Updates info label of the property editor by reusing properties provided by the current property set. - Read documentation of KexiPropertyEditorView class for information about accepted properties. + Read documentation of KexiPropertyPaneWidget class for information about accepted properties. If the current property is 0 and @a textToDisplayForNullSet string is not empty, this string is displayed (without icon or any other additional part). If the current property is 0 and @a textToDisplayForNullSet string is empty, the info label widget becomes hidden. Implemented for KexiMainWindow. @see KexiPropertyPaneViewBase::updateInfoLabelForPropertySet() */ - virtual void updatePropertyEditorInfoLabel(const QString& textToDisplayForNullSet); + void updatePropertyEditorInfoLabel(const QString& textToDisplayForNullSet = QString()) Q_DECL_OVERRIDE; //! Activates design tab when switching to design view, according to \a pluginId. void activateDesignTab(const QString &pluginId); //! Sets current global mode void setCurrentMode(Kexi::GlobalViewMode mode) Q_DECL_OVERRIDE; + void beginPropertyPaneUpdate() Q_DECL_OVERRIDE; + + void endPropertyPaneUpdate() Q_DECL_OVERRIDE; + protected Q_SLOTS: tristate createNewProject(const KexiProjectData &projectData); /*! Called once after timeout (after ctors are executed). */ void slotAutoOpenObjectsLater(); /*! Called if a window (tab) changes from \a prevWindow to \a window. Both parameters can be 0. */ void activeWindowChanged(KexiWindow *window, KexiWindow *prevWindow); void slotPartLoaded(KexiPart::Part* p); //! Internal - creates and initializes Kexi project object based on @a data. KexiProject* createKexiProjectObject(const KexiProjectData &data); /*! Handles event when user double clicked (or single -depending on settings) or pressed Return key on the part item in the navigator. This differs from openObject() signal in that if the object is already opened in view mode other than \a viewMode, the mode is not changed. \sa KexiProjectNavigator::openOrActivateItem() */ KexiWindow* openObjectFromNavigator(KexiPart::Item* item, Kexi::ViewMode viewMode, bool *openingCancelled); //! For convenience KexiWindow* openObjectFromNavigator(KexiPart::Item* item, Kexi::ViewMode viewMode); /*! Creates new object of type defined by \a info part info. \a openingCancelled is set to true if opening has been cancelled. \return true on success. */ virtual bool newObject(KexiPart::Info *info, bool *openingCancelled); //! For convenience bool newObject(KexiPart::Info *info) { bool openingCancelled; return newObject(info, &openingCancelled); } //! For convenience KexiWindow* openObject(KexiPart::Item *item, Kexi::ViewMode viewMode, QMap* staticObjectArgs = 0) { bool openingCancelled; return openObject(item, viewMode, &openingCancelled, staticObjectArgs); } /*! Removes object pointed by \a item from current project. Asks for confirmation. \return true on success or cancelled if removing was cancelled (only possible if \a dontAsk is false). */ tristate removeObject(KexiPart::Item *item, bool dontAsk = false); /*! Renames object pointed by \a item to a new name \a _newName. Sets \a success to false on failure. Used as a slot connected to KexiProjectNavigator::renameItem() signal. */ void renameObject(KexiPart::Item *item, const QString& _newName, bool *succes); /*! Changes caption of object pointed by \a item to \a _newCaption. Sets \a success to false on failure. Used as a slot connected to KexiProjectNavigator::changeItemCaption() signal. */ void setObjectCaption(KexiPart::Item *item, const QString& _newCaption, bool *succes); /*! Reaction for object rename (signalled by KexiProject). If this item has opened window, it's caption is updated, and also optionally application's caption. */ virtual void slotObjectRenamed(const KexiPart::Item &item, const QString& oldName); //! @todo virtual void fillWindowMenu(); void invalidateSharedActions(); void invalidateSharedActionsLater(); //! Updates the statusbar, navigator and "Insert->....." actions, dependent on read-only state. //! Only called on project opening and closing. void updateReadOnlyState(); void slotProjectWelcome(); void slotProjectNew(); void slotProjectOpen(); void slotProjectSave(); void slotProjectSaveAs(); void slotProjectPrint(); void slotProjectPrintPreview(); void slotProjectPageSetup(); void slotProjectProperties(); void slotProjectImportExportOrSend(); void slotProjectClose(); void slotProjectRelations(); void slotProjectImportDataTable(); void slotProjectExportDataTable(); void slotProjectQuit(); void slotEditPasteSpecialDataTable(); void slotEditCopySpecialDataTable(); void slotEditFind(); void slotEditFind(bool next); //!< helper void slotEditFindNext(); void slotEditFindPrevious(); void slotEditReplace(bool all); //!< helper void slotEditReplace(); void slotEditReplaceNext(); void slotEditReplaceAll(); void slotActivateNavigator(); void slotActivateMainArea(); void slotActivatePropertyPane(); void slotToggleProjectNavigator(); void slotTogglePropertyEditor(); void slotViewDataMode(); void slotViewDesignMode(); void slotViewTextMode(); //!< sometimes called "SQL View" void slotSettings(); void slotConfigureKeys(); void slotConfigureToolbars(); void slotToolsImportProject(); void slotToolsImportTables(); void slotToolsCompactDatabase(); void slotReportBug(); void slotTipOfTheDay(); void slotImportFile(); void slotImportServer(); //! There are performed all actions that need to be done immediately after ctro (using timer) void slotLastActions(); virtual void acceptPropertySetEditing(); virtual void propertySetSwitched(KexiWindow *window, bool force = false, bool preservePrevSelection = true, bool sortedProperties = false, const QByteArray& propertyToSelect = QByteArray()); /*! Handles changes in 'dirty' flag for windows. */ void slotDirtyFlagChanged(KexiWindow* window); /*! Shows Project Migration Wizard. \return true on successful migration, cancelled on cancellation, and false on failure. If \a mimeType and \a databaseName are not empty, the wizard will only ask about parameters of destination project and skip pages related to source project. */ tristate showProjectMigrationWizard(const QString& mimeType, const QString& databaseName); /*! @overload tristate showProjectMigrationWizard(const QString& mimeType, const QString& databaseName) @a cdata is used preselect a server-based connection. */ tristate showProjectMigrationWizard(const QString& mimeType, const QString& databaseName, const KDbConnectionData &cdata); //! Receives "selectionChanged()" signal from navigator to update some actions. void slotPartItemSelectedInNavigator(KexiPart::Item* item); /*! Receives the "executeItem" signal from navigator to perform "execute" action on \a item. \return true on success */ tristate executeItem(KexiPart::Item* item); //! Shows "export as data table" dialog for \a item. tristate exportItemAsDataTable(KexiPart::Item* item); //! Shows "copy special as data table" dialog for \a item. tristate copyItemToClipboardAsDataTable(KexiPart::Item* item); bool checkForDirtyFlagOnExport(KexiPart::Item *item, QMap *args); /*! Shows a question message * "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?" \return true if the user picked the first option, * false if the user picked the second option and cancelled value if user cancelled the export. */ tristate askOnExportingChangedQuery(KexiPart::Item* item) const; //! Shows "print" dialog for \a item. //! \return true on success. bool printItem(KexiPart::Item* item, const QString& titleText); //! Shows "print" dialog for \a item and \a settings. //! \return true on success. //! @todo reenable when ported bool printItem(KexiPart::Item* item, const KexiSimplePrintingSettings& settings, const QString& titleText = QString()); /*! Shows "print preview" window for \a item. The preview windoe is cached, so \a reload == true is sometimes needed if data or print settings have changed in the meantime. \return true on success. */ bool printPreviewForItem(KexiPart::Item* item, const QString& titleText, bool reload); //! Shows "print preview" window. //! \return true on success. //! @todo reenable when ported bool printPreviewForItem(KexiPart::Item* item, const KexiSimplePrintingSettings& settings, const QString& titleText = QString(), bool reload = false); /*! Implemented for KexiMainWindow. Helper for printItem() and printPreviewForItem(). Also used by KexiFormEventAction. \return true on success and cancelled when the action was cancelled. */ //! @todo reenable when ported tristate printActionForItem(KexiPart::Item* item, PrintActionType action); //void slotSetProjectNavigatorVisible(bool set); //void slotSetPropertyEditorVisible(bool set); //void slotProjectNavigatorVisibilityChanged(bool visible); //void slotPropertyEditorVisibilityChanged(bool visible); //void slotMultiTabBarTabClicked(int id); void slotCurrentModeChanged(); void slotProjectNavigatorVisibilityChanged(bool visible); private: //! Adds action @a name with text @a text and optional shortcut @a shortcut. //! This is helper method containing workaround for Kexi //! until QAction::setShortcut() works again. //! @return created action QAction * addAction(const char *name, const QString &text, const char *shortcut = 0); //! Like @ref addAction(const char *, const QString&, const char *) but also adds //! icon @a icon. //! @return created action QAction * addAction(const char *name, const QIcon &icon, const QString& text, const char *shortcut = 0); class MessageHandler; class Private; Private * const d; friend class KexiWindow; friend class KexiObjectViewWidget; }; #endif diff --git a/src/main/KexiMainWindow_p.h b/src/main/KexiMainWindow_p.h index 1da2adee0..0a88fc23f 100644 --- a/src/main/KexiMainWindow_p.h +++ b/src/main/KexiMainWindow_p.h @@ -1,618 +1,620 @@ /* 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. */ #ifndef KEXIMAINWINDOW_P_H #define KEXIMAINWINDOW_P_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "KexiMainWindow.h" #include "KexiSearchLineEdit.h" #include "KexiUserFeedbackAgent.h" #include "KexiMenuWidget.h" #include "kexifinddialog.h" #include "KexiStartup.h" #include "KexiGlobalViewModeSelector.h" #include #include #include #include #include #include +#include #define KEXI_NO_PROCESS_EVENTS #ifdef KEXI_NO_PROCESS_EVENTS # define KEXI_NO_PENDING_DIALOGS #endif #define PROJECT_NAVIGATOR_TABBAR_ID 0 #define PROPERTY_EDITOR_TABBAR_ID 1 #define KEXITABBEDTOOLBAR_SPACER_TAB_INDEX 1 class QPainter; class KexiProjectNavigator; -class KexiPropertyEditorView; +class KPropertyEditorView; //! @short Main application's tabbed toolbar class KexiTabbedToolBar : public QTabWidget { Q_OBJECT public: explicit KexiTabbedToolBar(QWidget *parent); virtual ~KexiTabbedToolBar(); KToolBar *createWidgetToolBar() const; KToolBar *toolBar(const QString& name) const; void appendWidgetToToolbar(const QString& name, QWidget* widget); void setWidgetVisibleInToolbar(QWidget* widget, bool visible); //! @todo replace with the final Actions API void addAction(const QString& toolBarName, QAction *action); bool mainMenuVisible() const; QRect tabRect(int index) const; void addSearchableModel(KexiSearchableModel *model); KToolBar *createToolBar(const char *name, const QString& caption); void setCurrentTab(const QString& name); //! Sets current tab to @a index, counting from first visible (non-Kexi) tab. //! In non-user mode, the first visible tab is "create" tab. void setCurrentTab(int index); void hideTab(const QString& name); void showTab(const QString& name); bool isTabVisible(const QString& name) const; bool isRolledUp(); public Q_SLOTS: void setMainMenuContent(QWidget *w); void selectMainMenuItem(const char *actionName); void showMainMenu(const char* actionName = 0); void hideMainMenu(); void toggleMainMenu(); void activateSearchLineEdit(); void toggleRollDown(); protected: virtual void mouseMoveEvent(QMouseEvent* event); virtual void leaveEvent(QEvent* event); virtual bool eventFilter(QObject* watched, QEvent* event); protected Q_SLOTS: void slotCurrentChanged(int index); void slotDelayedTabRaise(); void slotSettingsChanged(int category); //! Used for delayed loading of the "create" toolbar. Called only once. void setupCreateWidgetToolbar(); void slotTabDoubleClicked(int index); void tabBarAnimationFinished(); private: void addAction(KToolBar *tbar, const char* actionName); void addSeparatorAndAction(KToolBar *tbar, const char* actionName); class Private; Private * const d; }; class EmptyMenuContentWidget : public QWidget { Q_OBJECT public: explicit EmptyMenuContentWidget(QWidget* parent = 0); void alterBackground(); virtual void changeEvent(QEvent *e); }; //! @todo KEXI3 is KexiMenuWidgetStyle needed? #if 0 //! A style proxy for KexiMenuWidget class KexiMenuWidgetStyle : public KexiUtils::StyleProxy { public: explicit KexiMenuWidgetStyle(QStyle *style, QObject *parent = 0); virtual ~KexiMenuWidgetStyle(); virtual void drawControl(ControlElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget = 0) const; }; #endif //! Main menu class KexiMainMenu : public QWidget { Q_OBJECT public: explicit KexiMainMenu(KexiTabbedToolBar *toolBar, QWidget* parent = 0); ~KexiMainMenu(); virtual bool eventFilter(QObject * watched, QEvent* event); void setContent(QWidget *contentWidget); const QWidget *contentWidget() const; void setPersistentlySelectedAction(KexiMenuWidgetAction* action, bool set); /* void setActiveAction(QAction* action = 0);*/ void selectFirstItem(); tristate showProjectMigrationWizard( const QString& mimeType, const QString& databaseName, const KDbConnectionData *cdata); Q_SIGNALS: void contentAreaPressed(); void hideContentsRequested(); protected Q_SLOTS: //void contentWidgetDestroyed(); protected: virtual void showEvent(QShowEvent * event); private: QPointer m_menuWidget; KexiTabbedToolBar* m_toolBar; bool m_initialized; EmptyMenuContentWidget *m_content; QStackedLayout *m_contentLayout = nullptr; QPointer m_contentWidget; QVBoxLayout* m_mainContentLayout = nullptr; QPointer m_persistentlySelectedAction; bool m_selectFirstItem; }; class KexiTabbedToolBarTabBar; //! @internal class Q_DECL_HIDDEN KexiTabbedToolBar::Private : public QObject { Q_OBJECT public: explicit Private(KexiTabbedToolBar *t); KToolBar *createToolBar(const char *name, const QString& caption); int tabIndex; public Q_SLOTS: void showMainMenu(const char* actionName = 0); void hideMainMenu(); void hideContentsOrMainMenu(); void toggleMainMenu(); void updateMainMenuGeometry(); public: KexiTabbedToolBarTabBar *customTabBar; QPointer mainMenu; KexiTabbedToolBar *q; KActionCollection *ac; int createId; KToolBar *createWidgetToolBar; #ifdef KEXI_AUTORISE_TABBED_TOOLBAR //! Used for delayed tab raising int tabToRaise; //! Used for delayed tab raising QTimer tabRaiseTimer; #endif //! Toolbars for name QHash toolbarsForName; QHash toolbarsIndexForName; QHash toolbarsCaptionForName; QVector toolbarsVisibleForIndex; QHash extraActions; bool rolledUp; QPropertyAnimation tabBarAnimation; QGraphicsOpacityEffect tabBarOpacityEffect; int rolledUpIndex; KexiSearchLineEdit *searchLineEdit; void setCurrentTab(const QString& name); void hideTab(const QString& name); void showTab(const QString& name); bool isTabVisible(const QString& name) const; #ifndef NDEBUG void debugToolbars() const; #endif int lowestIndex; }; class KexiTabbedToolBarStyle; //! Tab bar reimplementation for KexiTabbedToolBar. /*! The main its purpose is to alter the width of "Kexi" tab. */ class KexiTabbedToolBarTabBar : public QTabBar { Q_OBJECT public: explicit KexiTabbedToolBarTabBar(QWidget *parent = 0); virtual QSize originalTabSizeHint(int index) const; virtual QSize tabSizeHint(int index) const; KexiTabbedToolBarStyle* customStyle; }; //! Style proxy for KexiTabbedToolBar, to get the "Kexi" tab style right. class KexiTabbedToolBarStyle : public QProxyStyle { Q_OBJECT public: explicit KexiTabbedToolBarStyle(const QString &baseStyleName); virtual ~KexiTabbedToolBarStyle(); virtual void drawControl(ControlElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget = 0) const; virtual void drawPrimitive(PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget = 0) const; virtual int pixelMetric(PixelMetric metric, const QStyleOption* option = 0, const QWidget* widget = 0) const; }; //! Style proxy for KexiTabbedToolBar, to fix the hardcoded margins (e.g. for Breeze). class KexiDockWidgetStyle : public QProxyStyle { Q_OBJECT public: explicit KexiDockWidgetStyle(const QString &baseStyleName); virtual ~KexiDockWidgetStyle(); using QProxyStyle::polish; void polish(QWidget* widget) Q_DECL_OVERRIDE; }; //------------------------------------------ //! @internal Dock widget with floating disabled but still collapsible class KexiDockWidget : public QDockWidget { Q_OBJECT public: KexiDockWidget(const QString &tabText, QWidget *parent); virtual ~KexiDockWidget(); virtual void setSizeHint(const QSize& hint); virtual QSize sizeHint() const; const QString tabText; //!< for tab bar tabs protected: virtual void paintEvent(QPaintEvent *pe); private: class Private; Private * const d; }; //------------------------------------------ //! @internal safer dictionary typedef QMap< int, KexiWindow* > KexiWindowDict; //! @internal class Q_DECL_HIDDEN KexiMainWindow::Private { public: explicit Private(KexiMainWindow* w); ~Private(); #ifndef KEXI_NO_PENDING_DIALOGS //! Job type. Currently used for marking items as being opened or closed. enum PendingJobType { NoJob = 0, WindowOpeningJob, WindowClosingJob }; KexiWindow *openedWindowFor(const KexiPart::Item* item, PendingJobType &pendingType); KexiWindow *openedWindowFor(int identifier, PendingJobType &pendingType); void addItemToPendingWindows(const KexiPart::Item* item, PendingJobType jobType); bool pendingWindowsExist(); void removePendingWindow(int identifier); #else KexiWindow *openedWindowFor(int identifier); #endif void insertWindow(KexiWindow *window); bool windowContainerExistsFor(int identifier) const; void setWindowContainerExistsFor(int identifier, bool set); void updateWindowId(KexiWindow *window, int oldItemID); void removeWindow(int identifier); int openedWindowsCount(); //! Used in KexiMainWindowe::closeProject() void clearWindows(); void showStartProcessMsg(const QStringList& args); //! Updates Property Editor Pane's visibility for the current window and the @a viewMode view mode. /*! @a info can be provided to hadle cases when current window is not yet defined (in openObject()). */ void updatePropEditorVisibility(Kexi::ViewMode viewMode, KexiPart::Info *info = 0); //void setTabBarVisible(KMultiTabBar::KMultiTabBarPosition position, int id, // KexiDockWidget *dockWidget, bool visible); //void setPropertyEditorTabBarVisible(bool visible); QObject *openedCustomObjectsForItem(KexiPart::Item* item, const char* name); void addOpenedCustomObjectForItem(KexiPart::Item* item, QObject* object, const char* name); KexiFindDialog *findDialog(); /*! Updates the find/replace dialog depending on the active view. Nothing is performed if the dialog is not instantiated yet or is invisible. */ void updateFindDialogContents(bool createIfDoesNotExist = false); //! \return the current view if it supports \a actionName, otherwise returns 0. KexiView *currentViewSupportingAction(const char* actionName) const; //! \return the current view if it supports KexiSearchAndReplaceViewInterface. KexiSearchAndReplaceViewInterface* currentViewSupportingSearchAndReplaceInterface() const; tristate showProjectMigrationWizard( const QString& mimeType, const QString& databaseName, const KDbConnectionData *cdata); - KexiPropertyEditorView *propertyEditor() const; + KPropertyEditorView *propertyEditor() const; //! Show mode for panes enum ShowMode { ShowImmediately, ShowAnimated }; //! Sets visibility of the project navigator without or without animating it. //! Related action is checked/unchecked accordingly. void setProjectNavigatorVisible(bool set, ShowMode mode = ShowImmediately); inline void addAction(QMenu *menu, const char* actionName) { menu->addAction(actionCollection->action(QLatin1String(actionName))); } KexiMainWindow *wnd; QStackedWidget *globalViewStack; KexiObjectViewWidget *objectViewWidget; + QPointer propertyPaneAnimation; KActionCollection *actionCollection; KexiGlobalViewModeSelector *modeSelector; KHelpMenu *helpMenu; KexiProject *prj; KSharedConfig::Ptr config; #ifdef KEXI_SHOW_CONTEXT_HELP KexiContextHelp *ctxHelp; #endif KexiTabbedToolBar *tabbedToolBar; QMap tabsToActivateOnShow; //! poits to kexi part which has been previously used to setup proppanel's tabs using //! KexiPart::setupCustomPropertyPanelTabs(), in updateCustomPropertyPanelTabs(). QPointer partForPreviouslySetupPropertyPanelTabs; QMap recentlySelectedPropertyPanelPages; QPointer propertySet; KexiNameDialog *nameDialog; QTimer timer; //!< helper timer QString appCaptionPrefix; // focus_before_popup; //! Set to true only in destructor, used by closeWindow() to know if //! user can cancel window closing. If true user even doesn't see any messages //! before closing a window. This is for extremely sanity... and shouldn't be even needed. bool forceWindowClosing; //! Indicates that we're inside closeWindow() method - to avoid inf. recursion //! on window removing bool insideCloseWindow; #ifndef KEXI_NO_PENDING_DIALOGS //! Used in executeActionWhenPendingJobsAreFinished(). enum ActionToExecuteWhenPendingJobsAreFinished { NoAction, QuitAction, CloseProjectAction }; ActionToExecuteWhenPendingJobsAreFinished actionToExecuteWhenPendingJobsAreFinished; void executeActionWhenPendingJobsAreFinished(); #endif //! Used for delayed windows closing for 'close all' QList windowsToClose; #ifdef KEXI_QUICK_PRINTING_SUPPORT //! Opened page setup dialogs, used by printOrPrintPreviewForItem(). QHash pageSetupWindows; /*! A map from Kexi dialog to "print setup" part item's ID of the data item used by closeWindow() to find an ID of the data item, so the entry can be removed from pageSetupWindows dictionary. */ QMap pageSetupWindowItemID2dataItemID_map; #endif //! Indicates if project is started in User Mode bool userMode; //! Indicates if project navigator should be visible bool isProjectNavigatorVisible; //! Indicates if the main menu should be visible bool isMainMenuVisible; //! Set in restoreSettings() and used in initNavigator() //! to customize navigator visibility on startup bool forceShowProjectNavigatorOnCreation; bool forceHideProjectNavigatorOnCreation; bool navWasVisibleBeforeProjectClosing; bool saveSettingsForShowProjectNavigator; //! Used by openedCustomObjectsForItem() and addOpenedCustomObjectForItem() QHash m_openedCustomObjectsForItem; int propEditorDockSeparatorPos, navDockSeparatorPos; bool wasAutoOpen; bool windowExistedBeforeCloseProject; bool propertyEditorCollapsed; bool enable_slotPropertyEditorVisibilityChanged; KexiUserFeedbackAgent userFeedback; KexiMigrateManagerInterface* migrateManager; private: //! @todo move to KexiProject KexiWindowDict windows; //! A set of item identifiers for whose there are KexiWindowContainer instances already. //! This lets to verify that KexiWindow is about to be constructed and opened so multiple //! opening can be avoided. QSet windowContainers; #ifndef KEXI_NO_PROCESS_EVENTS QHash pendingWindows; //!< part item identifiers for windows whoose opening has been started //! @todo QMutex dialogsMutex; //!< used for locking windows and pendingWindows dicts #endif KexiFindDialog *m_findDialog; }; //------------------------------------------ //! Action shortcut used by KexiMainWindow::setupMainMenuActionShortcut(QAction *) //! Activates action only if enabled. class KexiMainMenuActionShortcut : public QShortcut { Q_OBJECT public: KexiMainMenuActionShortcut(const QKeySequence& key, QAction *action, QWidget *parent); virtual ~KexiMainMenuActionShortcut(); protected Q_SLOTS: //! Triggers associated action only when this action is enabled void slotActivated(); private: QPointer m_action; }; #endif