diff --git a/src/formeditor/form.cpp b/src/formeditor/form.cpp index 27a4ac3af..24acedd5b 100644 --- a/src/formeditor/form.cpp +++ b/src/formeditor/form.cpp @@ -1,2686 +1,2687 @@ /* 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 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); - d->propertySet["objectName"].setAutoSync(false); // name should be updated only when pressing Enter + // 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; } // 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(); } } 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/main/KexiMainWindow.cpp b/src/main/KexiMainWindow.cpp index 040bbf5de..ac3a14f51 100644 --- a/src/main/KexiMainWindow.cpp +++ b/src/main/KexiMainWindow.cpp @@ -1,4391 +1,4391 @@ /* This file is part of the KDE project Copyright (C) 2003 Lucijan Busch Copyright (C) 2003-2016 Jarosław Staniek This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "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 #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 #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) QStyleOptionDockWidgetV2 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(); } //------------------------------------------------- KexiMainWindowTabWidget::KexiMainWindowTabWidget(QWidget *parent, KexiMainWidget* mainWidget) : QTabWidget(parent) , m_mainWidget(mainWidget) , m_tabIndex(-1) { m_closeAction = new QAction(koIcon("tab-close"), xi18n("&Close Tab"), this); m_closeAction->setToolTip(xi18n("Close the current tab")); m_closeAction->setWhatsThis(xi18n("Closes the current tab.")); m_closeAllTabsAction = new QAction(xi18n("Cl&ose All Tabs"), this); m_closeAllTabsAction->setToolTip(xi18n("Close all tabs")); m_closeAllTabsAction->setWhatsThis(xi18n("Closes all tabs.")); connect(m_closeAction, SIGNAL(triggered()), this, SLOT(closeTab())); connect(m_closeAllTabsAction, SIGNAL(triggered()), this, SLOT(closeAllTabs())); //! @todo insert window list in the corner widget as in firefox #if 0 // close-tab button: QToolButton* rightWidget = new QToolButton(this); rightWidget->setDefaultAction(m_closeAction); rightWidget->setText(QString()); rightWidget->setAutoRaise(true); rightWidget->adjustSize(); setCornerWidget(rightWidget, Qt::TopRightCorner); #endif setMovable(true); setDocumentMode(true); tabBar()->setExpanding(true); } KexiMainWindowTabWidget::~KexiMainWindowTabWidget() { } void KexiMainWindowTabWidget::paintEvent(QPaintEvent * event) { if (count() > 0) QTabWidget::paintEvent(event); else QWidget::paintEvent(event); } void KexiMainWindowTabWidget::mousePressEvent(QMouseEvent *event) { //! @todo KEXI3 test KexiMainWindowTabWidget's contextMenu event port from KTabWidget if (event->button() == Qt::RightButton) { int tab = tabBar()->tabAt(event->pos()); const QPoint realPos(tabBar()->mapToGlobal(event->pos())); if (QRect(tabBar()->mapToGlobal(QPoint(0,0)), tabBar()->mapToGlobal(QPoint(tabBar()->width()-1, tabBar()->height()-1))).contains(realPos)) { showContextMenuForTab(tab, tabBar()->mapToGlobal(event->pos())); return; } } QTabWidget::mousePressEvent(event); } void KexiMainWindowTabWidget::closeTab() { KexiMainWindow *main = dynamic_cast(KexiMainWindowIface::global()); if (main) { main->closeWindowForTab(m_tabIndex); } } tristate KexiMainWindowTabWidget::closeAllTabs() { tristate alternateResult = true; QList windowList; KexiMainWindow *main = dynamic_cast(KexiMainWindowIface::global()); if (!main) { return alternateResult; } for (int i = 0; i < count(); i++) { KexiWindow *window = main->windowForTab(i); if (window) { windowList.append(window); } } foreach (KexiWindow *window, windowList) { tristate result = main->closeWindow(window); if (result != true && result != false) { return result; } if (result == false) { alternateResult = false; } } return alternateResult; } void KexiMainWindowTabWidget::showContextMenuForTab(int index, const QPoint& point) { QMenu menu; if (index >= 0) { menu.addAction(m_closeAction); } if (count() > 0) { menu.addAction(m_closeAllTabsAction); } //! @todo add "&Detach Tab" if (menu.actions().isEmpty()) { return; } setTabIndexFromContextMenu(index); menu.exec(point); } void KexiMainWindowTabWidget::setTabIndexFromContextMenu(int clickedIndex) { if (currentIndex() == -1) { m_tabIndex = -1; return; } m_tabIndex = clickedIndex; } //------------------------------------------------- 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_MAC // 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(int &argc, char *argv[], const QString &componentName) { QApplication *app = qApp; QScopedPointer guard; if (!app) { //! @todo use non-GUI app when needed guard.reset(app = new QApplication(argc, argv)); } app->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")); tristate res = Kexi::startupHandler().init(); if (!res || ~res) { return (~res) ? 0 : 1; } //qDebug() << "startupActions OK"; /* Exit requested, e.g. after database removing. */ if (Kexi::startupHandler().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; }*/ guard.take(); 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) : KexiMainWindowSuper(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(); updateAppCaption(); if (!d->userMode) { setupContextHelp(); setupPropertyEditor(); } invalidateActions(); d->timer.singleShot(0, this, SLOT(slotLastActions())); if (Kexi::startupHandler().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 { return windowForTab(d->mainWidget->tabWidget()->currentIndex()); } KexiWindow* KexiMainWindow::windowForTab(int tabIndex) const { if (!d->mainWidget->tabWidget()) return 0; KexiWindowContainer *windowContainer = dynamic_cast(d->mainWidget->tabWidget()->widget(tabIndex)); if (!windowContainer) return 0; return windowContainer->window; } 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)); 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)); addThreeDotsToActionText(action); 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->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_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("window-close"), xi18nc("Close Project", "&Close"), 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..."), "Ctrl+R"); 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_QUICK_PRINTING_SUPPORT 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())); #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)); d->action_edit_replace = 0; //! @todo d->action_edit_replace = KStandardAction::replace( //! this, SLOT(slotEditReplace()), actionCollection(), "project_print_preview" ); d->action_edit_replace_all = 0; //! @todo d->action_edit_replace_all = new QAction( xi18n("Replace All"), "", 0, //! this, SLOT(slotEditReplaceAll()), actionCollection(), "edit_replaceall"); 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) { d->action_show_nav = addAction("view_navigator", xi18n("Show Project Navigator"), "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(slotShowNavigator())); } 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) { d->action_show_propeditor = addAction("view_propeditor", xi18n("Show Property Editor"), "Alt+3"); d->action_show_propeditor->setToolTip(xi18n("Show the Property Editor pane")); d->action_show_propeditor->setWhatsThis(xi18n("Shows the Property Editor pane.")); connect(d->action_show_propeditor, SIGNAL(triggered()), this, SLOT(slotShowPropertyEditor())); } else { d->action_show_propeditor = 0; } if (!d->userMode) { d->action_activate_propeditor = addAction("activate_propeditor", xi18n("Activate Property Editor"), "Alt+-"); d->action_activate_propeditor->setToolTip(xi18n("Activate the Property Editor pane")); d->action_activate_propeditor->setWhatsThis(xi18n("Activates the Property Editor pane. If it is hidden, shows it first.")); connect(d->action_activate_propeditor, SIGNAL(triggered()), this, SLOT(slotActivatePropertyEditor())); } else { d->action_activate_propeditor = 0; } d->action_view_global_search = addAction("view_global_search", xi18n("Switch to Global Search"), "Ctrl+K"); d->action_view_global_search->setToolTip(xi18n("Switch to Global Search box")); d->action_view_global_search->setWhatsThis(xi18n("Switches to Global Search 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 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.")); //TOOLS MENU //WINDOW MENU //additional 'Window' menu items d->action_window_next = addAction("window_next", xi18n("&Next Window"), #ifdef Q_OS_WIN "Ctrl+Tab" #else "Alt+Right" #endif ); d->action_window_next->setToolTip(xi18n("Next window")); d->action_window_next->setWhatsThis(xi18n("Switches to the next window.")); connect(d->action_window_next, SIGNAL(triggered()), this, SLOT(activateNextWindow())); d->action_window_previous = addAction("window_previous", xi18n("&Previous Window"), #ifdef Q_OS_WIN "Ctrl+Shift+Tab" #else "Alt+Left" #endif ); d->action_window_previous->setToolTip(xi18n("Previous window")); d->action_window_previous->setWhatsThis(xi18n("Switches to the previous window.")); connect(d->action_window_previous, SIGNAL(triggered()), this, SLOT(activatePreviousWindow())); d->action_window_fullscreen = KStandardAction::fullScreen(this, SLOT(toggleFullScreen(bool)), this, ac); ac->addAction("full_screen", d->action_window_fullscreen); QList shortcuts; shortcuts << d->action_window_fullscreen->shortcut() << QKeySequence("F11"); d->action_window_fullscreen->setShortcuts(shortcuts); QShortcut *s = new QShortcut(d->action_window_fullscreen->shortcut(), this); connect(s, SIGNAL(activated()), d->action_window_fullscreen, SLOT(trigger())); if (d->action_window_fullscreen->shortcuts().count() > 1) { QShortcut *sa = new QShortcut(d->action_window_fullscreen->shortcuts().value(1), this); connect(sa, SIGNAL(activated()), d->action_window_fullscreen, SLOT(trigger())); } //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 // GLOBAL d->action_show_help_menu = addAction("help_show_menu", xi18nc("Help Menu", "Help"), "Alt+H"); d->action_show_help_menu->setToolTip(xi18n("Show Help menu")); d->action_show_help_menu->setWhatsThis(xi18n("Shows Help menu.")); // (connection is added elsewhere) // ----- 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("window_close", Kexi::GlobalActionCategory | Kexi::WindowActionCategory); acat->setAllObjectTypesSupported("window_close", true); acat->addAction("window_next", Kexi::GlobalActionCategory); acat->addAction("window_previous", 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::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); //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_QUICK_PRINTING_SUPPORT 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); if (d->action_show_propeditor) d->action_show_propeditor->setEnabled(d->prj); #ifdef KEXI_SHOW_CONTEXT_HELP d->action_show_helper->setEnabled(d->prj); #endif //CREATE MENU if (d->tabbedToolBar && d->tabbedToolBar->createWidgetToolBar()) d->tabbedToolBar->createWidgetToolBar()->setEnabled(d->prj); // DATA MENU //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->navigator) { d->navigator->setEnabled(d->prj); } if (d->propEditor) d->propEditorTabWidget->setEnabled(d->prj); } tristate KexiMainWindow::startup() { tristate result = true; switch (Kexi::startupHandler().action()) { case KexiStartupHandler::CreateBlankProject: d->updatePropEditorVisibility(Kexi::NoViewMode); break; #ifdef KEXI_PROJECT_TEMPLATES case KexiStartupHandler::CreateFromTemplate: result = createProjectFromTemplate(*Kexi::startupHandler().projectData()); break; #endif case KexiStartupHandler::OpenProject: result = openProject(*Kexi::startupHandler().projectData()); break; case KexiStartupHandler::ImportProject: result = showProjectMigrationWizard( Kexi::startupHandler().importActionData().mimeType, Kexi::startupHandler().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(); setupProjectNavigator(); 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->showTab("data"); d->tabbedToolBar->showTab("external"); d->tabbedToolBar->showTab("tools"); 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->navigator) { d->navigator->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->navigator) { d->navWasVisibleBeforeProjectClosing = d->navDockWidget->isVisible(); d->navDockWidget->hide(); d->navigator->setProject(0); slotProjectNavigatorVisibilityChanged(true); // hide side tab } if (d->propEditorDockWidget) d->propEditorDockWidget->hide(); d->clearWindows(); //sanity! delete d->prj; d->prj = 0; updateReadOnlyState(); invalidateActions(); updateAppCaption(); 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() { QVBoxLayout *vlyr = new QVBoxLayout(this); 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_view_global_search); connect(d->action_view_global_search, 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); KMultiTabBar *mtbar = new KMultiTabBar(KMultiTabBar::Left); mtbar->setStyle(KMultiTabBar::VSNET); mainWidgetContainerLyr->addWidget(mtbar); d->multiTabBars.insert(mtbar->position(), mtbar); d->mainWidget = new KexiMainWidget(); d->mainWidget->setParent(this); d->mainWidget->tabWidget()->setTabsClosable(true); connect(d->mainWidget->tabWidget(), SIGNAL(tabCloseRequested(int)), this, SLOT(closeWindowForTab(int))); mainWidgetContainerLyr->addWidget(d->mainWidget, 1); mtbar = new KMultiTabBar(KMultiTabBar::Right); mtbar->setStyle(KMultiTabBar::VSNET); mainWidgetContainerLyr->addWidget(mtbar); d->multiTabBars.insert(mtbar->position(), mtbar); } 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::setupProjectNavigator() { if (!d->isProjectNavigatorVisible) return; if (d->navigator) { d->navDockWidget->show(); } else { KexiDockableWidget* navDockableWidget = new KexiDockableWidget; d->navigator = new KexiProjectNavigator(navDockableWidget); kexiTester() << KexiTestObject(d->navigator, "KexiProjectNavigator"); navDockableWidget->setWidget(d->navigator); d->navDockWidget = new KexiDockWidget(d->navigator->windowTitle(), d->mainWidget); d->navDockWidget->setObjectName("ProjectNavigatorDockWidget"); d->mainWidget->addDockWidget( applyRightToLeftToDockArea(Qt::LeftDockWidgetArea), d->navDockWidget, Qt::Vertical); navDockableWidget->setParent(d->navDockWidget); d->navDockWidget->setWidget(navDockableWidget); KConfigGroup mainWindowGroup(d->config->group("MainWindow")); const QSize projectNavigatorSize = mainWindowGroup.readEntry("ProjectNavigatorSize", QSize()); if (!projectNavigatorSize.isNull()) { navDockableWidget->setSizeHint(projectNavigatorSize); } connect(d->navDockWidget, SIGNAL(visibilityChanged(bool)), this, SLOT(slotProjectNavigatorVisibilityChanged(bool))); //Nav2 Signals connect(d->navigator, SIGNAL(openItem(KexiPart::Item*,Kexi::ViewMode)), this, SLOT(openObject(KexiPart::Item*,Kexi::ViewMode))); connect(d->navigator, SIGNAL(openOrActivateItem(KexiPart::Item*,Kexi::ViewMode)), this, SLOT(openObjectFromNavigator(KexiPart::Item*,Kexi::ViewMode))); connect(d->navigator, SIGNAL(newItem(KexiPart::Info*)), this, SLOT(newObject(KexiPart::Info*))); connect(d->navigator, SIGNAL(removeItem(KexiPart::Item*)), this, SLOT(removeObject(KexiPart::Item*))); connect(d->navigator->model(), SIGNAL(renameItem(KexiPart::Item*,QString,bool*)), this, SLOT(renameObject(KexiPart::Item*,QString,bool*))); connect(d->navigator->model(), SIGNAL(changeItemCaption(KexiPart::Item*,QString,bool*)), this, SLOT(setObjectCaption(KexiPart::Item*,QString,bool*))); connect(d->navigator, SIGNAL(executeItem(KexiPart::Item*)), this, SLOT(executeItem(KexiPart::Item*))); connect(d->navigator, SIGNAL(exportItemToClipboardAsDataTable(KexiPart::Item*)), this, SLOT(copyItemToClipboardAsDataTable(KexiPart::Item*))); connect(d->navigator, SIGNAL(exportItemToFileAsDataTable(KexiPart::Item*)), this, SLOT(exportItemAsDataTable(KexiPart::Item*))); #ifdef KEXI_QUICK_PRINTING_SUPPORT connect(d->navigator, SIGNAL(printItem(KexiPart::Item*)), this, SLOT(printItem(KexiPart::Item*))); connect(d->navigator, SIGNAL(pageSetupForItem(KexiPart::Item*)), this, SLOT(showPageSetupForItem(KexiPart::Item*))); #endif connect(d->navigator, SIGNAL(selectionChanged(KexiPart::Item*)), this, SLOT(slotPartItemSelectedInNavigator(KexiPart::Item*))); } if (d->prj->isConnected()) { QString partManagerErrorMessages; if (!partManagerErrorMessages.isEmpty()) { showWarningContinueMessage(partManagerErrorMessages, QString(), "ShowWarningsRelatedToPluginsLoading"); } d->navigator->setProject(d->prj, QString()/*all classes*/, &partManagerErrorMessages); } connect(d->prj, SIGNAL(newItemStored(KexiPart::Item*)), d->navigator->model(), SLOT(slotAddItem(KexiPart::Item*))); connect(d->prj, SIGNAL(itemRemoved(KexiPart::Item)), d->navigator->model(), SLOT(slotRemoveItem(KexiPart::Item))); d->navigator->setFocus(); if (d->forceShowProjectNavigatorOnCreation) { slotShowNavigator(); d->forceShowProjectNavigatorOnCreation = false; } else if (d->forceHideProjectNavigatorOnCreation) { d->forceHideProjectNavigatorOnCreation = false; } invalidateActions(); } void KexiMainWindow::slotLastActions() { } void KexiMainWindow::setupPropertyEditor() { if (!d->propEditor) { KConfigGroup mainWindowGroup(d->config->group("MainWindow")); //! @todo FIX LAYOUT PROBLEMS d->propEditorDockWidget = new KexiDockWidget(xi18n("Property Editor"), d->mainWidget); d->propEditorDockWidget->setObjectName("PropertyEditorDockWidget"); d->mainWidget->addDockWidget( applyRightToLeftToDockArea(Qt::RightDockWidgetArea), d->propEditorDockWidget, Qt::Vertical ); connect(d->propEditorDockWidget, SIGNAL(visibilityChanged(bool)), this, SLOT(slotPropertyEditorVisibilityChanged(bool))); d->propEditorDockableWidget = new KexiDockableWidget(d->propEditorDockWidget); d->propEditorDockWidget->setWidget(d->propEditorDockableWidget); const QSize propertyEditorSize = mainWindowGroup.readEntry("PropertyEditorSize", QSize()); if (!propertyEditorSize.isNull()) { d->propEditorDockableWidget->setSizeHint(propertyEditorSize); } QWidget *propEditorDockWidgetContents = new QWidget(d->propEditorDockableWidget); d->propEditorDockableWidget->setWidget(propEditorDockWidgetContents); QVBoxLayout *propEditorDockWidgetContentsLyr = new QVBoxLayout(propEditorDockWidgetContents); propEditorDockWidgetContentsLyr->setContentsMargins(0, 0, 0, 0); d->propEditorTabWidget = new QTabWidget(propEditorDockWidgetContents); d->propEditorTabWidget->setDocumentMode(true); propEditorDockWidgetContentsLyr->addWidget(d->propEditorTabWidget); d->propEditor = new KexiPropertyEditorView(d->propEditorTabWidget); d->propEditorTabWidget->setWindowTitle(d->propEditor->windowTitle()); d->propEditorTabWidget->addTab(d->propEditor, xi18n("Properties")); //! @todo REMOVE? d->propEditor->installEventFilter(this); KConfigGroup propertyEditorGroup(d->config->group("PropertyEditor")); QFont f(KexiUtils::smallestReadableFont()); 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); } } d->propEditorTabWidget->setFont(f); d->enable_slotPropertyEditorVisibilityChanged = false; d->propEditorDockWidget->setVisible(false); d->enable_slotPropertyEditorVisibilityChanged = true; } } 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) { d->mainWidget->closeEvent(ev); } 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); } } // Saved settings } 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->navigator) mainWindowGroup.writeEntry("ProjectNavigatorSize", d->navigator->parentWidget()->size()); if (d->propEditorDockableWidget) mainWindowGroup.writeEntry("PropertyEditorSize", d->propEditorDockableWidget->size()); 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->propEditorTabWidget) return; if ( !curWindowPart || (/*prevWindowPart &&*/ curWindowPart && (prevWindowPart != curWindowPart || prevViewMode != curViewMode) ) ) { 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->propEditorTabWidget->currentIndex()); } } //delete old custom tabs (other than 'property' tab) const int count = d->propEditorTabWidget->count(); for (int i = 1; i < count; i++) d->propEditorTabWidget->removeTab(1); } //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->setupCustomPropertyPanelTabs(d->propEditorTabWidget); //restore current page number for this part if (d->recentlySelectedPropertyPanelPages.contains(curWindowPart)) { d->propEditorTabWidget->setCurrentIndex( d->recentlySelectedPropertyPanelPages[ curWindowPart ] ); } } //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->mainWidget->tabWidget()->setCurrentWidget(window.parentWidget()/*container*/); window.activate(); return true; } void KexiMainWindow::activateNextWindow() { //! @todo activateNextWindow() } void KexiMainWindow::activatePreviousWindow() { //! @todo activatePreviousWindow() } 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->navigator){ connect(prj, SIGNAL(itemRemoved(KexiPart::Item)), d->navigator->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(); setupProjectNavigator(); d->prj->data()->setLastOpened(QDateTime::currentDateTime()); Kexi::recentProjects()->addProjectData(*d->prj->data()); 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; KexiStartupHandler &h = Kexi::startupHandler(); 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 = Kexi::startupHandler().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); 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->navigator) { return; } d->navigator->setFocus(); } void KexiMainWindow::slotActivateMainArea() { if (currentWindow()) currentWindow()->setFocus(); } void KexiMainWindow::slotActivatePropertyEditor() { if (!d->propEditor) { return; } if (d->propEditorTabWidget->currentWidget()) d->propEditorTabWidget->currentWidget()->setFocus(); } void KexiMainWindow::slotShowNavigator() { if (d->navDockWidget) d->navDockWidget->setVisible(!d->navDockWidget->isVisible()); } void KexiMainWindow::slotShowPropertyEditor() { if (d->propEditorDockWidget) d->propEditorDockWidget->setVisible(!d->propEditorDockWidget->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::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->propEditor) { // ah, closing detached window - better switch off property buffer right now... d->propertySet = 0; d->propEditor->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->navigator) { d->navigator->updateItemName(*window->partItem(), false); } } hideDesignTab(previousItemId, QString()); d->removeWindow(window_id); d->setWindowContainerExistsFor(window->partItem()->identifier(), false); QWidget *windowContainer = window->parentWidget(); d->mainWidget->tabWidget()->removeTab( d->mainWidget->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->navigator) { d->navigator->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->mainWidget->slotCurrentTabIndexChanged(d->mainWidget->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->propEditorDockWidget) 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; KexiWindowContainer *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); // open new tab earlier windowContainer = new KexiWindowContainer(d->mainWidget->tabWidget()); d->setWindowContainerExistsFor(item->identifier(), true); const int tabIndex = d->mainWidget->tabWidget()->addTab( windowContainer, QIcon::fromTheme(part ? part->info()->iconName() : QString()), KexiWindow::windowTitleForItem(*item)); d->mainWidget->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->mainWidget->tabWidget()->setTabWhatsThis(tabIndex, whatsThisText); d->mainWidget->tabWidget()->setCurrentWidget(windowContainer); #ifndef KEXI_NO_PENDING_DIALOGS d->addItemToPendingWindows(item, Private::WindowOpeningJob); #endif window = d->prj->openObject(windowContainer, item, viewMode, staticObjectArgs); if (window) { windowContainer->setWindow(window); // update text and icon d->mainWidget->tabWidget()->setTabText( d->mainWidget->tabWidget()->indexOf(windowContainer), window->windowTitle()); d->mainWidget->tabWidget()->setTabIcon( d->mainWidget->tabWidget()->indexOf(windowContainer), window->windowIcon()); } } if (!window || !activateWindow(*window)) { #ifndef KEXI_NO_PENDING_DIALOGS d->removePendingWindow(item->identifier()); #endif d->setWindowContainerExistsFor(item->identifier(), false); d->mainWidget->tabWidget()->removeTab( d->mainWidget->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->navigator->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->propEditor) d->propEditor->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->propEditor) { KPropertySet *newSet = _currentWindow ? _currentWindow->propertySet() : 0; if (!newSet || (force || static_cast(d->propertySet) != newSet)) { d->propertySet = newSet; if (preservePrevSelection || force) { - KPropertyEditorView::SetOptions options = KPropertyEditorView::ExpandChildItems; + KPropertyEditorView::SetOptions options; if (preservePrevSelection) { options |= KPropertyEditorView::PreservePreviousSelection; } if (sortedProperties) { options |= KPropertyEditorView::AlphabeticalOrder; } if (propertyToSelect.isEmpty()) { d->propEditor->editor()->changeSet(d->propertySet, options); } else { d->propEditor->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->navigator->updateItemName(*item, window->isDirty()); } invalidateActions(); updateAppCaption(); d->mainWidget->tabWidget()->setTabText( d->mainWidget->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->navigator->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) { slotShowNavigator(); if (!d->prj) return; KexiPart::Item *item = d->prj->itemForPluginId(pluginId, name); if (!item) return; if (d->navigator) { d->navigator->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->propEditor->updateInfoLabelForPropertySet(d->propertySet, textToDisplayForNullSet); } void KexiMainWindow::addSearchableModel(KexiSearchableModel *model) { if (d->tabbedToolBar) { d->tabbedToolBar->addSearchableModel(model); } } void KexiMainWindow::setReasonableDialogSize(QDialog *dialog) { dialog->setMinimumSize(600, 400); dialog->resize(size() * 0.8); } void KexiMainWindow::restoreDesignTabAndActivateIfNeeded(const QString &tabName) { if (!d->tabbedToolBar) { return; } d->tabbedToolBar->showTab(tabName); if (currentWindow() && currentWindow()->partItem() && currentWindow()->partItem()->identifier() != 0) // for unstored items id can be < 0 { const QString tabToActivate = d->tabsToActivateOnShow.value( currentWindow()->partItem()->identifier()); //qDebug() << "tabToActivate:" << tabToActivate << "tabName:" << tabName; if (tabToActivate == tabName) { d->tabbedToolBar->setCurrentTab(tabToActivate); } } } void KexiMainWindow::restoreDesignTabIfNeeded(const QString &pluginId, Kexi::ViewMode viewMode, int previousItemId) { //qDebug() << pluginId << viewMode << previousItemId; if (viewMode == Kexi::DesignViewMode) { switch (d->prj->typeIdForPluginId(pluginId)) { case KexiPart::FormObjectType: { hideDesignTab(previousItemId, "org.kexi-project.report"); restoreDesignTabAndActivateIfNeeded("form"); break; } case KexiPart::ReportObjectType: { hideDesignTab(previousItemId, "org.kexi-project.form"); restoreDesignTabAndActivateIfNeeded("report"); break; } default: hideDesignTab(previousItemId); } } else { hideDesignTab(previousItemId); } } void KexiMainWindow::activateDesignTab(const QString &pluginId) { if (!d->tabbedToolBar) { return; } switch (d->prj->typeIdForPluginId(pluginId)) { case KexiPart::FormObjectType: d->tabbedToolBar->setCurrentTab("form"); break; case KexiPart::ReportObjectType: d->tabbedToolBar->setCurrentTab("report"); break; default:; } } void KexiMainWindow::activateDesignTabIfNeeded(const QString &pluginId, Kexi::ViewMode viewMode) { if (!d->tabbedToolBar) { return; } const QString tabToActivate = d->tabsToActivateOnShow.value(currentWindow()->partItem()->identifier()); //qDebug() << pluginId << viewMode << tabToActivate; if (viewMode == Kexi::DesignViewMode && tabToActivate.isEmpty()) { activateDesignTab(pluginId); } else { d->tabbedToolBar->setCurrentTab(tabToActivate); } } void KexiMainWindow::hideDesignTab(int itemId, const QString &pluginId) { if (!d->tabbedToolBar) { return; } //qDebug() << itemId << pluginId; if ( itemId > 0 && d->tabbedToolBar->currentWidget()) { const QString currentTab = d->tabbedToolBar->currentWidget()->objectName(); //qDebug() << "d->tabsToActivateOnShow.insert" << itemId << currentTab; d->tabsToActivateOnShow.insert(itemId, currentTab); } switch (d->prj->typeIdForPluginId(pluginId)) { case KexiPart::FormObjectType: d->tabbedToolBar->hideTab("form"); break; case KexiPart::ReportObjectType: d->tabbedToolBar->hideTab("report"); break; default: d->tabbedToolBar->hideTab("form"); d->tabbedToolBar->hideTab("report"); } } void KexiMainWindow::showDesignTabIfNeeded(int previousItemId) { if (d->insideCloseWindow && d->tabbedToolBar) return; if (currentWindow()) { restoreDesignTabIfNeeded(currentWindow()->partItem()->pluginId(), currentWindow()->currentViewMode(), previousItemId); } else { hideDesignTab(previousItemId); } } KexiUserFeedbackAgent* KexiMainWindow::userFeedbackAgent() const { return &d->userFeedback; } KexiMigrateManagerInterface* KexiMainWindow::migrateManager() { if (!d->migrateManager) { d->migrateManager = dynamic_cast( KexiInternalPart::createObjectInstance( "org.kexi-project.migration", "manager", this, this, nullptr)); } return d->migrateManager; } void KexiMainWindow::toggleFullScreen(bool isFullScreen) { static bool isTabbarRolledDown; if (d->tabbedToolBar) { if (isFullScreen) { isTabbarRolledDown = !d->tabbedToolBar->isRolledUp(); if (isTabbarRolledDown) { d->tabbedToolBar->toggleRollDown(); } } else { if (isTabbarRolledDown && d->tabbedToolBar->isRolledUp()) { d->tabbedToolBar->toggleRollDown(); } } } const Qt::WindowStates s = windowState() & Qt::WindowMaximized; if (isFullScreen) { setWindowState(windowState() | Qt::WindowFullScreen | s); } else { setWindowState((windowState() & ~Qt::WindowFullScreen)); showMaximized(); } }