summaryrefslogtreecommitdiffstats
path: root/kexi/formeditor/form.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kexi/formeditor/form.cpp')
-rw-r--r--kexi/formeditor/form.cpp600
1 files changed, 600 insertions, 0 deletions
diff --git a/kexi/formeditor/form.cpp b/kexi/formeditor/form.cpp
new file mode 100644
index 00000000..a5d57002
--- /dev/null
+++ b/kexi/formeditor/form.cpp
@@ -0,0 +1,600 @@
+/* This file is part of the KDE project
+ Copyright (C) 2003 Lucijan Busch <lucijan@gmx.at>
+ Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr>
+ Copyright (C) 2004-2007 Jaroslaw Staniek <js@iidea.pl>
+
+ 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 <qwidget.h>
+#include <qlabel.h>
+#include <qobjectlist.h>
+#include <qptrdict.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+#include <kcommand.h>
+#include <kaction.h>
+#include <kmessagebox.h>
+
+#include "container.h"
+#include "objecttree.h"
+#include "widgetpropertyset.h"
+#include "formIO.h"
+#include "formmanager.h"
+#include "widgetlibrary.h"
+#include "spring.h"
+#include "pixmapcollection.h"
+#include "events.h"
+#include "utils.h"
+#include "form.h"
+#include <koproperty/property.h>
+#include <kexiutils/utils.h>
+
+using namespace KFormDesigner;
+
+FormPrivate::FormPrivate()
+{
+ toplevel = 0;
+ topTree = 0;
+ widget = 0;
+ resizeHandles.setAutoDelete(true);
+ dirty = false;
+ interactive = true;
+ design = true;
+ autoTabstops = false;
+ tabstops.setAutoDelete(false);
+ connBuffer = new ConnectionBuffer();
+ formatVersion = KFormDesigner::version();
+ originalFormatVersion = KFormDesigner::version();
+}
+
+FormPrivate::~FormPrivate()
+{
+ delete history;
+ delete topTree;
+ delete connBuffer;
+ connBuffer = 0;
+ resizeHandles.setAutoDelete(false);
+ // otherwise, it tries to delete widgets which doesn't exist anymore
+}
+
+//--------------------------------------
+
+FormWidget::FormWidget()
+ : m_form(0)
+{
+}
+
+FormWidget::~FormWidget()
+{
+ if (m_form) {
+ m_form->setFormWidget(0);
+ }
+}
+
+//--------------------------------------
+
+Form::Form(WidgetLibrary* library, const char *name, bool designMode)
+ : QObject(library, name)
+ , m_lib(library)
+{
+ d = new FormPrivate();
+// d->manager = manager;
+ d->design = designMode;
+
+ // Init actions
+ d->collection = new KActionCollection(0, this);
+ d->history = new KCommandHistory(d->collection, true);
+ connect(d->history, SIGNAL(commandExecuted()), this, SLOT(slotCommandExecuted()));
+ connect(d->history, SIGNAL(documentRestored()), this, SLOT(slotFormRestored()));
+}
+
+Form::~Form()
+{
+ emit destroying();
+ delete d;
+ d = 0;
+}
+
+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;
+}
+
+//////////////// Container -related functions ///////////////////////
+
+void
+Form::createToplevel(QWidget *container, FormWidget *formWidget, const QCString &)
+{
+ kdDebug() << "Form::createToplevel() container= "<< (container ? container->name() : "<NULL>")
+ << " formWidget=" << formWidget << "className=" << name() << endl;
+
+ setFormWidget( formWidget );
+ d->toplevel = new Container(0, container, this, name());
+ d->topTree = new ObjectTree(i18n("Form"), container->name(), container, d->toplevel);
+ d->toplevel->setObjectTree(d->topTree);
+ d->toplevel->setForm(this);
+ d->pixcollection = new PixmapCollection(container->name(), this);
+
+ d->topTree->setWidget(container);
+//! todo: copy caption in Kexi from object's caption
+// d->topTree->addModifiedProperty("caption", name());
+ //m_topTree->addModifiedProperty("icon");
+
+ connect(container, SIGNAL(destroyed()), this, SLOT(formDeleted()));
+
+ kdDebug() << "Form::createToplevel(): d->toplevel=" << d->toplevel << endl;
+}
+
+
+Container*
+Form::activeContainer()
+{
+ ObjectTreeItem *it;
+ if(d->selected.count() == 0)
+ return d->toplevel;
+
+ if(d->selected.count() == 1)
+ it = d->topTree->lookup(d->selected.last()->name());
+ else
+ it = commonParentContainer( &(d->selected) );
+
+ if (!it)
+ return 0;
+ if(it->container())
+ return it->container();
+ else
+ return it->parent()->container();
+}
+
+ObjectTreeItem*
+Form::commonParentContainer(WidgetList *wlist)
+{
+ ObjectTreeItem *item = 0;
+ WidgetList *list = new WidgetList();
+
+ // Creates a list of all widget parents
+ for(QWidget *w = wlist->first(); w; w = wlist->next())
+ {
+ if(list->findRef(w->parentWidget()) == -1)
+ list->append(w->parentWidget());
+ }
+
+ removeChildrenFromList(*list);
+
+ // one widget remains == the container we are looking for
+ if(list->count() == 1)
+ item = d->topTree->lookup(list->first()->name());
+ else // we need to go one level up
+ item = commonParentContainer(list);
+
+ delete list;
+ return item;
+}
+
+Container*
+Form::parentContainer(QWidget *w)
+{
+ ObjectTreeItem *it;
+ if(!w)
+ return 0;
+ // it = d->topTree->lookup(d->selected.last()->name());
+ //else
+ it = d->topTree->lookup(w->name());
+
+ if(it->parent()->container())
+ return it->parent()->container();
+ else
+ return it->parent()->parent()->container();
+}
+
+
+
+void
+Form::setDesignMode(bool design)
+{
+ d->design = design;
+ if(!design)
+ {
+ ObjectTreeDict *dict = new ObjectTreeDict( *(d->topTree->dict()) );
+ ObjectTreeDictIterator it(*dict);
+ for(; it.current(); ++it)
+ m_lib->previewWidget(it.current()->widget()->className(), it.current()->widget(), d->toplevel);
+ delete dict;
+
+ d->widget = d->topTree->widget();
+ delete (d->topTree);
+ d->topTree = 0;
+ delete (d->toplevel);
+ d->toplevel = 0;
+ }
+}
+
+
+///////////////////////////// Selection stuff ///////////////////////
+
+void
+Form::setSelectedWidget(QWidget *w, bool add, bool dontRaise, bool moreWillBeSelected)
+{
+ if((d->selected.isEmpty()) || (w == widget()) || (d->selected.first() == widget()))
+ add = false;
+
+ if(!w)
+ {
+ setSelectedWidget(widget());
+ return;
+ }
+
+ //raise selected widget and all possible parents
+ QWidget *wtmp = w;
+ while(!dontRaise && wtmp && wtmp->parentWidget() && (wtmp != widget()))
+ {
+ wtmp->raise();
+ if(d->resizeHandles[ wtmp->name() ])
+ d->resizeHandles[ wtmp->name() ]->raise();
+ wtmp = wtmp->parentWidget();
+ }
+
+ if (wtmp)
+ wtmp->setFocus();
+
+ if(!add)
+ {
+ d->selected.clear();
+ d->resizeHandles.clear();
+ }
+ d->selected.append(w);
+ emit selectionChanged(w, add, moreWillBeSelected);
+ emitActionSignals(false);
+
+ // WidgetStack and TabWidget pages widgets shouldn't have resize handles, but their parent
+ if(!FormManager::self()->isTopLevel(w) && w->parentWidget() && w->parentWidget()->isA("QWidgetStack"))
+ {
+ w = w->parentWidget();
+ if(w->parentWidget() && w->parentWidget()->inherits("QTabWidget"))
+ w = w->parentWidget();
+ }
+
+ if(w && w != widget())
+ d->resizeHandles.insert(w->name(), new ResizeHandleSet(w, this));
+}
+
+ResizeHandleSet*
+Form::resizeHandlesForWidget(QWidget* w)
+{
+ return d->resizeHandles[w->name()];
+}
+
+void
+Form::unSelectWidget(QWidget *w)
+{
+ d->selected.remove(w);
+ d->resizeHandles.remove(w->name());
+}
+
+void
+Form::selectFormWidget()
+{
+ setSelectedWidget(widget(), false);
+}
+
+void
+Form::clearSelection()
+{
+ d->selected.clear();
+ d->resizeHandles.clear();
+ emit selectionChanged(0, false);
+ emitActionSignals(false);
+}
+
+void
+Form::emitActionSignals(bool withUndoAction)
+{
+ // Update menu and toolbar items
+ if(d->selected.count() > 1)
+ FormManager::self()->emitWidgetSelected(this, true);
+ else if(d->selected.first() != widget())
+ FormManager::self()->emitWidgetSelected(this, false);
+ else
+ FormManager::self()->emitFormWidgetSelected(this);
+
+ if(!withUndoAction)
+ return;
+
+ KAction *undoAction = d->collection->action("edit_undo");
+ if(undoAction)
+ FormManager::self()->emitUndoEnabled(undoAction->isEnabled(), undoAction->text());
+
+ KAction *redoAction = d->collection->action("edit_redo");
+ if(redoAction)
+ FormManager::self()->emitRedoEnabled(redoAction->isEnabled(), redoAction->text());
+}
+
+void
+Form::emitSelectionSignals()
+{
+ emit selectionChanged(selectedWidgets()->first(), false);
+// for(QWidget *w = selectedWidgets()->next(); w; w = selectedWidgets()->next())
+// emit selectionChanged(selectedWidgets()->first(), true);
+ for (WidgetListIterator it(*selectedWidgets()); it.current(); ++it)
+ emit selectionChanged(it.current(), true);
+}
+
+/////////////////////////// Various slots and signals /////////////////////
+void
+Form::formDeleted()
+{
+// clearSelection();
+ d->selected.clear();
+ d->resizeHandles.setAutoDelete(false);
+ d->resizeHandles.clear();
+ d->resizeHandles.setAutoDelete(true);
+// emit selectionChanged(0, false);
+// emitActionSignals(false);
+
+ FormManager::self()->deleteForm(this);
+ //delete this;
+ deleteLater();
+}
+
+void
+Form::changeName(const QCString &oldname, const QCString &newname)
+{
+ if(oldname == newname)
+ return;
+ if(!d->topTree->rename(oldname, newname)) // rename failed
+ {
+ KMessageBox::sorry(widget()->topLevelWidget(),
+ i18n("Renaming widget \"%1\" to \"%2\" failed.").arg(oldname).arg(newname));
+//moved to WidgetPropertySet::slotChangeProperty()
+// KMessageBox::sorry(widget()->topLevelWidget(),
+// i18n("A widget with this name already exists. "
+// "Please choose another name or rename existing widget."));
+ kdDebug() << "Form::changeName() : ERROR : A widget named " << newname << " already exists" << endl;
+ FormManager::self()->propertySet()->property("name") = QVariant(oldname);
+ }
+ else
+ {
+ d->connBuffer->fixName(oldname, newname);
+ ResizeHandleSet *temp = d->resizeHandles.take(oldname);
+ d->resizeHandles.insert(newname, temp);
+ }
+}
+
+void
+Form::emitChildAdded(ObjectTreeItem *item)
+{
+ addWidgetToTabStops(item);
+ emit childAdded(item);
+}
+
+void
+Form::emitChildRemoved(ObjectTreeItem *item)
+{
+ d->tabstops.remove(item);
+ if(d->connBuffer)
+ d->connBuffer->removeAllConnectionsForWidget(item->name());
+ emit childRemoved(item);
+}
+
+void
+Form::addCommand(KCommand *command, bool execute)
+{
+ emit FormManager::self()->dirty(this, true);
+ d->dirty = true;
+ d->history->addCommand(command, execute);
+ if(!execute) // simulate command to activate 'undo' menu
+ slotCommandExecuted();
+}
+
+void
+Form::clearCommandHistory()
+{
+ d->history->clear();
+ FormManager::self()->emitUndoEnabled(false, QString::null);
+ FormManager::self()->emitRedoEnabled(false, QString::null);
+}
+
+void
+Form::slotCommandExecuted()
+{
+ emit FormManager::self()->dirty(this, true);
+ d->dirty = true;
+ // because actions text is changed after the commandExecuted() signal is emitted
+ QTimer::singleShot(10, this, SLOT(emitUndoEnabled()));
+ QTimer::singleShot(10, this, SLOT(emitRedoEnabled()));
+}
+
+void
+Form::emitUndoEnabled()
+{
+ KAction *undoAction = d->collection->action("edit_undo");
+ if(undoAction)
+ FormManager::self()->emitUndoEnabled(undoAction->isEnabled(), undoAction->text());
+}
+
+void
+Form::emitRedoEnabled()
+{
+ KAction *redoAction = d->collection->action("edit_redo");
+ if(redoAction)
+ FormManager::self()->emitRedoEnabled(redoAction->isEnabled(), redoAction->text());
+}
+
+void
+Form::slotFormRestored()
+{
+ emit FormManager::self()->dirty(this, false);
+ d->dirty = false;
+}
+
+
+/////////////////////////// Tab stops ////////////////////////
+
+void
+Form::addWidgetToTabStops(ObjectTreeItem *it)
+{
+ QWidget *w = it->widget();
+ if(!w)
+ return;
+ if(!(w->focusPolicy() & QWidget::TabFocus))
+ {
+ if (!w->children())
+ return;
+ // For composed widgets, we check if one of the child can have focus
+ for(QObjectListIterator chIt(*w->children()); chIt.current(); ++chIt) {
+ if(chIt.current()->isWidgetType()) {//QWidget::TabFocus flag will be checked later!
+ if(d->tabstops.findRef(it) == -1) {
+ d->tabstops.append(it);
+ return;
+ }
+ }
+ }
+ }
+ else if(d->tabstops.findRef(it) == -1) // not yet in the list
+ d->tabstops.append(it);
+}
+
+void
+Form::updateTabStopsOrder()
+{
+ for (ObjectTreeListIterator it(d->tabstops);it.current();) {
+ if(!(it.current()->widget()->focusPolicy() & QWidget::TabFocus)) {
+ kexidbg << "Form::updateTabStopsOrder(): widget removed because has no TabFocus: " << it.current()->widget()->name() << endl;
+ d->tabstops.remove( it.current() );
+ }
+ else
+ ++it;
+ }
+}
+
+//! Collects all the containers reculsively. Used by Form::autoAssignTabStops().
+void collectContainers(ObjectTreeItem* item, QPtrDict<char>& containers)
+{
+ if (!item->container())
+ return;
+ if (!containers[ item->container() ]) {
+ kdDebug() << "collectContainers() " << item->container()->objectTree()->className()
+ << " " << item->container()->objectTree()->name() << endl;
+ containers.insert( item->container(), (const char *)1 );
+ }
+ for (ObjectTreeListIterator it(*item->children()); it.current(); ++it)
+ collectContainers(it.current(), containers);
+}
+
+void
+Form::autoAssignTabStops()
+{
+ VerWidgetList list(toplevelContainer()->widget());
+ HorWidgetList hlist(toplevelContainer()->widget());
+
+ // 1. Collect all the containers, as we'll be sorting widgets groupped by containers
+ QPtrDict<char> containers;
+
+ collectContainers( toplevelContainer()->objectTree(), containers );
+
+ foreach_list(ObjectTreeListIterator, it, d->tabstops) {
+ if(it.current()->widget()) {
+ kdDebug() << "Form::autoAssignTabStops() widget to sort: " << it.current()->widget() << endl;
+ list.append(it.current()->widget());
+ }
+ }
+
+ list.sort();
+ foreach_list(QPtrListIterator<QWidget>, iter, list)
+ kdDebug() << iter.current()->className() << " " << iter.current()->name() << endl;
+
+ 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)
+ foreach_list(WidgetListIterator, it, list) {
+ QWidget *w = it.current();
+ hlist.append(w);
+
+ ++it;
+ QWidget *nextw = it.current();
+ QObject *page_w = 0;
+ KFormDesigner::TabWidget *tab_w = KFormDesigner::findParent<KFormDesigner::TabWidget>(w, "KFormDesigner::TabWidget", page_w);
+ while (nextw) {
+ if (KexiUtils::hasParent(w, nextw)) // do not group (sort) widgets where on 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<KFormDesigner::TabWidget>(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);
+ ++it;
+ nextw = it.current();
+ }
+ hlist.sort();
+
+ for(WidgetListIterator it2(hlist); it2.current() != 0; ++it2) {
+ ObjectTreeItem *tree = d->topTree->lookup(it2.current()->name());
+ if(tree) {
+ kdDebug() << "Form::autoAssignTabStops() adding " << tree->name() << endl;
+ d->tabstops.append(tree);
+ }
+ }
+
+ --it;
+ hlist.clear();
+ }
+}
+
+uint Form::formatVersion() const
+{
+ return d->formatVersion;
+}
+
+void Form::setFormatVersion(uint ver)
+{
+ d->formatVersion = ver;
+}
+uint Form::originalFormatVersion() const
+{
+ return d->originalFormatVersion;
+}
+
+void Form::setOriginalFormatVersion(uint ver)
+{
+ d->originalFormatVersion = ver;
+}
+
+void Form::setFormWidget(FormWidget* w)
+{
+ if (!d)
+ return;
+ d->formWidget = w;
+ if (!d->formWidget)
+ return;
+ d->formWidget->m_form = this;
+}
+
+#include "form.moc"