You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
koffice/kexi/formeditor/form.cpp

601 lines
15 KiB

/* 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"