diff options
author | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-01-20 01:29:50 +0000 |
---|---|---|
committer | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-01-20 01:29:50 +0000 |
commit | 8362bf63dea22bbf6736609b0f49c152f975eb63 (patch) | |
tree | 0eea3928e39e50fae91d4e68b21b1e6cbae25604 /kexi/formeditor | |
download | koffice-8362bf63dea22bbf6736609b0f49c152f975eb63.tar.gz koffice-8362bf63dea22bbf6736609b0f49c152f975eb63.zip |
Added old abandoned KDE3 version of koffice
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/koffice@1077364 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kexi/formeditor')
79 files changed, 21995 insertions, 0 deletions
diff --git a/kexi/formeditor/Makefile.am b/kexi/formeditor/Makefile.am new file mode 100644 index 00000000..8f8aacbe --- /dev/null +++ b/kexi/formeditor/Makefile.am @@ -0,0 +1,34 @@ +include $(top_srcdir)/kexi/Makefile.global + +INCLUDES = -I$(top_srcdir)/kexi/widget -I$(top_srcdir)/kexi/widget/utils \ + -I$(top_srcdir)/kexi/widget/tableview \ + -I$(top_srcdir)/kexi/core \ + -I$(top_srcdir)/kexi \ + -I$(top_srcdir)/lib/kofficecore \ + -I$(top_srcdir)/lib -I$(top_srcdir)/lib/koproperty $(all_includes) + +lib_LTLIBRARIES = libkformdesigner.la +libkformdesigner_la_LDFLAGS = -no-undefined $(all_libraries) $(VER_INFO) +libkformdesigner_la_SOURCES = container.cpp resizehandle.cpp widgetfactory.cpp \ + widgetlibrary.cpp libactionwidget.cpp form.cpp \ + objecttree.cpp formIO.cpp formmanager.cpp objecttreeview.cpp spring.cpp \ + commands.cpp events.cpp connectiondialog.cpp richtextdialog.cpp \ + tabstopdialog.cpp editlistviewdialog.cpp utils.cpp widgetpropertyset.cpp \ + kfdpixmapedit.cpp widgetwithsubpropertiesinterface.cpp + +METASOURCES = AUTO +libkformdesigner_la_LIBADD = \ + $(top_builddir)/kexi/widget/libkexiextendedwidgets.la \ + $(top_builddir)/kexi/widget/utils/libkexiguiutils.la \ + $(top_builddir)/kexi/widget/tableview/libkexidatatable.la \ + $(top_builddir)/lib/koproperty/libkoproperty.la \ + $(LIB_KPARTS) + +servicetypesdir = $(kde_servicetypesdir) +servicetypes_DATA=widgetfactory.desktop + +SUBDIRS = . factories #test #scripting #kdevelop_plugin + +messages: rc.cpp + $(EXTRACTRC) `find . -name "*.rc" -o -name "*.ui"` >> rc.cpp + $(XGETTEXT) `find . -name "*.cpp" -o -name "*.h"` -o $(podir)/kformdesigner.pot diff --git a/kexi/formeditor/TODO b/kexi/formeditor/TODO new file mode 100644 index 00000000..611d5c0c --- /dev/null +++ b/kexi/formeditor/TODO @@ -0,0 +1,66 @@ +^^^^^^^^^^^^^^^ IMPORTANT ^^^^^^^^^^^^^^^^^^^^^^ +**** New Features : ****** +* KMainWindow support : Make an action editor + a toolbar editor (as in MozillaFirebird ?) + a menu editor + (see Gambas and askk other devs if they already have done this) +* Automatically set label name to the text +* Create an event editor and manage signals/slots (EventBuffer ?) + HOWEVER, SIGNAL/SLOT ISSUES (js): + 1. signals/slots mechanism is hard to use for average folks + 2. it may be easily simplified to avoid confusing them + 3. TrollTech would try to get influence on the Kexi Project + because of violating their licenses, if we'd like to expose + all their technologies so plainly + (at least commercial) license mentions that there cannot be + a project that is a replacement of the Qt developed out of the Qt) + 4. It wouldn't hurt to ask them, as well. +* Add many other widgets :-) +* Create db-aware widget factory + +****** Bugfixes/Changes : ***** +* Add a grouping ability to Widget +* Switch WifdgetInfo::className() to QCString +* Make subform open design when double-clickng + +^^^^^^^^^^^^^^^^^^ OTHER ^^^^^^^^^^^^^^^^^^^^^^^^ +* Move tab between two QTabWidgets by drag'n drop +* Better editor for iconview +* during inline editing: CTRL+Enter could insert \n + +^^^^^^^^^^^^^^^^ DONE ^^^^^^^^^^^^^^ +* Finish sync between Form and ObjectTreeView (2/21/04) +* Only QTabWidget should be selected, not pages (2/21/04) +* Add a minimum size for created widgets (using size hint) (2/21/04) +* Move the pasted widget a little to avoid to have two widgets at the same pos (2/21/04) +* Make FormManager independant of the parent container (ie no longer depends on QWorkspace) (2/23/04) +* Fix pixmaps in Property Editor (2/24/04) +* Properties names and values are now i18n'ed (2/24/04) +* Add a classname and filename member into Form (2/24/04) +* Add support for layouts and special container (HBox, VBox ...) and spacers (partially 2/27/04) +* Allow Multiple selection : edit common properties (colors, font..),copy/cut them at one time, ... (2/28/04) +* Move multiple selected widgets at one time (2/29/04) +* Added real support for old values (2/29/04) +* Form previewing stuff (3/01/04) +* QByteArray methods in FormIO (3/01/04) +* Undo support(3/05/04) +* Alignment and layout properties also support old values (3/06/04) +* Better handling of multiple widgets in ObjectPropertyBuffer : each widget has its own old value, so reset will act as expected (3/07/04) +* Remove all these " Q*** " names (3/08/04) +* When double-clicking on a QLabel or a QLabel (others ?), the widget should be inline-editable (also on creation) (3/11/04) +* Add many other widgets :-) (part I : all basic widgets) (3/20/04) +* Add grid layout (3/27/04) +* Add many other widgets :-) (part II : all basic containers + more widgets) (4/13/04) +* Change tab position by drag'n drop (4/14/04) +* Icons in ObjectTreeView(in branch) + context menu (4/14/04) +* Modify Spacer for better compatibility (save orientation and expanding) (4/19/04) +* Rich text editor (see BasKet source) (4/20/04) +* Allow to select multiple widgets by drawing a rectangle on form (4/22/04) +* Add a "Lay out Vertcally / horizontally " menu item when multiple widgets are selected (4/22/04) +* Select all widgets when pasted (8/05/04) +* Handle tab stops (8/05/04) +* Make ObjectTreeView handle multiple widgets (11/05/04) +* Manage label buddies using a popup menu (24/05/04) +* dragging with CTRL key can cause object's copying (25/05/04) +* Create a PixmapCollection with an editor which allow to choose pixmaps by path or from KDE icons, no inline saving of pixmaps (9/06/04) +* Make actions toggable (with KToggleAction) (js 11/06/04) +* Make FormWidget pure virtual (17/06/04) +* Resize label to fit the text (22/06/04) diff --git a/kexi/formeditor/commands.cpp b/kexi/formeditor/commands.cpp new file mode 100644 index 00000000..ca4f0f20 --- /dev/null +++ b/kexi/formeditor/commands.cpp @@ -0,0 +1,1601 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr> + Copyright (C) 2005 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 <qdom.h> +#include <qwidget.h> +#include <qlayout.h> +#include <qlabel.h> +#include <qsplitter.h> +#include <qmetaobject.h> + +#include <kdebug.h> +#include <klocale.h> +#include <kpopupmenu.h> +#include <kmessagebox.h> +#include <kaccelmanager.h> + +#include "formIO.h" +#include "container.h" +#include "objecttree.h" +#include "formmanager.h" +#include "form.h" +#include "widgetlibrary.h" +#include "events.h" +#include "utils.h" +#include "widgetpropertyset.h" +#include "widgetwithsubpropertiesinterface.h" +#include <koproperty/property.h> + +#include "commands.h" + +using namespace KFormDesigner; + +// Command + +Command::Command() + : KCommand() +{ +} + +Command::~Command() +{ +} + +// PropertyCommand + +PropertyCommand::PropertyCommand(WidgetPropertySet *set, const QCString &wname, + const QVariant &oldValue, const QVariant &value, const QCString &property) + : Command(), m_propSet(set), m_value(value), m_property(property) +{ + m_oldvalues.insert(wname, oldValue); +} + +PropertyCommand::PropertyCommand(WidgetPropertySet *set, const QMap<QCString, QVariant> &oldvalues, + const QVariant &value, const QCString &property) + : Command(), m_propSet(set), m_value(value), m_oldvalues(oldvalues), m_property(property) +{ +} + +/* +MultiCommand::MultiCommand() +{ +} + +MultiCommandGroup::addSubCommand(PropertyCommand* subCommand) + : Command(), m_propSet(set), m_value(value), m_oldvalues(oldvalues), m_property(property) +{ +} +*/ + +void +PropertyCommand::setValue(const QVariant &value) +{ + m_value = value; + emit FormManager::self()->dirty(FormManager::self()->activeForm()); +} + +void +PropertyCommand::execute() +{ + FormManager::self()->activeForm()->selectFormWidget(); + m_propSet->setUndoing(true); + + QMap<QCString, QVariant>::ConstIterator endIt = m_oldvalues.constEnd(); + for(QMap<QCString, QVariant>::ConstIterator it = m_oldvalues.constBegin(); it != endIt; ++it) + { + ObjectTreeItem* item = FormManager::self()->activeForm()->objectTree()->lookup(it.key()); + if (item) {//we're checking for item!=0 because the name could be of a form widget + FormManager::self()->activeForm()->setSelectedWidget(item->widget(), true); + } + } + + (*m_propSet)[m_property] = m_value; + m_propSet->setUndoing(false); +} + +void +PropertyCommand::unexecute() +{ + FormManager::self()->activeForm()->selectFormWidget(); + m_propSet->setUndoing(true); + + QMap<QCString, QVariant>::ConstIterator endIt = m_oldvalues.constEnd(); + for(QMap<QCString, QVariant>::ConstIterator it = m_oldvalues.constBegin(); it != endIt; ++it) + { + ObjectTreeItem* item = FormManager::self()->activeForm()->objectTree()->lookup(it.key()); + if (!item) + continue; //better this than a crash + QWidget *widg = item->widget(); + FormManager::self()->activeForm()->setSelectedWidget(widg, true); + //m_propSet->setSelectedWidget(widg, true); + + WidgetWithSubpropertiesInterface* subpropIface = dynamic_cast<WidgetWithSubpropertiesInterface*>(widg); + QWidget *subWidget = (subpropIface && subpropIface->subwidget()) ? subpropIface->subwidget() : widg; + if (-1!=subWidget->metaObject()->findProperty( m_property, true )) + subWidget->setProperty(m_property, it.data()); + } + + (*m_propSet)[m_property] = m_oldvalues.begin().data(); + m_propSet->setUndoing(false); +} + +QString +PropertyCommand::name() const +{ + if(m_oldvalues.count() >= 2) + return i18n("Change \"%1\" property for multiple widgets" ).arg(m_property); + else + return i18n("Change \"%1\" property for widget \"%2\"" ).arg(m_property).arg(m_oldvalues.begin().key()); +} + +void +PropertyCommand::debug() +{ + kdDebug() << "PropertyCommand: name=\"" << name() << "\" widgets=" << m_oldvalues.keys() + << " value=" << m_value << " oldValues=" << m_oldvalues.values() << endl; +} + +// GeometryPropertyCommand (for multiples widgets) + +GeometryPropertyCommand::GeometryPropertyCommand(WidgetPropertySet *set, + const QStringList &names, const QPoint& oldPos) + : Command(), m_propSet(set), m_names(names), m_oldPos(oldPos) +{ +} + +void +GeometryPropertyCommand::execute() +{ + m_propSet->setUndoing(true); + int dx = m_pos.x() - m_oldPos.x(); + int dy = m_pos.y() - m_oldPos.y(); + + QStringList::ConstIterator endIt = m_names.constEnd(); + // We move every widget in our list by (dx, dy) + for(QStringList::ConstIterator it = m_names.constBegin(); it != endIt; ++it) + { + ObjectTreeItem* item = FormManager::self()->activeForm()->objectTree()->lookup(*it); + if (!item) + continue; //better this than a crash + QWidget *w = item->widget(); + w->move(w->x() + dx, w->y() + dy); + } + m_propSet->setUndoing(false); +} + +void +GeometryPropertyCommand::unexecute() +{ + m_propSet->setUndoing(true); + int dx = m_pos.x() - m_oldPos.x(); + int dy = m_pos.y() - m_oldPos.y(); + + QStringList::ConstIterator endIt = m_names.constEnd(); + // We move every widget in our list by (-dx, -dy) to undo the move + for(QStringList::ConstIterator it = m_names.constBegin(); it != endIt; ++it) + { + ObjectTreeItem* item = FormManager::self()->activeForm()->objectTree()->lookup(*it); + if (!item) + continue; //better this than a crash + QWidget *w = item->widget(); + w->move(w->x() - dx, w->y() - dy); + } + m_propSet->setUndoing(false); +} + +void +GeometryPropertyCommand::setPos(const QPoint& pos) +{ + m_pos = pos; + emit FormManager::self()->dirty(FormManager::self()->activeForm()); +} + +QString +GeometryPropertyCommand::name() const +{ + return i18n("Move multiple widgets"); +} + +void +GeometryPropertyCommand::debug() +{ + kdDebug() << "GeometryPropertyCommand: pos=" << m_pos << " oldPos=" << m_oldPos + << " widgets=" << m_names << endl; +} + +///////////////// AlignWidgetsCommand //////// + +AlignWidgetsCommand::AlignWidgetsCommand(int type, WidgetList &list, Form *form) +: Command(), m_form(form), m_type(type) +{ + for(QWidget *w = list.first(); w; w = list.next()) + m_pos.insert(w->name(), w->pos()); +} + +void +AlignWidgetsCommand::execute() +{ + // To avoid creation of GeometryPropertyCommand + m_form->selectFormWidget(); + + int gridX = m_form->gridSize(); + int gridY = m_form->gridSize(); + QWidget *parentWidget = m_form->selectedWidgets()->first()->parentWidget(); + int tmpx, tmpy; + + WidgetList list; + QMap<QCString, QPoint>::ConstIterator endIt = m_pos.constEnd(); + for(QMap<QCString, QPoint>::ConstIterator it = m_pos.constBegin(); it != endIt; ++it) + { + ObjectTreeItem *item = m_form->objectTree()->lookup(it.key()); + if(item && item->widget()) + list.append(item->widget()); + } + + switch(m_type) + { + case AlignToGrid: + { + for(QWidget *w = list.first(); w; w = list.next()) + { + tmpx = int( (float)w->x() / ((float)gridX) + 0.5 ) * gridX; + tmpy = int( (float)w->y() / ((float)gridY) + 0.5 ) * gridY; + + if((tmpx != w->x()) || (tmpy != w->y())) + w->move(tmpx, tmpy); + } + break; + } + + case AlignToLeft: + { + tmpx = parentWidget->width(); + for(QWidget *w = list.first(); w; w = list.next()) + { + if(w->x() < tmpx) + tmpx = w->x(); + } + + for(QWidget *w = list.first(); w; w = list.next()) + w->move(tmpx, w->y()); + break; + } + + case AlignToRight: + { + tmpx = 0; + for(QWidget *w = list.first(); w; w = list.next()) + { + if(w->x() + w->width() > tmpx) + tmpx = w->x() + w->width(); + } + + for(QWidget *w = list.first(); w; w = list.next()) + w->move(tmpx - w->width(), w->y()); + break; + } + + case AlignToTop: + { + tmpy = parentWidget->height(); + for(QWidget *w = list.first(); w; w = list.next()) + { + if(w->y() < tmpy) + tmpy = w->y(); + } + + for(QWidget *w = list.first(); w; w = list.next()) + w->move(w->x(), tmpy); + break; + } + + case AlignToBottom: + { + tmpy = 0; + for(QWidget *w = list.first(); w; w = list.next()) + { + if(w->y() + w->height() > tmpy) + tmpy = w->y() + w->height(); + } + + for(QWidget *w = list.first(); w; w = list.next()) + w->move(w->x(), tmpy - w->height()); + break; + } + + default: + return; + } + + // We restore selection + for(QWidget *w = list.first(); w; w = list.next()) + m_form->setSelectedWidget(w, true); +} + +void +AlignWidgetsCommand::unexecute() +{ + // To avoid creation of GeometryPropertyCommand + m_form->selectFormWidget(); + // We move widgets to their original pos + QMap<QCString, QPoint>::ConstIterator endIt = m_pos.constEnd(); + for(QMap<QCString, QPoint>::ConstIterator it = m_pos.constBegin(); it != endIt; ++it) + { + ObjectTreeItem *item = m_form->objectTree()->lookup(it.key()); + if(item && item->widget()) + item->widget()->move( m_pos[item->widget()->name()] ); + m_form->setSelectedWidget(item->widget(), true); // We restore selection + } +} + +QString +AlignWidgetsCommand::name() const +{ + switch(m_type) + { + case AlignToGrid: + return i18n("Align Widgets to Grid"); + case AlignToLeft: + return i18n("Align Widgets to Left"); + case AlignToRight: + return i18n("Align Widgets to Right"); + case AlignToTop: + return i18n("Align Widgets to Top"); + case AlignToBottom: + return i18n("Align Widgets to Bottom"); + default: + return QString::null; + } +} + +void +AlignWidgetsCommand::debug() +{ + kdDebug() << "AlignWidgetsCommand: name=\"" << name() << "\" form=" << m_form->widget()->name() + << " widgets=" << m_pos.keys() << endl; +} + +///// AdjustSizeCommand /////////// + +AdjustSizeCommand::AdjustSizeCommand(int type, WidgetList &list, Form *form) +: Command(), m_form(form), m_type(type) +{ + for(QWidget *w = list.first(); w; w = list.next()) + { + if(w->parentWidget() && w->parentWidget()->isA("QWidgetStack")) + { + w = w->parentWidget(); // widget is WidgetStack page + if(w->parentWidget() && w->parentWidget()->inherits("QTabWidget")) // widget is tabwidget page + w = w->parentWidget(); + } + + m_sizes.insert(w->name(), w->size()); + if(m_type == SizeToGrid) // SizeToGrid also move widgets + m_pos.insert(w->name(), w->pos()); + } +} + +void +AdjustSizeCommand::execute() +{ + // To avoid creation of GeometryPropertyCommand + m_form->selectFormWidget(); + + int gridX = m_form->gridSize(); + int gridY = m_form->gridSize(); + int tmpw=0, tmph=0; + + WidgetList list; + QMap<QCString, QSize>::ConstIterator endIt = m_sizes.constEnd(); + for(QMap<QCString, QSize>::ConstIterator it = m_sizes.constBegin(); it != endIt; ++it) + { + ObjectTreeItem *item = m_form->objectTree()->lookup(it.key()); + if(item && item->widget()) + list.append(item->widget()); + } + + switch(m_type) + { + case SizeToGrid: + { + int tmpx=0, tmpy=0; + // same as in 'Align to Grid' + for the size + for(QWidget *w = list.first(); w; w = list.next()) + { + tmpx = int( (float)w->x() / ((float)gridX) + 0.5 ) * gridX; + tmpy = int( (float)w->y() / ((float)gridY) + 0.5 ) * gridY; + tmpw = int( (float)w->width() / ((float)gridX) + 0.5 ) * gridX; + tmph = int( (float)w->height() / ((float)gridY) + 0.5 ) * gridY; + + if((tmpx != w->x()) || (tmpy != w->y())) + w->move(tmpx, tmpy); + if((tmpw != w->width()) || (tmph != w->height())) + w->resize(tmpw, tmph); + } + break; + } + + case SizeToFit: + { + for(QWidget *w = list.first(); w; w = list.next()) { + ObjectTreeItem *item = m_form->objectTree()->lookup(w->name()); + if(item && !item->children()->isEmpty()) { // container + QSize s; + if(item->container() && item->container()->layout()) + s = w->sizeHint(); + else + s = getSizeFromChildren(item); + // minimum size for containers + if(s.width() < 30) + s.setWidth(30); + if(s.height() < 30) + s.setHeight(30); + // small hack for flow layouts + int type = item->container() ? item->container()->layoutType() : Container::NoLayout; + if(type == Container::HFlow) + s.setWidth(s.width() + 5); + else if(type == Container::VFlow) + s.setHeight(s.height() + 5); + w->resize(s); + } + else if(item && item->container()) // empty container + w->resize(item->container()->form()->gridSize() * 5, item->container()->form()->gridSize() * 5); // basic size + else { + QSize sizeHint(w->sizeHint()); + if (sizeHint.isValid()) + w->resize(sizeHint); + } + } + break; + } + + case SizeToSmallWidth: + { + for(QWidget *w = list.first(); w; w = list.next()) + { + if((tmpw == 0) || (w->width() < tmpw)) + tmpw = w->width(); + } + + for(QWidget *w = list.first(); w; w = list.next()) + { + if(tmpw != w->width()) + w->resize(tmpw, w->height()); + } + break; + } + + case SizeToBigWidth: + { + for(QWidget *w = list.first(); w; w = list.next()) + { + if(w->width() > tmpw) + tmpw = w->width(); + } + + for(QWidget *w = list.first(); w; w = list.next()) + { + if(tmpw != w->width()) + w->resize(tmpw, w->height()); + } + break; + } + + case SizeToSmallHeight: + { + for(QWidget *w = list.first(); w; w = list.next()) + { + if((tmph == 0) || (w->height() < tmph)) + tmph = w->height(); + } + + for(QWidget *w = list.first(); w; w = list.next()) + { + if(tmph != w->height()) + w->resize(w->width(), tmph); + } + break; + } + + case SizeToBigHeight: + { + for(QWidget *w = list.first(); w; w = list.next()) + { + if(w->height() > tmph) + tmph = w->height(); + } + + for(QWidget *w = list.first(); w; w = list.next()) + { + if(tmph != w->height()) + w->resize(w->width(), tmph); + } + break; + } + + default: + break; + } + + // We restore selection + for(QWidget *w = list.first(); w; w = list.next()) + m_form->setSelectedWidget(w, true); +} + +QSize +AdjustSizeCommand::getSizeFromChildren(ObjectTreeItem *item) +{ + if(!item->container()) // multi pages containers (eg tabwidget) + { + QSize s; + // get size for each container, and keep the biggest one + for(ObjectTreeItem *tree = item->children()->first(); tree; tree = item->children()->next()) + s = s.expandedTo(getSizeFromChildren(tree)); + return s; + } + + int tmpw = 0, tmph = 0; + for(ObjectTreeItem *tree = item->children()->first(); tree; tree = item->children()->next()) { + if(!tree->widget()) + continue; + tmpw = QMAX(tmpw, tree->widget()->geometry().right()); + tmph = QMAX(tmph, tree->widget()->geometry().bottom()); + } + + return QSize(tmpw, tmph) + QSize(10, 10); +} + +void +AdjustSizeCommand::unexecute() +{ + // To avoid creation of GeometryPropertyCommand + m_form->selectFormWidget(); + // We resize widgets to their original size + QMap<QCString, QSize>::ConstIterator endIt = m_sizes.constEnd(); + for(QMap<QCString, QSize>::ConstIterator it = m_sizes.constBegin(); it != endIt; ++it) + { + ObjectTreeItem *item = m_form->objectTree()->lookup(it.key()); + if(item && item->widget()) + { + item->widget()->resize( m_sizes[item->widget()->name()] ); + if(m_type == SizeToGrid) + item->widget()->move( m_pos[item->widget()->name()] ); + m_form->setSelectedWidget(item->widget(), true); // We restore selection + } + } +} + +QString +AdjustSizeCommand::name() const +{ + switch(m_type) + { + case SizeToGrid: + return i18n("Resize Widgets to Grid"); + case SizeToFit: + return i18n("Resize Widgets to Fit Contents"); + case SizeToSmallWidth: + return i18n("Resize Widgets to Narrowest"); + case SizeToBigWidth: + return i18n("Resize Widgets to Widest"); + case SizeToSmallHeight: + return i18n("Resize Widgets to Shortest"); + case SizeToBigHeight: + return i18n("Resize Widgets to Tallest"); + default: + return QString::null; + } +} + +void +AdjustSizeCommand::debug() +{ + kdDebug() << "AdjustSizeCommand: name=\"" << name() << "\" form=" << m_form->widget()->name() + << " widgets=" << m_sizes.keys() << endl; +} + +// LayoutPropertyCommand + +LayoutPropertyCommand::LayoutPropertyCommand(WidgetPropertySet *buf, const QCString &wname, + const QVariant &oldValue, const QVariant &value) + : PropertyCommand(buf, wname, oldValue, value, "layout") +{ + m_form = FormManager::self()->activeForm(); + ObjectTreeItem* titem = m_form->objectTree()->lookup(wname); + if (!titem) + return; //better this than a crash + Container *m_container = titem->container(); + // We save the geometry of each wigdet + for(ObjectTreeItem *it = m_container->objectTree()->children()->first(); it; it = m_container->objectTree()->children()->next()) + m_geometries.insert(it->name().latin1(), it->widget()->geometry()); +} + +void +LayoutPropertyCommand::execute() +{ + PropertyCommand::execute(); +} + +void +LayoutPropertyCommand::unexecute() +{ + ObjectTreeItem* titem = m_form->objectTree()->lookup(m_oldvalues.begin().key()); + if (!titem) + return; //better this than a crash + Container *m_container = titem->container(); + m_container->setLayout(Container::NoLayout); + // We put every widget back in its old location + QMap<QCString,QRect>::ConstIterator endIt = m_geometries.constEnd(); + for(QMap<QCString,QRect>::ConstIterator it = m_geometries.constBegin(); it != endIt; ++it) + { + ObjectTreeItem *tree = m_container->form()->objectTree()->lookup(it.key()); + if(tree) + tree->widget()->setGeometry(it.data()); + } + + PropertyCommand::unexecute(); +} + +QString +LayoutPropertyCommand::name() const +{ + return i18n("Change layout of widget \"%1\"").arg(m_oldvalues.begin().key()); +} + +void +LayoutPropertyCommand::debug() +{ + kdDebug() << "LayoutPropertyCommand: name=\"" << name() << "\" oldValue=" << m_oldvalues.keys() + << " value=" << m_value << endl; +} + +// InsertWidgetCommand + +InsertWidgetCommand::InsertWidgetCommand(Container *container) + : Command() +{ + m_containername = container->widget()->name(); + m_form = container->form(); + m_class = FormManager::self()->selectedClass(); + m_insertRect = container->m_insertRect; + m_point = container->m_insertBegin; + m_name = container->form()->objectTree()->generateUniqueName( + container->form()->library()->namePrefix(m_class).latin1(), + /*!numberSuffixRequired*/false); +} + +InsertWidgetCommand::InsertWidgetCommand(Container *container, + const QCString& className, const QPoint& pos, const QCString& namePrefix) + : Command() +{ + m_containername = container->widget()->name(); + m_form = container->form(); + m_class = className; + //m_insertRect is null (default) + m_point = pos; + if (namePrefix.isEmpty()) { + m_name = container->form()->objectTree()->generateUniqueName( + container->form()->library()->namePrefix(m_class).latin1() ); + } + else { + m_name = container->form()->objectTree()->generateUniqueName( + namePrefix, false /*!numberSuffixRequired*/ ); + } +} + +void +InsertWidgetCommand::execute() +{ + if (!m_form->objectTree()) + return; + ObjectTreeItem* titem = m_form->objectTree()->lookup(m_containername); + if (!titem) + return; //better this than a crash + Container *m_container = titem->container(); + int options = WidgetFactory::DesignViewMode | WidgetFactory::AnyOrientation; + if (m_container->form()->library()->internalProperty(m_class, "orientationSelectionPopup")=="1") { + if(m_insertRect.isValid()) { + if (m_insertRect.width() < m_insertRect.height()) { + options |= WidgetFactory::VerticalOrientation; + options ^= WidgetFactory::AnyOrientation; + } + else if (m_insertRect.width() > m_insertRect.height()) { + options |= WidgetFactory::HorizontalOrientation; + options ^= WidgetFactory::AnyOrientation; + } + } + if (options & WidgetFactory::AnyOrientation) { + options ^= WidgetFactory::AnyOrientation; + options |= m_container->form()->library()->showOrientationSelectionPopup( + m_class, m_container->m_container, + m_container->form()->widget()->mapToGlobal(m_point)); + if (options & WidgetFactory::AnyOrientation) + return; //cancelled + } + } + else + options |= WidgetFactory::AnyOrientation; + + QWidget *w = m_container->form()->library()->createWidget(m_class, m_container->m_container, m_name, + m_container, options); + + if(!w) { + FormManager::self()->stopInsert(); + WidgetInfo *winfo = m_container->form()->library()->widgetInfoForClassName(m_class); + KMessageBox::sorry(FormManager::self()->activeForm() ? FormManager::self()->activeForm()->widget() : 0, + i18n("Could not insert widget of type \"%1\". A problem with widget's creation encountered.") + .arg(winfo ? winfo->name() : QString::null)); + kdWarning() << "InsertWidgetCommand::execute() ERROR: widget creation failed" << endl; + return; + } +#if KDE_VERSION >= KDE_MAKE_VERSION(3,4,0) +//! @todo allow setting this for data view mode as well + if (m_form->designMode()) { + //don't generate accelerators for widgets in design mode + KAcceleratorManager::setNoAccel(w); + } +#endif + + // if the insertRect is invalid (ie only one point), we use widget' size hint + if(( (m_insertRect.width() < 21) && (m_insertRect.height() < 21))) + { + QSize s = w->sizeHint(); + + if(s.isEmpty()) + s = QSize(20, 20); // Minimum size to avoid creating a (0,0) widget + int x, y; + if(m_insertRect.isValid()) + { + x = m_insertRect.x(); + y = m_insertRect.y(); + } + else + { + x = m_point.x(); + y = m_point.y(); + } + m_insertRect = QRect(x, y, s.width() + 16/* add some space so more text can be entered*/, + s.height()); + } + w->move(m_insertRect.x(), m_insertRect.y()); + w->resize(m_insertRect.width()-1, m_insertRect.height()-1); // -1 is not to hide dots + w->setStyle(&(m_container->widget()->style())); + w->setBackgroundOrigin(QWidget::ParentOrigin); + w->show(); + + FormManager::self()->stopInsert(); + + // ObjectTreeItem object already exists for widgets which corresponds to a Container + // it's already created in Container's constructor + ObjectTreeItem *item = m_container->form()->objectTree()->lookup(m_name); + if (!item) { //not yet created... + m_container->form()->objectTree()->addItem(m_container->m_tree, + item = new ObjectTreeItem(m_container->form()->library()->displayName(m_class), m_name, w, m_container) + ); + } + //assign item for its widget if it supports DesignTimeDynamicChildWidgetHandler interface + //(e.g. KexiDBAutoField) + if (m_form->designMode() && dynamic_cast<DesignTimeDynamicChildWidgetHandler*>(w)) { + dynamic_cast<DesignTimeDynamicChildWidgetHandler*>(w)->assignItem(item); + } + + // We add the autoSaveProperties in the modifProp list of the ObjectTreeItem, so that they are saved later + QValueList<QCString> list(m_container->form()->library()->autoSaveProperties(w->className())); + + QValueList<QCString>::ConstIterator endIt = list.constEnd(); + for(QValueList<QCString>::ConstIterator it = list.constBegin(); it != endIt; ++it) + item->addModifiedProperty(*it, w->property(*it)); + + m_container->reloadLayout(); // reload the layout to take the new wigdet into account + + m_container->setSelectedWidget(w, false); + if (m_container->form()->library()->internalProperty(w->className(), + "dontStartEditingOnInserting").isEmpty()) + { + m_container->form()->library()->startEditing( + w->className(), w, item->container() ? item->container() : m_container); // we edit the widget on creation + } +//! @todo update widget's width for entered text's metrics + kdDebug() << "Container::eventFilter(): widget added " << this << endl; +} + +void +InsertWidgetCommand::unexecute() +{ + ObjectTreeItem* titem = m_form->objectTree()->lookup(m_name); + if (!titem) + return; //better this than a crash + QWidget *m_widget = titem->widget(); + Container *m_container = m_form->objectTree()->lookup(m_containername)->container(); + m_container->deleteWidget(m_widget); +} + +QString +InsertWidgetCommand::name() const +{ + if(!m_name.isEmpty()) + return i18n("Insert widget \"%1\"").arg(m_name); + else + return i18n("Insert widget"); +} + +void +InsertWidgetCommand::debug() +{ + kdDebug() << "InsertWidgetCommand: name=\"" << name() << "\" generatedName=" << m_name + << " container=" << m_containername + << " form=" << m_form->widget()->name() << " class=" << m_class + << " rect=" << m_insertRect << " pos=" << m_point << endl; +} + +/// CreateLayoutCommand /////////////// + +CreateLayoutCommand::CreateLayoutCommand(int layoutType, WidgetList &list, Form *form) + : m_form(form), m_type(layoutType) +{ + WidgetList *m_list=0; + switch(layoutType) + { + case Container::HBox: + case Container::Grid: + case Container::HSplitter: + case Container::HFlow: + m_list = new HorWidgetList(form->toplevelContainer()->widget()); break; + case Container::VBox: + case Container::VSplitter: + case Container::VFlow: + m_list = new VerWidgetList(form->toplevelContainer()->widget()); break; + } + for(QWidget *w = list.first(); w; w = list.next()) + m_list->append(w); + m_list->sort(); // we sort them now, before creating the layout + + for(QWidget *w = m_list->first(); w; w = m_list->next()) + m_pos.insert(w->name(), w->geometry()); + ObjectTreeItem *item = form->objectTree()->lookup(m_list->first()->name()); + if(item && item->parent()->container()) + m_containername = item->parent()->name(); + delete m_list; +} + +void +CreateLayoutCommand::execute() +{ + WidgetLibrary *lib = m_form->library(); + if(!lib) + return; + ObjectTreeItem* titem = m_form->objectTree()->lookup(m_containername); + Container *container = titem ? titem->container() : 0; + if(!container) + container = m_form->toplevelContainer(); // use toplevelContainer by default + + QCString classname; + switch(m_type) { + case Container::HSplitter: case Container::VSplitter: + classname = "QSplitter"; break; + default: + classname = Container::layoutTypeToString(m_type).latin1(); + } + + if(m_name.isEmpty())// the name must be generated only once + m_name = m_form->objectTree()->generateUniqueName(classname); + + QWidget *w = lib->createWidget(classname, container->widget(), m_name.latin1(), container); +#if KDE_VERSION >= KDE_MAKE_VERSION(3,4,0) +//! @todo allow setting this for data view mode as well + if (w) { + if (m_form->designMode()) { + //don't generate accelerators for widgets in design mode + KAcceleratorManager::setNoAccel(w); + } + } +#endif + ObjectTreeItem *tree = w ? m_form->objectTree()->lookup(w->name()) : 0; + if(!tree) + return; + + container->setSelectedWidget(0, false); + w->move(m_pos.begin().data().topLeft()); // we move the layout at the position of the topleft widget + // sizeHint of these widgets depends on geometry, so give them appropriate geometry + if(m_type == Container::HFlow) + w->resize( QSize(700, 20) ); + else if(m_type == Container::VFlow) + w->resize( QSize(20, 700)); + w->show(); + + // We reparent every widget to the Layout and insert them into it + QMap<QCString,QRect>::ConstIterator endIt = m_pos.constEnd(); + for(QMap<QCString,QRect>::ConstIterator it = m_pos.constBegin(); it != endIt; ++it) + { + ObjectTreeItem *item = m_form->objectTree()->lookup(it.key()); + if(item && item->widget()) + { + item->widget()->reparent(w, item->widget()->pos(), true); + item->eventEater()->setContainer(tree->container()); + m_form->objectTree()->reparent(item->name(), m_name); + } + } + + if(m_type == Container::HSplitter) + ((QSplitter*)w)->setOrientation(QSplitter::Horizontal); + else if(m_type == Container::VSplitter) + ((QSplitter*)w)->setOrientation(QSplitter::Vertical); + else if(tree->container()) { + tree->container()->setLayout((Container::LayoutType)m_type); + w->resize(tree->container()->layout()->sizeHint()); // the layout doesn't have its own size + } + + container->setSelectedWidget(w, false); + FormManager::self()->windowChanged(m_form->widget()); // to reload the ObjectTreeView +} + +void +CreateLayoutCommand::unexecute() +{ + ObjectTreeItem *parent = m_form->objectTree()->lookup(m_containername); + if(!parent) + parent = m_form->objectTree(); + + // We reparent every widget to the Container and take them out of the layout + QMap<QCString,QRect>::ConstIterator endIt = m_pos.constEnd(); + for(QMap<QCString,QRect>::ConstIterator it = m_pos.constBegin(); it != endIt; ++it) + { + ObjectTreeItem *item = m_form->objectTree()->lookup(it.key()); + if(item && item->widget()) + { + item->widget()->reparent(parent->widget(), QPoint(0,0), true); + item->eventEater()->setContainer(parent->container()); + if(m_pos[it.key()].isValid()) + item->widget()->setGeometry(m_pos[it.key()]); + m_form->objectTree()->reparent(item->name(), m_containername); + } + } + + if(!parent->container()) + return; + ObjectTreeItem* titem = m_form->objectTree()->lookup(m_name); + if (!titem) + return; //better this than a crash + QWidget *w = titem->widget(); + parent->container()->deleteWidget(w); // delete the layout widget + FormManager::self()->windowChanged(m_form->widget()); // to reload ObjectTreeView +} + +QString +CreateLayoutCommand::name() const +{ + switch(m_type) + { + case Container::HBox: + return i18n("Group Widgets Horizontally"); + case Container::VBox: + return i18n("Group Widgets Vertically"); + case Container::Grid: + return i18n("Group Widgets in a Grid"); + case Container::HSplitter: + return i18n("Group Widgets Horizontally in a Splitter"); + case Container::VSplitter: + return i18n("Group Widgets Vertically in a Splitter"); + case Container::HFlow: + return i18n("Group Widgets By Rows"); + case Container::VFlow: + return i18n("Group Widgets Vertically By Columns"); + default: + return i18n("Group widgets"); + } +} + +void +CreateLayoutCommand::debug() +{ + kdDebug() << "CreateLayoutCommand: name=\"" << name() << "\" generatedName=" << m_name + << " widgets=" << m_pos.keys() << " container=" << m_containername + << " form=" << m_form->widget()->name() << endl; +} + +/// BreakLayoutCommand /////////////// + +BreakLayoutCommand::BreakLayoutCommand(Container *container) + : CreateLayoutCommand() +{ + m_containername = container->toplevel()->widget()->name(); + m_name = container->widget()->name(); + m_form = container->form(); + m_type = container->layoutType(); + + for(ObjectTreeItem *tree = container->objectTree()->children()->first(); tree; tree = container->objectTree()->children()->next()) + { + QRect r(container->widget()->mapTo(container->widget()->parentWidget(), tree->widget()->pos()), tree->widget()->size()); + m_pos.insert(tree->widget()->name(), r); + } +} + +void +BreakLayoutCommand::execute() +{ + CreateLayoutCommand::unexecute(); +} + +void +BreakLayoutCommand::unexecute() +{ + CreateLayoutCommand::execute(); +} + +QString +BreakLayoutCommand::name() const +{ + return i18n("Break Layout: \"%1\"").arg(m_name); +} + +void +BreakLayoutCommand::debug() +{ + kdDebug() << "BreakLayoutCommand: name=\"" << name() + << " widgets=" << m_pos.keys() << " container=" << m_containername + << " form=" << m_form->widget()->name() << endl; +} + +// PasteWidgetCommand + +PasteWidgetCommand::PasteWidgetCommand(QDomDocument &domDoc, Container *container, const QPoint& p) + : m_point(p) +{ + m_data = domDoc.toCString(); + m_containername = container->widget()->name(); + m_form = container->form(); + + if(domDoc.namedItem("UI").firstChild().nextSibling().toElement().tagName() != "widget") + return; + + QRect boundingRect; + for(QDomNode n = domDoc.namedItem("UI").firstChild(); !n.isNull(); n = n.nextSibling()) // more than one widget + { + if(n.toElement().tagName() != "widget") + continue; + QDomElement el = n.toElement(); + + QDomElement rect; + for(QDomNode n = el.firstChild(); !n.isNull(); n = n.nextSibling()) + { + if((n.toElement().tagName() == "property") && (n.toElement().attribute("name") == "geometry")) + rect = n.firstChild().toElement(); + } + + QDomElement x = rect.namedItem("x").toElement(); + QDomElement y = rect.namedItem("y").toElement(); + QDomElement wi = rect.namedItem("width").toElement(); + QDomElement h = rect.namedItem("height").toElement(); + + int rx = x.text().toInt(); + int ry = y.text().toInt(); + int rw = wi.text().toInt(); + int rh = h.text().toInt(); + QRect r(rx, ry, rw, rh); + boundingRect = boundingRect.unite(r); + } + + m_point = m_point - boundingRect.topLeft(); +} + +void +PasteWidgetCommand::execute() +{ + ObjectTreeItem* titem = m_form->objectTree()->lookup(m_containername); + if (!titem) + return; //better this than a crash + Container *container = titem->container(); + QString errMsg; + int errLine; + int errCol; + QDomDocument domDoc("UI"); + bool parsed = domDoc.setContent(m_data, false, &errMsg, &errLine, &errCol); + + if(!parsed) + { + kdDebug() << "WidgetWatcher::load(): " << errMsg << endl; + kdDebug() << "WidgetWatcher::load(): line: " << errLine << " col: " << errCol << endl; + return; + } + + //FormIO::setCurrentForm(m_container->form()); + + kdDebug() << domDoc.toString() << endl; + if(!domDoc.namedItem("UI").hasChildNodes()) // nothing in the doc + return; + if(domDoc.namedItem("UI").firstChild().nextSibling().toElement().tagName() != "widget") // only one widget, so we can paste it at cursor pos + { + QDomElement el = domDoc.namedItem("UI").firstChild().toElement(); + fixNames(el); + if(m_point.isNull()) + fixPos(el, container); + else + changePos(el, m_point); + + m_form->setInteractiveMode(false); + FormIO::loadWidget(container, el); + m_form->setInteractiveMode(true); + } + else for(QDomNode n = domDoc.namedItem("UI").firstChild(); !n.isNull(); n = n.nextSibling()) // more than one widget + { + if(n.toElement().tagName() != "widget") + continue; + QDomElement el = n.toElement(); + fixNames(el); + if(!m_point.isNull()) + moveWidgetBy(el, container, m_point); + else { + fixPos(el, container); + kdDebug() << "jdkjfldfksmfkdfjmqdsklfjdkkfmsqfksdfsm" << endl; + } + + m_form->setInteractiveMode(false); + FormIO::loadWidget(container, el); + m_form->setInteractiveMode(true); + } + + //FormIO::setCurrentForm(0); + m_names.clear(); + // We store the names of all the created widgets, to delete them later + for(QDomNode n = domDoc.namedItem("UI").firstChild(); !n.isNull(); n = n.nextSibling()) + { + if(n.toElement().tagName() != "widget") + continue; + for(QDomNode m = n.firstChild(); !m.isNull(); n = m.nextSibling()) + { + if((m.toElement().tagName() == "property") && (m.toElement().attribute("name") == "name")) + { + m_names.append(m.toElement().text()); + break; + } + } + } + + container->form()->selectFormWidget(); + QStringList::ConstIterator endIt = m_names.constEnd(); + for(QStringList::ConstIterator it = m_names.constBegin(); it != endIt; ++it) // We select all the pasted widgets + { + ObjectTreeItem *item = m_form->objectTree()->lookup(*it); + if(item) + container->setSelectedWidget(item->widget(), true); + } +} + +void +PasteWidgetCommand::unexecute() +{ + ObjectTreeItem* titem = m_form->objectTree()->lookup(m_containername); + if (!titem) + return; //better this than a crash + Container *container = titem->container(); + // We just delete all the widgets we have created + QStringList::ConstIterator endIt = m_names.constEnd(); + for(QStringList::ConstIterator it = m_names.constBegin(); it != endIt; ++it) + { + ObjectTreeItem* titem = container->form()->objectTree()->lookup(*it); + if (!titem) + continue; //better this than a crash + QWidget *w = titem->widget(); + container->deleteWidget(w); + } +} + +QString +PasteWidgetCommand::name() const +{ + return i18n("Paste"); +} + +void +//QDomElement +PasteWidgetCommand::changePos(QDomElement &el, const QPoint &newpos) +{ + //QDomElement el = widg.cloneNode(true).toElement(); + QDomElement rect; + // Find the widget geometry if there is one + for(QDomNode n = el.firstChild(); !n.isNull(); n = n.nextSibling()) + { + if((n.toElement().tagName() == "property") && (n.toElement().attribute("name") == "geometry")) + rect = n.firstChild().toElement(); + } + + QDomElement x = rect.namedItem("x").toElement(); + x.removeChild(x.firstChild()); + QDomText valueX = el.ownerDocument().createTextNode(QString::number(newpos.x())); + x.appendChild(valueX); + + QDomElement y = rect.namedItem("y").toElement(); + y.removeChild(y.firstChild()); + QDomText valueY = el.ownerDocument().createTextNode(QString::number(newpos.y())); + y.appendChild(valueY); + + //return el; +} + +void +PasteWidgetCommand::fixPos(QDomElement &el, Container *container) +{ +/* QDomElement rect; + for(QDomNode n = el.firstChild(); !n.isNull(); n = n.nextSibling()) + { + if((n.toElement().tagName() == "property") && (n.toElement().attribute("name") == "geometry")) + rect = n.firstChild().toElement(); + } + + QDomElement x = rect.namedItem("x").toElement(); + QDomElement y = rect.namedItem("y").toElement(); + QDomElement wi = rect.namedItem("width").toElement(); + QDomElement h = rect.namedItem("height").toElement(); + + int rx = x.text().toInt(); + int ry = y.text().toInt(); + int rw = wi.text().toInt(); + int rh = h.text().toInt(); + QRect r(rx, ry, rw, rh); + + QWidget *w = m_form->widget()->childAt(r.x() + 6, r.y() + 6, false); + if(!w) + return; + + while((w->geometry() == r) && (w != 0))// there is already a widget there, with the same size + { + w = m_form->widget()->childAt(w->x() + 16, w->y() + 16, false); + r.moveBy(10,10); + } + + // the pasted wigdet should stay inside container's boundaries + if(r.x() < 0) + r.moveLeft(0); + else if(r.right() > container->widget()->width()) + r.moveLeft(container->widget()->width() - r.width()); + + if(r.y() < 0) + r.moveTop(0); + else if(r.bottom() > container->widget()->height()) + r.moveTop(container->widget()->height() - r.height()); + + if(r != QRect(rx, ry, rw, rh)) + //return el; + //else + changePos(el, QPoint(r.x(), r.y())); +*/ + moveWidgetBy(el, container, QPoint(0, 0)); +} + +void +PasteWidgetCommand::moveWidgetBy(QDomElement &el, Container *container, const QPoint &p) +{ + QDomElement rect; + for(QDomNode n = el.firstChild(); !n.isNull(); n = n.nextSibling()) + { + if((n.toElement().tagName() == "property") && (n.toElement().attribute("name") == "geometry")) + rect = n.firstChild().toElement(); + } + + QDomElement x = rect.namedItem("x").toElement(); + QDomElement y = rect.namedItem("y").toElement(); + QDomElement wi = rect.namedItem("width").toElement(); + QDomElement h = rect.namedItem("height").toElement(); + + int rx = x.text().toInt(); + int ry = y.text().toInt(); + int rw = wi.text().toInt(); + int rh = h.text().toInt(); + QRect r(rx + p.x(), ry + p.y(), rw, rh); + kdDebug() << "Moving widget by " << p << " from " << rx << " " << ry << " to " << r.topLeft() << endl; + + QWidget *w = m_form->widget()->childAt(r.x() + 6, r.y() + 6, false); + + while(w && (w->geometry() == r))// there is already a widget there, with the same size + { + w = m_form->widget()->childAt(w->x() + 16, w->y() + 16, false); + r.moveBy(10,10); + } + + // the pasted wigdet should stay inside container's boundaries + if(r.x() < 0) + r.moveLeft(0); + else if(r.right() > container->widget()->width()) + r.moveLeft(container->widget()->width() - r.width()); + + if(r.y() < 0) + r.moveTop(0); + else if(r.bottom() > container->widget()->height()) + r.moveTop(container->widget()->height() - r.height()); + + if(r != QRect(rx, ry, rw, rh)) + //return el; + //else + changePos(el, QPoint(r.x(), r.y())); +} + +void +PasteWidgetCommand::fixNames(QDomElement &el) +{ + QString wname; + for(QDomNode n = el.firstChild(); !n.isNull(); n = n.nextSibling()) + { + if((n.toElement().tagName() == "property") && (n.toElement().attribute("name") == "name")) + { + wname = n.toElement().text(); + while(m_form->objectTree()->lookup(wname)) // name already exists + { + bool ok; + int num = wname.right(1).toInt(&ok, 10); + if(ok) + wname = wname.left(wname.length()-1) + QString::number(num+1); + else + wname += "2"; + } + if(wname != n.toElement().text()) // we change the name, so we recreate the element + { + n.removeChild(n.firstChild()); + QDomElement type = el.ownerDocument().createElement("string"); + QDomText valueE = el.ownerDocument().createTextNode(wname); + type.appendChild(valueE); + n.toElement().appendChild(type); + } + + } + if(n.toElement().tagName() == "widget") // fix child widgets names + { + QDomElement child = n.toElement(); + fixNames(child); + } + } +} + +void +PasteWidgetCommand::debug() +{ + kdDebug() << "PasteWidgetCommand: pos=" << m_point + << " widgets=" << m_names << " container=" << m_containername + << " form=" << m_form->widget()->name() + << " data=\"" << m_data.left(80) << "...\"" << endl; +} + +// DeleteWidgetCommand + +DeleteWidgetCommand::DeleteWidgetCommand(WidgetList &list, Form *form) + : Command(), m_form(form) +{ + m_domDoc = QDomDocument("UI"); + m_domDoc.appendChild(m_domDoc.createElement("UI")); + + QDomElement parent = m_domDoc.namedItem("UI").toElement(); + + //for(QWidget *w = list.first(); w; w = list.next()) + /*for(WidgetListIterator it(list); it.current() != 0; ++it) + { + QWidget *w = it.current(); + // Don't delete tabwidget or widgetstack pages + if(w->parentWidget()->inherits("QWidgetStack")) + { + list.remove(w); + continue; + } + }*/ + removeChildrenFromList(list); + + for(WidgetListIterator it(list); it.current() != 0; ++it) + { + ObjectTreeItem *item = m_form->objectTree()->lookup(it.current()->name()); + if (!item) + return; + + // We need to store both parentContainer and parentWidget as they may be different (eg for TabWidget page) + m_containers.insert(item->name().latin1(), m_form->parentContainer(item->widget())->widget()->name()); + m_parents.insert(item->name().latin1(), item->parent()->name().latin1()); + FormIO::saveWidget(item, parent, m_domDoc); + form->connectionBuffer()->saveAllConnectionsForWidget(item->widget()->name(), m_domDoc); + } + + FormIO::cleanClipboard(parent); +} + +void +DeleteWidgetCommand::execute() +{ + Container *containerToSelect = 0; + + QMap<QCString,QCString>::ConstIterator endIt = m_containers.constEnd(); + for(QMap<QCString,QCString>::ConstIterator it = m_containers.constBegin(); it != endIt; ++it) + { + ObjectTreeItem *item = m_form->objectTree()->lookup(it.key()); + if (!item || !item->widget()) + continue; + + Container *cont = m_form->parentContainer(item->widget()); + if (!containerToSelect) + containerToSelect = cont; + cont->deleteWidget(item->widget()); + } + //now we've nothing selecte: select parent container + if (containerToSelect) + m_form->setSelectedWidget( containerToSelect->widget() ); +} + +void +DeleteWidgetCommand::unexecute() +{ + QCString wname; + m_form->setInteractiveMode(false); + for(QDomNode n = m_domDoc.namedItem("UI").firstChild(); !n.isNull(); n = n.nextSibling()) + { + if(n.toElement().tagName() == "connections") // restore the widget connections + m_form->connectionBuffer()->load(n); + if(n.toElement().tagName() != "widget") + continue; + // We need first to know the name of the widget + for(QDomNode m = n.firstChild(); !m.isNull(); n = m.nextSibling()) + { + if((m.toElement().tagName() == "property") && (m.toElement().attribute("name") == "name")) + { + wname = m.toElement().text().latin1(); + break; + } + } + + ObjectTreeItem* titem = m_form->objectTree()->lookup(m_containers[wname]); + if (!titem) + return; //better this than a crash + Container *cont = titem->container(); + ObjectTreeItem *parent = m_form->objectTree()->lookup(m_parents[wname]); + QDomElement widg = n.toElement(); + if(parent) + FormIO::loadWidget(cont, widg, parent->widget()); + else + FormIO::loadWidget(cont, widg); + } + m_form->setInteractiveMode(true); +} + +QString +DeleteWidgetCommand::name() const +{ + return i18n("Delete widget"); +} + +void +DeleteWidgetCommand::debug() +{ + kdDebug() << "DeleteWidgetCommand: containers=" << m_containers.keys() + << " parents=" << m_parents.keys() << " form=" << m_form->widget()->name() << endl; +} + +// CutWidgetCommand + +CutWidgetCommand::CutWidgetCommand(WidgetList &list, Form *form) + : DeleteWidgetCommand(list, form) +{} + +void +CutWidgetCommand::execute() +{ + DeleteWidgetCommand::execute(); + m_data = FormManager::self()->m_domDoc.toCString(); + FormManager::self()->m_domDoc.setContent(m_domDoc.toCString()); +} + +void +CutWidgetCommand::unexecute() +{ + DeleteWidgetCommand::unexecute(); + FormManager::self()->m_domDoc.setContent(m_data); +} + +QString +CutWidgetCommand::name() const +{ + return i18n("Cut"); +} + +void +CutWidgetCommand::debug() +{ + kdDebug() << "CutWidgetCommand: containers=" << m_containers.keys() + << " parents=" << m_parents.keys() << " form=" << m_form->widget()->name() + << " data=\"" << m_data.left(80) << "...\"" << endl; +} + +// CommandGroup + +namespace KFormDesigner { +class CommandGroup::SubCommands : public KMacroCommand +{ + public: + SubCommands( const QString & name ) + : KMacroCommand(name) + { + } + const QPtrList<KCommand>& commands() const { return m_commands; } +}; +} + +CommandGroup::CommandGroup( const QString & name, WidgetPropertySet *propSet ) + : Command() + , m_subCommands(new SubCommands(name)) + , m_propSet(propSet) +{ +} + +CommandGroup::~CommandGroup() +{ + delete m_subCommands; +} + +const QPtrList<KCommand>& CommandGroup::commands() const +{ + return m_subCommands->commands(); +} + +void CommandGroup::addCommand(KCommand *command, bool allowExecute) +{ + if (!command) + return; + + m_subCommands->addCommand(command); + if (!allowExecute) + m_commandsShouldntBeExecuted.insert(command, (char*)1); +} + +void CommandGroup::execute() +{ + FormManager::self()->blockPropertyEditorUpdating(this); + for (QPtrListIterator<KCommand> it(m_subCommands->commands()); it.current(); ++it) { + if (!m_commandsShouldntBeExecuted[it.current()]) + it.current()->execute(); + } + FormManager::self()->unblockPropertyEditorUpdating(this, m_propSet); +} + +void CommandGroup::unexecute() +{ + FormManager::self()->blockPropertyEditorUpdating(this); + m_subCommands->unexecute(); + FormManager::self()->unblockPropertyEditorUpdating(this, m_propSet); +} + +QString CommandGroup::name() const +{ + return m_subCommands->name(); +} + +void CommandGroup::resetAllowExecuteFlags() +{ + m_commandsShouldntBeExecuted.clear(); +} + +void +CommandGroup::debug() +{ + kdDebug() << "*CommandGroup: name=\"" << name() << "\" #=" << m_subCommands->commands().count() << endl; + uint i = 1; + for (QPtrListIterator<KCommand> it(m_subCommands->commands()); it.current(); ++it, i++) { + kdDebug() << "#" << i << ":" + << (m_commandsShouldntBeExecuted[it.current()] ? "!" : "") << "allowExecute:" << endl; + if (dynamic_cast<Command*>(it.current())) + dynamic_cast<Command*>(it.current())->debug(); + else if (dynamic_cast<CommandGroup*>(it.current())) + dynamic_cast<CommandGroup*>(it.current())->debug(); + else + kdDebug() << "(other KCommand)" << endl; + } + kdDebug() << "End of CommandGroup" << endl; +} diff --git a/kexi/formeditor/commands.h b/kexi/formeditor/commands.h new file mode 100644 index 00000000..619ef791 --- /dev/null +++ b/kexi/formeditor/commands.h @@ -0,0 +1,380 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr> + Copyright (C) 2005 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. +*/ + +#ifndef KFORMEDITOR_COMMANDS_H +#define KFORMEDITOR_COMMANDS_H + +#include <qmap.h> +#include <qdict.h> +#include <qptrlist.h> +#include <qptrdict.h> +#include <qvariant.h> +#include <qdom.h> + +#include <kcommand.h> +#include "utils.h" + +class QWidget; +class QRect; +class QPoint; +class QStringList; +class QCString; + +namespace KFormDesigner { + +class WidgetPropertySet; +class ObjectTreeItem; +class Container; +class Form; + +//! Base class for KFormDesigner's commands +class KFORMEDITOR_EXPORT Command : public KCommand +{ + public: + Command(); + virtual ~Command(); + + virtual void debug() = 0; +}; + +/*! This command is used when changing a property for one or more widgets. \a oldvalues is a QMap + of the old values of the property for every widget, to allow reverting the change. \a value is + the new value of the property. You can use the simpler constructor for a single widget. + */ +class KFORMEDITOR_EXPORT PropertyCommand : public Command +{ + public: + PropertyCommand(WidgetPropertySet *set, const QCString &wname, const QVariant &oldValue, + const QVariant &value, const QCString &property); + PropertyCommand(WidgetPropertySet *set, const QMap<QCString, QVariant> &oldvalues, + const QVariant &value, const QCString &property); + + virtual void execute(); + virtual void unexecute(); + virtual QString name() const; + QCString property() const { return m_property; } + + void setValue(const QVariant &value); + const QMap<QCString, QVariant>& oldValues() const { return m_oldvalues; } + virtual void debug(); + + protected: + WidgetPropertySet *m_propSet; + QVariant m_value; + QMap<QCString, QVariant> m_oldvalues; + QCString m_property; +}; + +/*! This command is used when moving multiples widgets at the same time, while holding Ctrl or Shift. + You need to supply a list of widget names, and the position of the cursor before moving. Use setPos() + to tell the new cursor pos every time it changes.*/ +class KFORMEDITOR_EXPORT GeometryPropertyCommand : public Command +{ + public: + GeometryPropertyCommand(WidgetPropertySet *set, const QStringList &names, const QPoint& oldPos); + + virtual void execute(); + virtual void unexecute(); + virtual QString name() const; + void setPos(const QPoint& pos); + virtual void debug(); + + protected: + WidgetPropertySet *m_propSet; + QStringList m_names; + QPoint m_oldPos; + QPoint m_pos; +}; + +/*! This command is used when an item in 'Align Widgets position' is selected. You just need +to give the list of widget names (the selected ones), and the + type of alignment (see the enum for possible values). */ +class KFORMEDITOR_EXPORT AlignWidgetsCommand : public Command +{ + public: + enum { AlignToGrid = 100, AlignToLeft, AlignToRight, AlignToTop, AlignToBottom }; + + AlignWidgetsCommand(int type, WidgetList &list, Form *form); + + virtual void execute(); + virtual void unexecute(); + virtual QString name() const; + virtual void debug(); + + protected: + Form *m_form; + int m_type; + QMap<QCString, QPoint> m_pos; +}; + +/*! This command is used when an item in 'Adjust Widgets Size' is selected. You just need + to give the list of widget names (the selected ones), and the + type of size modification (see the enum for possible values). */ +class KFORMEDITOR_EXPORT AdjustSizeCommand : public Command +{ + public: + enum { SizeToGrid = 200, SizeToFit, SizeToSmallWidth, SizeToBigWidth, + SizeToSmallHeight, SizeToBigHeight }; + + AdjustSizeCommand(int type, WidgetList &list, Form *form); + + virtual void execute(); + virtual void unexecute(); + virtual QString name() const; + virtual void debug(); + + protected: + QSize getSizeFromChildren(ObjectTreeItem *item); + + protected: + Form *m_form; + int m_type; + QMap<QCString, QPoint> m_pos; + QMap<QCString, QSize> m_sizes; +}; + +/*! This command is used when switching the layout of a Container. It remembers the old pos + of every widget inside the Container. */ +class KFORMEDITOR_EXPORT LayoutPropertyCommand : public PropertyCommand +{ + public: + LayoutPropertyCommand(WidgetPropertySet *set, const QCString &wname, + const QVariant &oldValue, const QVariant &value); + + virtual void execute(); + virtual void unexecute(); + virtual QString name() const; + virtual void debug(); + + protected: + Form *m_form; + QMap<QCString,QRect> m_geometries; +}; + +/*! This command is used when inserting a widger using toolbar or menu. You only need to give +the parent Container and the widget pos. + The other information is taken from FormManager. */ +class KFORMEDITOR_EXPORT InsertWidgetCommand : public Command +{ + public: + InsertWidgetCommand(Container *container); + + /*! This ctor allows to set explicit class name and position. + Used for dropping widgets on the form surface. + If \a namePrefix is empty, widget's unique name is constructed using + hint for \a className (WidgetLibrary::namePrefix()), + otherwise, \a namePrefix is used to generate widget's name. + This allows e.g. inserting a widgets having name constructed using + */ + InsertWidgetCommand(Container *container, const QCString& className, + const QPoint& pos, const QCString& namePrefix = QCString()); + + virtual void execute(); + virtual void unexecute(); + virtual QString name() const; + virtual void debug(); + + //! \return inserted widget's name + QCString widgetName() const { return m_name; } + + protected: + Form *m_form; + QString m_containername; + QPoint m_point; + QCString m_name; + QCString m_class; + QRect m_insertRect; +}; + +/*! This command is used when creating a layout from some widgets using "Lay out in..." menu item. + It remembers the old pos of every widget, and takes care of updating ObjectTree too. You need + to supply a WidgetList of the selected widgets. */ +class KFORMEDITOR_EXPORT CreateLayoutCommand : public Command +{ + public: + CreateLayoutCommand(int layoutType, WidgetList &list, Form *form); + CreateLayoutCommand() {;} // for BreakLayoutCommand + + virtual void execute(); + virtual void unexecute(); + virtual QString name() const; + virtual void debug(); + + protected: + Form *m_form; + QString m_containername; + QString m_name; + QMap<QCString,QRect> m_pos; + int m_type; +}; + +/*! This command is used when the 'Break Layout' menu item is selected. It does exactly the + opposite of CreateLayoutCommand. */ +class KFORMEDITOR_EXPORT BreakLayoutCommand : public CreateLayoutCommand +{ + public: + BreakLayoutCommand(Container *container); + + virtual void execute(); + virtual void unexecute(); + virtual QString name() const; + virtual void debug(); +}; + +/*! This command is used when pasting widgets. You need to give the QDomDocument containing +the widget(s) to paste, and optionnally the point where to paste widgets. */ +class KFORMEDITOR_EXPORT PasteWidgetCommand : public Command +{ + public: + PasteWidgetCommand(QDomDocument &domDoc, Container *container, const QPoint& p = QPoint()); + + virtual void execute(); + virtual void unexecute(); + virtual QString name() const; + virtual void debug(); + + protected: + /*! Internal function used to change the coordinates of a widget to \a newpos + before pasting it (to paste it at the position of the contextual menu). It modifies + the "geometry" property of the QDomElement representing the widget. */ + void changePos(QDomElement &widg, const QPoint &newpos); + /*! Internal function used to fix the coordinates of a widget before pasting it + (to avoid having two widgets at the same position). It moves the widget by + (10, 10) increment (several times if there are already pasted widgets at this position). */ + void fixPos(QDomElement &el, Container *container); + void moveWidgetBy(QDomElement &el, Container *container, const QPoint &p); + /*! Internal function used to fix the names of the widgets before pasting them. + It prevents from pasting a widget with + the same name as an actual widget. The child widgets are also fixed recursively.\n + If the name of the widget ends with a number (eg "QLineEdit1"), the new name is + just incremented by one (eg becomes "QLineEdit2"). Otherwise, a "2" is just + appended at the end of the name (eg "myWidget" becomes "myWidget2"). */ + void fixNames(QDomElement &el); + + protected: + Form *m_form; + QCString m_data; + QString m_containername; + QPoint m_point; + QStringList m_names; +}; + +/*! This command is used when deleting a widget using the "Delete" menu item. +You need to give a WidgetList of the selected widgets. */ +class KFORMEDITOR_EXPORT DeleteWidgetCommand : public Command +{ + public: + DeleteWidgetCommand(WidgetList &list, Form *form); + + virtual void execute(); + virtual void unexecute(); + virtual QString name() const; + virtual void debug(); + + protected: + QDomDocument m_domDoc; + Form *m_form; + QMap<QCString, QCString> m_containers; + QMap<QCString, QCString> m_parents; +}; + +/*! This command is used when cutting widgets. It is basically a DeleteWidgetCommand +which also updates the clipboard contents. */ +class KFORMEDITOR_EXPORT CutWidgetCommand : public DeleteWidgetCommand +{ + public: + CutWidgetCommand(WidgetList &list, Form *form); + + virtual void execute(); + virtual void unexecute(); + virtual QString name() const; + virtual void debug(); + + protected: + QCString m_data; +}; + +/*! A Command Group is a command that holds several subcommands. + It will appear as one to the user and in the command history, + but it can use the implementation of multiple commands internally. + It extends KMacroCommand by providing the list of commands executed. + Selected subcommands can be marked as nonexecutable by adding them using + addCommand(KCommand *command, bool allowExecute) special method. +*/ +class KFORMEDITOR_EXPORT CommandGroup : public Command +{ + public: + CommandGroup( const QString & name, WidgetPropertySet *propSet ); + virtual ~CommandGroup(); + + /*! Like KmacroCommand::addCommand(KCommand*) + but if \a allowExecute is false, \a command will not be executed + as a subcommand when CommandGroup::execute() is called. + + This is useful e.g. in KexiFormView::insertAutoFields(), + where a number of subcommands of InsertWidgetCommand type and subcommands + is groupped using CommandGroup but some of these subcommands are executed + before executing CommandGroup::execute(). + + If \a allowExecute is true, this method behaves exactly like + KmacroCommand::addCommand(KCommand*). + + Note that unexecute() doesn't check \a allowExecute flag: + all subcommands will be unexecuted (in reverse order + to the one in which they were added). + */ + void addCommand(KCommand *command, bool allowExecute); + + /*! Executes all subcommands added to this group + in the same order as they were added. Subcommands added with + addCommand(KCommand *command, bool allowExecute) where allowExecute == false, + will not be executed. */ + virtual void execute(); + + /*! Unexecutes all subcommands added to this group, + (in reversed order). */ + virtual void unexecute(); + + virtual QString name() const; + + /*! \return a list of all subcommands of this group. + Note that if a given subcommand is a group itself, + it will not be expanded to subcommands on this list. */ + const QPtrList<KCommand>& commands() const; + + /*! Resets all 'allowExecute' flags that was set in addCommand(). + Call this after calling CommandGroup::execute() to ensure that + in the future, when REDO is be executed, all subcommands will be executed. */ + void resetAllowExecuteFlags(); + + virtual void debug(); + + protected: + class SubCommands; + SubCommands *m_subCommands; + //! Used to store pointers to subcommands that shouldn't be executed + //! on CommandGroup::execute() + QPtrDict<char> m_commandsShouldntBeExecuted; + WidgetPropertySet *m_propSet; +}; + +} + +#endif diff --git a/kexi/formeditor/connectiondialog.cpp b/kexi/formeditor/connectiondialog.cpp new file mode 100644 index 00000000..a40348e7 --- /dev/null +++ b/kexi/formeditor/connectiondialog.cpp @@ -0,0 +1,420 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr> + Copyright (C) 2004 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 <qlayout.h> +#include <qhbox.h> +#include <qlabel.h> +#include <qregexp.h> +#include <qmetaobject.h> +#include <qstrlist.h> + +#include <kpushbutton.h> +#include <kiconloader.h> +#include <kmessagebox.h> +#include <kdebug.h> +#include <klocale.h> + +#include "kexitableview.h" +#include "kexitableviewdata.h" +#include "events.h" +#include "form.h" +#include "formmanager.h" +#include "objecttree.h" + +#include "connectiondialog.h" + +namespace KFormDesigner { + +///////////////////////////////////////////////////////////////////////////////// +///////////// The dialog to edit or add/remove connections ////////////////////// +///////////////////////////////////////////////////////////////////////////////// +ConnectionDialog::ConnectionDialog(QWidget *parent) +: KDialogBase(parent, "connections_dialog", true, i18n("Edit Form Connections"), + Ok|Cancel|Details, Ok, false) +, m_buffer(0) +{ + QFrame *frame = makeMainWidget(); + QHBoxLayout *layout = new QHBoxLayout(frame, 0, 6); + + // Setup the details widget ///////// + QHBox *details = new QHBox(frame); + setDetailsWidget(details); + setDetails(true); + + m_pixmapLabel = new QLabel(details); + m_pixmapLabel->setFixedWidth( int(IconSize(KIcon::Desktop) * 1.5) ); + m_pixmapLabel->setAlignment(AlignHCenter | AlignTop); + + m_textLabel = new QLabel(details); + m_textLabel->setAlignment(AlignLeft | AlignTop); + //setStatusOk(); + + // And the KexiTableView //////// + m_data = new KexiTableViewData(); + m_table = new KexiTableView(0, frame, "connections_tableview"); + m_table->setSpreadSheetMode(); + m_table->setInsertingEnabled(true); + initTable(); + m_table->setData(m_data, false); + m_table->adjustColumnWidthToContents(0); + layout->addWidget(m_table); + + //// Setup the icon toolbar ///////////////// + QVBoxLayout *vlayout = new QVBoxLayout(layout, 3); + KPushButton *newItem = new KPushButton(SmallIconSet("filenew"), i18n("&New Connection"), frame); + vlayout->addWidget(newItem); + m_buttons.insert(BAdd, newItem); + connect(newItem, SIGNAL(clicked()), this, SLOT(newItem())); + + KPushButton *delItem = new KPushButton(SmallIconSet("editdelete"), i18n("&Remove Connection"), frame); + vlayout->addWidget(delItem); + m_buttons.insert(BRemove, delItem); + connect(delItem, SIGNAL(clicked()), this, SLOT(removeItem())); + + vlayout->addStretch(); + + setInitialSize(QSize(600, 300)); + //setWFlags(WDestructiveClose); + + connect(m_table,SIGNAL(cellSelected(int, int)), this, SLOT(slotCellSelected(int, int))); + connect(m_table->data(), SIGNAL(rowInserted(KexiTableItem*,bool)), this, SLOT(slotRowInserted(KexiTableItem*,bool))); + this->newItem(); +} + +void +ConnectionDialog::initTable() +{ + KexiTableViewColumn *col0 = new KexiTableViewColumn(i18n("OK?"), KexiDB::Field::Text); + col0->field()->setSubType("KIcon"); + col0->setReadOnly(true); + col0->field()->setDescription(i18n("Connection correctness")); + m_data->addColumn(col0); + + KexiTableViewColumn *col1 = new KexiTableViewColumn(i18n("Sender"), KexiDB::Field::Enum); + m_widgetsColumnData = new KexiTableViewData(KexiDB::Field::Text, KexiDB::Field::Text); + col1->setRelatedData( m_widgetsColumnData ); + m_data->addColumn(col1); + + KexiTableViewColumn *col2 = new KexiTableViewColumn(i18n("Signal"), KexiDB::Field::Enum); + m_signalsColumnData = new KexiTableViewData(KexiDB::Field::Text, KexiDB::Field::Text); + col2->setRelatedData( m_signalsColumnData ); + m_data->addColumn(col2); + + KexiTableViewColumn *col3 = new KexiTableViewColumn(i18n("Receiver"), KexiDB::Field::Enum); + col3->setRelatedData( m_widgetsColumnData ); + m_data->addColumn(col3); + + KexiTableViewColumn *col4 = new KexiTableViewColumn(i18n("Slot"), KexiDB::Field::Enum); + m_slotsColumnData = new KexiTableViewData(KexiDB::Field::Text, KexiDB::Field::Text); + col4->setRelatedData( m_slotsColumnData ); + m_data->addColumn(col4); + + QValueList<int> c; + c << 2 << 4; + m_table->maximizeColumnsWidth(c); + m_table->setColumnStretchEnabled( true, 4 ); + + connect(m_data, SIGNAL(aboutToChangeCell(KexiTableItem*, int, QVariant&, KexiDB::ResultInfo*)), + this,SLOT(slotCellChanged(KexiTableItem*, int, QVariant, KexiDB::ResultInfo*))); + connect(m_data, SIGNAL(rowUpdated(KexiTableItem*)), this, SLOT(checkConnection(KexiTableItem *))); + connect(m_table, SIGNAL(itemSelected(KexiTableItem *)), this, SLOT(checkConnection(KexiTableItem *))); +} + +void +ConnectionDialog::exec(Form *form) +{ + m_form = form; + updateTableData(); + + KDialogBase::exec(); +} + +void ConnectionDialog::slotCellSelected(int col, int row) +{ + m_buttons[BRemove]->setEnabled( row < m_table->rows() ); + KexiTableItem *item = m_table->itemAt(row); + if (!item) + return; + if(col == 2) // signal col + updateSignalList(item); + else if(col == 4) // slot col + updateSlotList(item); +} + +void ConnectionDialog::slotRowInserted(KexiTableItem* item,bool) +{ + m_buffer->append(new Connection()); + checkConnection( item ); +} + +void +ConnectionDialog::slotOk() +{ + // First we update our buffer contents + for(int i=0; i < m_table->rows(); i++) + { + KexiTableItem *item = m_table->itemAt(i); + Connection *c = m_buffer->at(i); + + c->setSender( (*item)[1].toString() ); + c->setSignal( (*item)[2].toString() ); + c->setReceiver( (*item)[3].toString() ); + c->setSlot( (*item)[4].toString() ); + } + + // then me make it replace form's current one + delete m_form->connectionBuffer(); + m_form->setConnectionBuffer(m_buffer); + + QDialog::accept(); +} + +void +ConnectionDialog::updateTableData() +{ + // First we update the columns data + ObjectTreeDict *dict = new ObjectTreeDict( *(m_form->objectTree()->dict()) ); + ObjectTreeDictIterator it(*dict); + for(; it.current(); ++it) + { + KexiTableItem *item = m_widgetsColumnData->createItem(); //new KexiTableItem(2); + (*item)[0] = it.current()->name(); + (*item)[1] = (*item)[0]; + m_widgetsColumnData->append(item); + } + delete dict; + + // Then we fill the columns with the form connections + for(Connection *c = m_form->connectionBuffer()->first(); c ; c = m_form->connectionBuffer()->next()) + { + KexiTableItem *item = m_table->data()->createItem(); //new KexiTableItem(5); + (*item)[1] = c->sender(); + (*item)[2] = c->signal(); + (*item)[3] = c->receiver(); + (*item)[4] = c->slot(); + m_table->insertItem(item, m_table->rows()); + } + + m_buffer = new ConnectionBuffer(*(m_form->connectionBuffer())); +} + +void +ConnectionDialog::setStatusOk(KexiTableItem *item) +{ + m_pixmapLabel->setPixmap( DesktopIcon("button_ok") ); + m_textLabel->setText("<qt><h2>The connection is OK.</h2></qt>"); + + if (!item) + item = m_table->selectedItem(); + if (m_table->currentRow() >= m_table->rows()) + item = 0; + + if (item) + (*item)[0] = "button_ok"; + else { + m_pixmapLabel->setPixmap( QPixmap() ); + m_textLabel->setText(QString::null); + } +} + +void +ConnectionDialog::setStatusError(const QString &msg, KexiTableItem *item) +{ + m_pixmapLabel->setPixmap( DesktopIcon("button_cancel") ); + m_textLabel->setText("<qt><h2>The connection is invalid.</h2></qt>" + msg); + + if (!item) + item = m_table->selectedItem(); + if (m_table->currentRow() >= m_table->rows()) + item = 0; + + if (item) + (*item)[0] = "button_cancel"; + else { + m_pixmapLabel->setPixmap( QPixmap() ); + m_textLabel->setText(QString::null); + } +} + +void +ConnectionDialog::slotCellChanged(KexiTableItem *item, int col, QVariant&, KexiDB::ResultInfo*) +{ + switch(col) + { + // sender changed, we clear siganl and slot + case 1: + (*item)[2] = QString(""); + // signal or receiver changed, we clear the slot cell + case 2: + case 3: + { + (*item)[4] = QString(""); + break; + } + default: + break; + } +} + +void +ConnectionDialog::updateSlotList(KexiTableItem *item) +{ + m_slotsColumnData->deleteAllRows(); + QString widget = (*item)[1].toString(); + QString signal = (*item)[2].toString(); + + if((widget.isEmpty()) || signal.isEmpty()) + return; + ObjectTreeItem *tree = m_form->objectTree()->lookup(widget); + if(!tree || !tree->widget()) + return; + + QString signalArg(signal); + signalArg = signalArg.remove( QRegExp(".*[(]|[)]") ); + + QStrList slotList = tree->widget()->metaObject()->slotNames(true); + QStrListIterator it(slotList); + for(; it.current() != 0; ++it) + { + // we add the slot only if it is compatible with the signal + QString slotArg(*it); + slotArg = slotArg.remove( QRegExp(".*[(]|[)]") ); + + if(!signalArg.startsWith(slotArg, true) && (!signal.isEmpty())) // args not compatible + continue; + + KexiTableItem *item = m_slotsColumnData->createItem(); //new KexiTableItem(2); + (*item)[0] = QString(*it); + (*item)[1] = (*item)[0]; + m_slotsColumnData->append(item); + } +} + +void +ConnectionDialog::updateSignalList(KexiTableItem *item) +{ + ObjectTreeItem *tree = m_form->objectTree()->lookup((*item)[1].toString()); + if(!tree || !tree->widget()) + return; + + m_signalsColumnData->deleteAllRows(); + QStrList signalList = tree->widget()->metaObject()->signalNames(true); + QStrListIterator it(signalList); + for(; it.current() != 0; ++it) + { + KexiTableItem *item = m_signalsColumnData->createItem(); //new KexiTableItem(2); + (*item)[0] = QString(*it); + (*item)[1] = (*item)[0]; + m_signalsColumnData->append(item); + } +} + +void +ConnectionDialog::checkConnection(KexiTableItem *item) +{ + // First we check if one column is empty + for(int i = 1; i < 5; i++) + { + if( !item || (*item)[i].toString().isEmpty()) + { + setStatusError( i18n("<qt>You have not selected item: <b>%1</b>.</qt>").arg(m_data->column(i)->captionAliasOrName()), + item); + return; + } + } + + // Then we check if signal/slot args are compatible + QString signal = (*item)[2].toString(); + signal = signal.remove( QRegExp(".*[(]|[)]") ); // just keep the args list + QString slot = (*item)[4].toString(); + slot = slot.remove( QRegExp(".*[(]|[)]") ); + + if(!signal.startsWith(slot, true)) + { + setStatusError( i18n("The signal/slot arguments are not compatible."), item); + return; + } + + setStatusOk(item); +} + +void +ConnectionDialog::newItem() +{ + m_table->acceptRowEdit(); + m_table->setCursorPosition(m_table->rows(), 1); +} + +void +ConnectionDialog::newItemByDragnDrop() +{ + KFormDesigner::FormManager::self()->startCreatingConnection(); + connect(KFormDesigner::FormManager::self(), SIGNAL(connectionAborted(KFormDesigner::Form*)), this, SLOT(slotConnectionAborted(KFormDesigner::Form*))); + connect(KFormDesigner::FormManager::self(), SIGNAL(connectionCreated(KFormDesigner::Form*, Connection&)), this, SLOT(slotConnectionCreated(KFormDesigner::Form*, Connection&)) ); + + hide(); +} + +void +ConnectionDialog::slotConnectionCreated(KFormDesigner::Form *form, Connection &connection) +{ + show(); + if(form != m_form) + return; + + Connection *c = new Connection(connection); + KexiTableItem *item = m_table->data()->createItem(); //new KexiTableItem(5); + (*item)[1] = c->sender(); + (*item)[2] = c->signal(); + (*item)[3] = c->receiver(); + (*item)[4] = c->slot(); + m_table->insertItem(item, m_table->rows()); + m_buffer->append(c); +} + +void +ConnectionDialog::slotConnectionAborted(KFormDesigner::Form *form) +{ + show(); + if(form != m_form) + return; + + newItem(); +} + +void +ConnectionDialog::removeItem() +{ + if(m_table->currentRow() == -1 || m_table->currentRow()>=m_table->rows()) + return; + + int confirm = KMessageBox::warningContinueCancel(parentWidget(), + QString("<qt>")+i18n("Do you want to delete this connection ?")+"</qt>", QString::null, KGuiItem(i18n("&Delete Connection")), + "dontAskBeforeDeleteConnection"/*config entry*/); + if(confirm == KMessageBox::Cancel) + return; + + m_buffer->remove(m_table->currentRow()); + m_table->deleteItem(m_table->selectedItem()); +} + +} + +#include "connectiondialog.moc" diff --git a/kexi/formeditor/connectiondialog.h b/kexi/formeditor/connectiondialog.h new file mode 100644 index 00000000..3e6ac07b --- /dev/null +++ b/kexi/formeditor/connectiondialog.h @@ -0,0 +1,114 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr> + Copyright (C) 2004 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. +*/ + +#ifndef FORMCONNECTIONDIALOG_H +#define FORMCONNECTIONDIALOG_H + +#include <qintdict.h> +#include <kdialogbase.h> + +namespace KexiDB { + class ResultInfo; +} + +class QLabel; +class QButton; +class KexiTableView; +class KexiTableViewData; +class KexiTableItem; + +namespace KFormDesigner { + +class Form; +class ConnectionBuffer; +class Connection; + + +/*! This dialog is used to edit the connections of a form. It uses KexiTableView for this. There is also a details widget (icon + text)) that shows correctness + of current connection. */ +class KFORMEDITOR_EXPORT ConnectionDialog : public KDialogBase +{ + Q_OBJECT + + public: + ConnectionDialog(QWidget *parent); + ~ConnectionDialog() {;} + + /*! Displays as modal dialog, to edit connections in Form::connectionBuffer(). */ + void exec(Form *form); + + protected: + /*! Used when connection is ok. Displays a message in details widget and changes icon in 'OK?' column. */ + void setStatusOk(KexiTableItem *item = 0); + /*! Used when connection is wrong. Displays a message in details widget and changes icon in 'OK?' column. \a msg is + the message explaining what's wrong. */ + void setStatusError(const QString &msg, KexiTableItem *item = 0); + //! Inits table data, columns, etc. + void initTable(); + /*! Updates the widget list (shown in receiver and sender columns). Then fill in the table with the connections in m_buffer. */ + void updateTableData(); + /*! Updates the slot list, according to the receiver name, and only shows those who are compatible with signal args. */ + void updateSlotList(KexiTableItem *item); + //! Updates the signal list, according to the sender name. + void updateSignalList(KexiTableItem *item); + + protected slots: + /*! Slot called when the user modifies a cell. Signal and/or slot cells are cleared if necessary (not valid anymore). */ + void slotCellChanged(KexiTableItem*, int, QVariant&, KexiDB::ResultInfo*); + /*! This function checks if the connection represented by KexiTableItem \a item is valid. It checks if all args (sender, receiver, signal and slot) + are given, and then if signal/slot args are compatible (should be always true, as we don't show non-compatible slots). It calls \ref setStatusOk() + or \ref setStatusError() following the result of checks. */ + void checkConnection(KexiTableItem *item); + + /*! Hides the dialog and allow the user to create the Connection by drag-and-drop in the Form itself. Currently disabled in the GUI. + \sa FormManager::startCreatingConnection() */ + void newItemByDragnDrop(); + /*! Creates a new item. It just moves the cursor to the last empty row. */ + void newItem(); + void removeItem(); + + /*! This slot is called when the user ends connection creation (when in drag-and-drop mode). The dialog is restored, + and the created connection is added to the list. */ + void slotConnectionCreated(KFormDesigner::Form *form, KFormDesigner::Connection &connection); + /*! This slot is called when the user aborts connection creation (when in drag-and-drop mode). The dialog is restored, + and an empty connection is created. */ + void slotConnectionAborted(KFormDesigner::Form *form); + + void slotCellSelected(int col, int row); + void slotRowInserted(KexiTableItem*,bool); + + /*! Slot called when the user presses 'Ok' button. The Form::connectionBuffer() is deleted, created again and filled with Connection. + If the user presses 'Cancel', nothing happens. */ + virtual void slotOk(); + + protected: + enum {BAdd = 10, BRemove}; + Form *m_form; + ConnectionBuffer *m_buffer; + KexiTableView *m_table; + KexiTableViewData *m_data; + KexiTableViewData *m_widgetsColumnData, *m_slotsColumnData, *m_signalsColumnData; + QLabel *m_pixmapLabel, *m_textLabel; + QIntDict<QButton> m_buttons; //! dict of button (for disabling them) +}; + +} + +#endif diff --git a/kexi/formeditor/container.cpp b/kexi/formeditor/container.cpp new file mode 100644 index 00000000..75a6dea6 --- /dev/null +++ b/kexi/formeditor/container.cpp @@ -0,0 +1,1182 @@ +/* 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> + + 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 <qpainter.h> +#include <qpixmap.h> +#include <qrect.h> +#include <qevent.h> +#include <qvaluevector.h> +#include <qlayout.h> +#include <qcursor.h> + +#include <kdebug.h> +#include <klocale.h> +#include <kpopupmenu.h> + +#include <cstdlib> // for abs() + +#include "utils.h" +#include "container.h" +#include "widgetlibrary.h" +#include "objecttree.h" +#include "form.h" +#include "formmanager.h" +#include "commands.h" +#include "events.h" +#include "kexiflowlayout.h" + +using namespace KFormDesigner; + +EventEater::EventEater(QWidget *widget, QObject *container) + : QObject(container) +{ + m_widget = widget; + m_container = container; + + installRecursiveEventFilter(m_widget, this); +} + +bool +EventEater::eventFilter(QObject *, QEvent *ev) +{ + if(!m_container) + return false; + + // When the user click the empty part of tab bar, only MouseReleaseEvent is sent, + // we need to simulate the Press event + if(ev->type() == QEvent::MouseButtonRelease && m_widget->inherits("QTabWidget")) + { + QMouseEvent *mev = static_cast<QMouseEvent*>(ev); + if(mev->button() == LeftButton) + { + QMouseEvent *myev = new QMouseEvent(QEvent::MouseButtonPress, mev->pos(), mev->button(), mev->state()); + m_container->eventFilter(m_widget, myev); + delete myev; + //return true; + } + } +// else if(ev->type() == QEvent::ChildInserted) { + // widget's children have changed, we need to reinstall filter +// installRecursiveEventFilter(m_widget, this); +// } + + return m_container->eventFilter(m_widget, ev); +} + +EventEater::~EventEater() +{ + if(m_widget) + removeRecursiveEventFilter(m_widget, this); +} + +// Container itself + +Container::Container(Container *toplevel, QWidget *container, QObject *parent, const char *name) +: QObject(parent, name) +, m_insertBegin(-1,-1) +, m_mousePressEventReceived(false) +, m_mouseReleaseEvent(QEvent::None, QPoint(), 0, 0) +{ + m_container = container; + m_toplevel = toplevel; + + m_moving = 0; + m_tree = 0; + m_form = toplevel ? toplevel->form() : 0; + m_layout = 0; + m_layType = NoLayout; + m_state = DoingNothing; + + QCString classname = container->className(); + if((classname == "HBox") || (classname == "Grid") || (classname == "VBox") || + (classname == "HFlow") || (classname == "VFlow")) + m_margin = 4; // those containers don't have frames, so little margin + else + m_margin = m_form ? m_form->defaultMargin() : 0; + m_spacing = m_form ? m_form->defaultSpacing() : 0; + + if(toplevel) + { + ObjectTreeItem *it = new ObjectTreeItem(m_form->library()->displayName(classname), + widget()->name(), widget(), this, this); + setObjectTree(it); + + if(parent->isWidgetType()) + { + QString n = parent->name(); + ObjectTreeItem *parent = m_form->objectTree()->lookup(n); + m_form->objectTree()->addItem(parent, it); + } + else + m_form->objectTree()->addItem(toplevel->objectTree(), it); + + connect(toplevel, SIGNAL(destroyed()), this, SLOT(widgetDeleted())); + } + + connect(container, SIGNAL(destroyed()), this, SLOT(widgetDeleted())); +} + +Container::~Container() +{ + kdDebug() << " Container being deleted this == " << name() << endl; +} + +void +Container::setForm(Form *form) +{ + m_form = form; + m_margin = m_form ? m_form->defaultMargin() : 0; + m_spacing = m_form ? m_form->defaultSpacing() : 0; +} + +bool +Container::eventFilter(QObject *s, QEvent *e) +{ +// kdDebug() << e->type() << endl; + switch(e->type()) + { + case QEvent::MouseButtonPress: + { + m_insertBegin = QPoint(-1, -1); + m_mousePressEventReceived = true; + + kdDebug() << "QEvent::MouseButtonPress sender object = " << s->name() + << "of type " << s->className() << endl; + kdDebug() << "QEvent::MouseButtonPress this = " << name() << endl; + + m_moving = static_cast<QWidget*>(s); + QMouseEvent *mev = static_cast<QMouseEvent*>(e); + m_grab = QPoint(mev->x(), mev->y()); + + // we are drawing a connection + if(FormManager::self()->isCreatingConnection()) { + drawConnection(mev); + return true; + } + + if(((mev->state() == ControlButton) || (mev->state() == ShiftButton)) + && (!FormManager::self()->isInserting())) // multiple selection mode + { + if(m_form->selectedWidgets()->findRef(m_moving) != -1) // widget is already selected + { + if(m_form->selectedWidgets()->count() > 1) // we remove it from selection + unSelectWidget(m_moving); + else // the widget is the only selected, so it means we want to copy it + { + //m_copyRect = m_moving->geometry(); + m_state = CopyingWidget; + if(m_form->formWidget()) + m_form->formWidget()->initBuffer(); + } + } + else // the widget is not yet selected, we add it + setSelectedWidget(m_moving, true, (mev->button() == RightButton)); + } + else if((m_form->selectedWidgets()->count() > 1))//&& (!m_form->manager()->isInserting())) // more than one widget selected + { + if(m_form->selectedWidgets()->findRef(m_moving) == -1) // widget is not selected, it becomes the only selected widget + setSelectedWidget(m_moving, false, (mev->button() == RightButton)); + // If the widget is already selected, we do nothing (to ease widget moving, etc.) + } + else// if(!m_form->manager()->isInserting()) + setSelectedWidget(m_moving, false, (mev->button() == RightButton)); + + // we are inserting a widget or drawing a selection rect in the form + if((/*s == m_container &&*/ FormManager::self()->isInserting()) || ((s == m_container) && !m_toplevel)) + { + int tmpx,tmpy; + if(!FormManager::self()->snapWidgetsToGrid() || (mev->state() == (LeftButton|ControlButton|AltButton))) + { + tmpx = mev->x(); + tmpy = mev->y(); + } + else + { + int gridX = m_form->gridSize(); + int gridY = m_form->gridSize(); + tmpx = int( (float)mev->x() / ((float)gridX) + 0.5 ); // snap to grid + tmpx *= gridX; + tmpy = int( (float)mev->y() / ((float)gridY) + 0.5 ); + tmpy *= gridX; + } + + m_insertBegin = (static_cast<QWidget*>(s))->mapTo(m_container, QPoint(tmpx, tmpy)); + if(m_form->formWidget()) + m_form->formWidget()->initBuffer(); + + if(!FormManager::self()->isInserting()) + m_state = DrawingSelectionRect; + } + else { + if(s->inherits("QTabWidget")) // to allow changing page by clicking tab + return false; + } + + if (m_objectForMouseReleaseEvent) { + const bool res = handleMouseReleaseEvent(m_objectForMouseReleaseEvent, &m_mouseReleaseEvent); + m_objectForMouseReleaseEvent = 0; + return res; + } + return true; + } + + case QEvent::MouseButtonRelease: + { + QMouseEvent *mev = static_cast<QMouseEvent*>(e); + if (!m_mousePressEventReceived) { + m_mouseReleaseEvent = *mev; + m_objectForMouseReleaseEvent = s; + return true; + } + m_mousePressEventReceived = false; + m_objectForMouseReleaseEvent = 0; + return handleMouseReleaseEvent(s, mev); + } + + case QEvent::MouseMove: + { + QMouseEvent *mev = static_cast<QMouseEvent*>(e); + if(m_insertBegin!=QPoint(-1,-1) && FormManager::self()->isInserting() && ((mev->state() == LeftButton) || (mev->state() == (LeftButton|ControlButton)) || + (mev->state() == (LeftButton|ControlButton|AltButton)) || (mev->state() == (LeftButton|ShiftButton)) ) ) + // draw the insert rect + { + drawInsertRect(mev, s); + return true; + } + // Creating a connection, we highlight sender and receiver, and we draw a link between them + else if(FormManager::self()->isCreatingConnection() && !FormManager::self()->createdConnection()->sender().isNull()) + { + ObjectTreeItem *tree = m_form->objectTree()->lookup(FormManager::self()->createdConnection()->sender()); + if(!tree || !tree->widget()) + return true; + + if(m_form->formWidget() && (tree->widget() != s)) + m_form->formWidget()->highlightWidgets(tree->widget(), static_cast<QWidget*>(s)); + } + else if(m_insertBegin!=QPoint(-1,-1) && s == m_container && !m_toplevel && (mev->state() != ControlButton) && !FormManager::self()->isCreatingConnection()) // draw the selection rect + { + if((mev->state() != LeftButton) || /*m_inlineEditing*/ m_state == InlineEditing) + return true; + int topx = (m_insertBegin.x() < mev->x()) ? m_insertBegin.x() : mev->x(); + int topy = (m_insertBegin.y() < mev->y()) ? m_insertBegin.y() : mev->y(); + int botx = (m_insertBegin.x() > mev->x()) ? m_insertBegin.x() : mev->x(); + int boty = (m_insertBegin.y() > mev->y()) ? m_insertBegin.y() : mev->y(); + QRect r = QRect(QPoint(topx, topy), QPoint(botx, boty)); + m_insertRect = r; + + if(m_form->formWidget()) + m_form->formWidget()->drawRect(r, 1); + + m_state = DoingNothing; + return true; + } + else if(mev->state() == (LeftButton|ControlButton)) // draw the insert rect for the copied widget + { + if(s == m_container)// || (m_form->selectedWidgets()->count() > 1)) + return true; + drawCopiedWidgetRect(mev); + return true; + } + else if( ( (mev->state() == Qt::LeftButton) || (mev->state() == (LeftButton|ControlButton|AltButton)) ) + && !FormManager::self()->isInserting() && (m_state != CopyingWidget)) // we are dragging the widget(s) to move it + { + if(!m_toplevel && m_moving == m_container) // no effect for form + return false; + if((!m_moving) || (!m_moving->parentWidget()))// || (m_moving->parentWidget()->inherits("QWidgetStack"))) + return true; + + moveSelectedWidgetsBy(mev->x() - m_grab.x(), mev->y() - m_grab.y()); + m_state = MovingWidget; + } + + return true; // eat + } + + case QEvent::Paint: // Draw the dotted background + { + if(s != m_container) + return false; + int gridX = m_form->gridSize(); + int gridY = m_form->gridSize(); + + QPainter p(m_container); + p.setPen(QPen(white, 2)); + p.setRasterOp(XorROP); + int cols = m_container->width() / gridX; + int rows = m_container->height() / gridY; + + for(int rowcursor = 1; rowcursor <= rows; ++rowcursor) + { + for(int colcursor = 1; colcursor <= cols; ++colcursor) + { + p.drawPoint(-1 + colcursor *gridX, -1 + rowcursor *gridY); + } + } + + return false; + } + + case QEvent::Resize: // we are resizing a widget, so we set m_move to true -> the layout will be reloaded when releasing mouse + { + if(m_form->interactiveMode()) + m_state = MovingWidget; + break; + } + + //case QEvent::AccelOverride: + case QEvent::KeyPress: + { + QKeyEvent *kev = static_cast<QKeyEvent*>(e); + + if(kev->key() == Key_F2) // pressing F2 == double-clicking + { + m_state = InlineEditing; + QWidget *w; + + // try to find the widget which was clicked last and should be edited + if(m_form->selectedWidgets()->count() == 1) + w = m_form->selectedWidgets()->first(); + else if(m_form->selectedWidgets()->findRef(m_moving) != -1) + w = m_moving; + else + w = m_form->selectedWidgets()->last(); + m_form->library()->startEditing(w->className(), w, this); + } + else if(kev->key() == Key_Escape) + { + if(FormManager::self()->isCreatingConnection()) + FormManager::self()->stopCreatingConnection(); + else if(FormManager::self()->isInserting()) + FormManager::self()->stopInsert(); + return true; + } + else if((kev->key() == Key_Control) && (m_state == MovingWidget)) + { + if(!m_moving) + return true; + // we simulate a mouse move event to update screen + QMouseEvent *mev = new QMouseEvent(QEvent::MouseMove, m_moving->mapFromGlobal(QCursor::pos()), NoButton, + LeftButton|ControlButton ); + eventFilter(m_moving, mev); + delete mev; + } + else if(kev->key() == FormManager::self()->contextMenuKey()) + { + FormManager::self()->createContextMenu(static_cast<QWidget*>(s), this, false); + return true; + } + else if (kev->key() == Key_Delete) + { + FormManager::self()->deleteWidget(); + return true; + } + // directional buttons move the widget + else if(kev->key() == Key_Left){ // move the widget of gridX to the left + moveSelectedWidgetsBy(-form()->gridSize(), 0); + return true; + } + else if(kev->key() == Key_Right){ // move the widget of gridX to the right + moveSelectedWidgetsBy(form()->gridSize(), 0); + return true; + } + else if(kev->key() == Key_Up){ // move the widget of gridY to the top + moveSelectedWidgetsBy(0, - form()->gridSize()); + return true; + } + else if(kev->key() == Key_Down){ // move the widget of gridX to the bottom + moveSelectedWidgetsBy(0, form()->gridSize()); + return true; + } + else if((kev->key() == Key_Tab) || (kev->key() == Key_BackTab)){ + ObjectTreeItem *item = form()->objectTree()->lookup(form()->selectedWidgets()->first()->name()); + if(!item || !item->parent()) + return true; + ObjectTreeList *list = item->parent()->children(); + if(list->count() == 1) + return true; + int index = list->findRef(item); + + if(kev->key() == Key_BackTab){ + if(index == 0) // go back to the last item + index = list->count() - 1; + else + index = index - 1; + } + else { + if(index == int(list->count() - 1)) // go back to the first item + index = 0; + else + index = index + 1; + } + + ObjectTreeItem *nextItem = list->at(index); + if(nextItem && nextItem->widget()) + form()->setSelectedWidget(nextItem->widget(), false); + } + return true; + } + + case QEvent::KeyRelease: + { + QKeyEvent *kev = static_cast<QKeyEvent*>(e); + if((kev->key() == Key_Control) && (m_state == CopyingWidget)) { + // cancel copying + //m_copyRect = QRect(); + if(m_form->formWidget()) + m_form->formWidget()->clearForm(); + } + return true; + } + + case QEvent::MouseButtonDblClick: // editing + { + kdDebug() << "Container: Mouse dbl click for widget " << s->name() << endl; + QWidget *w = static_cast<QWidget*>(s); + if(!w) + return false; + + //m_inlineEditing = true; + m_state = InlineEditing; + m_form->library()->startEditing(w->className(), w, this); + return true; + } + + case QEvent::ContextMenu: + case QEvent::Enter: + case QEvent::Leave: + case QEvent::FocusIn: + case QEvent::FocusOut: +// case QEvent::DragEnter: +// case QEvent::DragMove: +// case QEvent::DragLeave: + return true; // eat them + + default: + return false; // let the widget do the rest ... + } + return false; +} + +bool +Container::handleMouseReleaseEvent(QObject *s, QMouseEvent *mev) +{ + if(FormManager::self()->isInserting()) // we insert the widget at cursor pos + { + if(m_form->formWidget()) + m_form->formWidget()->clearForm(); + KCommand *com = new InsertWidgetCommand(this/*, mev->pos()*/); + m_form->addCommand(com, true); + m_insertBegin = QPoint(-1,-1); + m_insertRect = QRect(); + return true; + } + else if(s == m_container && !m_toplevel && (mev->button() != RightButton) && m_insertRect.isValid()) // we are drawing a rect to select widgets + { + drawSelectionRect(mev); + return true; + } + if(mev->button() == RightButton) // Right-click -> context menu + { + FormManager::self()->createContextMenu(static_cast<QWidget*>(s), this); + } + else if(mev->state() == (Qt::LeftButton|Qt::ControlButton))// && (m_copyRect.isValid())) + { + // copying a widget by Ctrl+dragging + + if(m_form->formWidget()) + m_form->formWidget()->clearForm(); + if(s == m_container) // should have no effect on form + return true; + + // prevent accidental copying of widget (when moving mouse a little while selecting) + if( ( (mev->pos().x() - m_grab.x()) < form()->gridSize() && (m_grab.x() - mev->pos().x()) < form()->gridSize() ) && + ( (mev->pos().y() - m_grab.y()) < form()->gridSize() && (m_grab.y() - mev->pos().y()) < form()->gridSize() ) ) + { + kdDebug() << "The widget has not been moved. No copying" << endl; + return true; + } + + m_form->setInteractiveMode(false); + // We simulate copy and paste + FormManager::self()->copyWidget(); + if(m_form->selectedWidgets()->count() > 1) + FormManager::self()->setInsertPoint( mev->pos() ); + else + FormManager::self()->setInsertPoint( static_cast<QWidget*>(s)->mapTo(m_container, mev->pos() - m_grab) ); + FormManager::self()->pasteWidget(); + m_form->setInteractiveMode(true); + + //m_initialPos = QPoint(); + } + else if(m_state == MovingWidget) // one widget has been moved, so we need to update the layout + reloadLayout(); + + // cancel copying as user released Ctrl before releasing mouse button + m_insertBegin = QPoint(-1,-1); + m_insertRect = QRect(); + m_state = DoingNothing; + return true; // eat +} + +void +Container::setSelectedWidget(QWidget *w, bool add, bool dontRaise, bool moreWillBeSelected) +{ + if (w) + kdDebug() << "slotSelectionChanged " << w->name()<< endl; + + if(!w) + { + m_form->setSelectedWidget(m_container); + return; + } + + m_form->setSelectedWidget(w, add, dontRaise, moreWillBeSelected); +} + +void +Container::unSelectWidget(QWidget *w) +{ + if(!w) + return; + + m_form->unSelectWidget(w); +} + +Container* +Container::toplevel() +{ + if(m_toplevel) + return m_toplevel; + else + return this; +} + +void +Container::deleteWidget(QWidget *w) +{ + if(!w) + return; +// kdDebug() << "Deleting a widget: " << w->name() << endl; + m_form->objectTree()->removeItem(w->name()); + FormManager::self()->deleteWidgetLater( w ); + m_form->setSelectedWidget(m_container); +} + +void +Container::widgetDeleted() +{ + m_container = 0; + deleteLater(); +} + +/// Layout functions + +void +Container::setLayout(LayoutType type) +{ + if(m_layType == type) + return; + + delete m_layout; + m_layout = 0; + m_layType = type; + + switch(type) + { + case HBox: + { + m_layout = (QLayout*) new QHBoxLayout(m_container, m_margin, m_spacing); + createBoxLayout(new HorWidgetList(m_form->toplevelContainer()->widget())); + break; + } + case VBox: + { + m_layout = (QLayout*) new QVBoxLayout(m_container, m_margin, m_spacing); + createBoxLayout(new VerWidgetList(m_form->toplevelContainer()->widget())); + break; + } + case Grid: + { + createGridLayout(); + break; + } + case HFlow: + { + KexiFlowLayout *flow = new KexiFlowLayout(m_container,m_margin, m_spacing); + flow->setOrientation(Horizontal); + m_layout = (QLayout*)flow; + createFlowLayout(); + break; + } + case VFlow: + { + KexiFlowLayout *flow = new KexiFlowLayout(m_container,m_margin, m_spacing); + flow->setOrientation(Vertical); + m_layout = (QLayout*)flow; + createFlowLayout(); + break; + } + default: + { + m_layType = NoLayout; + return; + } + } + m_container->setGeometry(m_container->geometry()); // just update layout + m_layout->activate(); +} + +void +Container::reloadLayout() +{ + LayoutType type = m_layType; + setLayout(NoLayout); + setLayout(type); +} + +void +Container::createBoxLayout(WidgetList *list) +{ + QBoxLayout *layout = static_cast<QBoxLayout*>(m_layout); + + for(ObjectTreeItem *tree = m_tree->children()->first(); tree; tree = m_tree->children()->next()) + list->append( tree->widget()); + list->sort(); + + for(QWidget *obj = list->first(); obj; obj = list->next()) + layout->addWidget(obj); + delete list; +} + +void +Container::createFlowLayout() +{ + KexiFlowLayout *flow = dynamic_cast<KexiFlowLayout*>(m_layout); + if(!flow || m_tree->children()->isEmpty()) + return; + + const int offset = 15; + WidgetList *list=0, *list2=0; + if(flow->orientation() == Horizontal) { + list = new VerWidgetList(m_form->toplevelContainer()->widget()); + list2 = new HorWidgetList(m_form->toplevelContainer()->widget()); + } + else { + list = new HorWidgetList(m_form->toplevelContainer()->widget()); + list2 = new VerWidgetList(m_form->toplevelContainer()->widget()); + } + + // fill the list + for(ObjectTreeItem *tree = m_tree->children()->first(); tree; tree = m_tree->children()->next()) + list->append( tree->widget()); + list->sort(); + + if(flow->orientation() == Horizontal) { + int y = list->first()->y(); + for(QWidget *w = list->first(); w; w = list->next()) { + if( (w->y() > y +offset)) { + // start a new line + list2->sort(); + for(QWidget *obj = list2->first(); obj; obj = list2->next()) + flow->add(obj); + list2->clear(); + y = w->y(); + } + list2->append(w); + } + + list2->sort(); // don't forget the last line + for(QWidget *obj = list2->first(); obj; obj = list2->next()) + flow->add(obj); + } + else { + int x = list->first()->x(); + for(QWidget *w = list->first(); w; w = list->next()) { + if( (w->x() > x +offset)) { + // start a new column + list2->sort(); + for(QWidget *obj = list2->first(); obj; obj = list2->next()) + flow->add(obj); + list2->clear(); + x = w->x(); + } + list2->append(w); + } + + list2->sort(); // don't forget the last column + for(QWidget *obj = list2->first(); obj; obj = list2->next()) + flow->add(obj); + } + + delete list; + delete list2; +} + +void +Container::createGridLayout(bool testOnly) +{ + //Those lists sort widgets by y and x + VerWidgetList *vlist = new VerWidgetList(m_form->toplevelContainer()->widget()); + HorWidgetList *hlist = new HorWidgetList(m_form->toplevelContainer()->widget()); + // The vector are used to store the x (or y) beginning of each column (or row) + QValueVector<int> cols; + QValueVector<int> rows; + int end=-1000; + bool same = false; + + for(ObjectTreeItem *tree = m_tree->children()->first(); tree; tree = m_tree->children()->next()) + vlist->append( tree->widget()); + vlist->sort(); + + for(ObjectTreeItem *tree = m_tree->children()->first(); tree; tree = m_tree->children()->next()) + hlist->append( tree->widget()); + hlist->sort(); + + // First we need to make sure that two widgets won't be in the same row, + // ie that no widget overlap another one + if(!testOnly) { + for(WidgetListIterator it(*vlist); it.current() != 0; ++it) + { + QWidget *w = it.current(); + WidgetListIterator it2 = it; + + for(; it2.current() != 0; ++it2) { + QWidget *nextw = it2.current(); + if((w->y() >= nextw->y()) || (nextw->y() >= w->geometry().bottom())) + break; + + if(!w->geometry().intersects(nextw->geometry())) + break; + // If the geometries of the two widgets intersect each other, + // we move one of the widget to the rght or bottom of the other + if((nextw->y() - w->y()) > abs(nextw->x() - w->x())) + nextw->move(nextw->x(), w->geometry().bottom()+1); + else if(nextw->x() >= w->x()) + nextw->move(w->geometry().right()+1, nextw->y()); + else + w->move(nextw->geometry().right()+1, nextw->y()); + } + } + } + + // Then we count the number of rows in the layout, and set their beginnings + for(WidgetListIterator it(*vlist); it.current() != 0; ++it) + { + QWidget *w = it.current(); + WidgetListIterator it2 = it; + if(!same) { // this widget will make a new row + end = w->geometry().bottom(); + rows.append(w->y()); + } + + // If same == true, it means we are in the same row as prev widget + // (so no need to create a new column) + ++it2; + if(!it2.current()) + break; + + QWidget *nextw = it2.current(); + if(nextw->y() >= end) + same = false; + else { + same = !(same && (nextw->y() >= w->geometry().bottom())); + if(!same) + end = w->geometry().bottom(); + } + } + kdDebug() << "the new grid will have n rows: n == " << rows.size() << endl; + + end = -10000; + same = false; + // We do the same thing for the columns + for(WidgetListIterator it(*hlist); it.current() != 0; ++it) + { + QWidget *w = it.current(); + WidgetListIterator it2 = it; + if(!same) { + end = w->geometry().right(); + cols.append(w->x()); + } + + ++it2; + if(!it2.current()) + break; + + QWidget *nextw = it2.current(); + if(nextw->x() >= end) + same = false; + else { + same = !(same && (nextw->x() >= w->geometry().right())); + if(!same) + end = w->geometry().right(); + } + } + kdDebug() << "the new grid will have n columns: n == " << cols.size() << endl; + + // We create the layout .. + QGridLayout *layout=0; + if(!testOnly) { + layout = new QGridLayout(m_container, rows.size(), cols.size(), m_margin, m_spacing, "grid"); + m_layout = (QLayout*)layout; + } + + // .. and we fill it with widgets + for(WidgetListIterator it(*vlist); it.current() != 0; ++it) + { + QWidget *w = it.current(); + QRect r = w->geometry(); + uint wcol=0, wrow=0, endrow=0, endcol=0; + uint i = 0; + + // We look for widget row(s) .. + while(r.y() >= rows[i]) + { + if(rows.size() <= i+1) // we are the last row + { + wrow = i; + break; + } + if(r.y() < rows[i+1]) + { + wrow = i; // the widget will be in this row + uint j = i + 1; + // Then we check if the widget needs to span multiple rows + while(rows.size() >= j+1 && r.bottom() > rows[j]) + { + endrow = j; + j++; + } + + break; + } + i++; + } + //kdDebug() << "the widget " << w->name() << " wil be in the row " << wrow << + //" and will go to the row " << endrow << endl; + + // .. and column(s) + i = 0; + while(r.x() >= cols[i]) + { + if(cols.size() <= i+1) // last column + { + wcol = i; + break; + } + if(r.x() < cols[i+1]) + { + wcol = i; + uint j = i + 1; + // Then we check if the widget needs to span multiple columns + while(cols.size() >= j+1 && r.right() > cols[j]) + { + endcol = j; + j++; + } + + break; + } + i++; + } + //kdDebug() << "the widget " << w->name() << " wil be in the col " << wcol << + // " and will go to the col " << endcol << endl; + + ObjectTreeItem *item = m_form->objectTree()->lookup(w->name()); + if(!endrow && !endcol) { + if(!testOnly) + layout->addWidget(w, wrow, wcol); + item->setGridPos(wrow, wcol, 0, 0); + } + else { + if(!endcol) endcol = wcol; + if(!endrow) endrow = wrow; + if(!testOnly) + layout->addMultiCellWidget(w, wrow, endrow, wcol, endcol); + item->setGridPos(wrow, wcol, endrow-wrow+1, endcol-wcol+1); + } + } +} + +QString +Container::layoutTypeToString(int type) +{ + switch(type) + { + case HBox: return "HBox"; + case VBox: return "VBox"; + case Grid: return "Grid"; + case HFlow: return "HFlow"; + case VFlow: return "VFlow"; + default: return "NoLayout"; + } +} + +Container::LayoutType +Container::stringToLayoutType(const QString &name) +{ + if(name == "HBox") return HBox; + if(name == "VBox") return VBox; + if(name == "Grid") return Grid; + if(name == "HFlow") return HFlow; + if(name == "VFlow") return VFlow; + return NoLayout; +} + +/// Drawing functions used by eventFilter +void +Container::drawConnection(QMouseEvent *mev) +{ + if(mev->button() != LeftButton) + { + FormManager::self()->resetCreatedConnection(); + return; + } + // First click, we select the sender and display menu to choose signal + if(FormManager::self()->createdConnection()->sender().isNull()) + { + FormManager::self()->createdConnection()->setSender(m_moving->name()); + if(m_form->formWidget()) + { + m_form->formWidget()->initBuffer(); + m_form->formWidget()->highlightWidgets(m_moving, 0/*, QPoint()*/); + } + FormManager::self()->createSignalMenu(m_moving); + return; + } + // the user clicked outside the menu, we cancel the connection + if(FormManager::self()->createdConnection()->signal().isNull()) + { + FormManager::self()->stopCreatingConnection(); + return; + } + // second click to choose the receiver + if(FormManager::self()->createdConnection()->receiver().isNull()) + { + FormManager::self()->createdConnection()->setReceiver(m_moving->name()); + FormManager::self()->createSlotMenu(m_moving); + m_container->repaint(); + return; + } + // the user clicked outside the menu, we cancel the connection + if(FormManager::self()->createdConnection()->slot().isNull()) + { + FormManager::self()->stopCreatingConnection(); + return; + } +} + +void +Container::drawSelectionRect(QMouseEvent *mev) +{ + //finish drawing unclipped selection rectangle: clear the surface + if(m_form->formWidget()) + m_form->formWidget()->clearForm(); + int topx = (m_insertBegin.x() < mev->x()) ? m_insertBegin.x() : mev->x(); + int topy = (m_insertBegin.y() < mev->y()) ? m_insertBegin.y() : mev->y(); + int botx = (m_insertBegin.x() > mev->x()) ? m_insertBegin.x() : mev->x(); + int boty = (m_insertBegin.y() > mev->y()) ? m_insertBegin.y() : mev->y(); + QRect r = QRect(QPoint(topx, topy), QPoint(botx, boty)); + + setSelectedWidget(m_container, false); + QWidget *widgetToSelect = 0; + // We check which widgets are in the rect and select them + for(ObjectTreeItem *item = m_tree->children()->first(); item; item = m_tree->children()->next()) + { + QWidget *w = item->widget(); + if(!w) + continue; + if(w->geometry().intersects(r) && w != m_container) { + if (widgetToSelect) + setSelectedWidget(widgetToSelect, true/*add*/, false/*raise*/, true/*moreWillBeSelected*/); + widgetToSelect = w; //select later + } + } + if (widgetToSelect) //the last one left + setSelectedWidget(widgetToSelect, true/*add*/, false/*raise*/, false/*!moreWillBeSelected*/); + + m_insertRect = QRect(); + m_state = DoingNothing; + m_container->repaint(); +} + +void +Container::drawInsertRect(QMouseEvent *mev, QObject *s) +{ + int tmpx, tmpy; + QPoint pos = static_cast<QWidget*>(s)->mapTo(m_container, mev->pos()); + int gridX = m_form->gridSize(); + int gridY = m_form->gridSize(); + if(!FormManager::self()->snapWidgetsToGrid() || (mev->state() == (LeftButton|ControlButton|AltButton)) ) + { + tmpx = pos.x(); + tmpy = pos.y(); + } + else + { + tmpx = int( (float) pos.x() / ((float)gridX) + 0.5); + tmpx *= gridX; + tmpy = int( (float)pos.y() / ((float)gridY) + 0.5); + tmpy *= gridX; + } + + int topx = (m_insertBegin.x() < tmpx) ? m_insertBegin.x() : tmpx; + int topy = (m_insertBegin.y() < tmpy) ? m_insertBegin.y() : tmpy; + int botx = (m_insertBegin.x() > tmpx) ? m_insertBegin.x() : tmpx; + int boty = (m_insertBegin.y() > tmpy) ? m_insertBegin.y() : tmpy; + m_insertRect = QRect(QPoint(topx, topy), QPoint(botx, boty)); + + if(m_insertRect.x() < 0) + m_insertRect.setLeft(0); + if(m_insertRect.y() < 0) + m_insertRect.setTop(0); + if(m_insertRect.right() > m_container->width()) + m_insertRect.setRight(m_container->width()); + if(m_insertRect.bottom() > m_container->height()) + m_insertRect.setBottom(m_container->height()); + + if(FormManager::self()->isInserting() && m_insertRect.isValid()) + { + if(m_form->formWidget()) + { + QRect drawRect = QRect(m_container->mapTo(m_form->widget(), m_insertRect.topLeft()) + , m_insertRect.size()); + m_form->formWidget()->drawRect(drawRect, 2); + } + } +} + +void +Container::drawCopiedWidgetRect(QMouseEvent *mev) +{ + // We've been dragging a widget, but Ctrl was hold, so we start copy + if(m_state == MovingWidget) { + //FormManager::self()->undo(); // undo last moving + //m_moving->move(m_initialPos); + if(m_form->formWidget()) { + m_container->repaint(); + m_form->formWidget()->initBuffer(); + } + m_state = CopyingWidget; + } + + //m_copyRect.moveTopLeft(m_container->mapFromGlobal( mev->globalPos()) - m_grab); + + if(m_form->formWidget()) { + QValueList<QRect> rectList; + for(QWidget *w = m_form->selectedWidgets()->first(); w; w = m_form->selectedWidgets()->next()) { + QRect drawRect = w->geometry(); + QPoint p = mev->pos() - m_grab; + drawRect.moveBy(p.x(), p.y()); + p = m_container->mapTo(m_form->widget(), QPoint(0, 0)); + //drawRect = QRect( ((QWidget*)s)->mapTo(m_form->widget(), drawRect.topLeft()), drawRect.size()); + drawRect.moveBy(p.x(), p.y()); + rectList.append(drawRect); + } + + m_form->formWidget()->drawRects(rectList, 2); + } +} + +/// Other functions used by eventFilter +void +Container::moveSelectedWidgetsBy(int realdx, int realdy, QMouseEvent *mev) +{ + if (m_form->selectedWidget() == m_form->widget()) + return; //do not move top-level widget + + const int gridX = m_form->gridSize(); + const int gridY = m_form->gridSize(); + int dx=realdx, dy=realdy; + + for(QWidget *w = m_form->selectedWidgets()->first(); w; w = m_form->selectedWidgets()->next()) + { + if(!w || !w->parent() || w->parent()->inherits("QTabWidget") || w->parent()->inherits("QWidgetStack")) + continue; + + if(w->parentWidget() && w->parentWidget()->isA("QWidgetStack")) + { + w = w->parentWidget(); // widget is WidgetStack page + if(w->parentWidget() && w->parentWidget()->inherits("QTabWidget")) // widget is tabwidget page + w = w->parentWidget(); + } + + int tmpx = w->x() + realdx; + int tmpy = w->y() + realdy; + if(tmpx < 0) + dx = QMAX(0 - w->x(), dx); // because dx is <0 + else if(tmpx > w->parentWidget()->width() - gridX) + dx = QMIN(w->parentWidget()->width() - gridX - w->x(), dx); + + if(tmpy < 0) + dy = QMAX(0 - w->y(), dy); // because dy is <0 + else if(tmpy > w->parentWidget()->height() - gridY) + dy = QMIN(w->parentWidget()->height() - gridY - w->y(), dy); + } + + for(QWidget *w = m_form->selectedWidgets()->first(); w; w = m_form->selectedWidgets()->next()) + { + // Don't move tab widget pages (or widget stack pages) + if(!w || !w->parent() || w->parent()->inherits("QTabWidget") || w->parent()->inherits("QWidgetStack")) + continue; + + if(w->parentWidget() && w->parentWidget()->isA("QWidgetStack")) + { + w = w->parentWidget(); // widget is WidgetStack page + if(w->parentWidget() && w->parentWidget()->inherits("QTabWidget")) // widget is tabwidget page + w = w->parentWidget(); + } + + int tmpx, tmpy; + if(!FormManager::self()->snapWidgetsToGrid() || (mev && mev->state() == (LeftButton|ControlButton|AltButton)) ) + { + tmpx = w->x() + dx; + tmpy = w->y() + dy; + } + else + { + tmpx = int( float( w->x() + dx) / float(gridX) + 0.5) * gridX; + tmpy = int( float( w->y() + dy) / float(gridY) + 0.5) * gridY; + } + + if((tmpx != w->x()) || (tmpy != w->y())) + w->move(tmpx,tmpy); + } +} + +//////////// + +DesignTimeDynamicChildWidgetHandler::DesignTimeDynamicChildWidgetHandler() + : m_item(0) +{ +} + +DesignTimeDynamicChildWidgetHandler::~DesignTimeDynamicChildWidgetHandler() +{ +} + +void +DesignTimeDynamicChildWidgetHandler::childWidgetAdded(QWidget* w) +{ + if (m_item) { + installRecursiveEventFilter(w, m_item->eventEater()); + } +} + +#include "container.moc" diff --git a/kexi/formeditor/container.h b/kexi/formeditor/container.h new file mode 100644 index 00000000..b7036aa7 --- /dev/null +++ b/kexi/formeditor/container.h @@ -0,0 +1,248 @@ +/* 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> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef FORMEDITORCONTAINER_H +#define FORMEDITORCONTAINER_H + +#include <qobject.h> +#include <qguardedptr.h> +#include <qptrlist.h> +#include <qwidget.h> + +#include "utils.h" + +class QEvent; +class QWidget; +class QLayout; + +namespace KFormDesigner { + +class Container; +class WidgetLibrary; +class ObjectTreeItem; +class Form; + +/** + * This class is used to filter the events from any widget (and all its subwidgets) + * and direct it to the Container. + */ +//! A class for redirecting events +class KFORMEDITOR_EXPORT EventEater : public QObject +{ + Q_OBJECT + + public: + /*! Constructs eater object. All events for \a widget and it's subwidgets + will be redirected to \a container. \a container will be also parent of eater object, + so you don't need to care about deleting it. */ + EventEater(QWidget *widget, QObject *container); + ~EventEater(); + + //! Sets the object which will receive the events + void setContainer(QObject *container) { m_container = container; } + bool eventFilter(QObject *o, QEvent *ev); + + private: + QGuardedPtr<QWidget> m_widget; + QGuardedPtr<QObject> m_container; +}; + +/** + * This class makes a container out of any QWidget. You can then create child widgets, and + the background is dotted. + */ +//! A class to make a container from any widget +class KFORMEDITOR_EXPORT Container : public QObject +{ + Q_OBJECT + + public: + enum LayoutType { NoLayout=0, HBox, VBox, Grid, HFlow, VFlow, /* special types */ HSplitter, VSplitter }; + + /** + * Creates a Container from the widget \a container, which have + \a toplevel as parent Container. */ + Container(Container *toplevel, QWidget *container, QObject *parent=0, const char *name=0); + virtual ~Container(); + + //! \return a pointer to the toplevel Container, or 0 if this Container is toplevel + Container* toplevel(); + + //! \return The form this Container belongs to. + Form* form() const { return m_form; } + + //! \return The watched widget. + QWidget* widget() const { return m_container; } + + //! \return The ObjectTreeItem associated with this Container's widget. + ObjectTreeItem* objectTree() const { return m_tree; } + + //! Sets the Form which this Container belongs to. + void setForm(Form *form); + + /*! Sets the ObjectTree of this Container.\n + * NOTE: this is needed only if we are toplevel. */ + void setObjectTree(ObjectTreeItem *t) { m_tree = t; } + + //! \return a pointer to the QLayout of this Container, or 0 if there is not. + QLayout* layout() const { return m_layout; } + + //! \return the type of the layout associated to this Container's widget (see LayoutType enum). + LayoutType layoutType() const { return m_layType; } + + //! \return the margin of this Container. + int layoutMargin() { return m_margin; } + + //! \return the spacing of this Container. + int layoutSpacing() { return m_spacing; } + + /*! Sets this Container to use \a type of layout. The widget are inserted + automatically in the layout following their positions. + \sa createBoxLayout(), createGridLayout() */ + void setLayout(LayoutType type); + + //! Sets the spacing of this Container. + void setLayoutSpacing(int spacing) { m_spacing = spacing;} + + //! Sets the margin of this Container. + void setLayoutMargin(int margin) { m_margin = margin;} + + //! \return the string representing the layoutType \a type. + static QString layoutTypeToString(int type); + + //! \return the LayoutType (an int) for a given layout name. + static LayoutType stringToLayoutType(const QString &name); + + /*! Stops the inline editing of the current widget (as when you click + on another widget or press Esc). */ + void stopInlineEditing() { m_state = DoingNothing; } + + /*! This is the main function of Container, which filters the event sent + to the watched widget.\n It takes care of drawing the background and + the insert rect, of creating the new child widgets, of moving the widgets + and pop up a menu when right-clicking. */ + virtual bool eventFilter(QObject *o, QEvent *e); + + public slots: + /*! Sets \a selected to be the selected widget of this container + (and so of the Form). If \a add is true, the formerly selected widget + is still selected, and the new one is just added. If false, \a selected + replace the actually selected widget. If \a dontRaise is true, then + the widget \a selected (and its parent) won't be raised (eg when you + select widget in ObjectTreeView). + \sa Form::setSelectedWidget() */ + void setSelectedWidget(QWidget *selected, bool add, bool dontRaise=false, + bool moreWillBeSelected = false); + + /*! Unselects the widget \a w. The widget is removed from the Form's list + and its resizeHandles are removed. */ + void unSelectWidget(QWidget *w); + + /*! Deletes the widget \a w. Removes it from ObjectTree, and sets selection + to Container's widget. */ + void deleteWidget(QWidget *w); + + /*! Recreates the Container layout. Calls this when a widget has been moved + or added to update the layout. */ + void reloadLayout(); + + protected slots: + /*! This slot is called when the watched widget is deleted. Deletes the Container too. */ + void widgetDeleted(); + + protected: + /*! Internal function to create a HBoxLayout or VBoxLayout for this container. + \a list is a subclass of QObjectList that can sort widgets + following their position (such as HorWidgetList or VerWidgetList). */ + void createBoxLayout(WidgetList *list); + + /*! Internal function to create a KexiFlowLayout. */ + void createFlowLayout(); + + /*! Internal function to create a GridLayout. if \a testOnly is true, the layout + is simulated, and only the widget's grid info aris filled. */ + void createGridLayout(bool testOnly=false); + + void drawConnection(QMouseEvent *mev); + void drawSelectionRect(QMouseEvent *mev); + void drawInsertRect(QMouseEvent *mev, QObject *s); + void drawCopiedWidgetRect(QMouseEvent *mev); + + void moveSelectedWidgetsBy(int realdx, int realdy, QMouseEvent *mev=0); + + private: + bool handleMouseReleaseEvent(QObject *s, QMouseEvent *mev); + + // the watched container and it's toplevel one... + QGuardedPtr<QWidget> m_container; + QGuardedPtr<Container> m_toplevel; + + int m_state; + enum { DoingNothing = 100, DrawingSelectionRect, CopyingWidget, + MovingWidget, InlineEditing }; + + // Layout + QLayout *m_layout; + LayoutType m_layType; + int m_margin, m_spacing; + + // moving etc. + QPoint m_grab; + //QPoint m_initialPos; + QGuardedPtr<QWidget> m_moving; + //QRect m_copyRect; + + //inserting + QPoint m_insertBegin; + QRect m_insertRect; + ObjectTreeItem *m_tree; + + QGuardedPtr<Form> m_form; + bool m_mousePressEventReceived; + QMouseEvent m_mouseReleaseEvent; + QGuardedPtr<QObject> m_objectForMouseReleaseEvent; + + friend class InsertWidgetCommand; + friend class PasteWidgetCommand; + friend class DeleteWidgetCommand; + friend class FormIO; +}; + +//! Interface for adding dynamically created (at design time) widget to event eater. +/*! This is currently used by KexiDBFieldEdit from Kexi forms. */ +class KFORMEDITOR_EXPORT DesignTimeDynamicChildWidgetHandler +{ + public: + DesignTimeDynamicChildWidgetHandler(); + ~DesignTimeDynamicChildWidgetHandler(); + + protected: + void childWidgetAdded(QWidget* w); + void assignItem(ObjectTreeItem* item) { m_item = item; } + + private: + ObjectTreeItem* m_item; + friend class InsertWidgetCommand; + friend class FormIO; +}; + +} + +#endif diff --git a/kexi/formeditor/editlistviewdialog.cpp b/kexi/formeditor/editlistviewdialog.cpp new file mode 100644 index 00000000..5b128ec8 --- /dev/null +++ b/kexi/formeditor/editlistviewdialog.cpp @@ -0,0 +1,460 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr> + + 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 <qheader.h> +#include <qlayout.h> + +#include <klistview.h> +#include <ktabwidget.h> +#include <klistbox.h> +#include <kiconloader.h> +#include <kdebug.h> +#include <klocale.h> + +#include <koproperty/editor.h> +#include <koproperty/set.h> +#include <koproperty/property.h> + +#include "editlistviewdialog.h" + +namespace KFormDesigner { + +////////////////////////////////////////////////////////////////////////////////// +/// A Dialog to edit the contents of a listview ///////////////////// +///////////////////////////////////////////////////////////////////////////////// + +EditListViewDialog::EditListViewDialog(QWidget *parent) +//js(kde3.2 dependent) : KDialogBase(Tabbed, 0/* WFlags */, parent, "editlistview_dialog", true, i18n("Edit listview contents"), Ok|Cancel, Ok, false) +: KDialogBase(Tabbed, i18n("Edit Listview Contents"), Ok|Cancel, Ok, parent, "editlistview_dialog", true /* modal */, false) +{ + m_column = addPage(i18n("Columns")); + m_contents = addPage(i18n("Contents")); + + ///////// Setup the "Contents" page ///////////////////////////// + QHBoxLayout *layout = new QHBoxLayout(m_contents, 0, 6); + + //// Setup the icon toolbar ///////////////// + QVBoxLayout *vlayout = new QVBoxLayout(layout, 3); + QToolButton *newRow = new QToolButton(m_contents); + newRow->setIconSet(BarIconSet("edit_add")); + newRow->setTextLabel(i18n("&Add Item"), true); + vlayout->addWidget(newRow); + m_buttons.insert(BNewRow, newRow); + connect(newRow, SIGNAL(clicked()), this, SLOT(newRow())); + + QToolButton *newChild = new QToolButton(m_contents); + newChild->setIconSet(BarIconSet("1rightarrow")); + newChild->setTextLabel(i18n("New &Subitem"), true); + vlayout->addWidget(newChild); + m_buttons.insert(BNewChild, newChild); + connect(newChild, SIGNAL(clicked()), this, SLOT(newChildRow())); + + QToolButton *delRow = new QToolButton(m_contents); + delRow->setIconSet(BarIconSet("edit_remove")); + delRow->setTextLabel(i18n("&Remove Item"), true); + vlayout->addWidget(delRow); + m_buttons.insert(BRemRow, delRow); + connect(delRow, SIGNAL(clicked()), this, SLOT(removeRow())); + + QToolButton *rowUp = new QToolButton(m_contents); + rowUp->setIconSet(BarIconSet("1uparrow")); + rowUp->setTextLabel(i18n("Move Item &Up"), true); + vlayout->addWidget(rowUp); + m_buttons.insert(BRowUp, rowUp); + connect(rowUp, SIGNAL(clicked()), this, SLOT(MoveRowUp())); + + QToolButton *rowDown = new QToolButton(m_contents); + rowDown->setIconSet(BarIconSet("1downarrow")); + rowDown->setTextLabel(i18n("Move Item &Down"), true); + vlayout->addWidget(rowDown); + m_buttons.insert(BRowDown, rowDown); + connect(rowDown, SIGNAL(clicked()), this, SLOT(MoveRowDown())); + vlayout->addStretch(); + + //// The listview /////////// + m_listview = new KListView(m_contents, "editlistview_listview"); + m_listview->setItemsRenameable(true); + m_listview->setItemsMovable(true); + m_listview->setDragEnabled(true); + m_listview->setAllColumnsShowFocus(true); + m_listview->setRootIsDecorated(true); + m_listview->setDropVisualizer(true); + m_listview->setAcceptDrops(true); + m_listview->setSorting(-1); + layout->addWidget(m_listview); + m_listview->setFocus(); + connect(m_listview, SIGNAL(currentChanged(QListViewItem*)), this, SLOT(updateButtons(QListViewItem*))); + connect(m_listview, SIGNAL(moved(QListViewItem*, QListViewItem*, QListViewItem*)), this, SLOT(updateButtons(QListViewItem*))); + + /////////////////// Setup the columns page //////////////// + QHBoxLayout *hbox = new QHBoxLayout(m_column, 0, 6); + + // The "item properties" field + m_editor = new KoProperty::Editor(m_column, "editcolumn_propeditor"); + m_propSet = new KoProperty::Set(this, "columns"); + m_propSet->addProperty(new KoProperty::Property("caption", "Caption", i18n("Caption"),i18n("Caption"))); + m_propSet->addProperty(new KoProperty::Property("width", 100, i18n("Width"), i18n("Width"))); + m_propSet->addProperty(new KoProperty::Property("clickable", QVariant(true, 3), i18n("Clickable"), i18n("Clickable") )); + m_propSet->addProperty(new KoProperty::Property("resizable", QVariant(true, 3), i18n("Resizable"), i18n("Resizable") )); + m_propSet->addProperty(new KoProperty::Property("fullwidth", QVariant(false, 3), i18n("Full Width"), i18n("Full Width") )); + m_editor->changeSet(m_propSet); + connect(m_propSet, SIGNAL(propertyChanged(KoProperty::Set & KoProperty::Property&)), + this, SLOT(changeProperty(KoProperty::Set & KoProperty::Property&))); + + // Setup the icon toolbar ////////// + QVBoxLayout *vbox = new QVBoxLayout(hbox, 3); + QToolButton *add = new QToolButton(m_column); + add->setIconSet(BarIconSet("edit_add")); + add->setTextLabel(i18n("&Add Item"), true); + vbox->addWidget(add); + m_buttons.insert(BColAdd, add); + connect(add, SIGNAL(clicked()), this, SLOT(newItem())); + + QToolButton *remove = new QToolButton(m_column); + remove->setIconSet(BarIconSet("edit_remove")); + remove->setTextLabel(i18n("&Remove Item"), true); + vbox->addWidget(remove); + m_buttons.insert(BColRem, remove); + connect(remove, SIGNAL(clicked()), this, SLOT(removeItem())); + + QToolButton *up = new QToolButton(m_column); + up->setIconSet(BarIconSet("1uparrow")); + up->setTextLabel(i18n("Move Item &Up"), true); + vbox->addWidget(up); + m_buttons.insert(BColUp, up); + connect(up, SIGNAL(clicked()), this, SLOT(MoveItemUp())); + + QToolButton *down = new QToolButton(m_column); + down->setIconSet(BarIconSet("1downarrow")); + down->setTextLabel(i18n("Move Item &Down"), true); + vbox->addWidget(down); + m_buttons.insert(BColDown, down); + connect(down, SIGNAL(clicked()), this, SLOT(MoveItemDown())); + vbox->addStretch(); + + // The listbox with columns name ///// + m_listbox = new KListBox(m_column, "editlistview_columns"); + m_listbox->setFocus(); + hbox->insertWidget(0, m_listbox); + hbox->addWidget(m_editor); + connect(m_listbox, SIGNAL(currentChanged(QListBoxItem*)), this, SLOT(updateItemProperties(QListBoxItem*))); + + //// Init dialog and display it //////////////////////// + setInitialSize(QSize(500, 300), true); +} + +int +EditListViewDialog::exec(QListView *listview) +{ + if(!listview) + { + kdDebug() << "EditListViewDialog ERROR: no listview " << endl; + return 0; + } + + // We copy the contents of the listview into our listview + for(int i = 0; i < listview->columns(); i++) + { + m_listview->addColumn(listview->columnText(i), listview->columnWidth(i)); + m_listview->header()->setClickEnabled(listview->header()->isClickEnabled(i), i); + m_listview->header()->setResizeEnabled(listview->header()->isResizeEnabled(i), i); + m_listview->header()->setStretchEnabled(listview->header()->isStretchEnabled(i), i); + m_listview->setRenameable(i, true); + } + QListViewItem *item = listview->firstChild(); + while(item) { + loadChildNodes(m_listview, item, 0); + item = item->nextSibling(); + } + + m_listview->setSelected(m_listview->firstChild(), true); + if(!m_listview->firstChild()) + updateButtons(0); + + for(int i = 0; i < listview->columns(); i++) + m_listbox->insertItem(listview->columnText(i)); + m_listbox->setSelected(0, true); + + // and we exec the dialog + int r = KDialogBase::exec(); + if(r == QDialog::Accepted) + { + listview->clear(); + // We copy the contents of our listview back in the listview + for(int i = 0; i < m_listview->columns(); i++) + { + if(listview->columns() <= i) + listview->addColumn(m_listview->columnText(i), m_listview->columnWidth(i)); + else + { + listview->setColumnText(i, m_listview->columnText(i)); + listview->setColumnWidth(i, m_listview->columnWidth(i)); + } + listview->header()->setClickEnabled(m_listview->header()->isClickEnabled(i), i); + listview->header()->setResizeEnabled(m_listview->header()->isResizeEnabled(i), i); + listview->header()->setStretchEnabled(m_listview->header()->isStretchEnabled(i), i); + } + + QListViewItem *item = m_listview->firstChild(); + while(item) + { + loadChildNodes(listview, item, 0); + item = item->nextSibling(); + } + } + return r; +} + +/// Columns page slots /////// +void +EditListViewDialog::changeProperty(KoProperty::Set& set, KoProperty::Property& property) +{ + if(&set != m_propSet) + return; + + QString name = property.name(); + QVariant value = property.value(); + if(name == "caption") { + m_propSet->blockSignals(true); // we need to block signals because changeItem will modify selection, and call updateItemProperties + m_listbox->changeItem(value.toString(), m_listbox->currentItem()); + m_listview->setColumnText(m_listbox->currentItem(), value.toString()); + m_propSet->blockSignals(false); + } + else if(name == "width") + m_listview->setColumnWidth(m_listbox->currentItem(), value.toInt()); + else if(name == "resizable") + m_listview->header()->setResizeEnabled(value.toBool(), m_listbox->currentItem()); + else if(name == "clickable") + m_listview->header()->setClickEnabled(value.toBool(), m_listbox->currentItem()); + else if(name == "fullwidth") + m_listview->header()->setStretchEnabled(value.toBool(), m_listbox->currentItem()); +} + +void +EditListViewDialog::updateItemProperties(QListBoxItem *item) +{ + if(!item) + return; + + int id = m_listbox->index(item); + if(m_propSet) { + m_propSet->blockSignals(true); // we don't want changeProperty to be called + (*m_propSet)["caption"].setValue(m_listview->columnText(id), false); + (*m_propSet)["width"].setValue(m_listview->columnWidth(id), false); + (*m_propSet)["clickable"].setValue(QVariant(m_listview->header()->isClickEnabled(id), 4), false); + (*m_propSet)["resizable"].setValue(QVariant(m_listview->header()->isResizeEnabled(id), 4), false); + (*m_propSet)["fullwidth"].setValue(QVariant(m_listview->header()->isStretchEnabled(id), 4), false); + m_propSet->blockSignals(false); + m_editor->changeSet(m_propSet); + } + + m_buttons[BColUp]->setEnabled(item->prev()); + m_buttons[BColDown]->setEnabled(item->next()); +} + +void +EditListViewDialog::newItem() +{ + m_listbox->insertItem(i18n("New Column")); + m_listview->addColumn(i18n("New Column")); + m_listview->setRenameable(m_listview->columns() - 1, true); + m_listbox->setCurrentItem(m_listbox->count() - 1); + m_buttons[BColRem]->setEnabled(true); +} + +void +EditListViewDialog::removeItem() +{ + int current = m_listbox->currentItem(); + if(m_listbox->item(current + 1)) + m_listbox->setCurrentItem(current +1); + else + m_listbox->setCurrentItem(current - 1); + + m_listview->removeColumn(current); + m_listbox->removeItem(current); + if(m_listbox->count() == 0) + m_buttons[BColRem]->setEnabled(false); +} + +void +EditListViewDialog::MoveItemUp() +{ + int current = m_listbox->currentItem(); + QString text = m_listbox->text(current); + m_listbox->blockSignals(true); + + m_listbox->changeItem(m_listbox->text(current - 1), current); + m_listview->setColumnText(current, m_listview->columnText(current - 1)); + m_listview->setColumnWidth(current, m_listview->columnWidth(current - 1)); + m_listview->header()->setClickEnabled(m_listview->header()->isClickEnabled(current - 1), current); + m_listview->header()->setResizeEnabled(m_listview->header()->isResizeEnabled(current - 1), current); + m_listview->header()->setStretchEnabled(m_listview->header()->isStretchEnabled(current - 1), current); + + m_listbox->changeItem(text, current - 1); + m_listview->setColumnText(current - 1, (*m_propSet)["caption"].value().toString()); + m_listview->setColumnWidth(current - 1,(*m_propSet)["width"].value().toBool()); + m_listview->header()->setClickEnabled((*m_propSet)["clickable"].value().toBool(), current - 1); + m_listview->header()->setResizeEnabled((*m_propSet)["resizable"].value().toBool(), current - 1); + m_listview->header()->setStretchEnabled((*m_propSet)["fullwidth"].value().toBool(), current - 1); + + m_listbox->blockSignals(false); + m_listbox->setCurrentItem(current - 1); +} + +void +EditListViewDialog::MoveItemDown() +{ + int current = m_listbox->currentItem(); + QString text = m_listbox->text(current); + m_listbox->blockSignals(true); + + m_listbox->changeItem(m_listbox->text(current+1), current); + m_listview->setColumnText(current, m_listview->columnText(current + 1)); + m_listview->setColumnWidth(current, m_listview->columnWidth(current + 1)); + m_listview->header()->setClickEnabled(m_listview->header()->isClickEnabled(current + 1), current); + m_listview->header()->setResizeEnabled(m_listview->header()->isResizeEnabled(current + 1), current); + m_listview->header()->setStretchEnabled(m_listview->header()->isStretchEnabled(current + 1), current); + + m_listbox->changeItem(text, current+1); + m_listview->setColumnText(current + 1, (*m_propSet)["caption"].value().toString()); + m_listview->setColumnWidth(current + 1,(*m_propSet)["width"].value().toBool()); + m_listview->header()->setClickEnabled((*m_propSet)["clickable"].value().toBool(), current + 1); + m_listview->header()->setResizeEnabled((*m_propSet)["resizable"].value().toBool(), current + 1); + m_listview->header()->setStretchEnabled((*m_propSet)["fullwidth"].value().toBool(), current + 1); + + m_listbox->blockSignals(false); + m_listbox->setCurrentItem(current + 1); +} + + +/// Contents page slots //////// +void +EditListViewDialog::updateButtons(QListViewItem *item) +{ + if(!item) + { + for(int i = BNewChild; i <= BRowDown; i++) + m_buttons[i]->setEnabled(false); + return; + } + + m_buttons[BNewChild]->setEnabled(true); + m_buttons[BRemRow]->setEnabled(true); + m_buttons[BRowUp]->setEnabled( (item->itemAbove() && (item->itemAbove()->parent() == item->parent())) ); + m_buttons[BRowDown]->setEnabled(item->nextSibling()); +} + +void +EditListViewDialog::loadChildNodes(QListView *listview, QListViewItem *item, QListViewItem *parent) +{ + QListViewItem *newItem; + if(listview->inherits("KListView")) + { + if(parent) + newItem = new KListViewItem(parent); + else + newItem = new KListViewItem(listview); + } + else + { + if(parent) + newItem = new QListViewItem(parent); + else + newItem = new QListViewItem(listview); + } + + // We need to move the item at the end, which is the expected behaviour (by default it is inserted at the beginning) + QListViewItem *last; + if(parent) + last = parent->firstChild(); + else + last = listview->firstChild(); + + while(last->nextSibling()) + last = last->nextSibling(); + newItem->moveItem(last); + + // We copy the text of all the columns + for(int i = 0; i < listview->columns(); i++) + newItem->setText(i, item->text(i)); + + QListViewItem *child = item->firstChild(); + if(child) + newItem->setOpen(true); + while(child) { + loadChildNodes(listview, child, newItem); + child = child->nextSibling(); + } +} + +void +EditListViewDialog::newRow() +{ + KListViewItem *parent = (KListViewItem*)m_listview->selectedItem(); + if(parent) + parent = (KListViewItem*)parent->parent(); + KListViewItem *item; + if(parent) + item = new KListViewItem(parent, m_listview->selectedItem()); + else + item = new KListViewItem(m_listview, m_listview->selectedItem()); + item->setText(0, i18n("New Item")); + m_listview->setCurrentItem(item); +} + +void +EditListViewDialog::newChildRow() +{ + KListViewItem *parent = (KListViewItem*)m_listview->currentItem(); + KListViewItem *item; + if(parent) + item = new KListViewItem(parent); + else + item = new KListViewItem(m_listview, m_listview->currentItem()); + item->setText(0, i18n("Sub Item")); + + m_listview->setCurrentItem(item); + parent->setOpen(true); +} + +void +EditListViewDialog::removeRow() +{ + delete m_listview->currentItem(); +} + +void +EditListViewDialog::MoveRowUp() +{ + QListViewItem *item = m_listview->currentItem()->itemAbove(); + item->moveItem(m_listview->currentItem()); + updateButtons(m_listview->currentItem()); +} + +void +EditListViewDialog::MoveRowDown() +{ + QListViewItem *before = m_listview->currentItem(); + before->moveItem(before->nextSibling()); + updateButtons(before); +} + +} + +#include "editlistviewdialog.moc" diff --git a/kexi/formeditor/editlistviewdialog.h b/kexi/formeditor/editlistviewdialog.h new file mode 100644 index 00000000..09de4429 --- /dev/null +++ b/kexi/formeditor/editlistviewdialog.h @@ -0,0 +1,93 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef EDITLISTVIEW_DIALOG_H +#define EDITLISTVIEW_DIALOG_H + +#include <qintdict.h> +#include <qtoolbutton.h> +#include <kdialogbase.h> + +class QFrame; +class QListView; +class QListViewItem; +class KListViewItem; +class KListView; +class KListBox; +class QListBoxItem; + +namespace KoProperty { + class Property; + class Set; + class Editor; +} + +namespace KFormDesigner { + +//! A dialog to edit the contents of a listvuew (KListView or QListView) +/*! The dialog contains two pages, one to edit columns and one to edit ist items. + KoProperty::Editor is used in columns to edit column properties + (there are two properties not supported by Qt Designer: 'width' and 'resizable'). + The user can enter list contents inside the list + using KListViewItem::setRenameable(). Pixmaps are not yet supported. */ +class KFORMEDITOR_EXPORT EditListViewDialog : public KDialogBase +{ + Q_OBJECT + + public: + EditListViewDialog(QWidget *parent); + ~EditListViewDialog() {} + + int exec(QListView *listview); + + public slots: + // Columns page + void updateItemProperties(QListBoxItem*); + void newItem(); + void removeItem(); + void MoveItemUp(); + void MoveItemDown(); + void changeProperty(KoProperty::Set& set, KoProperty::Property& property); + + // Contents page + void updateButtons(QListViewItem*); + void newRow(); + void newChildRow(); + void removeRow(); + void MoveRowUp(); + void MoveRowDown(); + + protected: + /*! Loads all child items of \a item into \a listview (may be different from the \a items 's listview) as child of \a parent item. + This is used to copy the contents of a listview into another listview. */ + void loadChildNodes(QListView *listview, QListViewItem *item, QListViewItem *parent); + + protected: + enum { BNewRow = 10, BNewChild, BRemRow, BRowUp, BRowDown , BColAdd = 20, BColRem, BColUp, BColDown }; + KoProperty::Editor *m_editor; + KoProperty::Set *m_propSet; + QFrame *m_contents, *m_column; + KListBox *m_listbox; + KListView *m_listview; + QIntDict<QToolButton> m_buttons; +}; + +} + +#endif diff --git a/kexi/formeditor/events.cpp b/kexi/formeditor/events.cpp new file mode 100644 index 00000000..e96b9f4f --- /dev/null +++ b/kexi/formeditor/events.cpp @@ -0,0 +1,145 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr> + + 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 <qdom.h> +#include <kdebug.h> + +#include "events.h" + +namespace KFormDesigner { + +Connection::Connection(const QString &sender, const QString &signal, + const QString &receiver, const QString &slot) +{ + m_sender = sender; + m_signal = signal; + m_receiver = receiver; + m_slot = slot; +} + + /////////////////////////////////////// + +ConnectionBuffer::ConnectionBuffer() +{ + setAutoDelete(true); +} + +void +ConnectionBuffer::fixName(const QString &oldName, const QString &newName) +{ + for(Connection *c = first(); c; c = next()) + { + if(c->sender() == oldName) + c->setSender(newName); + if(c->receiver() == oldName) + c->setReceiver(newName); + } +} + +ConnectionBuffer* +ConnectionBuffer::allConnectionsForWidget(const QString &widget) +{ + ConnectionBuffer *list = new ConnectionBuffer(); + list->setAutoDelete(false); // or it will delete all our connections + for(Connection *c = first(); c; c = next()) + { + if((c->sender() == widget) || (c->receiver() == widget)) + list->append(c); + } + + return list; +} + +void +ConnectionBuffer::save(QDomNode &parentNode) +{ + if(isEmpty()) + return; + + QDomDocument domDoc = parentNode.ownerDocument(); + QDomElement connections; + if(!parentNode.namedItem("connections").isNull()) + connections = parentNode.namedItem("connections").toElement(); + else + connections = domDoc.createElement("connections"); + parentNode.appendChild(connections); + + for(Connection *c = first(); c; c = next()) + { + QDomElement connection = domDoc.createElement("connection"); + connection.setAttribute("language", "C++"); + connections.appendChild(connection); + + QDomElement sender = domDoc.createElement("sender"); + connection.appendChild(sender); + QDomText senderText = domDoc.createTextNode(c->sender()); + sender.appendChild(senderText); + + QDomElement signal = domDoc.createElement("signal"); + connection.appendChild(signal); + QDomText signalText = domDoc.createTextNode(c->signal()); + signal.appendChild(signalText); + + QDomElement receiver = domDoc.createElement("receiver"); + connection.appendChild(receiver); + QDomText receiverText = domDoc.createTextNode(c->receiver()); + receiver.appendChild(receiverText); + + QDomElement slot = domDoc.createElement("slot"); + connection.appendChild(slot); + QDomText slotText = domDoc.createTextNode(c->slot()); + slot.appendChild(slotText); + } +} + +void +ConnectionBuffer::saveAllConnectionsForWidget(const QString &widget, QDomNode parentNode) +{ + ConnectionBuffer *buff = allConnectionsForWidget(widget); + buff->save(parentNode); + delete buff; +} + +void +ConnectionBuffer::load(QDomNode node) +{ + for(QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling()) + { + Connection *conn = new Connection(); + conn->setSender(n.namedItem("sender").toElement().text()); + conn->setSignal(n.namedItem("signal").toElement().text()); + conn->setReceiver(n.namedItem("receiver").toElement().text()); + conn->setSlot(n.namedItem("slot").toElement().text()); + append(conn); + } +} + +void +ConnectionBuffer::removeAllConnectionsForWidget(const QString &widget) +{ + for(Connection *c = first(); c; c = next()) + { + if((c->sender() == widget) || (c->receiver() == widget)) + removeRef(c); + } +} + +} + +//#include "events.moc" diff --git a/kexi/formeditor/events.h b/kexi/formeditor/events.h new file mode 100644 index 00000000..c9e1b2cd --- /dev/null +++ b/kexi/formeditor/events.h @@ -0,0 +1,78 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef KFORMDESIGNEREVENTS_H +#define KFORMDESIGNEREVENTS_H + +#include <qptrlist.h> +#include <qstring.h> + +class QDomNode; + +namespace KFormDesigner { + +class KFORMEDITOR_EXPORT Connection +{ + public: + Connection(const QString &sender, const QString &signal, + const QString &receiver, const QString &slot); + Connection() {;} + ~Connection() {;} + + QString sender() const { return m_sender; } + QString receiver() const { return m_receiver; } + QString signal() const { return m_signal; } + QString slot() const { return m_slot; } + + void setSender(const QString &v) { m_sender = v; } + void setReceiver(const QString &v) { m_receiver = v; } + void setSignal(const QString &v) { m_signal = v; } + void setSlot(const QString &v) { m_slot = v; } + + protected: + QString m_sender; + QString m_signal; + QString m_receiver; + QString m_slot; +}; + +typedef QPtrList<Connection> ConnectionList; + +class KFORMEDITOR_EXPORT ConnectionBuffer : public ConnectionList +{ + public: + ConnectionBuffer(); + ~ConnectionBuffer() {;} + + void save(QDomNode &parentNode); + void load(QDomNode parentNode); + + /*! This function is called when a widget is renamed from \a oldname + to \a newname. All the Connections for this widget are updated. */ + void fixName(const QString &oldname, const QString &newName); + + ConnectionBuffer* allConnectionsForWidget(const QString &widget); + void saveAllConnectionsForWidget(const QString &widget, QDomNode parentNode); + void removeAllConnectionsForWidget(const QString &widget); +}; + +} + +#endif + diff --git a/kexi/formeditor/factories/Makefile.am b/kexi/formeditor/factories/Makefile.am new file mode 100644 index 00000000..e19bf6b3 --- /dev/null +++ b/kexi/formeditor/factories/Makefile.am @@ -0,0 +1,20 @@ +include $(top_srcdir)/kexi/Makefile.global + +INCLUDES = -I$(top_srcdir)/kexi -I$(top_srcdir)/kexi/formeditor \ + -I$(top_srcdir)/kexi/core \ + -I$(top_srcdir)/lib -I$(top_srcdir)/lib/koproperty -I$(top_srcdir)/lib/kofficecore $(all_includes) +kde_module_LTLIBRARIES = kformdesigner_containers.la kformdesigner_stdwidgets.la +kformdesigner_containers_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) $(VER_INFO) -module +kformdesigner_containers_la_SOURCES = containerfactory.cpp +kformdesigner_containers_la_LIBADD = $(top_builddir)/kexi/formeditor/libkformdesigner.la + +kformdesigner_stdwidgets_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) $(VER_INFO) -module +kformdesigner_stdwidgets_la_SOURCES = stdwidgetfactory.cpp +kformdesigner_stdwidgets_la_LIBADD = $(top_builddir)/kexi/formeditor/libkformdesigner.la + + +servicesdir=$(kde_servicesdir)/kformdesigner +services_DATA = kformdesigner_containers.desktop kformdesigner_stdwidgets.desktop + +METASOURCES = AUTO + diff --git a/kexi/formeditor/factories/containerfactory.cpp b/kexi/formeditor/factories/containerfactory.cpp new file mode 100644 index 00000000..d098c290 --- /dev/null +++ b/kexi/formeditor/factories/containerfactory.cpp @@ -0,0 +1,936 @@ +/* This file is part of the KDE project + Copyright (C) 2003 Lucijan Busch <lucijan@kde.org> + Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr> + Copyright (C) 2006-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 <qwidgetstack.h> +#include <qframe.h> +#include <qbuttongroup.h> +#include <qwidget.h> +#include <qhbox.h> +#include <qvbox.h> +#include <qstring.h> +#include <qpopupmenu.h> +#include <qdom.h> +#include <qevent.h> +#include <qobjectlist.h> +#include <qpainter.h> +#include <qvaluevector.h> +#include <qfileinfo.h> +#include <qscrollview.h> +#include <qtabbar.h> +#include <qsplitter.h> +#include <qlayout.h> + +#include <kiconloader.h> +#include <kgenericfactory.h> +#include <ktextedit.h> +#include <klineedit.h> +#include <klocale.h> +#include <kdebug.h> +#include <kdeversion.h> + +#include "containerfactory.h" +#include "container.h" +#include "form.h" +#include "formIO.h" +#include "objecttree.h" +#include "commands.h" +#include "formmanager.h" +#include "widgetlibrary.h" +#include <formeditor/utils.h> + +#if KDE_VERSION < KDE_MAKE_VERSION(3,1,9) +# define KInputDialog QInputDialog +# include <qinputdialog.h> +# include <qlineedit.h> +#else +# include <kinputdialog.h> +#endif + +ContainerWidget::ContainerWidget(QWidget *parent, const char *name) + : QWidget(parent, name) +{ +} + +ContainerWidget::~ContainerWidget() +{ +} + +QSize ContainerWidget::sizeHint() const +{ + return QSize(30,30); //default +} + +void ContainerWidget::dragMoveEvent( QDragMoveEvent *e ) +{ + QWidget::dragMoveEvent(e); + emit handleDragMoveEvent(e); +} + +void ContainerWidget::dropEvent( QDropEvent *e ) +{ + QWidget::dropEvent(e); + emit handleDropEvent(e); +} + +//////////////////////// + +GroupBox::GroupBox(const QString & title, QWidget *parent, const char *name) + : QGroupBox(title, parent, name) +{ +} + +GroupBox::~GroupBox() +{ +} + +void GroupBox::dragMoveEvent( QDragMoveEvent *e ) +{ + QGroupBox::dragMoveEvent(e); + emit handleDragMoveEvent(e); +} + +void GroupBox::dropEvent( QDropEvent *e ) +{ + QGroupBox::dropEvent(e); + emit handleDropEvent(e); +} + +//////////////////////// + +KFDTabWidget::KFDTabWidget(QWidget *parent, const char *name) + : KFormDesigner::TabWidget(parent, name) +{ +} + +KFDTabWidget::~KFDTabWidget() +{ +} + +QSize +KFDTabWidget::sizeHint() const +{ + QSize s(30,30); // default min size + for(int i=0; i < count(); i++) + s = s.expandedTo( KFormDesigner::getSizeFromChildren(page(i)) ); + + return s + QSize(10/*margin*/, tabBar()->height() + 20/*margin*/); +} + +void KFDTabWidget::dragMoveEvent( QDragMoveEvent *e ) +{ + TabWidgetBase::dragMoveEvent( e ); + if (dynamic_cast<ContainerWidget*>(currentPage())) + emit dynamic_cast<ContainerWidget*>(currentPage())->handleDragMoveEvent(e); + emit handleDragMoveEvent(e); +} + +void KFDTabWidget::dropEvent( QDropEvent *e ) +{ + TabWidgetBase::dropEvent( e ); + if (dynamic_cast<ContainerWidget*>(currentPage())) + emit dynamic_cast<ContainerWidget*>(currentPage())->handleDropEvent(e); + emit handleDropEvent(e); +} + +/// Various layout widgets /////////////////: + +HBox::HBox(QWidget *parent, const char *name) + : QFrame(parent, name), m_preview(false) +{} + +void +HBox::paintEvent(QPaintEvent *) +{ + if(m_preview) return; + QPainter p(this); + p.setPen(QPen(red, 2, Qt::DashLine)); + p.drawRect(1, 1, width()-1, height() - 1); +} + +VBox::VBox(QWidget *parent, const char *name) + : QFrame(parent, name), m_preview(false) +{} + +void +VBox::paintEvent(QPaintEvent *) +{ + if(m_preview) return; + QPainter p(this); + p.setPen(QPen(blue, 2, Qt::DashLine)); + p.drawRect(1, 1, width()-1, height() - 1); +} + +Grid::Grid(QWidget *parent, const char *name) + : QFrame(parent, name), m_preview(false) +{} + +void +Grid::paintEvent(QPaintEvent *) +{ + if(m_preview) return; + QPainter p(this); + p.setPen(QPen(darkGreen, 2, Qt::DashLine)); + p.drawRect(1, 1, width()-1, height() - 1); +} + +HFlow::HFlow(QWidget *parent, const char *name) + : QFrame(parent, name), m_preview(false) +{} + +void +HFlow::paintEvent(QPaintEvent *) +{ + if(m_preview) return; + QPainter p(this); + p.setPen(QPen(magenta, 2, Qt::DashLine)); + p.drawRect(1, 1, width()-1, height() - 1); +} + +VFlow::VFlow(QWidget *parent, const char *name) + : QFrame(parent, name), m_preview(false) +{} + +void +VFlow::paintEvent(QPaintEvent *) +{ + if(m_preview) return; + QPainter p(this); + p.setPen(QPen(cyan, 2, Qt::DashLine)); + p.drawRect(1, 1, width()-1, height() - 1); +} + +QSize +VFlow::sizeHint() const +{ + if(layout()) + return layout()->sizeHint(); + else + return QSize(700, 50); // default +} + +/////// Tab related KCommand (to allow tab creation/deletion undoing) + +InsertPageCommand::InsertPageCommand(KFormDesigner::Container *container, QWidget *parent) + : KCommand() +{ + m_containername = container->widget()->name(); + m_form = container->form(); + m_parentname = parent->name(); + m_pageid = -1; +} + +void +InsertPageCommand::execute() +{ + KFormDesigner::Container *container = m_form->objectTree()->lookup(m_containername)->container(); + QWidget *parent = m_form->objectTree()->lookup(m_parentname)->widget(); + if(m_name.isEmpty()) { + m_name = container->form()->objectTree()->generateUniqueName( + container->form()->library()->displayName("QWidget").latin1(), + /*!numberSuffixRequired*/false); + } + + QWidget *page = container->form()->library()->createWidget("QWidget", parent, m_name.latin1(), container); +// QWidget *page = new ContainerWidget(parent, m_name.latin1()); +// new KFormDesigner::Container(container, page, parent); + + QCString classname = parent->className(); + if(classname == "KFDTabWidget") + { + TabWidgetBase *tab = dynamic_cast<TabWidgetBase*>(parent); + QString n = i18n("Page %1").arg(tab->count() + 1); + tab->addTab(page, n); + tab->showPage(page); + + KFormDesigner::ObjectTreeItem *item = container->form()->objectTree()->lookup(m_name); + item->addModifiedProperty("title", n); + } + else if(classname == "QWidgetStack") + { + QWidgetStack *stack = (QWidgetStack*)parent; + stack->addWidget(page, m_pageid); + stack->raiseWidget(page); + m_pageid = stack->id(page); + + KFormDesigner::ObjectTreeItem *item = container->form()->objectTree()->lookup(m_name); + item->addModifiedProperty("id", stack->id(page)); + } +} + +void +InsertPageCommand::unexecute() +{ + QWidget *page = m_form->objectTree()->lookup(m_name)->widget(); + QWidget *parent = m_form->objectTree()->lookup(m_parentname)->widget(); + + KFormDesigner::WidgetList list; + list.append(page); + KCommand *com = new KFormDesigner::DeleteWidgetCommand(list, m_form); + + QCString classname = parent->className(); + if(classname == "KFDTabWidget") + { + TabWidgetBase *tab = dynamic_cast<TabWidgetBase*>(parent); + tab->removePage(page); + } + else if(classname == "QWidgetStack") + { + QWidgetStack *stack = (QWidgetStack*)parent; + int id = stack->id(page) - 1; + while(!stack->widget(id)) + id--; + + stack->raiseWidget(id); + stack->removeWidget(page); + } + + com->execute(); + delete com; +} + +QString +InsertPageCommand::name() const +{ + return i18n("Add Page"); +} + +/////// Sub forms ////////////////////////: + +SubForm::SubForm(QWidget *parent, const char *name) +: QScrollView(parent, name), m_form(0), m_widget(0) +{ + setFrameStyle(QFrame::WinPanel | QFrame::Sunken); + viewport()->setPaletteBackgroundColor(colorGroup().mid()); +} + +void +SubForm::setFormName(const QString &name) +{ + if(name.isEmpty()) + return; + + QFileInfo info(name); + if(!info.exists() + || (KFormDesigner::FormManager::self()->activeForm() + && (info.fileName() == KFormDesigner::FormManager::self()->activeForm()->filename()) ) ) + return; // we check if this is valid form + + // we create the container widget + delete m_widget; + m_widget = new QWidget(viewport(), "subform_widget"); +// m_widget->show(); + addChild(m_widget); + m_form = new KFormDesigner::Form( + KFormDesigner::FormManager::self()->activeForm()->library(), this->name()); + m_form->createToplevel(m_widget); + + // and load the sub form + KFormDesigner::FormIO::loadFormFromFile(m_form, m_widget, name); + m_form->setDesignMode(false); + + m_formName = name; + +} + +///// The factory ///////////////////////// + +ContainerFactory::ContainerFactory(QObject *parent, const char *, const QStringList &) + : KFormDesigner::WidgetFactory(parent, "containers") +{ + KFormDesigner::WidgetInfo *wBtnGroup = new KFormDesigner::WidgetInfo(this); + wBtnGroup->setPixmap("frame"); + wBtnGroup->setClassName("QButtonGroup"); + wBtnGroup->setName(i18n("Button Group")); + wBtnGroup->setNamePrefix( + i18n("Widget name. This string will be used to name widgets of this class. It must _not_ contain white spaces and non latin1 characters.", "buttonGroup")); + wBtnGroup->setDescription(i18n("A simple container to group buttons")); + addClass(wBtnGroup); + + KFormDesigner::WidgetInfo *wTabWidget = new KFormDesigner::WidgetInfo(this); + wTabWidget->setPixmap("tabwidget"); + wTabWidget->setClassName("KFDTabWidget"); +#if KDE_VERSION >= KDE_MAKE_VERSION(3,1,9) + wTabWidget->addAlternateClassName("KTabWidget"); + wTabWidget->addAlternateClassName("QTabWidget"); +//tmp: wTabWidget->setSavingName("QTabWidget"); + wTabWidget->setSavingName("KTabWidget"); +#else + wTabWidget->setSavingName("QTabWidget"); +#endif + wTabWidget->setIncludeFileName("ktabwidget.h"); + wTabWidget->setName(i18n("Tab Widget")); + wTabWidget->setNamePrefix( + i18n("Widget name. This string will be used to name widgets of this class. It must _not_ contain white spaces and non latin1 characters.", "tabWidget")); + wTabWidget->setDescription(i18n("A widget to display multiple pages using tabs")); + addClass(wTabWidget); + + KFormDesigner::WidgetInfo *wWidget = new KFormDesigner::WidgetInfo(this); + wWidget->setPixmap("frame"); + wWidget->setClassName("QWidget"); + wWidget->addAlternateClassName("ContainerWidget"); + wWidget->setName(i18n("Basic container")); + wWidget->setNamePrefix( + i18n("Widget name. This string will be used to name widgets of this class. It must _not_ contain white spaces and non latin1 characters.", "container")); + wWidget->setDescription(i18n("An empty container with no frame")); + addClass(wWidget); + + KFormDesigner::WidgetInfo *wGroupBox = new KFormDesigner::WidgetInfo(this); + wGroupBox->setPixmap("groupbox"); + wGroupBox->setClassName("QGroupBox"); + wGroupBox->addAlternateClassName("GroupBox"); + wGroupBox->setName(i18n("Group Box")); + wGroupBox->setNamePrefix( + i18n("Widget name. This string will be used to name widgets of this class. It must _not_ contain white spaces and non latin1 characters.", "groupBox")); + wGroupBox->setDescription(i18n("A container to group some widgets")); + addClass(wGroupBox); + + KFormDesigner::WidgetInfo *wFrame = new KFormDesigner::WidgetInfo(this); + wFrame->setPixmap("frame"); + wFrame->setClassName("QFrame"); + wFrame->setName(i18n("Frame")); + wFrame->setNamePrefix( + i18n("Widget name. This string will be used to name widgets of this class. It must _not_ contain white spaces and non latin1 characters.", "frame")); + wFrame->setDescription(i18n("A simple frame container")); + addClass(wFrame); + + KFormDesigner::WidgetInfo *wWidgetStack = new KFormDesigner::WidgetInfo(this); + wWidgetStack->setPixmap("widgetstack"); + wWidgetStack->setClassName("QWidgetStack"); + wWidgetStack->setName(i18n("Widget Stack")); + wWidgetStack->setNamePrefix( + i18n("Widget name. This string will be used to name widgets of this class. It must _not_ contain white spaces and non latin1 characters.", "widgetStack")); + wWidgetStack->setDescription(i18n("A container with multiple pages")); + addClass(wWidgetStack); + + KFormDesigner::WidgetInfo *wHBox = new KFormDesigner::WidgetInfo(this); + wHBox->setPixmap("frame"); + wHBox->setClassName("HBox"); + wHBox->setName(i18n("Horizontal Box")); + wHBox->setNamePrefix( + i18n("Widget name. This string will be used to name widgets of this class. It must _not_ contain white spaces and non latin1 characters.", "horizontalBox")); + wHBox->setDescription(i18n("A simple container to group widgets horizontally")); + addClass(wHBox); + + KFormDesigner::WidgetInfo *wVBox = new KFormDesigner::WidgetInfo(this); + wVBox->setPixmap("frame"); + wVBox->setClassName("VBox"); + wVBox->setName(i18n("Vertical Box")); + wVBox->setNamePrefix( + i18n("Widget name. This string will be used to name widgets of this class. It must _not_ contain white spaces and non latin1 characters.", "verticalBox")); + wVBox->setDescription(i18n("A simple container to group widgets vertically")); + addClass(wVBox); + + KFormDesigner::WidgetInfo *wGrid = new KFormDesigner::WidgetInfo(this); + wGrid->setPixmap("frame"); + wGrid->setClassName("Grid"); + wGrid->setName(i18n("Grid Box")); + wGrid->setNamePrefix( + i18n("Widget name. This string will be used to name widgets of this class. It must _not_ contain white spaces and non latin1 characters.", "gridBox")); + wGrid->setDescription(i18n("A simple container to group widgets in a grid")); + addClass(wGrid); + + KFormDesigner::WidgetInfo *wSplitter = new KFormDesigner::WidgetInfo(this); +//! @todo horizontal/vertical splitter icons + wSplitter->setPixmap("frame"); + wSplitter->setClassName("Splitter"); + wSplitter->addAlternateClassName("QSplitter"); + wSplitter->setName(i18n("Splitter")); + wSplitter->setNamePrefix( + i18n("Widget name. This string will be used to name widgets of this class. It must _not_ contain white spaces and non latin1 characters.", "splitter")); + wSplitter->setDescription(i18n("A container that enables user to resize its children")); + addClass(wSplitter); + + KFormDesigner::WidgetInfo *wHFlow = new KFormDesigner::WidgetInfo(this); +//! @todo hflow icon + wHFlow->setPixmap("frame"); + wHFlow->setClassName("HFlow"); + wHFlow->setName(i18n("Row Layout")); + wHFlow->setNamePrefix( + i18n("Widget name. This string will be used to name widgets of this class. It must _not_ contain white spaces and non latin1 characters.", "rowLayout")); + wHFlow->setDescription(i18n("A simple container to group widgets by rows")); + addClass(wHFlow); + + KFormDesigner::WidgetInfo *wVFlow = new KFormDesigner::WidgetInfo(this); +//! @todo vflow icon + wVFlow->setPixmap("frame"); + wVFlow->setClassName("VFlow"); + wVFlow->setName(i18n("Column Layout")); + wVFlow->setNamePrefix( + i18n("Widget name. This string will be used to name widgets of this class. It must _not_ contain white spaces and non latin1 characters.", "columnLayout")); + wVFlow->setDescription(i18n("A simple container to group widgets by columns")); + addClass(wVFlow); + + KFormDesigner::WidgetInfo *wSubForm = new KFormDesigner::WidgetInfo(this); + wSubForm->setPixmap("form"); + wSubForm->setClassName("SubForm"); + wSubForm->setName(i18n("Sub Form")); + wSubForm->setNamePrefix( + i18n("Widget name. This string will be used to name widgets of this class. It must _not_ contain white spaces and non latin1 characters.", "subForm")); + wSubForm->setDescription(i18n("A form widget included in another Form")); + wSubForm->setAutoSyncForProperty( "formName", false ); + addClass(wSubForm); + + //groupbox + m_propDesc["title"] = i18n("Title"); + m_propDesc["flat"] = i18n("Flat"); + + //tab widget + m_propDesc["tabPosition"] = i18n("Tab Position"); + m_propDesc["currentPage"] = i18n("Current Page"); + m_propDesc["tabShape"] = i18n("Tab Shape"); + + m_propDesc["tabPosition"] = i18n("Tab Position"); + m_propDesc["tabPosition"] = i18n("Tab Position"); + + m_propValDesc["Rounded"] = i18n("for Tab Shape", "Rounded"); + m_propValDesc["Triangular"] = i18n("for Tab Shape", "Triangular"); +} + +QWidget* +ContainerFactory::createWidget(const QCString &c, QWidget *p, const char *n, + KFormDesigner::Container *container, int options) +{ + if(c == "QButtonGroup") + { + QString text = container->form()->library()->textForWidgetName(n, c); + QButtonGroup *w = new QButtonGroup(/*i18n("Button Group")*/text, p, n); + new KFormDesigner::Container(container, w, container); + return w; + } + else if(c == "KFDTabWidget") + { + KFDTabWidget *tab = new KFDTabWidget(p, n); +#if defined(USE_KTabWidget) && KDE_VERSION >= KDE_MAKE_VERSION(3,1,9) + tab->setTabReorderingEnabled(true); + connect(tab, SIGNAL(movedTab(int,int)), this, SLOT(reorderTabs(int,int))); +#endif + container->form()->objectTree()->addItem(container->objectTree(), + new KFormDesigner::ObjectTreeItem( + container->form()->library()->displayName(c), n, tab, container)); +// m_manager = container->form()->manager(); + + // if we are loading, don't add this tab + if(container->form()->interactiveMode()) + { + //m_widget=tab; + setWidget(tab, container); +// m_container=container; + addTabPage(); + } + + return tab; + } + else if(c == "QWidget" || c=="ContainerWidget") + { + QWidget *w = new ContainerWidget(p, n); + new KFormDesigner::Container(container, w, p); + return w; + } + else if(c == "QGroupBox" || c == "GroupBox") + { + QString text = container->form()->library()->textForWidgetName(n, c); + QGroupBox *w = new GroupBox(text, p, n); + new KFormDesigner::Container(container, w, container); + return w; + } + else if(c == "QFrame") + { + QFrame *w = new QFrame(p, n); + w->setLineWidth(2); + w->setFrameStyle(QFrame::StyledPanel|QFrame::Raised); + new KFormDesigner::Container(container, w, container); + return w; + } + else if(c == "QWidgetStack") + { + QWidgetStack *stack = new QWidgetStack(p, n); + stack->setLineWidth(2); + stack->setFrameStyle(QFrame::StyledPanel|QFrame::Raised); + container->form()->objectTree()->addItem( container->objectTree(), + new KFormDesigner::ObjectTreeItem( + container->form()->library()->displayName(c), n, stack, container)); + + if(container->form()->interactiveMode()) + { + //m_widget = stack; + setWidget(stack, container); +// m_container = container; + addStackPage(); + } + return stack; + } + else if(c == "HBox") { + HBox *w = new HBox(p, n); + new KFormDesigner::Container(container, w, container); + return w; + } + else if(c == "VBox") { + VBox *w = new VBox(p, n); + new KFormDesigner::Container(container, w, container); + return w; + } + else if(c == "Grid") { + Grid *w = new Grid(p, n); + new KFormDesigner::Container(container, w, container); + return w; + } + else if(c == "HFlow") { + HFlow *w = new HFlow(p, n); + new KFormDesigner::Container(container, w, container); + return w; + } + else if(c == "VFlow") { + VFlow *w = new VFlow(p, n); + new KFormDesigner::Container(container, w, container); + return w; + } + else if(c == "SubForm") { + SubForm *subform = new SubForm(p, n); + return subform; + } + else if(c == "QSplitter") { + QSplitter *split = new QSplitter(p, n); + if (0 == (options & WidgetFactory::AnyOrientation)) + split->setOrientation( + (options & WidgetFactory::VerticalOrientation) ? Qt::Vertical : Qt::Horizontal); + new KFormDesigner::Container(container, split, container); + return split; + } + + return 0; +} + +bool +ContainerFactory::previewWidget(const QCString &classname, QWidget *widget, KFormDesigner::Container *container) +{ + if(classname == "WidgetStack") + { + QWidgetStack *stack = ((QWidgetStack*)widget); + KFormDesigner::ObjectTreeItem *tree = container->form()->objectTree()->lookup(widget->name()); + if(!tree->modifiedProperties()->contains("frameShape")) + stack->setFrameStyle(QFrame::NoFrame); + } + else if(classname == "HBox") + ((HBox*)widget)->setPreviewMode(); + else if(classname == "VBox") + ((VBox*)widget)->setPreviewMode(); + else if(classname == "Grid") + ((Grid*)widget)->setPreviewMode(); + else if(classname == "HFlow") + ((HFlow*)widget)->setPreviewMode(); + else if(classname == "VFlow") + ((VFlow*)widget)->setPreviewMode(); + else + return false; + return true; +} + +bool +ContainerFactory::createMenuActions(const QCString &classname, QWidget *w, QPopupMenu *menu, + KFormDesigner::Container *container) +{ + setWidget(w, container); + //m_widget = w; +// m_container = container; + + if((classname == "KFDTabWidget") || (w->parentWidget()->parentWidget()->inherits("QTabWidget"))) + { + if(w->parentWidget()->parentWidget()->inherits("QTabWidget")) + { + //m_widget = w->parentWidget()->parentWidget(); + setWidget(w->parentWidget()->parentWidget(), m_container->toplevel()); +// m_container = m_container->toplevel(); + } + + int id = menu->insertItem(SmallIconSet("tab_new"), i18n("Add Page"), this, SLOT(addTabPage()) ); + id = menu->insertItem(SmallIconSet("edit"), i18n("Rename Page..."), this, SLOT(renameTabPage())); + id = menu->insertItem(SmallIconSet("tab_remove"), i18n("Remove Page"), this, SLOT(removeTabPage())); +// if( dynamic_cast<TabWidgetBase*>(m_widget)->count() == 1) + if( dynamic_cast<TabWidgetBase*>(widget())->count() == 1) + menu->setItemEnabled(id, false); + return true; + } + else if(w->parentWidget()->isA("QWidgetStack") && !w->parentWidget()->parentWidget()->inherits("QTabWidget")) + { + //m_widget = w->parentWidget(); + QWidgetStack *stack = (QWidgetStack*)w->parentWidget(); //m_widget; + setWidget( + w->parentWidget(), + container->form()->objectTree()->lookup(stack->name())->parent()->container() + ); +// m_container = container->form()->objectTree()->lookup(m_widget->name())->parent()->container(); +// m_container = container->form()->objectTree()->lookup(stack->name())->parent()->container(); + + int id = menu->insertItem(SmallIconSet("tab_new"), i18n("Add Page"), this, SLOT(addStackPage()) ); + + id = menu->insertItem(SmallIconSet("tab_remove"), i18n("Remove Page"), this, SLOT(removeStackPage()) ); +// if( ((QWidgetStack*)m_widget)->children()->count() == 4) // == the stack has only one page + if(stack->children()->count() == 4) // == the stack has only one page + menu->setItemEnabled(id, false); + + id = menu->insertItem(SmallIconSet("next"), i18n("Jump to Next Page"), this, SLOT(nextStackPage())); + if(!stack->widget(stack->id(stack->visibleWidget())+1)) + menu->setItemEnabled(id, false); + + id = menu->insertItem(SmallIconSet("previous"), i18n("Jump to Previous Page"), this, SLOT(prevStackPage())); + if(!stack->widget(stack->id(stack->visibleWidget()) -1) ) + menu->setItemEnabled(id, false); + return true; + } + return false; +} + +bool +ContainerFactory::startEditing(const QCString &classname, QWidget *w, KFormDesigner::Container *container) +{ + m_container = container; + if(classname == "QButtonGroup") + { + QButtonGroup *group = static_cast<QButtonGroup*>(w); + QRect r = QRect(group->x()+2, group->y()-5, group->width()-10, w->fontMetrics().height() + 10); + createEditor(classname, group->title(), group, container, r, Qt::AlignAuto); + return true; + } + if(classname == "QGroupBox" || classname == "GroupBox") + { + QGroupBox *group = static_cast<QGroupBox*>(w); + QRect r = QRect(group->x()+2, group->y()-5, group->width()-10, w->fontMetrics().height() + 10); + createEditor(classname, group->title(), group, container, r, Qt::AlignAuto); + return true; + } + return false; +} + +bool +ContainerFactory::saveSpecialProperty(const QCString &, const QString &name, const QVariant &, QWidget *w, QDomElement &parentNode, QDomDocument &parent) +{ + if((name == "title") && (w->parentWidget()->parentWidget()->inherits("QTabWidget"))) + { + TabWidgetBase *tab = dynamic_cast<TabWidgetBase*>(w->parentWidget()->parentWidget()); + KFormDesigner::FormIO::savePropertyElement(parentNode, parent, "attribute", "title", tab->tabLabel(w)); + } + else if((name == "id") && (w->parentWidget()->isA("QWidgetStack"))) + { + QWidgetStack *stack = (QWidgetStack*)w->parentWidget(); + KFormDesigner::FormIO::savePropertyElement(parentNode, parent, "attribute", "id", stack->id(w)); + } + else + return false; + return true; +} + +bool +ContainerFactory::readSpecialProperty(const QCString &, QDomElement &node, QWidget *w, KFormDesigner::ObjectTreeItem *item) +{ + QString name = node.attribute("name"); + if((name == "title") && (item->parent()->widget()->inherits("QTabWidget"))) + { + TabWidgetBase *tab = dynamic_cast<TabWidgetBase*>(w->parentWidget()); + tab->addTab(w, node.firstChild().toElement().text()); + item->addModifiedProperty("title", node.firstChild().toElement().text()); + return true; + } + + if((name == "id") && (w->parentWidget()->isA("QWidgetStack"))) + { + QWidgetStack *stack = (QWidgetStack*)w->parentWidget(); + int id = KFormDesigner::FormIO::readPropertyValue(node.firstChild(), w, name).toInt(); + stack->addWidget(w, id); + stack->raiseWidget(w); + item->addModifiedProperty("id", id); + return true; + } + + return false; +} + +QValueList<QCString> +ContainerFactory::autoSaveProperties(const QCString &c) +{ + QValueList<QCString> lst; +// if(c == "SubForm") +// lst << "formName"; + if(c == "QSplitter") + lst << "orientation"; + return lst; +} + +bool +ContainerFactory::isPropertyVisibleInternal(const QCString &classname, + QWidget *w, const QCString &property, bool isTopLevel) +{ + bool ok = true; + + if((classname == "HBox") || (classname == "VBox") || (classname == "Grid") || + (classname == "HFlow") || (classname == "VFlow")) + { + return property == "name" || property == "geometry"; + } + else if (classname == "QGroupBox" || classname=="GroupBox") { + ok = +#ifdef KEXI_NO_UNFINISHED +/*! @todo Hidden for now in Kexi. "checkable" and "checked" props need adding +a fake properties which will allow to properly work in design mode, otherwise +child widgets become frozen when checked==true */ + (m_showAdvancedProperties || (property != "checkable" && property != "checked")) && +#endif + true + ; + } + else if (classname == "KFDTabWidget") { + ok = (m_showAdvancedProperties || (property != "tabReorderingEnabled" && property != "hoverCloseButton" && property != "hoverCloseButtonDelayed")); + } + + return ok && WidgetFactory::isPropertyVisibleInternal(classname, w, property, isTopLevel); +} + +bool +ContainerFactory::changeText(const QString &text) +{ + changeProperty("title", text, m_container->form()); + return true; +} + +void +ContainerFactory::resizeEditor(QWidget *editor, QWidget *widget, const QCString &) +{ + QSize s = widget->size(); + editor->move(widget->x() + 2, widget->y() - 5); + editor->resize(s.width() - 20, widget->fontMetrics().height() +10); +} + +// Widget Specific slots used in menu items + +void ContainerFactory::addTabPage() +{ +// if (!m_widget->inherits("QTabWidget")) + if (!widget()->inherits("QTabWidget")) + return; + KCommand *com = new InsertPageCommand(m_container, widget()); + if(dynamic_cast<TabWidgetBase*>(widget())->count() == 0) + { + com->execute(); + delete com; + } + else + m_container->form()->addCommand(com, true); +} + +void ContainerFactory::removeTabPage() +{ + if (!widget()->inherits("QTabWidget")) + return; + TabWidgetBase *tab = dynamic_cast<TabWidgetBase*>(widget()); + QWidget *w = tab->currentPage(); + + KFormDesigner::WidgetList list; + list.append(w); + KCommand *com = new KFormDesigner::DeleteWidgetCommand(list, m_container->form()); + tab->removePage(w); + m_container->form()->addCommand(com, true); +} + +void ContainerFactory::renameTabPage() +{ + if (!widget()->inherits("QTabWidget")) + return; + TabWidgetBase *tab = dynamic_cast<TabWidgetBase*>(widget()); + QWidget *w = tab->currentPage(); + bool ok; + + QString name = KInputDialog::getText(i18n("New Page Title"), i18n("Enter a new title for the current page:"), +#if KDE_VERSION < KDE_MAKE_VERSION(3,1,9) + QLineEdit::Normal, +#endif + tab->tabLabel(w), &ok, w->topLevelWidget()); + if(ok) + tab->changeTab(w, name); +} + +void ContainerFactory::reorderTabs(int oldpos, int newpos) +{ + KFormDesigner::ObjectTreeItem *tab + = KFormDesigner::FormManager::self()->activeForm()->objectTree()->lookup(sender()->name()); + if(!tab) + return; + + KFormDesigner::ObjectTreeItem *item = tab->children()->take(oldpos); + tab->children()->insert(newpos, item); +} + +void ContainerFactory::addStackPage() +{ + if (!widget()->isA("QWidgetStack")) + return; + KCommand *com = new InsertPageCommand(m_container, widget()); + if(!((QWidgetStack*)widget())->visibleWidget()) + { + com->execute(); + delete com; + } + else + m_container->form()->addCommand(com, true); +} + +void ContainerFactory::removeStackPage() +{ + if (!widget()->isA("QWidgetStack")) + return; + QWidgetStack *stack = (QWidgetStack*)widget(); + QWidget *page = stack->visibleWidget(); + + KFormDesigner::WidgetList list; + list.append(page); + KCommand *com = new KFormDesigner::DeleteWidgetCommand(list, m_container->form()); + + // raise prev widget + int id = stack->id(page) - 1; + while(!stack->widget(id)) + id--; + stack->raiseWidget(id); + + stack->removeWidget(page); + m_container->form()->addCommand(com, true); +} + +void ContainerFactory::prevStackPage() +{ + QWidgetStack *stack = (QWidgetStack*)widget(); + int id = stack->id(stack->visibleWidget()) - 1; + if(stack->widget(id)) + stack->raiseWidget(id); +} + +void ContainerFactory::nextStackPage() +{ + QWidgetStack *stack = (QWidgetStack*)widget(); + int id = stack->id(stack->visibleWidget()) + 1; + if(stack->widget(id)) + stack->raiseWidget(id); +} + +ContainerFactory::~ContainerFactory() +{ +} + +KFORMDESIGNER_WIDGET_FACTORY(ContainerFactory, containers) + +#include "containerfactory.moc" diff --git a/kexi/formeditor/factories/containerfactory.h b/kexi/formeditor/factories/containerfactory.h new file mode 100644 index 00000000..1092d852 --- /dev/null +++ b/kexi/formeditor/factories/containerfactory.h @@ -0,0 +1,271 @@ +/* This file is part of the KDE project + Copyright (C) 2003 Lucijan Busch <lucijan@kde.org> + Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr> + Copyright (C) 2006-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. +*/ + +#ifndef CONTAINERFACTORY_H +#define CONTAINERFACTORY_H + +#include <kcommand.h> + +#include "widgetfactory.h" +#include "../utils.h" + +namespace KFormDesigner +{ + class Form; + class FormManager; + class Container; +} + +class InsertPageCommand : public KCommand +{ + public: + InsertPageCommand(KFormDesigner::Container *container, QWidget *widget); + + virtual void execute(); + virtual void unexecute(); + virtual QString name() const; + + protected: + KFormDesigner::Form *m_form; + QString m_containername; + QString m_name; + QString m_parentname; + int m_pageid; +}; + +//! Helper widget (used when using 'Lay out horizontally') +class KFORMEDITOR_EXPORT HBox : public QFrame +{ + Q_OBJECT + + public: + HBox(QWidget *parent, const char *name); + virtual ~HBox(){;} + void setPreviewMode() {m_preview = true;} + virtual void paintEvent(QPaintEvent *ev); + + protected: + bool m_preview; +}; + +//! Helper widget (used when using 'Lay out vertically') +class KFORMEDITOR_EXPORT VBox : public QFrame +{ + Q_OBJECT + + public: + VBox(QWidget *parent, const char *name); + virtual ~VBox(){;} + void setPreviewMode() {m_preview = true;} + virtual void paintEvent(QPaintEvent *ev); + + protected: + bool m_preview; +}; + +//! Helper widget (used when using 'Lay out in a grid') +class KFORMEDITOR_EXPORT Grid : public QFrame +{ + Q_OBJECT + + public: + Grid(QWidget *parent, const char *name); + virtual ~Grid(){;} + void setPreviewMode() {m_preview = true;} + virtual void paintEvent(QPaintEvent *ev); + + protected: + bool m_preview; +}; + +//! Helper widget (used when using 'Lay out with horizontal flow') +class KFORMEDITOR_EXPORT HFlow : public QFrame +{ + Q_OBJECT + + public: + HFlow(QWidget *parent, const char *name); + virtual ~HFlow(){;} + void setPreviewMode() {m_preview = true;} + virtual void paintEvent(QPaintEvent *ev); + + protected: + bool m_preview; +}; + +//! Helper widget (used when using 'Lay out with horizontal flow') +class KFORMEDITOR_EXPORT VFlow : public QFrame +{ + Q_OBJECT + + public: + VFlow(QWidget *parent, const char *name); + virtual ~VFlow(){;} + void setPreviewMode() {m_preview = true;} + virtual void paintEvent(QPaintEvent *ev); + virtual QSize sizeHint() const; + + protected: + bool m_preview; +}; + +//! A simple container widget +class KFORMEDITOR_EXPORT ContainerWidget : public QWidget +{ + Q_OBJECT + + friend class KFDTabWidget; + + public: + ContainerWidget(QWidget *parent, const char *name); + virtual ~ContainerWidget(); + + virtual QSize sizeHint() const; + + //! Used to emit handleDragMoveEvent() signal needed to control dragging over the container's surface + virtual void dragMoveEvent( QDragMoveEvent *e ); + + //! Used to emit handleDropEvent() signal needed to control dropping on the container's surface + virtual void dropEvent( QDropEvent *e ); + + signals: + //! Needed to control dragging over the container's surface + void handleDragMoveEvent(QDragMoveEvent *e); + + //! Needed to control dropping on the container's surface + void handleDropEvent(QDropEvent *e); +}; + +//! A tab widget +class KFORMEDITOR_EXPORT KFDTabWidget : public KFormDesigner::TabWidget +{ + Q_OBJECT + + public: + KFDTabWidget(QWidget *parent, const char *name); + virtual ~KFDTabWidget(); + + virtual QSize sizeHint() const; + + //! Used to emit handleDragMoveEvent() signal needed to control dragging over the container's surface + virtual void dragMoveEvent( QDragMoveEvent *e ); + + //! Used to emit handleDropEvent() signal needed to control dropping on the container's surface + virtual void dropEvent( QDropEvent *e ); + + signals: + //! Needed to control dragging over the container's surface + void handleDragMoveEvent(QDragMoveEvent *e); + + //! Needed to control dropping on the container's surface + void handleDropEvent(QDropEvent *e); +}; + +//! A group box widget +class KFORMEDITOR_EXPORT GroupBox : public QGroupBox +{ + Q_OBJECT + + public: + GroupBox(const QString & title, QWidget *parent, const char *name); + virtual ~GroupBox(); + + //! Used to emit handleDragMoveEvent() signal needed to control dragging over the container's surface + virtual void dragMoveEvent( QDragMoveEvent *e ); + + //! Used to emit handleDropEvent() signal needed to control dropping on the container's surface + virtual void dropEvent( QDropEvent *e ); + + signals: + //! Needed to control dragging over the container's surface + void handleDragMoveEvent(QDragMoveEvent *e); + + //! Needed to control dropping on the container's surface + void handleDropEvent(QDropEvent *e); +}; + +//! A form embedded as a widget inside other form +class KFORMEDITOR_EXPORT SubForm : public QScrollView +{ + Q_OBJECT + Q_PROPERTY(QString formName READ formName WRITE setFormName DESIGNABLE true) + + public: + SubForm(QWidget *parent, const char *name); + ~SubForm() {} + + //! \return the name of the subform inside the db + QString formName() const { return m_formName; } + void setFormName(const QString &name); + + private: +// KFormDesigner::FormManager *m_manager; + KFormDesigner::Form *m_form; + QWidget *m_widget; + QString m_formName; +}; + +//! Standard Factory for all container widgets +class ContainerFactory : public KFormDesigner::WidgetFactory +{ + Q_OBJECT + + public: + ContainerFactory(QObject *parent, const char *name, const QStringList &args); + virtual ~ContainerFactory(); + + virtual QWidget *createWidget(const QCString & classname, QWidget *parent, const char *name, KFormDesigner::Container *container, + int options = DefaultOptions); + virtual bool createMenuActions(const QCString& classname, QWidget *w, QPopupMenu *menu, + KFormDesigner::Container *container); + virtual bool startEditing(const QCString &classname, QWidget *w, + KFormDesigner::Container *container); + virtual bool previewWidget(const QCString &classname, QWidget *widget, + KFormDesigner::Container *container); + virtual bool saveSpecialProperty(const QCString &classname, const QString &name, + const QVariant &value, QWidget *w, QDomElement &parentNode, QDomDocument &parent); + virtual bool readSpecialProperty(const QCString &classname, QDomElement &node, QWidget *w, + KFormDesigner::ObjectTreeItem *item); + virtual QValueList<QCString> autoSaveProperties(const QCString &classname); + + protected: + virtual bool isPropertyVisibleInternal(const QCString &classname, QWidget *w, + const QCString &property, bool isTopLevel); + virtual bool changeText(const QString &newText); + virtual void resizeEditor(QWidget *editor, QWidget *widget, const QCString &classname); + + public slots: + void addTabPage(); + void addStackPage(); + void renameTabPage(); + void removeTabPage(); + void removeStackPage(); + void prevStackPage(); + void nextStackPage(); + void reorderTabs(int oldpos, int newpos); + + private: +// QWidget *m_widget; +// KFormDesigner::Container *m_container; +// KFormDesigner::FormManager *m_manager; +}; + +#endif diff --git a/kexi/formeditor/factories/kformdesigner_containers.desktop b/kexi/formeditor/factories/kformdesigner_containers.desktop new file mode 100644 index 00000000..ae30820c --- /dev/null +++ b/kexi/formeditor/factories/kformdesigner_containers.desktop @@ -0,0 +1,53 @@ +[Desktop Entry] +Type=Service +ServiceTypes=KFormDesigner/WidgetFactory + +Name=Container Widgets +Name[bg]=Контейнери +Name[ca]=Estris contenidors +Name[cy]=Celfigion Cynhwysydd +Name[da]=Container-kontroller +Name[de]=Container-Elemente +Name[el]=Γραφικά συστατικά υποδοχείς +Name[eo]=Ujaj fenestraĵoj +Name[es]=Widgets contenedores +Name[et]=Konteinervidinad +Name[eu]=Trepeta edukitzaileak +Name[fa]=عناصر محتوی +Name[fi]=Säiliön osat +Name[fr]=Conteneur d'éléments graphiques +Name[fy]=Kontainerwidgets +Name[gl]=Elementos Contedores +Name[he]=כלי קיבול +Name[hr]=Sadržajni widgeti +Name[hu]=Tartóelemek +Name[is]=Geymihlutir +Name[it]=Oggetti contenitori +Name[ja]=コンテナウィジェット +Name[km]=ធាតុក្រាហ្វិកកុងតឺន័រ +Name[lv]=Konteinera logdaļa +Name[ms]=Widget Bekas +Name[nb]=Beholder-elementer +Name[nds]=Gelaatselementen +Name[ne]=कन्टेनर विजेट +Name[nl]=Containerwidgets +Name[nn]=Kjeraldelement +Name[pl]=Kontrolki pojemników +Name[pt]=Elementos Contentores +Name[pt_BR]=Widgets Recipientes +Name[ru]=Контейнеры +Name[se]=Lihtteáđat +Name[sk]=Kontajnerové komponenty +Name[sl]=Vsebovalni gradniki +Name[sr]=Контејнерске контроле +Name[sr@Latn]=Kontejnerske kontrole +Name[sv]=Omgivande komponenter +Name[ta]=கொள்கலன் சாளர உருக்கள் +Name[tr]= Containers +Name[uk]=Віджети контейнера +Name[zh_CN]=容器部件 +Name[zh_TW]=容器視窗元件 + +X-KDE-Library=kformdesigner_containers +X-KFormDesigner-FactoryGroup= +X-KFormDesigner-WidgetFactoryVersion=2 diff --git a/kexi/formeditor/factories/kformdesigner_stdwidgets.desktop b/kexi/formeditor/factories/kformdesigner_stdwidgets.desktop new file mode 100644 index 00000000..d5d599fa --- /dev/null +++ b/kexi/formeditor/factories/kformdesigner_stdwidgets.desktop @@ -0,0 +1,55 @@ +[Desktop Entry] +Type=Service +ServiceTypes=KFormDesigner/WidgetFactory + +Name=Basic Widgets +Name[bg]=Основни графични обекти +Name[br]=Widgets Diazez +Name[ca]=Estris estàndard +Name[cy]=Celfigion Sylfaenol +Name[da]=Basale kontroller +Name[de]=Basis-Elemente +Name[el]=Βασικά γραφικά συστατικά +Name[eo]=Bazaj fenestraĵoj +Name[es]=Widgets básicos +Name[et]=Standardvidinad +Name[eu]=Oinarrizko trepetak +Name[fa]=عناصر پایهای +Name[fi]=yleiset elementit +Name[fr]=Éléments graphiques basiques +Name[fy]=Basiswidgets +Name[gl]=Elementos Básicos +Name[he]=פריטים בסיסיים +Name[hr]=Osnovni widgeti +Name[hu]=Alapelemek +Name[is]=Grunnhlutir +Name[it]=Oggetti di base +Name[ja]=基本的なウィジェット +Name[km]=ធាតុក្រាហ្វិកមូលដ្ឋាន +Name[lv]=Pamata logdaļas +Name[ms]=Widget Asas +Name[nb]=Elementære elementer +Name[nds]=Grundelementen +Name[ne]=आधारभूत विजेट +Name[nl]=Basiswidgets +Name[nn]=Grunnleggjande element +Name[pl]=Proste kontrolki +Name[pt]=Elementos Básicos +Name[pt_BR]=Widgets Básicos +Name[ru]=Стандартные виджеты +Name[se]=Oktageardanis áđat +Name[sk]=Základné komponenty +Name[sl]=Osnovni gradniki +Name[sr]=Основне контроле +Name[sr@Latn]=Osnovne kontrole +Name[sv]=Standardkomponenter +Name[ta]=அடிப்படை சாளர உருக்கள் +Name[uk]=Основні віджети +Name[uz]=Oddiy vidjetlar +Name[uz@cyrillic]=Оддий виджетлар +Name[zh_CN]=基本部件 +Name[zh_TW]=基本視窗元件 + +X-KDE-Library=kformdesigner_stdwidgets +X-KFormDesigner-FactoryGroup= +X-KFormDesigner-WidgetFactoryVersion=2 diff --git a/kexi/formeditor/factories/stdwidgetfactory.cpp b/kexi/formeditor/factories/stdwidgetfactory.cpp new file mode 100644 index 00000000..fade100a --- /dev/null +++ b/kexi/formeditor/factories/stdwidgetfactory.cpp @@ -0,0 +1,984 @@ +/*************************************************************************** + * Copyright (C) 2003 by Lucijan Busch <lucijan@kde.org> * + * Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr> * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Library General Public License as * + * published by the Free Software Foundation; either version 2 of the * + * License, or (at your option) any later version. * + ***************************************************************************/ + +#include <qlabel.h> +#include <qpopupmenu.h> +#include <qcursor.h> +#include <qradiobutton.h> +#include <qcheckbox.h> +#include <qslider.h> +#include <qobjectlist.h> +#include <qstring.h> +#include <qvariant.h> +#include <qheader.h> +#include <qdom.h> +#include <qstyle.h> +#include <qvaluevector.h> + +#include <klineedit.h> +#include <kpushbutton.h> +#include <knuminput.h> +#include <kcombobox.h> +#include <klistbox.h> +#include <ktextedit.h> +#include <klistview.h> +#include <kprogress.h> +#include <kiconloader.h> +#include <kgenericfactory.h> +#include <klocale.h> +#include <kdebug.h> +#include <kdeversion.h> + +#if KDE_VERSION < KDE_MAKE_VERSION(3,1,9) +# include <qdatetimeedit.h> +# define KTimeWidget QTimeEdit +# define KDateWidget QDateEdit +# define KDateTimeWidget QDateTimeEdit +#else +# include <ktimewidget.h> +# include <kdatewidget.h> +# include <kdatetimewidget.h> +#endif + +#include "spring.h" +#include "formIO.h" +#include "form.h" +#include "formmanager.h" +#include "widgetlibrary.h" +#include "widgetpropertyset.h" +#include <koproperty/property.h> + +#include "stdwidgetfactory.h" + +// Some widgets subclass to allow event filtering and some other things +KexiPictureLabel::KexiPictureLabel(const QPixmap &pix, QWidget *parent, const char *name) + : QLabel(parent, name) +{ + setPixmap(pix); + setScaledContents(false); +} + +bool +KexiPictureLabel::setProperty(const char *name, const QVariant &value) +{ + if(QString(name) == "pixmap") + resize(value.toPixmap().height(), value.toPixmap().width()); + return QLabel::setProperty(name, value); +} + +Line::Line(Qt::Orientation orient, QWidget *parent, const char *name) + : QFrame(parent, name) +{ + setFrameShadow(Sunken); + if(orient == Horizontal) + setFrameShape(HLine); + else + setFrameShape(VLine); +} + +void +Line::setOrientation(Qt::Orientation orient) +{ + if(orient == Horizontal) + setFrameShape(HLine); + else + setFrameShape(VLine); +} + +Qt::Orientation +Line::orientation() const +{ + if(frameShape() == HLine) + return Horizontal; + else + return Vertical; +} + +// The factory itself + +StdWidgetFactory::StdWidgetFactory(QObject *parent, const char *, const QStringList &) + : KFormDesigner::WidgetFactory(parent, "stdwidgets") +{ + KFormDesigner::WidgetInfo *wFormWidget = new KFormDesigner::WidgetInfo(this); + wFormWidget->setPixmap("form"); + wFormWidget->setClassName("FormWidgetBase"); + wFormWidget->setName(i18n("Form")); + wFormWidget->setNamePrefix(i18n("This string will be used to name widgets of this class. It must _not_ contain white " + "spaces and non latin1 characters.", "form")); + wFormWidget->setDescription(i18n("A simple form widget")); + addClass(wFormWidget); + + KFormDesigner::WidgetInfo *wCustomWidget = new KFormDesigner::WidgetInfo(this); + wCustomWidget->setPixmap("unknown_widget"); + wCustomWidget->setClassName("CustomWidget"); + wCustomWidget->setName(i18n("Custom Widget")); + wCustomWidget->setNamePrefix(i18n("This string will be used to name widgets of this class. It must _not_ contain white " + "spaces and non latin1 characters.", "customWidget")); + wCustomWidget->setDescription(i18n("A custom or non-supported widget")); + addClass(wCustomWidget); + + KFormDesigner::WidgetInfo *wLabel = new KFormDesigner::WidgetInfo(this); + wLabel->setPixmap("label"); + wLabel->setClassName("QLabel"); + wLabel->setName(i18n("Text Label")); + wLabel->setNamePrefix( + i18n("Widget name. This string will be used to name widgets of this class. It must _not_ contain white spaces and non latin1 characters.", "label")); + wLabel->setDescription(i18n("A widget to display text")); + addClass(wLabel); + + KFormDesigner::WidgetInfo *wPixLabel = new KFormDesigner::WidgetInfo(this); + wPixLabel->setPixmap("pixmaplabel"); + wPixLabel->setClassName("KexiPictureLabel"); + wPixLabel->setName(i18n("Picture Label")); +//! @todo Qt designer compatibility: maybe use this class when QLabel has a pixmap set...? + //wPixLabel->addAlternateClassName("QLabel"); + wPixLabel->setSavingName("KexiPictureLabel"); + wPixLabel->setNamePrefix( + i18n("Widget name. This string will be used to name widgets of this class. It must _not_ contain white spaces and non latin1 characters.", "picture")); + wPixLabel->setDescription(i18n("A widget to display pictures")); + addClass(wPixLabel); + + KFormDesigner::WidgetInfo *wLineEdit = new KFormDesigner::WidgetInfo(this); + wLineEdit->setPixmap("lineedit"); + wLineEdit->setClassName("KLineEdit"); + wLineEdit->addAlternateClassName("QLineEdit"); + wLineEdit->setIncludeFileName("klineedit.h"); + wLineEdit->setName(i18n("Line Edit")); + wLineEdit->setNamePrefix( + i18n("Widget name. This string will be used to name widgets of this class. It must _not_ contain white spaces and non latin1 characters.", "lineEdit")); + wLineEdit->setDescription(i18n("A widget to input text")); + addClass(wLineEdit); + + KFormDesigner::WidgetInfo *wSpring = new KFormDesigner::WidgetInfo(this); + wSpring->setPixmap("spring"); + wSpring->setClassName("Spring"); + wSpring->setName(i18n("Spring")); + wSpring->setNamePrefix( + i18n("Widget name. This string will be used to name widgets of this class. It must _not_ contain white spaces and non latin1 characters.", "spring")); + wSpring->setDescription(i18n("A spring to place between widgets")); + addClass(wSpring); + + KFormDesigner::WidgetInfo *wPushButton = new KFormDesigner::WidgetInfo(this); + wPushButton->setPixmap("button"); + wPushButton->setClassName("KPushButton"); + wPushButton->addAlternateClassName("QPushButton"); + wPushButton->setIncludeFileName("kpushbutton.h"); + wPushButton->setName(i18n("Push Button")); + wPushButton->setNamePrefix( + i18n("Widget name. This string will be used to name widgets of this class. It must _not_ contain white spaces and non latin1 characters.", "button")); + wPushButton->setDescription(i18n("A simple push button to execute actions")); + addClass(wPushButton); + + KFormDesigner::WidgetInfo *wRadioButton = new KFormDesigner::WidgetInfo(this); + wRadioButton->setPixmap("radio"); + wRadioButton->setClassName("QRadioButton"); + wRadioButton->setName(i18n("Option Button")); + wRadioButton->setNamePrefix( + i18n("Widget name. This string will be used to name widgets of this class. It must _not_ contain white spaces and non latin1 characters.", "optionButton")); + wRadioButton->setDescription(i18n("An option button with text or pixmap label")); + addClass(wRadioButton); + + KFormDesigner::WidgetInfo *wCheckBox = new KFormDesigner::WidgetInfo(this); + wCheckBox->setPixmap("check"); + wCheckBox->setClassName("QCheckBox"); + wCheckBox->setName(i18n("Check Box")); + wCheckBox->setNamePrefix( + i18n("Widget name. This string will be used to name widgets of this class. It must _not_ contain white spaces and non latin1 characters.", "checkBox")); + wCheckBox->setDescription(i18n("A check box with text or pixmap label")); + addClass(wCheckBox); + + KFormDesigner::WidgetInfo *wSpinBox = new KFormDesigner::WidgetInfo(this); + wSpinBox->setPixmap("spin"); + wSpinBox->setClassName("KIntSpinBox"); + wSpinBox->addAlternateClassName("QSpinBox"); + wSpinBox->setIncludeFileName("knuminput.h"); + wSpinBox->setName(i18n("Spin Box")); + wSpinBox->setNamePrefix( + i18n("Widget name. This string will be used to name widgets of this class. It must _not_ contain white spaces and non latin1 characters.", "spinBox")); + wSpinBox->setDescription(i18n("A spin box widget")); + addClass(wSpinBox); + + KFormDesigner::WidgetInfo *wComboBox = new KFormDesigner::WidgetInfo(this); + wComboBox->setPixmap("combo"); + wComboBox->setClassName("KComboBox"); + wComboBox->addAlternateClassName("QComboBox"); + wComboBox->setIncludeFileName("kcombobox.h"); + wComboBox->setName(i18n("Combo Box")); + wComboBox->setNamePrefix( + i18n("Widget name. This string will be used to name widgets of this class. It must _not_ contain white spaces and non latin1 characters.", "comboBox")); + wComboBox->setDescription(i18n("A combo box widget")); + addClass(wComboBox); + + KFormDesigner::WidgetInfo *wListBox = new KFormDesigner::WidgetInfo(this); + wListBox->setPixmap("listbox"); + wListBox->setClassName("KListBox"); + wListBox->addAlternateClassName("QListBox"); + wListBox->setIncludeFileName("klistbox.h"); + wListBox->setName(i18n("List Box")); + wListBox->setNamePrefix( + i18n("Widget name. This string will be used to name widgets of this class. It must _not_ contain white spaces and non latin1 characters.", "listBox")); + wListBox->setDescription(i18n("A simple list widget")); + addClass(wListBox); + + KFormDesigner::WidgetInfo *wTextEdit = new KFormDesigner::WidgetInfo(this); + wTextEdit->setPixmap("textedit"); + wTextEdit->setClassName("KTextEdit"); + wTextEdit->addAlternateClassName("QTextEdit"); + wTextEdit->setIncludeFileName("ktextedit.h"); + wTextEdit->setName(i18n("Text Editor")); + wTextEdit->setNamePrefix( + i18n("Widget name. This string will be used to name widgets of this class. It must _not_ contain white spaces and non latin1 characters.", "textEditor")); + wTextEdit->setDescription(i18n("A simple single-page rich text editor")); + addClass(wTextEdit); + + KFormDesigner::WidgetInfo *wListView = new KFormDesigner::WidgetInfo(this); + wListView->setPixmap("listview"); + wListView->setClassName("KListView"); + wListView->addAlternateClassName("QListView"); + wListView->setIncludeFileName("klistview.h"); + wListView->setName(i18n("List View")); + wListView->setNamePrefix( + i18n("Widget name. This string will be used to name widgets of this class. It must _not_ contain white spaces and non latin1 characters.", "listView")); + wListView->setDescription(i18n("A list (or tree) widget")); + addClass(wListView); + + KFormDesigner::WidgetInfo *wSlider = new KFormDesigner::WidgetInfo(this); + wSlider->setPixmap("slider"); + wSlider->setClassName("QSlider"); + wSlider->setName(i18n("Slider")); + wSlider->setNamePrefix( + i18n("Widget name. This string will be used to name widgets of this class. It must _not_ contain white spaces and non latin1 characters.", "slider")); + wSlider->setDescription(i18n("An horizontal slider")); + addClass(wSlider); + + KFormDesigner::WidgetInfo *wProgressBar = new KFormDesigner::WidgetInfo(this); + wProgressBar->setPixmap("progress"); + wProgressBar->setClassName("KProgress"); + wProgressBar->addAlternateClassName("QProgressBar"); + wProgressBar->setIncludeFileName("kprogress.h"); + wProgressBar->setName(i18n("Progress Bar")); + wProgressBar->setNamePrefix( + i18n("Widget name. This string will be used to name widgets of this class. It must _not_ contain white spaces and non latin1 characters.", "progressBar")); + wProgressBar->setDescription(i18n("A progress indicator widget")); + addClass(wProgressBar); + + KFormDesigner::WidgetInfo *wLine = new KFormDesigner::WidgetInfo(this); + wLine->setPixmap("line"); + wLine->setClassName("Line"); + wLine->setName(i18n("Line")); + wLine->setNamePrefix( + i18n("Widget name. This string will be used to name widgets of this class. It must _not_ contain white spaces and non latin1 characters.", "line")); + wLine->setDescription(i18n("A line to be used as a separator")); + addClass(wLine); + + KFormDesigner::WidgetInfo *wDate = new KFormDesigner::WidgetInfo(this); + wDate->setPixmap("dateedit"); + wDate->setClassName("KDateWidget"); +#if KDE_VERSION >= KDE_MAKE_VERSION(3,1,9) + wDate->addAlternateClassName("QDateEdit"); + wDate->setIncludeFileName("kdatewidget.h"); +#endif + wDate->setName(i18n("Date Widget")); + wDate->setNamePrefix( + i18n("Widget name. This string will be used to name widgets of this class. It must _not_ contain white spaces and non latin1 characters.", "dateWidget")); + wDate->setDescription(i18n("A widget to input and display a date")); + addClass(wDate); + + KFormDesigner::WidgetInfo *wTime = new KFormDesigner::WidgetInfo(this); + wTime->setPixmap("timeedit"); + wTime->setClassName("KTimeWidget"); +#if KDE_VERSION >= KDE_MAKE_VERSION(3,1,9) + wTime->addAlternateClassName("QTimeEdit"); + wTime->setIncludeFileName("ktimewidget.h"); +#endif + wTime->setName(i18n("Time Widget")); + wTime->setNamePrefix( + i18n("Widget name. This string will be used to name widgets of this class. It must _not_ contain white spaces and non latin1 characters.", "timeWidget")); + wTime->setDescription(i18n("A widget to input and display a time")); + addClass(wTime); + + KFormDesigner::WidgetInfo *wDateTime = new KFormDesigner::WidgetInfo(this); + wDateTime->setPixmap("datetimeedit"); + wDateTime->setClassName("KDateTimeWidget"); +#if KDE_VERSION >= KDE_MAKE_VERSION(3,1,9) + wDateTime->addAlternateClassName("QDateTimeEdit"); + wDateTime->setIncludeFileName("kdatetimewidget.h"); +#endif + wDateTime->setName(i18n("Date/Time Widget")); + wDateTime->setNamePrefix( + i18n("Widget name. This string will be used to name widgets of this class. It must _not_ contain white spaces and non latin1 characters.", "dateTimeWidget")); + wDateTime->setDescription(i18n("A widget to input and display a time and a date")); + addClass(wDateTime); + + m_propDesc["toggleButton"] = i18n("Toggle"); + m_propDesc["autoRepeat"] = i18n("Auto Repeat"); + m_propDesc["autoDefault"] = i18n("Auto Default"); + m_propDesc["default"] = i18n("Default"); + m_propDesc["flat"] = i18n("Flat"); + m_propDesc["echoMode"] = + i18n("Echo mode for Line Edit widget eg. Normal, NoEcho, Password","Echo Mode"); + m_propDesc["indent"] = i18n("Indent"); + //line + m_propDesc["orientation"] = i18n("Orientation"); + //checkbox + m_propDesc["checked"] = i18n("Checked checkbox", "Checked"); + m_propDesc["tristate"] = i18n("Tristate checkbox", "Tristate"); + + //for EchoMode + m_propValDesc["Normal"] = i18n("For Echo Mode", "Normal"); + m_propValDesc["NoEcho"] = i18n("For Echo Mode", "No Echo"); + m_propValDesc["Password"] = i18n("For Echo Mode", "Password"); + + //for spring + m_propDesc["sizeType"] = i18n("Size Type"); + + //for labels + m_propDesc["textFormat"] = i18n("Text Format"); + m_propValDesc["PlainText"] = i18n("For Text Format", "Plain"); + m_propValDesc["RichText"] = i18n("For Text Format", "Hypertext"); + m_propValDesc["AutoText"] = i18n("For Text Format", "Auto"); + m_propValDesc["LogText"] = i18n("For Text Format", "Log"); + + //KTextEdit + m_propDesc["tabStopWidth"] = i18n("Tab Stop Width"); + m_propDesc["tabChangesFocus"] = i18n("Tab Changes Focus"); + m_propDesc["wrapPolicy"] = i18n("Word Wrap Policy"); + m_propValDesc["AtWordBoundary"] = i18n("For Word Wrap Policy", "At Word Boundary"); + m_propValDesc["Anywhere"] = i18n("For Word Wrap Policy", "Anywhere"); + m_propValDesc["AtWordOrDocumentBoundary"] = i18n("For Word Wrap Policy", "At Word Boundary If Possible"); + m_propDesc["wordWrap"] = i18n("Word Wrapping"); + m_propDesc["wrapColumnOrWidth"] = i18n("Word Wrap Position"); + m_propValDesc["NoWrap"] = i18n("For Word Wrap Position", "None"); + m_propValDesc["WidgetWidth"] = i18n("For Word Wrap Position", "Widget's Width"); + m_propValDesc["FixedPixelWidth"] = i18n("For Word Wrap Position", "In Pixels"); + m_propValDesc["FixedColumnWidth"] = i18n("For Word Wrap Position", "In Columns"); + m_propDesc["linkUnderline"] = i18n("Links Underlined"); + + //internal props + setInternalProperty("Line","orientationSelectionPopup","1"); + setInternalProperty("Line","orientationSelectionPopup:horizontalIcon","line_horizontal"); + setInternalProperty("Line","orientationSelectionPopup:verticalIcon","line_vertical"); + setInternalProperty("Line","orientationSelectionPopup:horizontalText",i18n("Insert &Horizontal Line")); + setInternalProperty("Line","orientationSelectionPopup:verticalText",i18n("Insert &Vertical Line")); + setInternalProperty("Spring","orientationSelectionPopup","1"); + setInternalProperty("Spring","orientationSelectionPopup:horizontalIcon","spring"); + setInternalProperty("Spring","orientationSelectionPopup:verticalIcon","spring_vertical"); + setInternalProperty("Spring","orientationSelectionPopup:horizontalText",i18n("Insert &Horizontal Spring")); + setInternalProperty("Spring","orientationSelectionPopup:verticalText",i18n("Insert &Vertical Spring")); +} + +StdWidgetFactory::~StdWidgetFactory() +{ +} + +QWidget* +StdWidgetFactory::createWidget(const QCString &c, QWidget *p, const char *n, + KFormDesigner::Container *container, int options) +{ + QWidget *w=0; + QString text( container->form()->library()->textForWidgetName(n, c) ); + const bool designMode = options & KFormDesigner::WidgetFactory::DesignViewMode; + + if(c == "QLabel") + w = new QLabel(text, p, n); + else if(c == "KexiPictureLabel") + w = new KexiPictureLabel(DesktopIcon("image"), p, n); + + else if(c == "KLineEdit") + { + w = new KLineEdit(p, n); + if (designMode) + w->setCursor(QCursor(Qt::ArrowCursor)); + } + else if(c == "KPushButton") + w = new KPushButton(/*i18n("Button")*/text, p, n); + + else if(c == "QRadioButton") + w = new QRadioButton(/*i18n("Radio Button")*/text, p, n); + + else if(c == "QCheckBox") + w = new QCheckBox(/*i18n("Check Box")*/text, p, n); + + else if(c == "KIntSpinBox") + w = new KIntSpinBox(p, n); + + else if(c == "KComboBox") + w = new KComboBox(p, n); + + else if(c == "KListBox") + w = new KListBox(p, n); + + else if(c == "KTextEdit") + w = new KTextEdit(/*i18n("Enter your text here")*/text, QString::null, p, n); + + else if(c == "KListView") + { + w = new KListView(p, n); + if(container->form()->interactiveMode()) + ((KListView*)w)->addColumn(i18n("Column 1")); + ((KListView*)w)->setRootIsDecorated(true); + } + else if(c == "QSlider") + w = new QSlider(Qt::Horizontal, p, n); + + else if(c == "KProgress") + w = new KProgress(p, n); + + else if(c == "KDateWidget") + w = new KDateWidget(QDate::currentDate(), p, n); + + else if(c == "KTimeWidget") + w = new KTimeWidget(QTime::currentTime(), p, n); + + else if(c == "KDateTimeWidget") + w = new KDateTimeWidget(QDateTime::currentDateTime(), p, n); + + else if(c == "Line") + w = new Line(options & WidgetFactory::VerticalOrientation ? Line::Vertical : Line::Horizontal, p, n); + + else if(c == "Spring") { + w = new Spring(p, n); + if (0 == (options & WidgetFactory::AnyOrientation)) + static_cast<Spring*>(w)->setOrientation( + (options & WidgetFactory::VerticalOrientation) ? Qt::Vertical : Qt::Horizontal); + } + + if(w) + return w; + + kdDebug() << "WARNING :: w == 0 " << endl; + return 0; +} + +bool +StdWidgetFactory::previewWidget(const QCString &classname, QWidget *widget, KFormDesigner::Container *) +{ + if(classname == "Spring") { + ((Spring*)widget)->setPreviewMode(); + return true; + } + return false; +} + +bool +StdWidgetFactory::createMenuActions(const QCString &classname, QWidget *, QPopupMenu *menu, + KFormDesigner::Container *) +{ + if((classname == "QLabel") || (classname == "KTextEdit")) + { + menu->insertItem(SmallIconSet("edit"), i18n("Edit Rich Text"), this, SLOT(editText())); + return true; + } + else if(classname == "KListView") + { + menu->insertItem(SmallIconSet("edit"), i18n("Edit Listview Contents"), this, SLOT(editListContents())); + return true; + } + + return false; +} + +bool +StdWidgetFactory::startEditing(const QCString &classname, QWidget *w, KFormDesigner::Container *container) +{ + setWidget(w, container); +// m_container = container; + if(classname == "KLineEdit") + { + KLineEdit *lineedit = static_cast<KLineEdit*>(w); + createEditor(classname, lineedit->text(), lineedit, container, lineedit->geometry(), lineedit->alignment(), true); + return true; + } + else if(classname == "QLabel") + { + QLabel *label = static_cast<QLabel*>(w); + if(label->textFormat() == RichText) + { + //m_widget = w; +// setWidget(w, container); + editText(); + } + else + createEditor(classname, label->text(), label, container, label->geometry(), label->alignment()); + return true; + } + else if(classname == "KPushButton") + { + KPushButton *push = static_cast<KPushButton*>(w); + QRect r = w->style().subRect(QStyle::SR_PushButtonContents, w); + QRect editorRect = QRect(push->x() + r.x(), push->y() + r.y(), r.width(), r.height()); + //r.setX(r.x() + 5); + //r.setY(r.y() + 5); + //r.setWidth(r.width()-10); + //r.setHeight(r.height() - 10); + createEditor(classname, push->text(), push, container, editorRect, Qt::AlignCenter, false, false, Qt::PaletteButton); + return true; + } + else if(classname == "QRadioButton") + { + QRadioButton *radio = static_cast<QRadioButton*>(w); + QRect r = w->style().subRect(QStyle::SR_RadioButtonContents, w); + QRect editorRect = QRect(radio->x() + r.x(), radio->y() + r.y(), r.width(), r.height()); + createEditor(classname, radio->text(), radio, container, editorRect, Qt::AlignAuto); + return true; + } + else if(classname == "QCheckBox") + { + QCheckBox *check = static_cast<QCheckBox*>(w); + //QRect r(check->geometry()); + //r.setX(r.x() + 20); + QRect r = w->style().subRect(QStyle::SR_CheckBoxContents, w); + QRect editorRect = QRect(check->x() + r.x(), check->y() + r.y(), r.width(), r.height()); + createEditor(classname, check->text(), check, container, editorRect, Qt::AlignAuto); + return true; + } + else if((classname == "KComboBox") || (classname == "KListBox")) + { + QStringList list; + if(classname == "KListBox") + { + KListBox *listbox = (KListBox*)w; + for(uint i=0; i < listbox->count(); i++) + list.append(listbox->text(i)); + } + else if(classname == "KComboBox") + { + KComboBox *combo = (KComboBox*)w; + for(int i=0; i < combo->count(); i++) + list.append(combo->text(i)); + } + + if(editList(w, list)) + { + if(classname == "KListBox") + { + ((KListBox*)w)->clear(); + ((KListBox*)w)->insertStringList(list); + } + else if(classname == "KComboBox") + { + ((KComboBox*)w)->clear(); + ((KComboBox*)w)->insertStringList(list); + } + } + return true; + } + else if((classname == "KTextEdit") || (classname == "KDateTimeWidget") || (classname == "KTimeWidget") || + (classname == "KDateWidget") || (classname == "KIntSpinBox")) { + disableFilter(w, container); + return true; + } + return false; +} + +bool +StdWidgetFactory::clearWidgetContent(const QCString &classname, QWidget *w) +{ + if(classname == "KLineEdit") + ((KLineEdit*)w)->clear(); + else if(classname == "KListBox") + ((KListBox*)w)->clear(); + else if(classname == "KListView") + ((KListView*)w)->clear(); + else if(classname == "KComboBox") + ((KComboBox*)w)->clear(); + else if(classname == "KTextEdit") + ((KTextEdit*)w)->clear(); + else + return false; + return true; +} + +bool +StdWidgetFactory::changeText(const QString &text) +{ + QCString n = WidgetFactory::widget()->className(); + QWidget *w = WidgetFactory::widget(); + if(n == "KIntSpinBox") + ((KIntSpinBox*)w)->setValue(text.toInt()); + else + changeProperty("text", text, m_container->form()); + + /* By-hand method not needed as sizeHint() can do that for us + QFontMetrics fm = w->fontMetrics(); + QSize s(fm.width( text ), fm.height()); + int width; + if(n == "QLabel") // labels are resized to fit the text + { + w->resize(w->sizeHint()); + WidgetFactory::m_editor->resize(w->size()); + return; + } + // and other widgets are just enlarged if needed + else if(n == "KPushButton") + width = w->style().sizeFromContents( QStyle::CT_PushButton, w, s).width(); + else if(n == "QCheckBox") + width = w->style().sizeFromContents( QStyle::CT_CheckBox, w, s).width(); + else if(n == "QRadioButton") + width = w->style().sizeFromContents( QStyle::CT_RadioButton, w, s).width(); + else + return; + int width = w->sizeHint().width();*/ + +#if 0 //not needed here, size hint is used on creation in InsertWidgetCommand::execute() + if(w->width() < width) + { + w->resize(width, w->height() ); + //WidgetFactory::m_editor->resize(w->size()); + } +#endif + return true; +} + +void +StdWidgetFactory::resizeEditor(QWidget *editor, QWidget *widget, const QCString &classname) +{ + QSize s = widget->size(); + QPoint p = widget->pos(); + QRect r; + + if(classname == "QRadioButton") + { + r = widget->style().subRect(QStyle::SR_RadioButtonContents, widget); + p += r.topLeft(); + s.setWidth(r.width()); + } + else if(classname == "QCheckBox") + { + r = widget->style().subRect(QStyle::SR_CheckBoxContents, widget); + p += r.topLeft(); + s.setWidth(r.width()); + } + else if(classname == "KPushButton") + { + r = widget->style().subRect(QStyle::SR_PushButtonContents, widget); + p += r.topLeft(); + s = r.size(); + } + + editor->resize(s); + editor->move(p); +} + +bool +StdWidgetFactory::saveSpecialProperty(const QCString &classname, const QString &name, const QVariant &, QWidget *w, QDomElement &parentNode, QDomDocument &domDoc) +{ + if(name == "list_items" && classname == "KComboBox") + { + KComboBox *combo = (KComboBox*)w; + for(int i=0; i < combo->count(); i++) + { + QDomElement item = domDoc.createElement("item"); + KFormDesigner::FormIO::savePropertyElement(item, domDoc, "property", "text", combo->text(i)); + parentNode.appendChild(item); + } + return true; + } + else if(name == "list_items" && classname == "KListBox") + { + KListBox *listbox = (KListBox*)w; + for(uint i=0; i < listbox->count(); i++) + { + QDomElement item = domDoc.createElement("item"); + KFormDesigner::FormIO::savePropertyElement(item, domDoc, "property", "text", listbox->text(i)); + parentNode.appendChild(item); + } + return true; + } + else if(name == "list_contents" && classname == "KListView") + { + KListView *listview = (KListView*)w; + // First we save the columns + for(int i = 0; i < listview->columns(); i++) + { + QDomElement item = domDoc.createElement("column"); + KFormDesigner::FormIO::savePropertyElement(item, domDoc, "property", "text", listview->columnText(i)); + KFormDesigner::FormIO::savePropertyElement(item, domDoc, "property", "width", listview->columnWidth(i)); + KFormDesigner::FormIO::savePropertyElement(item, domDoc, "property", "resizable", listview->header()->isResizeEnabled(i)); + KFormDesigner::FormIO::savePropertyElement(item, domDoc, "property", "clickable", listview->header()->isClickEnabled(i)); + KFormDesigner::FormIO::savePropertyElement(item, domDoc, "property", "fullwidth", listview->header()->isStretchEnabled(i)); + parentNode.appendChild(item); + } + + // Then we save the list view items + QListViewItem *item = listview->firstChild(); + while(item) + { + saveListItem(item, parentNode, domDoc); + item = item->nextSibling(); + } + return true; + } + + return false; +} + +void +StdWidgetFactory::saveListItem(QListViewItem *item, QDomNode &parentNode, QDomDocument &domDoc) +{ + QDomElement element = domDoc.createElement("item"); + parentNode.appendChild(element); + + // We save the text of each column + for(int i = 0; i < item->listView()->columns(); i++) + KFormDesigner::FormIO::savePropertyElement(element, domDoc, "property", "text", item->text(i)); + + // Then we save every sub items + QListViewItem *child = item->firstChild(); + while(child) + { + saveListItem(child, element, domDoc); + child = child->nextSibling(); + } +} + +bool +StdWidgetFactory::readSpecialProperty(const QCString &classname, QDomElement &node, QWidget *w, KFormDesigner::ObjectTreeItem *) +{ + QString tag = node.tagName(); + QString name = node.attribute("name"); + + if((tag == "item") && (classname == "KComboBox")) + { + KComboBox *combo = (KComboBox*)w; + QVariant val = KFormDesigner::FormIO::readPropertyValue(node.firstChild().firstChild(), w, name); + if(val.canCast(QVariant::Pixmap)) + combo->insertItem(val.toPixmap()); + else + combo->insertItem(val.toString()); + return true; + } + + if((tag == "item") && (classname == "KListBox")) + { + KListBox *listbox = (KListBox*)w; + QVariant val = KFormDesigner::FormIO::readPropertyValue(node.firstChild().firstChild(), w, name); + if(val.canCast(QVariant::Pixmap)) + listbox->insertItem(val.toPixmap()); + else + listbox->insertItem(val.toString()); + return true; + } + + if((tag == "column") && (classname == "KListView")) + { + KListView *listview = (KListView*)w; + int id=0; + for(QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling()) + { + QString prop = n.toElement().attribute("name"); + QVariant val = KFormDesigner::FormIO::readPropertyValue(n.firstChild(), w, name); + if(prop == "text") + id = listview->addColumn(val.toString()); + else if(prop == "width") + listview->setColumnWidth(id, val.toInt()); + else if(prop == "resizable") + listview->header()->setResizeEnabled(val.toBool(), id); + else if(prop == "clickable") + listview->header()->setClickEnabled(val.toBool(), id); + else if(prop == "fullwidth") + listview->header()->setStretchEnabled(val.toBool(), id); + } + return true; + } + else if((tag == "item") && (classname == "KListView")) + { + KListView *listview = (KListView*)w; + readListItem(node, 0, listview); + return true; + } + + return false; +} + +void +StdWidgetFactory::readListItem(QDomElement &node, QListViewItem *parent, KListView *listview) +{ + QListViewItem *item; + if(parent) + item = new KListViewItem(parent); + else + item = new KListViewItem(listview); + + // We need to move the item at the end of the list + QListViewItem *last; + if(parent) + last = parent->firstChild(); + else + last = listview->firstChild(); + + while(last->nextSibling()) + last = last->nextSibling(); + item->moveItem(last); + + int i = 0; + for(QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling()) + { + QDomElement childEl = n.toElement(); + QString prop = childEl.attribute("name"); + QString tag = childEl.tagName(); + + // We read sub items + if(tag == "item") + { + item->setOpen(true); + readListItem(childEl, item, listview); + } + // and column texts + else if((tag == "property") && (prop == "text")) + { + QVariant val = KFormDesigner::FormIO::readPropertyValue(n.firstChild(), listview, "item"); + item->setText(i, val.toString()); + i++; + } + } +} + +bool +StdWidgetFactory::isPropertyVisibleInternal(const QCString &classname, + QWidget *w, const QCString &property, bool isTopLevel) +{ + bool ok = true; + if(classname == "FormWidgetBase") + { + if(property == "iconText" + || property == "geometry" /*nonsense for toplevel widget*/) + return false; + } + else if (classname == "CustomWidget") + { + } + else if(classname == "Spring") + { + return Spring::isPropertyVisible(property); + } + else if(classname == "KexiPictureLabel") + { + if((property == "text") || (property == "indent") || (property == "textFormat") || (property == "font") || (property == "alignment")) + return false; + } + else if(classname == "QLabel") + { + if(property == "pixmap") + return false; + } + else if(classname == "KLineEdit") + { + if(property == "vAlign") + return false; + } + else if(classname == "KTextEdit") + ok = m_showAdvancedProperties || + property!="undoDepth" + && property!="undoRedoEnabled" //always true! + && property!="dragAutoScroll" //always true! + && property!="overwriteMode" //always false! + && property!="resizePolicy" + && property!="autoFormatting" //too complex +#ifdef KEXI_NO_UNFINISHED + && property!="paper" +#endif + ; + else if(classname == "Line") + { + if((property == "frameShape") || (property == "font") || (property == "margin")) + return false; + } + else if(classname=="QCheckBox") + { + ok = m_showAdvancedProperties || (property != "autoRepeat"); + } + else if(classname=="QRadioButton") + { + ok = m_showAdvancedProperties || (property != "autoRepeat"); + } + else if(classname=="KPushButton") + { +//! @todo reenable autoDefault / default if the top level window is dialog... + ok = m_showAdvancedProperties || (property != "autoDefault" && property != "default"); + } + return ok && WidgetFactory::isPropertyVisibleInternal(classname, w, property, isTopLevel); +} + +QValueList<QCString> +StdWidgetFactory::autoSaveProperties(const QCString &classname) +{ + QValueList<QCString> l; + + if(classname == "QLabel") + l << "text"; + if(classname == "KPushButton") + l << "text"; + else if(classname == "KexiPictureLabel") + l << "pixmap"; + else if(classname == "KComboBox") + l << "list_items"; + else if(classname == "KListBox") + l << "list_items"; + else if(classname == "KListView") + l << "list_contents"; + else if(classname == "Line") + l << "orientation"; + else if(classname == "KTimeWidget") + l << "time"; + else if(classname == "KDateWidget") + l << "date"; + else if(classname == "KDateTimeWidget") + l << "dateTime"; + else if(classname == "Spring") + l << "sizeType" << "orientation"; + else if(classname == "KTextEdit") + l << "textFormat" << "text"; + + return l; +} + +void +StdWidgetFactory::editText() +{ + QCString classname = widget()->className(); + QString text; + if(classname == "KTextEdit") + text = ((KTextEdit*)widget())->text(); + else if(classname == "QLabel") + text = ((QLabel*)widget())->text(); + + if(editRichText(widget(), text)) + { + changeProperty("textFormat", "RichText", m_container->form()); + changeProperty("text", text, m_container->form()); + } + + if(classname == "QLabel") + widget()->resize(widget()->sizeHint()); +} + +void +StdWidgetFactory::editListContents() +{ + if(widget()->inherits("QListView")) + editListView((QListView*)widget()); +} + +void +StdWidgetFactory::setPropertyOptions( KFormDesigner::WidgetPropertySet& buf, const KFormDesigner::WidgetInfo& info, QWidget *w ) +{ + Q_UNUSED( info ); + Q_UNUSED( w ); + + if (buf.contains("indent")) { + buf["indent"].setOption("min", -1); + buf["indent"].setOption("minValueText", i18n("default indent value", "default")); + } +} + +KFORMDESIGNER_WIDGET_FACTORY(StdWidgetFactory, stdwidgets) + +#include "stdwidgetfactory.moc" + diff --git a/kexi/formeditor/factories/stdwidgetfactory.h b/kexi/formeditor/factories/stdwidgetfactory.h new file mode 100644 index 00000000..c0e56c5b --- /dev/null +++ b/kexi/formeditor/factories/stdwidgetfactory.h @@ -0,0 +1,99 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef STDWIDGETFACTORY_H +#define STDWIDGETFACTORY_H + +#include <qframe.h> + +#include "widgetfactory.h" +#include "container.h" + +class KFORMEDITOR_EXPORT KexiPictureLabel : public QLabel +{ + Q_OBJECT + + public: + KexiPictureLabel(const QPixmap &pix, QWidget *parent, const char *name); + ~KexiPictureLabel(){;} + + virtual bool setProperty(const char *name, const QVariant &value); +}; + +class KFORMEDITOR_EXPORT Line : public QFrame +{ + Q_OBJECT + Q_PROPERTY(Orientation orientation READ orientation WRITE setOrientation) + + public: + Line(Orientation orient, QWidget *parent, const char *name); + ~Line(){;} + + void setOrientation(Orientation orient); + Orientation orientation() const; +}; + +//! Factory for all basic widgets, including Spring (not containers) +class StdWidgetFactory : public KFormDesigner::WidgetFactory +{ + Q_OBJECT + + public: + StdWidgetFactory(QObject *parent, const char *name, const QStringList &args); + ~StdWidgetFactory(); + + virtual QWidget *createWidget(const QCString &c, QWidget *p, const char *n, + KFormDesigner::Container *container, int options = DefaultOptions); + + virtual bool createMenuActions(const QCString &classname, QWidget *w, QPopupMenu *menu, + KFormDesigner::Container *container); + virtual bool startEditing(const QCString &classname, QWidget *w, + KFormDesigner::Container *container); + virtual bool previewWidget(const QCString &classname, QWidget *widget, + KFormDesigner::Container *container); + virtual bool clearWidgetContent(const QCString &classname, QWidget *w); + + virtual bool saveSpecialProperty(const QCString &classname, + const QString &name, const QVariant &value, QWidget *w, + QDomElement &parentNode, QDomDocument &parent); + virtual bool readSpecialProperty(const QCString &classname, QDomElement &node, + QWidget *w, KFormDesigner::ObjectTreeItem *item); + virtual QValueList<QCString> autoSaveProperties(const QCString &classname); + + virtual void setPropertyOptions( KFormDesigner::WidgetPropertySet& buf, + const KFormDesigner::WidgetInfo& info, QWidget *w ); + + public slots: + void editText(); + void editListContents(); + + protected: + virtual bool isPropertyVisibleInternal(const QCString &classname, QWidget *w, + const QCString &property, bool isTopLevel); + virtual bool changeText(const QString &newText); + virtual void resizeEditor(QWidget *editor, QWidget *widget, const QCString &classname); + void saveListItem(QListViewItem *item, QDomNode &parentNode, QDomDocument &domDoc); + void readListItem(QDomElement &node, QListViewItem *parent, KListView *listview); + + private: +// KFormDesigner::Container *m_container; +// QWidget *m_widget; +}; + +#endif 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" diff --git a/kexi/formeditor/form.h b/kexi/formeditor/form.h new file mode 100644 index 00000000..899da955 --- /dev/null +++ b/kexi/formeditor/form.h @@ -0,0 +1,397 @@ +/* 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. +*/ + +#ifndef KFORMDESIGNERFORM_H +#define KFORMDESIGNERFORM_H + +#include <qobject.h> +#include <qptrlist.h> + +#include "resizehandle.h" +#include "utils.h" +#include "objecttree.h" + +class QWidget; +class QDomElement; +class KActionCollection; +class KCommandHistory; +class KCommand; +class PixmapCollection; + +namespace KFormDesigner { + +class Container; +class WidgetPropertySet; +class WidgetLibrary; +class FormManager; +class ObjectTree; +class ObjectTreeItem; +class ConnectionBuffer; + +//! Base (virtual) class for all form widgets +/*! You need to inherit this class, and implement the drawing functions. This is necessary + because you cannot inherit QWidget twice, and we want form widgets to be any widget. + See FormWidgetBase in test/kfd_part.cpp and just copy functions there. */ +class KFORMEDITOR_EXPORT FormWidget +{ + public: + FormWidget(); + virtual ~FormWidget(); + + /*! This function draws the rects in the \a list in the Form, above of all widgets, + using double-buffering. \a type can be 1 (selection rect) + or 2 (insert rect, dotted). */ + + virtual void drawRects(const QValueList<QRect> &list, int type) = 0; + + virtual void drawRect(const QRect &r, int type) = 0; + + /*! This function inits the buffer used for double-buffering. Called before drawing rect. */ + virtual void initBuffer() = 0; + + /*! Clears the form, ie pastes the whole buffer to repaint the Form. */ + virtual void clearForm() = 0; + + /*! This function highlights two widgets (to is optional), which are + sender and receiver, and draws a link between them. */ + virtual void highlightWidgets(QWidget *from, QWidget *to) = 0; + + protected: + Form *m_form; + + friend class Form; +}; + +//! @internal +class FormPrivate +{ + public: + FormPrivate(); + ~FormPrivate(); + +// FormManager *manager; + QGuardedPtr<Container> toplevel; + ObjectTree *topTree; + QGuardedPtr<QWidget> widget; + + WidgetList selected; + ResizeHandleSet::Dict resizeHandles; + + bool dirty; + bool interactive; + bool design; + QString filename; + + KCommandHistory *history; + KActionCollection *collection; + + ObjectTreeList tabstops; + bool autoTabstops; + ConnectionBuffer *connBuffer; + + PixmapCollection *pixcollection; + + //! This map is used to store cursor shapes before inserting (so we can restore them later) + QMap<QObject*,QCursor> cursors; + + //!This string list is used to store the widgets which hasMouseTracking() == true (eg lineedits) + QStringList *mouseTrackers; + + FormWidget *formWidget; + + //! A set of head properties to be stored in a .ui file. + //! This includes KFD format version. + QMap<QCString,QString> headerProperties; + + //! Format version, set by FormIO or on creating a new form. + uint formatVersion; + //! Format version, set by FormIO's loader or on creating a new form. + uint originalFormatVersion; +}; + +/*! + This class represents one form and holds the corresponding ObjectTree and Containers. + It takes care of widget selection and pasting widgets. + **/ + //! A simple class representing a form +class KFORMEDITOR_EXPORT Form : public QObject +{ + Q_OBJECT + + public: + /*! Creates a simple Form, child of the FormManager \a manager. + */ + Form(WidgetLibrary* library, const char *name=0, bool designMode = true); + ~Form(); + + //! \return A pointer to the WidgetLibrary supporting this form. + WidgetLibrary* library() const { return m_lib; } + + /*! + Creates a toplevel widget out of another widget. + \a container will become the Form toplevel widget, + will be associated to an ObjectTree and so on. + \code QWidget *toplevel = new QWidget(this); + form->createToplevel(toplevel); \endcode + */ + void createToplevel(QWidget *container, FormWidget *formWidget =0, + const QCString &classname="QWidget"); + + /*! \return the toplevel Container or 0 if this is a preview Form or createToplevel() + has not been called yet. */ + Container* toplevelContainer() const { return d->toplevel; } + + //! \return the FormWidget that holds this Form + FormWidget* formWidget() const { return d->formWidget; } + + //! \return a pointer to this form's ObjectTree. + ObjectTree* objectTree() const { return d->topTree; } + + //! \return the form's toplevel widget, or 0 if designMode() == false. + QWidget* widget() const; + +// //! \return the FormManager parent of this form. +// FormManager* manager() const { return d->manager; } + + /*! \return A pointer to the currently active Container, ie the parent Container for a simple widget, + and the widget's Container if it is itself a container. + */ + Container* activeContainer(); + + /*! \return A pointer to the parent Container of the currently selected widget. + It is the same as activeContainer() for a simple widget, but unlike this function + it will also return the parent Container if the widget itself is a Container. + */ + Container* parentContainer(QWidget *w=0); + + /*! \return The \ref Container which is a parent of all widgets in \a wlist. + Used by \ref activeContainer(), and to find where + to paste widgets when multiple widgets are selected. */ + ObjectTreeItem* commonParentContainer(WidgetList *wlist); + + //! \return the list of currently selected widgets in this form + WidgetList* selectedWidgets() const {return &(d->selected);} + + /*! \return currently selected widget in this form, + or 0 if there is no widget selected or more than one widget selected. + \see selectedWidgets() */ + QWidget* selectedWidget() const { return d->selected.count()==1 ? d->selected.first() : 0; } + + /*! Emits the action signals, and optionaly the undo/redo related signals + if \a withUndoAction == true. See \a FormManager for signals description. */ + void emitActionSignals(bool withUndoAction=true); + + /*! Emits again all signal related to selection (ie Form::selectionChanged()). + Called eg when the user has the focus again. */ + void emitSelectionSignals(); + + /*! Sets the Form interactivity mode. Form is not interactive when + pasting widgets, or loading a Form. + */ + void setInteractiveMode(bool interactive) { d->interactive = interactive; } + + /*! \return true if the Form is being updated by the user, ie the created + widget were drawn on the Form. + \return false if the Form is being updated by the program, ie the widget + are created by FormIO, and so composed widgets + should not be populated automatically (such as QTabWidget). + */ + bool interactiveMode() const { return d->interactive; } + + /*! If \a design is true, the Form is in Design Mode (by default). + If \a design is false, then the Form is in Preview Mode, so + the ObjectTree and the Containers are removed. */ + void setDesignMode(bool design); + + //! \return The actual mode of the Form. + bool designMode() const { return d->design; } + + bool isModified() { return d->dirty; } + + //! \return the distance between two dots in the form background. +//! @todo make gridSize configurable at global level + int gridSize() { return 10; } + + //! \return the default margin for all the layout inside this Form. + int defaultMargin() { return 11;} + + //! \return the default spacing for all the layout inside this Form. + int defaultSpacing() { return 6;} + + /*! This function is used by ObjectTree to emit childAdded() signal (as it is not a QObject). */ + void emitChildAdded(ObjectTreeItem *item); + + /*! This function is used by ObjectTree to emit childRemoved() signal (as it is not a QObject). */ + void emitChildRemoved(ObjectTreeItem *item); + + /*! \return The filename of the UI file this Form was saved to, + or QString::null if the Form hasn't be saved yet. */ + QString filename() const { return d->filename; } + + //! Sets the filename of this Form to \a filename. + void setFilename(const QString &file) { d->filename = file; } + + KCommandHistory* commandHistory() const { return d->history; } + ConnectionBuffer* connectionBuffer() const { return d->connBuffer; } + PixmapCollection* pixmapCollection() const { return d->pixcollection; } + + /*! Adds a widget in the form's command history. Please use it instead + of calling directly actionCollection()->addCommand(). */ + void addCommand(KCommand *command, bool execute); + + /*! Clears form's command history. */ + void clearCommandHistory(); + + /*! \return A pointer to this Form tabstops list : it contains all the widget + that can have focus ( ie no labels, etc) + in the order of the tabs.*/ + ObjectTreeList* tabStops() const { return &(d->tabstops); } + + inline ObjectTreeListIterator tabStopsIterator() const { return ObjectTreeListIterator(d->tabstops); } + + /*! Called (e.g. by KexiDBForm) when certain widgets can have updated focusPolicy properties + these having no TabFocus flags set are removed from tabStops() list. */ + void updateTabStopsOrder(); + + /*! Adds the widget at the end of tabstops list. Called on widget creation. */ + void addWidgetToTabStops(ObjectTreeItem *it); + + /*! \return True if the Form automatically handles tab stops. */ + bool autoTabStops() const { return d->autoTabstops; } + + /*! If \a autoTab is true, then the Form will automatically handle tab stops, + and the "Edit Tab Order" dialog will be disabled. + The tab widget will be set from the top-left to the bottom-right corner.\n + If \ autoTab is false, then it's up to the user to change tab stops + (which are by default in order of creation).*/ + void setAutoTabStops(bool autoTab) { d->autoTabstops = autoTab;} + + /*! Tells the Form to reassign the tab stops because the widget layout has changed + (called for example before saving or displaying the tab order dialog). + Automatically sorts widget from the top-left to bottom-right corner. + Widget can be grouped with containers. In paticular, for tab widgets, + child widgets should ordered by parent tab's order. */ + void autoAssignTabStops(); + +#ifdef KEXI_DEBUG_GUI + //! For debugging purposes + QString m_recentlyLoadedUICode; +#endif + + /*! Internal: called by ResizeHandle when mouse move event causes first + resize handle's dragging. As a result, current widget's editing (if any) + is finished - see WidgetFactory::resetEditor(). */ +// void resizeHandleDraggingStarted(QWidget *draggedWidget); + + ResizeHandleSet* resizeHandlesForWidget(QWidget* w); + + /*! A set of value/key pairs provided to be stored as attributes in + <kfd:customHeader/> XML element (saved as a first child of \<UI> element). */ + QMap<QCString,QString>* headerProperties() const { return &d->headerProperties; } + + //! \return format version number for this form. + //! For new forms it is equal to KFormDesigner::version(). + uint formatVersion() const; + void setFormatVersion(uint ver); + + //! \return original format version number for this form (as loaded from .ui XML string) + //! For new forms it is equal to KFormDesigner::version(). + uint originalFormatVersion() const; + void setOriginalFormatVersion(uint ver); + + public slots: + /*! This slot is called when the name of a widget was changed in Property Editor. + It renames the ObjectTreeItem associated to this widget. + */ + void changeName(const QCString &oldname, const QCString &newname); + + /*! Sets \a selected to be the selected widget of this Form. + If \a add is true, the formerly selected widget is still selected, + and the new one is just added. If false, \a selected replace the actually selected widget. + The form widget is always selected alone. + \a moreWillBeSelected indicates whether more widgets will be selected soon + (so for multiselection we should not update the property pane before the last widget is selected) */ + void setSelectedWidget(QWidget *selected, bool add=false, bool dontRaise=false, + bool moreWillBeSelected = false); + + /*! Unselects the widget \a w. Te widget is removed from the Cntainer 's list + and its resizeHandle is removed. */ + void unSelectWidget(QWidget *w); + + /*! Sets the form widget (it will be uniquely selected widget). */ + void selectFormWidget(); + + void clearSelection(); + + protected slots: + /*! This slot is called when the toplevel widget of this Form is deleted + (ie the window closed) so that the Form gets deleted at the same time. + */ + void formDeleted(); + + void emitUndoEnabled(); + void emitRedoEnabled(); + + /*! This slot is called when a command is executed. The undo/redo signals + are emitted to update actions. */ + void slotCommandExecuted(); + + /*! This slot is called when form is restored, ie when the user has undone + all actions. The form modified flag is updated, and + \ref FormManager::dirty() is called. */ + void slotFormRestored(); + + signals: + /*! This signal is emitted by setSelectedWidget() when user selects a new widget, + to update both Property Editor and ObjectTreeView. + \a w is the newly selected widget. + */ + void selectionChanged(QWidget *w, bool add, bool moreWillBeSelected = false); + + /*! This signal is emitted when a new widget is created, to update ObjectTreeView. + \a it is the ObjectTreeItem representing this new widget. + */ + void childAdded(ObjectTreeItem *it); + + /*! This signal is emitted when a widget is deleted, to update ObjectTreeView. + \a it is the ObjectTreeItem representing this deleted widget. + */ + void childRemoved(ObjectTreeItem *it); + + //! This signal emitted when Form is about to be destroyed + void destroying(); + + protected: + void setConnectionBuffer(ConnectionBuffer *b) { d->connBuffer = b; } + + void setFormWidget(FormWidget* w); + private: + WidgetLibrary *m_lib; + FormPrivate *d; + + friend class FormManager; + friend class FormWidget; + friend class ConnectionDialog; +}; + +} + +#endif diff --git a/kexi/formeditor/formIO.cpp b/kexi/formeditor/formIO.cpp new file mode 100644 index 00000000..2327fb91 --- /dev/null +++ b/kexi/formeditor/formIO.cpp @@ -0,0 +1,1626 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr> + Copyright (C) 2005-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 <kdebug.h> + +#include <qmetaobject.h> +#include <qdom.h> +#include <qfile.h> +#include <qtextstream.h> +#include <qcursor.h> +#include <qbuffer.h> +#include <qimage.h> +#include <qlayout.h> +#include <qobjectlist.h> +#include <qdatetime.h> +#include <qlabel.h> +#include <qpainter.h> + +#include <kfiledialog.h> +#include <klocale.h> +#include <kcommand.h> +#include <kaccelmanager.h> + +#include "form.h" +#include "container.h" +#include "objecttree.h" +#include "formmanager.h" +#include "widgetlibrary.h" +#include "spring.h" +#include "pixmapcollection.h" +#include "events.h" +#include "utils.h" +#include "kexiflowlayout.h" +#include "widgetwithsubpropertiesinterface.h" +#include "formIO.h" + +/// A blank widget used when the class name is not supported +CustomWidget::CustomWidget(const QCString &className, QWidget *parent, const char *name) +: QWidget(parent, name), m_className(className) +{ + setBackgroundMode(Qt::PaletteDark); +} + +CustomWidget::~CustomWidget() +{ +} + +void +CustomWidget::paintEvent(QPaintEvent *) +{ + QPainter p(this); + p.setPen(palette().active().text()); + QRect r(rect()); + r.setX(r.x()+2); + p.drawText(r, Qt::AlignTop, m_className); +} + +using namespace KFormDesigner; + +QDict<QLabel> *FormIO::m_buddies = 0; +ObjectTreeItem *FormIO::m_currentItem = 0; +Form *FormIO::m_currentForm = 0; +bool FormIO::m_savePixmapsInline = false; + +// FormIO itself + +KFORMEDITOR_EXPORT uint KFormDesigner::version() +{ + return KFORMDESIGNER_VERSION; +} + +///////////////////////////////////////////////////////////////////////////// +///////////// Saving/loading functions ////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// + +FormIO::FormIO() +{ +} + +FormIO::~FormIO() +{ +} + +bool +FormIO::saveFormToFile(Form *form, const QString &filename) +{ + QString m_filename; + if(!form->filename().isNull() && filename.isNull()) + m_filename = form->filename(); + + if(filename.isNull()) + { + m_filename = KFileDialog::getSaveFileName(QString::null, i18n("*.ui|Qt Designer UI Files")); + if(m_filename.isNull()) + return false; + } + else + m_filename = filename; + form->setFilename(m_filename); + + QDomDocument domDoc; + if (!saveFormToDom(form, domDoc)) + return false; + + QFile file(m_filename); + if (!file.open(IO_WriteOnly)) + return false; + + QTextStream stream(&file); + stream << domDoc.toString(3) << endl; + file.close(); + + return true; +} + +bool +FormIO::saveFormToByteArray(Form *form, QByteArray &dest) +{ + QDomDocument domDoc; + if (!saveFormToDom(form, domDoc)) + return false; + dest = domDoc.toCString(); + return true; +} + +bool +FormIO::saveFormToString(Form *form, QString &dest, int indent) +{ + QDomDocument domDoc; + if (!saveFormToDom(form, domDoc)) + return false; + dest = domDoc.toString(indent); + return true; +} + +bool +FormIO::saveFormToDom(Form *form, QDomDocument &domDoc) +{ + m_currentForm = form; + + domDoc = QDomDocument("UI"); + QDomElement uiElement = domDoc.createElement("UI"); + domDoc.appendChild(uiElement); + uiElement.setAttribute("version", "3.1"); + uiElement.setAttribute("stdsetdef", 1); + + //update format version information + form->headerProperties()->insert("version", QString::number(form->formatVersion())); + //custom properties + QDomElement headerPropertiesEl = domDoc.createElement("kfd:customHeader"); + for (QMapConstIterator<QCString,QString> it=form->headerProperties()->constBegin(); it!=form->headerProperties()->constEnd(); ++it) { + headerPropertiesEl.setAttribute(it.key(), it.data()); + } + uiElement.appendChild(headerPropertiesEl); + + /// We save the savePixmapsInline property in the Form + QDomElement inlinePix = domDoc.createElement("pixmapinproject"); + uiElement.appendChild(inlinePix); + + // We create the top class element + QDomElement baseClass = domDoc.createElement("class"); + uiElement.appendChild(baseClass); + QDomText baseClassV = domDoc.createTextNode("QWidget"); + baseClass.appendChild(baseClassV); + + // Save the toplevel widgets, and so the whole Form + saveWidget(form->objectTree(), uiElement, domDoc); + + // We then save the layoutdefaults element + QDomElement layoutDefaults = domDoc.createElement("layoutDefaults"); + layoutDefaults.setAttribute("spacing", QString::number(form->defaultSpacing())); + layoutDefaults.setAttribute("margin", QString::number(form->defaultMargin())); + uiElement.appendChild(layoutDefaults); + + /// Save tab Stops + if(form->autoTabStops()) + form->autoAssignTabStops(); + QDomElement tabStops = domDoc.createElement("tabstops"); + uiElement.appendChild(tabStops); + for(ObjectTreeListIterator it( form->tabStopsIterator() ); it.current(); ++it) + { + QDomElement tabstop = domDoc.createElement("tabstop"); + tabStops.appendChild(tabstop); + QDomText tabStopText = domDoc.createTextNode(it.current()->name()); + tabstop.appendChild(tabStopText); + } + + // Save the Form 's PixmapCollection + form->pixmapCollection()->save(uiElement); + // Save the Form connections + form->connectionBuffer()->save(uiElement); + + form->commandHistory()->documentSaved(); + + m_currentForm = 0; + m_currentItem = 0; + //m_currentWidget = 0; + + return true; +} + +bool +FormIO::loadFormFromByteArray(Form *form, QWidget *container, QByteArray &src, bool preview) +{ + QString errMsg; + int errLine; + int errCol; + + QDomDocument inBuf; + bool parsed = inBuf.setContent(src, false, &errMsg, &errLine, &errCol); + + if(!parsed) + { + kdDebug() << "WidgetWatcher::load(): " << errMsg << endl; + kdDebug() << "WidgetWatcher::load(): line: " << errLine << " col: " << errCol << endl; + return false; + } + + if (!loadFormFromDom(form, container, inBuf)) + return false; + if(preview) + form->setDesignMode(false); + return true; +} + +bool +FormIO::loadFormFromString(Form *form, QWidget *container, QString &src, bool preview) +{ + QString errMsg; + int errLine; + int errCol; + +#ifdef KEXI_DEBUG_GUI + form->m_recentlyLoadedUICode = src; +#endif + + QDomDocument inBuf; + bool parsed = inBuf.setContent(src, false, &errMsg, &errLine, &errCol); + + if(!parsed) + { + kdDebug() << "WidgetWatcher::load(): " << errMsg << endl; + kdDebug() << "WidgetWatcher::load(): line: " << errLine << " col: " << errCol << endl; + return false; + } + + if (!loadFormFromDom(form, container, inBuf)) + return false; + if(preview) + form->setDesignMode(false); + return true; +} + +bool +FormIO::loadFormFromFile(Form *form, QWidget *container, const QString &filename) +{ + QString errMsg; + int errLine; + int errCol; + QString m_filename; + + if(filename.isNull()) + { + m_filename = KFileDialog::getOpenFileName(QString::null, i18n("*.ui|Qt Designer UI Files")); + if(m_filename.isNull()) + return false; + } + else + m_filename = filename; + + QFile file(m_filename); + if(!file.open(IO_ReadOnly)) + { + kdDebug() << "Cannot open the file " << filename << endl; + return false; + } + QTextStream stream(&file); + QString text = stream.read(); + + QDomDocument inBuf; + bool parsed = inBuf.setContent(text, false, &errMsg, &errLine, &errCol); + + if(!parsed) + { + kdDebug() << "WidgetWatcher::load(): " << errMsg << endl; + kdDebug() << "WidgetWatcher::load(): line: " << errLine << " col: " << errCol << endl; + return false; + } + + return loadFormFromDom(form, container, inBuf); +} + +bool +FormIO::loadFormFromDom(Form *form, QWidget *container, QDomDocument &inBuf) +{ + m_currentForm = form; + + QDomElement ui = inBuf.namedItem("UI").toElement(); + + //custom properties + form->headerProperties()->clear(); + QDomElement headerPropertiesEl = ui.namedItem("kfd:customHeader").toElement(); + QDomAttr attr = headerPropertiesEl.firstChild().toAttr(); + while (!attr.isNull() && attr.isAttr()) { + form->headerProperties()->insert(attr.name().latin1(), attr.value()); + attr = attr.nextSibling().toAttr(); + } + //update format version information + uint ver = 1; //the default + if (form->headerProperties()->contains("version")) { + bool ok; + uint v = (*form->headerProperties())["version"].toUInt(&ok); + if (ok) + ver = v; + } + kdDebug() << "FormIO::loadFormFromDom(): original format version: " << ver << endl; + form->setOriginalFormatVersion( ver ); + if (ver < KFormDesigner::version()) { +//! @todo We can either 1) convert from old format and later save in a new one or 2) keep old format. +//! To do this we may need to look at the original format version number. + kdDebug() << "FormIO::loadFormFromDom(): original format is older than current: " << KFormDesigner::version() << endl; + form->setFormatVersion( KFormDesigner::version() ); + } + else + form->setFormatVersion( ver ); + + if (ver > KFormDesigner::version()) { +//! @todo display information about too new format and that "some information will not be available". + kdDebug() << "FormIO::loadFormFromDom(): original format is newer than current: " << KFormDesigner::version() << endl; + } + + // Load the pixmap collection + m_savePixmapsInline = ( (ui.namedItem("pixmapinproject").isNull()) || (!ui.namedItem("images").isNull()) ); + form->pixmapCollection()->load(ui.namedItem("collection")); + + QDomElement element = ui.namedItem("widget").toElement(); + createToplevelWidget(form, container, element); + + // Loading the tabstops + QDomElement tabStops = ui.namedItem("tabstops").toElement(); +// if(tabStops.isNull()) +// return 1; + if(!tabStops.isNull()) { + int i = 0; + uint itemsNotFound = 0; + for(QDomNode n = tabStops.firstChild(); !n.isNull(); n = n.nextSibling(), i++) + { + QString name = n.toElement().text(); + ObjectTreeItem *item = form->objectTree()->lookup(name); + if(!item) + { + kdDebug() << "FormIO::loadFormFromDom ERROR : no ObjectTreeItem " << endl; + continue; + } + const int index = form->tabStops()->findRef(item); + /* Compute a real destination index: "a number of not found items so far". */ + const int realIndex = i - itemsNotFound; + if((index != -1) && (index != realIndex)) // the widget is not in the same place, so we move it + { + form->tabStops()->remove(item); + form->tabStops()->insert(realIndex, item); + } + if(index == -1) { + itemsNotFound++; + kdDebug() << "FormIO: item '" << name << "' not in list" << endl; + } + } + } + + // Load the form connections + form->connectionBuffer()->load(ui.namedItem("connections")); + + m_currentForm = 0; + m_currentItem = 0; + + return true; +} + +///////////////////////////////////////////////////////////////////////////// +///////////// Functions to save/load properties ///////////////////////////// +///////////////////////////////////////////////////////////////////////////// + +void +FormIO::savePropertyValue(QDomElement &parentNode, QDomDocument &parent, const char *name, + const QVariant &value, QWidget *w, WidgetLibrary *lib) +{ + // Widget specific properties and attributes /////////////// +// kdDebug() << "FormIO::savePropertyValue() Saving the property: " << name << endl; + WidgetWithSubpropertiesInterface* subpropIface = dynamic_cast<WidgetWithSubpropertiesInterface*>(w); + QWidget *subwidget = w; + bool addSubwidgetFlag = false; + int propertyId = w->metaObject()->findProperty(name, true); + if (propertyId == -1 && subpropIface && subpropIface->subwidget()) { // try property from subwidget + subwidget = subpropIface->subwidget(); + propertyId = subpropIface->subwidget()->metaObject()->findProperty(name, true); + addSubwidgetFlag = true; + } + if(propertyId == -1) + { + kdDebug() << "FormIO::savePropertyValue() The object doesn't have this property. Let's try the WidgetLibrary." << endl; + if(lib) + lib->saveSpecialProperty(w->className(), name, value, w, parentNode, parent); + return; + } + + const QMetaProperty *meta = subwidget->metaObject()->property(propertyId, true); + if (!meta->stored( subwidget )) //not storable + return; + QDomElement propertyE = parent.createElement("property"); + propertyE.setAttribute("name", name); + if (addSubwidgetFlag) + propertyE.setAttribute("subwidget", "true"); + + if(meta && meta->isEnumType()) + { + // this property is enum or set type + QDomElement type; + QDomText valueE; + + if(meta->isSetType()) + { + QStringList list = QStringList::fromStrList(meta->valueToKeys(value.toInt())); + type = parent.createElement("set"); + valueE = parent.createTextNode(list.join("|")); + type.appendChild(valueE); + } + else + { + QString s = meta->valueToKey(value.toInt()); + type = parent.createElement("enum"); + valueE = parent.createTextNode(s); + type.appendChild(valueE); + } + propertyE.appendChild(type); + parentNode.appendChild(propertyE); + return; + } + + if(value.type() == QVariant::Pixmap) { + QDomText valueE; + QDomElement type = parent.createElement("pixmap"); + QCString property = propertyE.attribute("name").latin1(); +//todo QCString pixmapName = m_currentItem->widget()->property("pixmapName").toCString(); + if(m_savePixmapsInline /* (js)too risky: || m_currentItem->pixmapName(property).isNull() */) + valueE = parent.createTextNode(saveImage(parent, value.toPixmap())); + else + valueE = parent.createTextNode(m_currentItem->pixmapName(property)); + type.appendChild(valueE); + propertyE.appendChild(type); + parentNode.appendChild(propertyE); + return; + } + + // Saving a "normal" property + writeVariant(parent, propertyE, value); + parentNode.appendChild(propertyE); +} + +void +FormIO::writeVariant(QDomDocument &parent, QDomElement &parentNode, QVariant value) +{ + QDomElement type; + QDomText valueE; + + switch(value.type()) + { + case QVariant::String: + { + type = parent.createElement("string"); + valueE = parent.createTextNode(value.toString()); + type.appendChild(valueE); + break; + } + case QVariant::CString: + { + type = parent.createElement("cstring"); + valueE = parent.createTextNode(value.toString()); + type.appendChild(valueE); + break; + } + case QVariant::Rect: + { + type = parent.createElement("rect"); + QDomElement x = parent.createElement("x"); + QDomElement y = parent.createElement("y"); + QDomElement w = parent.createElement("width"); + QDomElement h = parent.createElement("height"); + QDomText valueX = parent.createTextNode(QString::number(value.toRect().x())); + QDomText valueY = parent.createTextNode(QString::number(value.toRect().y())); + QDomText valueW = parent.createTextNode(QString::number(value.toRect().width())); + QDomText valueH = parent.createTextNode(QString::number(value.toRect().height())); + + x.appendChild(valueX); + y.appendChild(valueY); + w.appendChild(valueW); + h.appendChild(valueH); + + type.appendChild(x); + type.appendChild(y); + type.appendChild(w); + type.appendChild(h); + break; + } + case QVariant::Color: + { + type = parent.createElement("color"); + QDomElement r = parent.createElement("red"); + QDomElement g = parent.createElement("green"); + QDomElement b = parent.createElement("blue"); + QDomText valueR = parent.createTextNode(QString::number(value.toColor().red())); + QDomText valueG = parent.createTextNode(QString::number(value.toColor().green())); + QDomText valueB = parent.createTextNode(QString::number(value.toColor().blue())); + + r.appendChild(valueR); + g.appendChild(valueG); + b.appendChild(valueB); + + type.appendChild(r); + type.appendChild(g); + type.appendChild(b); + break; + } + case QVariant::Bool: + { + type = parent.createElement("bool"); + //valueE = parent.createTextNode(QString::number(value.toBool())); + valueE = parent.createTextNode(value.toBool() ? "true" : "false"); + type.appendChild(valueE); + break; + } + case QVariant::Int: + case QVariant::UInt: + { + type = parent.createElement("number"); + valueE = parent.createTextNode(QString::number(value.toInt())); + type.appendChild(valueE); + break; + } + case QVariant::Size: + { + type = parent.createElement("size"); + QDomElement w = parent.createElement("width"); + QDomElement h = parent.createElement("height"); + QDomText valueW = parent.createTextNode(QString::number(value.toSize().width())); + QDomText valueH = parent.createTextNode(QString::number(value.toSize().height())); + + w.appendChild(valueW); + h.appendChild(valueH); + + type.appendChild(w); + type.appendChild(h); + break; + } + case QVariant::Point: + { + type = parent.createElement("point"); + QDomElement x = parent.createElement("x"); + QDomElement y = parent.createElement("y"); + QDomText valueX = parent.createTextNode(QString::number(value.toPoint().x())); + QDomText valueY = parent.createTextNode(QString::number(value.toPoint().y())); + + x.appendChild(valueX); + y.appendChild(valueY); + + type.appendChild(x); + type.appendChild(y); + break; + } + case QVariant::Font: + { + type = parent.createElement("font"); + QDomElement f = parent.createElement("family"); + QDomElement p = parent.createElement("pointsize"); + QDomElement w = parent.createElement("weight"); + QDomElement b = parent.createElement("bold"); + QDomElement i = parent.createElement("italic"); + QDomElement u = parent.createElement("underline"); + QDomElement s = parent.createElement("strikeout"); + QDomText valueF = parent.createTextNode(value.toFont().family()); + QDomText valueP = parent.createTextNode(QString::number(value.toFont().pointSize())); + QDomText valueW = parent.createTextNode(QString::number(value.toFont().weight())); + QDomText valueB = parent.createTextNode(QString::number(value.toFont().bold())); + QDomText valueI = parent.createTextNode(QString::number(value.toFont().italic())); + QDomText valueU = parent.createTextNode(QString::number(value.toFont().underline())); + QDomText valueS = parent.createTextNode(QString::number(value.toFont().strikeOut())); + + f.appendChild(valueF); + p.appendChild(valueP); + w.appendChild(valueW); + b.appendChild(valueB); + i.appendChild(valueI); + u.appendChild(valueU); + s.appendChild(valueS); + + type.appendChild(f); + type.appendChild(p); + type.appendChild(w); + type.appendChild(b); + type.appendChild(i); + type.appendChild(u); + type.appendChild(s); + break; + } + case QVariant::Cursor: + { + type = parent.createElement("cursor"); + valueE = parent.createTextNode(QString::number(value.toCursor().shape())); + type.appendChild(valueE); + break; + } + case QVariant::SizePolicy: + { + type = parent.createElement("sizepolicy"); + QDomElement h = parent.createElement("hsizetype"); + QDomElement v = parent.createElement("vsizetype"); + QDomElement hs = parent.createElement("horstretch"); + QDomElement vs = parent.createElement("verstretch"); + QDomText valueH = parent.createTextNode(QString::number(value.toSizePolicy().horData())); + QDomText valueV = parent.createTextNode(QString::number(value.toSizePolicy().verData())); + QDomText valueHS = parent.createTextNode(QString::number(value.toSizePolicy().horStretch())); + QDomText valueVS = parent.createTextNode(QString::number(value.toSizePolicy().verStretch())); + + h.appendChild(valueH); + v.appendChild(valueV); + hs.appendChild(valueHS); + vs.appendChild(valueVS); + + type.appendChild(h); + type.appendChild(v); + type.appendChild(hs); + type.appendChild(vs); + break; + } + case QVariant::Time: + { + type = parent.createElement("time"); + QDomElement h = parent.createElement("hour"); + QDomElement m = parent.createElement("minute"); + QDomElement s = parent.createElement("second"); + QDomText valueH = parent.createTextNode(QString::number(value.toTime().hour())); + QDomText valueM = parent.createTextNode(QString::number(value.toTime().minute())); + QDomText valueS = parent.createTextNode(QString::number(value.toTime().second())); + + h.appendChild(valueH); + m.appendChild(valueM); + s.appendChild(valueS); + + type.appendChild(h); + type.appendChild(m); + type.appendChild(s); + break; + } + case QVariant::Date: + { + type = parent.createElement("date"); + QDomElement y = parent.createElement("year"); + QDomElement m = parent.createElement("month"); + QDomElement d = parent.createElement("day"); + QDomText valueY = parent.createTextNode(QString::number(value.toDate().year())); + QDomText valueM = parent.createTextNode(QString::number(value.toDate().month())); + QDomText valueD = parent.createTextNode(QString::number(value.toDate().day())); + + y.appendChild(valueY); + m.appendChild(valueM); + d.appendChild(valueD); + + type.appendChild(y); + type.appendChild(m); + type.appendChild(d); + break; + } + case QVariant::DateTime: + { + type = parent.createElement("datetime"); + QDomElement h = parent.createElement("hour"); + QDomElement m = parent.createElement("minute"); + QDomElement s = parent.createElement("second"); + QDomElement y = parent.createElement("year"); + QDomElement mo = parent.createElement("month"); + QDomElement d = parent.createElement("day"); + QDomText valueH = parent.createTextNode(QString::number(value.toDateTime().time().hour())); + QDomText valueM = parent.createTextNode(QString::number(value.toDateTime().time().minute())); + QDomText valueS = parent.createTextNode(QString::number(value.toDateTime().time().second())); + QDomText valueY = parent.createTextNode(QString::number(value.toDateTime().date().year())); + QDomText valueMo = parent.createTextNode(QString::number(value.toDateTime().date().month())); + QDomText valueD = parent.createTextNode(QString::number(value.toDateTime().date().day())); + + h.appendChild(valueH); + m.appendChild(valueM); + s.appendChild(valueS); + y.appendChild(valueY); + mo.appendChild(valueMo); + d.appendChild(valueD); + + type.appendChild(h); + type.appendChild(m); + type.appendChild(s); + type.appendChild(y); + type.appendChild(mo); + type.appendChild(d); + break; + } + default: + break; + } + + parentNode.appendChild(type); +} + +void +FormIO::savePropertyElement(QDomElement &parentNode, QDomDocument &domDoc, const QString &tagName, const QString &property, const QVariant &value) +{ + QDomElement propertyE = domDoc.createElement(tagName); + propertyE.setAttribute("name", property); + writeVariant(domDoc, propertyE, value); + parentNode.appendChild(propertyE); +} + +QVariant +FormIO::readPropertyValue(QDomNode node, QObject *obj, const QString &name) +{ + QDomElement tag = node.toElement(); + QString text = tag.text(); + QString type = tag.tagName(); + + if(type == "string" || type == "cstring") + return text; + else if(type == "rect") + { + QDomElement x = node.namedItem("x").toElement(); + QDomElement y = node.namedItem("y").toElement(); + QDomElement w = node.namedItem("width").toElement(); + QDomElement h = node.namedItem("height").toElement(); + + int rx = x.text().toInt(); + int ry = y.text().toInt(); + int rw = w.text().toInt(); + int rh = h.text().toInt(); + + return QRect(rx, ry, rw, rh); + } + else if(type == "color") + { + QDomElement r = node.namedItem("red").toElement(); + QDomElement g = node.namedItem("green").toElement(); + QDomElement b = node.namedItem("blue").toElement(); + + int red = r.text().toInt(); + int green = g.text().toInt(); + int blue = b.text().toInt(); + + return QColor(red, green, blue); + } + else if(type == "bool") + { + if(text == "true") + return QVariant(true, 3); + else if(text == "false") + return QVariant(false, 3); + return QVariant(text.toInt(), 3); + } + else if(type == "number") + { + return text.toInt(); + } + else if(type == "size") + { + QDomElement w = node.namedItem("width").toElement(); + QDomElement h = node.namedItem("height").toElement(); + + return QSize(w.text().toInt(), h.text().toInt()); + } + else if(type == "point") + { + QDomElement x = node.namedItem("x").toElement(); + QDomElement y = node.namedItem("y").toElement(); + + return QPoint(x.text().toInt(), y.text().toInt()); + } + else if(type == "font") + { + QDomElement fa = node.namedItem("family").toElement(); + QDomElement p = node.namedItem("pointsize").toElement(); + QDomElement w = node.namedItem("weight").toElement(); + QDomElement b = node.namedItem("bold").toElement(); + QDomElement i = node.namedItem("italic").toElement(); + QDomElement u = node.namedItem("underline").toElement(); + QDomElement s = node.namedItem("strikeout").toElement(); + + QFont f; + f.setFamily(fa.text()); + f.setPointSize(p.text().toInt()); + f.setWeight(w.text().toInt()); + f.setBold(b.text().toInt()); + f.setItalic(i.text().toInt()); + f.setUnderline(u.text().toInt()); + f.setStrikeOut(s.text().toInt()); + + return f; + } + else if(type == "cursor") + { + return QCursor(text.toInt()); + } + else if(type == "time") + { + QDomElement h = node.namedItem("hour").toElement(); + QDomElement m = node.namedItem("minute").toElement(); + QDomElement s = node.namedItem("second").toElement(); + + return QTime(h.text().toInt(), m.text().toInt(), s.text().toInt()); + } + else if(type == "date") + { + QDomElement y = node.namedItem("year").toElement(); + QDomElement m = node.namedItem("month").toElement(); + QDomElement d = node.namedItem("day").toElement(); + + return QDate(y.text().toInt(), m.text().toInt(), d.text().toInt()); + } + else if(type == "datetime") + { + QDomElement h = node.namedItem("hour").toElement(); + QDomElement m = node.namedItem("minute").toElement(); + QDomElement s = node.namedItem("second").toElement(); + QDomElement y = node.namedItem("year").toElement(); + QDomElement mo = node.namedItem("month").toElement(); + QDomElement d = node.namedItem("day").toElement(); + + QTime t(h.text().toInt(), m.text().toInt(), s.text().toInt()); + QDate da(y.text().toInt(), mo.text().toInt(), d.text().toInt()); + + return QDateTime(da, t); + } + else if(type == "sizepolicy") + { + QDomElement h = node.namedItem("hsizetype").toElement(); + QDomElement v = node.namedItem("vsizetype").toElement(); + QDomElement hs = node.namedItem("horstretch").toElement(); + QDomElement vs = node.namedItem("verstretch").toElement(); + + QSizePolicy s; + s.setHorData((QSizePolicy::SizeType)h.text().toInt()); + s.setVerData((QSizePolicy::SizeType)v.text().toInt()); + s.setHorStretch(hs.text().toInt()); + s.setVerStretch(vs.text().toInt()); + return s; + } + else if(type == "pixmap") + { + if(m_savePixmapsInline || !m_currentForm || !m_currentItem || !m_currentForm->pixmapCollection()->contains(text)) + return loadImage(tag.ownerDocument(), text); + else + { + m_currentItem->setPixmapName(name.latin1(), text); + return m_currentForm->pixmapCollection()->getPixmap(text); + } + return QVariant(QPixmap()); + } + else if(type == "enum") + return text; + else if(type == "set") + { + WidgetWithSubpropertiesInterface* subpropIface = dynamic_cast<WidgetWithSubpropertiesInterface*>(obj); + QObject *subobject = (subpropIface && subpropIface->subwidget()) ? subpropIface->subwidget() : obj; + const int count = subobject->metaObject()->findProperty(name.latin1(), true); + const QMetaProperty *meta = count!=-1 ? subobject->metaObject()->property(count, true) : 0; + + if (meta) { + if (meta->isSetType()) { + QStrList keys; + const QStringList list( QStringList::split("|", text) ); + for (QStringList::ConstIterator it = list.constBegin(); it != list.constEnd(); ++it) + keys.append((*it).latin1()); + + return meta->keysToValue(keys); + } + } + else { + // Metaproperty not found, probably because subwidget is not created. + // We will return a string list here with hope that names will + // be resolved and translated into an integer value later when subwidget is created, + // e.g. near KexiFormView::updateValuesForSubproperties() + return QStringList::split("|", text); + } + } + return QVariant(); +} + +///////////////////////////////////////////////////////////////////////////// +///////////// Functions to save/load widgets //////////////////////////////// +///////////////////////////////////////////////////////////////////////////// + +void +FormIO::saveWidget(ObjectTreeItem *item, QDomElement &parent, QDomDocument &domDoc, bool insideGridLayout) +{ + if (!item) + return; + bool savedAlignment = false; + // we let Spring class handle saving itself + if(item->className() == "Spring") + { + Spring::saveSpring(item, parent, domDoc, insideGridLayout); + return; + } + + bool resetCurrentForm = false; + m_currentItem = item; + if(!m_currentForm) // copying widget + { + resetCurrentForm = true; + m_currentForm = item->container() ? item->container()->form() : item->parent()->container()->form(); + } + + + WidgetLibrary *lib = m_currentForm->library(); +// if(item->container()) +// lib = item->container()->form()->manager()->lib(); +// else +// lib = item->parent()->container()->form()->manager()->lib(); + + // We create the "widget" element + QDomElement tclass = domDoc.createElement("widget"); + parent.appendChild(tclass); + + if(insideGridLayout) + { + tclass.setAttribute("row", item->gridRow()); + tclass.setAttribute("column", item->gridCol()); + if(item->spanMultipleCells()) + { + tclass.setAttribute("rowspan", item->gridRowSpan()); + tclass.setAttribute("colspan", item->gridColSpan()); + } + } + + if(!item->parent()) // Toplevel widget + tclass.setAttribute("class", "QWidget"); + // For compatibility, HBox, VBox and Grid are saved as "QLayoutWidget" + else if(item->widget()->isA("HBox") || item->widget()->isA("VBox") || item->widget()->isA("Grid") + || item->widget()->isA("HFlow") || item->widget()->isA("VFlow")) + tclass.setAttribute("class", "QLayoutWidget"); + else if(item->widget()->isA("CustomWidget")) + tclass.setAttribute("class", item->className()); + else // Normal widgets + tclass.setAttribute("class", lib->savingName(item->widget()->className()) ); + + savePropertyValue(tclass, domDoc, "name", item->widget()->property("name"), item->widget()); + + // Important: save dataSource property FIRST before properties like "alignment" + // - needed when subproperties are defined after subwidget creation, and subwidget is created after setting "dataSource" + // (this is the case for KexiDBAutoField) +//! @todo more properties like "dataSource" may needed here... +// if (-1 != item->widget()->metaObject()->findProperty("dataSource")) + // savePropertyValue(tclass, domDoc, "dataSource", item->widget()->property("dataSource"), item->widget()); + + // We don't want to save the geometry if the widget is inside a layout (so parent.tagName() == "grid" for example) + if(item && !item->parent()) { + // save form widget size, but not its position + savePropertyValue(tclass, domDoc, "geometry", + QRect( QPoint(0,0), item->widget()->size()), + item->widget()); + } + // normal widget (if == "UI', it means we're copying widget) + else if(parent.tagName() == "widget" || parent.tagName() == "UI") + savePropertyValue(tclass, domDoc, "geometry", item->widget()->property("geometry"), item->widget()); + + // Save the buddy widget for a label + if(item->widget()->inherits("QLabel") && ((QLabel*)item->widget())->buddy()) + savePropertyElement(tclass, domDoc, "property", "buddy", ((QLabel*)item->widget())->buddy()->name()); + + // We save every property in the modifProp list of the ObjectTreeItem + QVariantMap *map = new QVariantMap( *(item->modifiedProperties()) ); + QMap<QString,QVariant>::ConstIterator endIt = map->constEnd(); + for(QMap<QString,QVariant>::ConstIterator it = map->constBegin(); it != endIt; ++it) + { + const QCString name( it.key().latin1() ); + if(name == "hAlign" || name == "vAlign" || name == "wordbreak" || name == "alignment") { + if(!savedAlignment) // not to save it twice + { + savePropertyValue(tclass, domDoc, "alignment", item->widget()->property("alignment"), item->widget()); + savedAlignment = true; + } + } + else if(name == "name" || name == "geometry" || name == "layout") { + // these have already been saved + } + else { + savePropertyValue(tclass, domDoc, it.key().latin1(), item->widget()->property(it.key().latin1()), + item->widget(), lib); + } + } + delete map; + + if(item->widget()->isA("CustomWidget")) { + QDomDocument doc("TEMP"); + doc.setContent(item->m_unknownProps); + for(QDomNode n = doc.firstChild(); !n.isNull(); n = n.nextSibling()) { + tclass.appendChild(n.cloneNode()); + } + + } + // Saving container 's layout if there is one + QDomElement layout; + if(item->container() && item->container()->layoutType() != Container::NoLayout) + { + if(item->container()->layout()) // there is a layout + { + layout = domDoc.createElement("temp"); + savePropertyValue(layout, domDoc, "name", "unnamed", item->widget()); + if(item->modifiedProperties()->contains("layoutMargin")) + savePropertyElement(layout, domDoc, "property", "margin", item->container()->layoutMargin()); + if(item->modifiedProperties()->contains("layoutSpacing")) + savePropertyElement(layout, domDoc, "property", "spacing", item->container()->layoutSpacing()); + tclass.appendChild(layout); + } + } + + int layoutType = item->container() ? item->container()->layoutType() : Container::NoLayout; + switch(layoutType) { + case Container::Grid: // grid layout + { + layout.setTagName("grid"); + for(ObjectTreeItem *objIt = item->children()->first(); objIt; objIt = item->children()->next()) + saveWidget(objIt, layout, domDoc, true); + break; + } + case Container::HBox: case Container::VBox: + { + // as we don't save geometry, we need to sort widgets in the right order, not creation order + WidgetList *list; + if(layout.tagName() == "hbox") { + list = new HorWidgetList(item->container()->form()->toplevelContainer()->widget()); + layout.setTagName("hbox"); + } + else { + list = new VerWidgetList(item->container()->form()->toplevelContainer()->widget()); + layout.setTagName("vbox"); + } + + for(ObjectTreeItem *objTree = item->children()->first(); objTree; objTree = item->children()->next()) + list->append(objTree->widget()); + list->sort(); + + for(QWidget *obj = list->first(); obj; obj = list->next()) { + ObjectTreeItem *titem = item->container()->form()->objectTree()->lookup(obj->name()); + if(item) + saveWidget(titem, layout, domDoc); + } + delete list; + break; + } + case Container::HFlow: case Container::VFlow: + { + layout.setTagName("grid"); + KexiFlowLayout *flow = static_cast<KexiFlowLayout*>(item->container()->layout()); + if(!flow) break; + WidgetList *list = (WidgetList*)flow->widgetList(); + + // save some special properties + savePropertyElement(layout, domDoc, "property", "customLayout", Container::layoutTypeToString(item->container()->layoutType()) ); + savePropertyElement(layout, domDoc, "property", "justify", QVariant(static_cast<KexiFlowLayout*>(item->container()->layout())->isJustified(), 3) ); + + // fill the widget's grid info, ie just simulate grid layout + item->container()->createGridLayout(true); + for(QWidget *obj = list->first(); obj; obj = list->next()) { + ObjectTreeItem *titem = item->container()->form()->objectTree()->lookup(obj->name()); + if(item) + saveWidget(titem, layout, domDoc, true); // save grid info for compatibility with QtDesigner + } + delete list; + break; + } + default: + { + for(ObjectTreeItem *objIt = item->children()->first(); objIt; objIt = item->children()->next()) + saveWidget(objIt, tclass, domDoc); + } + } + + addIncludeFileName(lib->includeFileName(item->widget()->className()), domDoc); + + if(resetCurrentForm) + m_currentForm = 0; + m_currentItem = 0; +} + +void +FormIO::cleanClipboard(QDomElement &uiElement) +{ + // remove includehints element not needed + if(!uiElement.namedItem("includehints").isNull()) + uiElement.removeChild(uiElement.namedItem("includehints")); + // and ensure images and connection are at the end + if(!uiElement.namedItem("connections").isNull()) + uiElement.insertAfter(uiElement.namedItem("connections"), QDomNode()); + if(!uiElement.namedItem("images").isNull()) + uiElement.insertAfter(uiElement.namedItem("images"), QDomNode()); +} + +void +FormIO::loadWidget(Container *container, const QDomElement &el, QWidget *parent) +{ + bool resetCurrentForm = false; + if(!m_currentForm) // pasting widget + { + resetCurrentForm = true; + m_currentForm = container->form(); + } + + // We first look for the widget's name + QString wname; + for(QDomNode n = el.firstChild(); !n.isNull(); n = n.nextSibling()) + { + if((n.toElement().tagName() == "property") && (n.toElement().attribute("name") == "name")) + { + wname = n.toElement().text(); + break; + } + } + + QWidget *w; + QCString classname, alternate; + // We translate some name (for compatibility) + if(el.tagName() == "spacer") + classname = "Spring"; + else if(el.attribute("class") == "QLayoutWidget") + { + for(QDomNode n = el.firstChild(); !n.isNull(); n = n.nextSibling()) + { + QString tagName = n.toElement().tagName(); + if(tagName == "property") + continue; + if(tagName == "hbox") + classname = "HBox"; + else if(tagName == "vbox") + classname = "VBox"; + else if(tagName == "grid") { + // first, see if it is flow layout + for(QDomNode child = n.firstChild(); !child.isNull(); child = child.nextSibling()) { + if((child.toElement().tagName() == "property") + && (child.toElement().attribute("name") == "customLayout")) + { + classname = child.toElement().text().latin1(); + break; + } + } + + if(classname.isEmpty()) // normal grid + classname = "Grid"; + } + } + } + else + // We check if this classname is an alternate one, and replace it if necessary + { + classname = el.attribute("class").latin1(); + alternate = container->form()->library()->classNameForAlternate(classname); + } + + if(alternate == "CustomWidget") + w = new CustomWidget(classname, container->widget(), wname.latin1()); + else + { + if(!alternate.isNull()) + classname = alternate; + + int widgetOptions = WidgetFactory::DefaultOptions; + if (!container->form()->designMode()) { + widgetOptions ^= WidgetFactory::DesignViewMode; + } + + if(!parent) + w = container->form()->library()->createWidget(classname, container->widget(), + wname.latin1(), container, widgetOptions); + else + w = container->form()->library()->createWidget(classname, parent, wname.latin1(), + container, widgetOptions); + } + + if(!w) + return; +#if KDE_VERSION >= KDE_MAKE_VERSION(3,4,0) +//! @todo allow setting this for data view mode as well + if (m_currentForm->designMode()) { + //don't generate accelerators for widgets in design mode + KAcceleratorManager::setNoAccel(w); + } +#endif + w->setStyle(&(container->widget()->style())); + w->show(); + + // We create and insert the ObjectTreeItem at the good place in the ObjectTree + ObjectTreeItem *item = container->form()->objectTree()->lookup(wname); + if (!item) { + // not yet created + item = new ObjectTreeItem(container->form()->library()->displayName(classname), + wname, w, container); + if(parent) { + ObjectTreeItem *titem = container->form()->objectTree()->lookup(parent->name()); + if(titem) + container->form()->objectTree()->addItem(titem, item); + else + kdDebug() << "FORMIO :: ERROR no parent widget " << endl; + } + else + container->form()->objectTree()->addItem(container->objectTree(), item); + } + //assign item for its widget if it supports DesignTimeDynamicChildWidgetHandler interface + //(e.g. KexiDBAutoField) + if (container->form()->designMode() && dynamic_cast<DesignTimeDynamicChildWidgetHandler*>(w)) { + dynamic_cast<DesignTimeDynamicChildWidgetHandler*>(w)->assignItem(item); + } + + m_currentItem = item; + // if we are inside a Grid, we need to insert the widget in the good cell + if(container->layoutType() == Container::Grid) { + QGridLayout *layout = (QGridLayout*)container->layout(); + if(el.hasAttribute("rowspan")) { // widget spans multiple cells + if(layout) + layout->addMultiCellWidget(w, el.attribute("row").toInt(), el.attribute("row").toInt() + el.attribute("rowspan").toInt()-1, + el.attribute("column").toInt(), el.attribute("column").toInt() + el.attribute("colspan").toInt()-1); + item->setGridPos(el.attribute("row").toInt(), el.attribute("column").toInt(), el.attribute("rowspan").toInt(), + el.attribute("colspan").toInt()); + } + else { + if(layout) + layout->addWidget(w, el.attribute("row").toInt(), el.attribute("column").toInt()); + item->setGridPos(el.attribute("row").toInt(), el.attribute("column").toInt(), 0, 0); + } + } + else if(container->layout()) + container->layout()->add(w); + + readChildNodes(item, container, el, w); + + if(item->container() && item->container()->layout()) + item->container()->layout()->activate(); + + // We add the autoSaveProperties in the modifProp list of the ObjectTreeItem, so that they are saved later + QValueList<QCString> list(container->form()->library()->autoSaveProperties(w->className())); + QValueList<QCString>::ConstIterator endIt = list.constEnd(); + KFormDesigner::WidgetWithSubpropertiesInterface* subpropIface + = dynamic_cast<KFormDesigner::WidgetWithSubpropertiesInterface*>(w); + QWidget *subwidget = (subpropIface && subpropIface->subwidget()) ? subpropIface->subwidget() : w; + for(QValueList<QCString>::ConstIterator it = list.constBegin(); it != endIt; ++it) { + if(subwidget->metaObject()->findProperty(*it, true) != -1) + item->addModifiedProperty(*it, subwidget->property(*it)); + } + + if(resetCurrentForm) + m_currentForm = 0; + m_currentItem = 0; +} + +void +FormIO::createToplevelWidget(Form *form, QWidget *container, QDomElement &el) +{ + // We first look for the widget's name + QString wname; + for(QDomNode n = el.firstChild(); !n.isNull(); n = n.nextSibling()) + { + if((n.toElement().tagName() == "property") && (n.toElement().attribute("name") == "name")) + { + wname = n.toElement().text(); + break; + } + + } + // And rename the widget and its ObjectTreeItem + container->setName(wname.latin1()); + if(form->objectTree()) + form->objectTree()->rename(form->objectTree()->name(), wname); + form->setInteractiveMode(false); + + QDict<QLabel> *oldBuddies = 0; + if(m_buddies) // save old buddies (for subforms) + oldBuddies = m_buddies; + m_buddies = new QDict<QLabel>(); + m_currentItem = form->objectTree(); + + readChildNodes(form->objectTree(), form->toplevelContainer(), el, container); + + // Now the Form is fully loaded, we can assign the buddies + QDictIterator<QLabel> it(*m_buddies); + for(; it.current(); ++it) + { + ObjectTreeItem *item = form->objectTree()->lookup(it.currentKey()); + if(!item || !item->widget()) + { + kdDebug() << "Cannot assign buddy for widget " << it.current()->name() << " to " << it.currentKey() << endl; + continue; + } + it.current()->setBuddy(item->widget()); + } + delete m_buddies; + m_buddies = oldBuddies; // and restore it + + m_currentItem = 0; + + form->setInteractiveMode(true); +} + +void +FormIO::readChildNodes(ObjectTreeItem *item, Container *container, const QDomElement &el, QWidget *w) +{ + QString eltag = el.tagName(); + + WidgetWithSubpropertiesInterface* subpropIface = dynamic_cast<WidgetWithSubpropertiesInterface*>(w); + QWidget *subwidget = (subpropIface && subpropIface->subwidget()) ? subpropIface->subwidget() : w; + + for(QDomNode n = el.firstChild(); !n.isNull(); n = n.nextSibling()) + { + QString tag = n.toElement().tagName(); + QDomElement node = n.toElement(); + + if((tag == "property") || (tag == "attribute")) + { + QString name = node.attribute("name"); + //if(name == "geometry") + // hasGeometryProp = true; + if( ((eltag == "grid") || (eltag == "hbox") || (eltag == "vbox")) && + (name == "name")) // we don't care about layout names + continue; + + if (node.attribute("subwidget")=="true") { + //this is property for subwidget: remember it for delayed setting + //because now the subwidget could be not created yet (true e.g. for KexiDBAutoField) + const QVariant val( readPropertyValue(node.firstChild(), w, name) ); + kdDebug() << val.toStringList() << endl; + item->addSubproperty( name.latin1(), val ); + //subwidget->setProperty(name.latin1(), val); + item->addModifiedProperty( name.latin1(), val ); + continue; + } + + // We cannot assign the buddy now as the buddy widget may not be created yet + if(name == "buddy") + m_buddies->insert(readPropertyValue(node.firstChild(), w, name).toString(), (QLabel*)w); + else if(((eltag == "grid") || (eltag == "hbox") || (eltag == "vbox")) && + item->container() && item->container()->layout()) { + // We load the margin of a Layout + if(name == "margin") { + int margin = readPropertyValue(node.firstChild(), w, name).toInt(); + item->container()->setLayoutMargin(margin); + item->container()->layout()->setMargin(margin); + } + // We load the spacing of a Layout + else if(name == "spacing") { + int spacing = readPropertyValue(node.firstChild(), w, name).toInt(); + item->container()->setLayoutSpacing(spacing); + item->container()->layout()->setSpacing(spacing); + } + else if((name == "justify")){ + bool justify = readPropertyValue(node.firstChild(), w, name).toBool(); + KexiFlowLayout *flow = static_cast<KexiFlowLayout*>(item->container()->layout()); + if(flow) + flow->setJustified(justify); + } + } + // If the object doesn't have this property, we let the Factory handle it (maybe a special property) + else if(subwidget->metaObject()->findProperty(name.latin1(), true) == -1) + { + if(w->className() == QString::fromLatin1("CustomWidget")) + item->storeUnknownProperty(node); + else { + bool read = container->form()->library()->readSpecialProperty( + w->className(), node, w, item); + if(!read) // the factory doesn't support this property neither + item->storeUnknownProperty(node); + } + } + else // we have a normal property, let's load it + { + QVariant val( readPropertyValue(node.firstChild(), w, name) ); + if(name == "geometry" && dynamic_cast<FormWidget*>(w)) { + //fix geometry if needed - this is top level form widget + QRect r( val.toRect() ); + if (r.left()<0) //negative X! + r.moveLeft(0); + if (r.top()<0) //negative Y! + r.moveTop(0); + val = r; + } + subwidget->setProperty(name.latin1(), val); +// int count = w->metaObject()->findProperty(name, true); +// const QMetaProperty *meta = w->metaObject()->property(count, true); +// if(meta && meta->isEnumType()) { +// val = w->property(name.latin1()); //update: we want a numeric value of enum +// } + item->addModifiedProperty(name.latin1(), val); + } + } + else if(tag == "widget") // a child widget + { + if(item->container()) // we are a Container + loadWidget(item->container(), node); + else + loadWidget(container, node, w); + } + else if(tag == "spacer") { + loadWidget(container, node, w); + } + else if(tag == "grid") { + // first, see if it is flow layout + QString layoutName; + for(QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { + if((child.toElement().tagName() == "property") && (child.toElement().attribute("name") == "customLayout")) { + layoutName = child.toElement().text(); + break; + } + } + + if(layoutName == "HFlow") { + item->container()->m_layType = Container::HFlow; + KexiFlowLayout *layout = new KexiFlowLayout(item->widget()); + layout->setOrientation(Horizontal); + item->container()->m_layout = (QLayout*)layout; + } + else if(layoutName == "VFlow") { + item->container()->m_layType = Container::VFlow; + KexiFlowLayout *layout = new KexiFlowLayout(item->widget()); + layout->setOrientation(Vertical); + item->container()->m_layout = (QLayout*)layout; + } + else { // grid layout + item->container()->m_layType = Container::Grid; + QGridLayout *layout = new QGridLayout(item->widget(), 1, 1); + item->container()->m_layout = (QLayout*)layout; + } + readChildNodes(item, container, node, w); + } + else if(tag == "vbox") { + item->container()->m_layType = Container::VBox; + QVBoxLayout *layout = new QVBoxLayout(item->widget()); + item->container()->m_layout = (QLayout*)layout; + readChildNodes(item, container, node, w); + } + else if(tag == "hbox") { + item->container()->m_layType = Container::HBox; + QHBoxLayout *layout = new QHBoxLayout(item->widget()); + item->container()->m_layout = (QLayout*)layout; + readChildNodes(item, container, node, w); + } + else {// unknown tag, we let the Factory handle it + if(w->className() == QString::fromLatin1("CustomWidget")) + item->storeUnknownProperty(node); + else { + bool read = container->form()->library()->readSpecialProperty( + w->className(), node, w, item); + if(!read) // the factory doesn't suport this property neither + item->storeUnknownProperty(node); + } + } + } +} + +///////////////////////////////////////////////////////////////////////////// +///////////// Helper functions ////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// + +void +FormIO::addIncludeFileName(const QString &include, QDomDocument &domDoc) +{ + if(include.isEmpty()) + return; + + QDomElement includes; + QDomElement uiEl = domDoc.namedItem("UI").toElement(); + if(uiEl.namedItem("includehints").isNull()) + { + includes = domDoc.createElement("includehints"); + uiEl.appendChild(includes); + } + else + includes = uiEl.namedItem("includehints").toElement(); + + // Check if this include has already been saved, and return if it is the case + for(QDomNode n = includes.firstChild(); !n.isNull(); n = n.nextSibling()) + { + if(n.toElement().text() == include) + return; + } + + QDomElement includeHint = domDoc.createElement("includehint"); + includes.appendChild(includeHint); + QDomText includeText = domDoc.createTextNode(include); + includeHint.appendChild(includeText); +} + +//////// Qt Designer code: these two functions were copied (and adapted) from Qt Designer for compatibility //////// + +QString +FormIO::saveImage(QDomDocument &domDoc, const QPixmap &pixmap) +{ + QDomNode node = domDoc.namedItem("images"); + QDomElement images; + if(node.isNull()) + { + images = domDoc.createElement("images"); + QDomElement ui = domDoc.namedItem("UI").toElement(); + ui.appendChild(images); + } + else + images = node.toElement(); + + int count = images.childNodes().count(); + QDomElement image = domDoc.createElement("image"); + QString name = "image" + QString::number(count); + image.setAttribute("name", name); + + QImage img = pixmap.convertToImage(); + QByteArray ba; + QBuffer buf(ba); + buf.open( IO_WriteOnly | IO_Translate ); + QString format = img.depth() > 1 ? "XPM" : "XBM"; + QImageIO iio( &buf, format.latin1() ); + iio.setImage( img ); + iio.write(); + buf.close(); + QByteArray bazip = qCompress( ba ); + ulong len = bazip.size(); + + QDomElement data = domDoc.createElement("data"); + data.setAttribute("format", format + ".GZ"); + data.setAttribute("length", ba.size()); + + static const char hexchars[] = "0123456789abcdef"; + QString content; + for(int i = 4; i < (int)len; ++i) + { + uchar s = (uchar) bazip[i]; + content += hexchars[s >> 4]; + content += hexchars[s & 0x0f]; + } + + QDomText text = domDoc.createTextNode(content); + data.appendChild(text); + image.appendChild(data); + images.appendChild(image); + + return name; +} + +QPixmap +FormIO::loadImage(QDomDocument domDoc, const QString& name) +{ + QDomElement images = domDoc.namedItem("UI").namedItem("images").toElement(); + if(images.isNull()) + return 0; + + QDomElement image; + for(QDomNode n = images.firstChild(); !n.isNull(); n = n.nextSibling()) + { + if((n.toElement().tagName() == "image") && (n.toElement().attribute("name") == name)) + { + image = n.toElement(); + break; + } + } + + QPixmap pix; + QString data = image.namedItem("data").toElement().text(); + const int lengthOffset = 4; + int baSize = data.length() / 2 + lengthOffset; + uchar *ba = new uchar[baSize]; + for(int i = lengthOffset; i < baSize; ++i) + { + char h = data[2 * (i-lengthOffset)].latin1(); + char l = data[2 * (i-lengthOffset) + 1].latin1(); + uchar r = 0; + if(h <= '9') + r += h - '0'; + else + r += h - 'a' + 10; + r = r << 4; + if(l <= '9') + r += l - '0'; + else + r += l - 'a' + 10; + ba[i] = r; + } + + QString format = image.namedItem("data").toElement().attribute("format", "PNG"); + if((format == "XPM.GZ") || (format == "XBM.GZ")) + { + ulong len = image.attribute("length").toULong(); + if(len < data.length() * 5) + len = data.length() * 5; + // qUncompress() expects the first 4 bytes to be the expected length of + // the uncompressed data + ba[0] = ( len & 0xff000000 ) >> 24; + ba[1] = ( len & 0x00ff0000 ) >> 16; + ba[2] = ( len & 0x0000ff00 ) >> 8; + ba[3] = ( len & 0x000000ff ); + QByteArray baunzip = qUncompress(ba, baSize); + pix.loadFromData( (const uchar*)baunzip.data(), baunzip.size(), format.left(format.find('.')).latin1() ); + } + else + pix.loadFromData( (const uchar*)ba+lengthOffset, baSize-lengthOffset, format.latin1() ); + + delete[] ba; + + return pix; +} + +//////// End of Qt Designer code //////////////////////////////////////////////////////// + +#include "formIO.moc" diff --git a/kexi/formeditor/formIO.h b/kexi/formeditor/formIO.h new file mode 100644 index 00000000..b7337182 --- /dev/null +++ b/kexi/formeditor/formIO.h @@ -0,0 +1,222 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr> + Copyright (C) 2005-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. +*/ + +#ifndef FORMIO_H +#define FORMIO_H + +#include <qobject.h> +#include <qdict.h> +#include <qstring.h> +#include <qwidget.h> +#include <qmap.h> + +class QString; +class QDomElement; +class QDomNode; +class QDomDocument; +class QDomText; +class QVariant; +class QLabel; + +//! A blank widget displayed when class is not supported +class KFORMEDITOR_EXPORT CustomWidget : public QWidget +{ + Q_OBJECT + + public: + CustomWidget(const QCString &className, QWidget *parent, const char *name); + virtual ~CustomWidget(); + + virtual void paintEvent(QPaintEvent *ev); + + private: + QCString m_className; +}; + +namespace KFormDesigner { + +class WidgetPropertySet; +class Form; +class ObjectTreeItem; +class Container; +class WidgetLibrary; + +//! KFormDesigner API version number. Increased on every breaking of backward compatibility. +//! Use KFormDesigner::version() to get real version number of the library. +#define KFORMDESIGNER_VERSION 2 + +//! \return KFormDesigner API version number for this library. This information is stored +KFORMEDITOR_EXPORT uint version(); + +/** This class act as a namespace for all .ui files related functions, ie saving/loading .ui files. + You don't need to create a FormIO object, as all methods are static.\n + This class is able to read and write Forms to .ui files, and to save each type of properties, including set and enum + properties, and pixmaps(pixmap-related code was taken from Qt Designer). + **/ + //! A class to save/load forms from .ui files +class KFORMEDITOR_EXPORT FormIO : public QObject +{ + Q_OBJECT + + public: + FormIO(); + ~FormIO(); + + /*! Save the Form in the \a domDoc QDomDocument. Called by saveForm(). + \return true if saving succeeded. + \sa saveForm() */ + static bool saveFormToDom(Form *form, QDomDocument &domDoc); + + /*! Save the Form \a form to the file \a filename. If \a filename is null or not given, + a Save File dialog will be shown to choose dest file. + \return true if saving succeeded. + \todo Add errors code and error dialog + */ + static bool saveFormToFile(Form *form, const QString &filename=QString::null); + + /*! Saves the Form to the \a dest string. \a indent can be specified to apply indentation. + \return true if saving succeeded. + \sa saveForm() + */ + static bool saveFormToString(Form *form, QString &dest, int indent = 0); + + /*! Saves the \a form inside the \a dest QByteArray. + \return true if saving succeeded. + \sa saveFormToDom(), saveForm() + */ + static bool saveFormToByteArray(Form *form, QByteArray &dest); + + /*! Loads a form from the \a domDoc QDomDocument. Called by loadForm() and loadFormData(). + \return true if loading succeeded. */ + static bool loadFormFromDom(Form *form, QWidget *container, QDomDocument &domDoc); + + /*! Loads a form from the \a src QByteArray. + \sa loadFormFromDom(), loadForm(). + \return true if loading succeeded. + */ + static bool loadFormFromByteArray(Form *form, QWidget *container, QByteArray &src, + bool preview=false); + + static bool loadFormFromString(Form *form, QWidget *container, QString &src, bool preview=false); + + /*! Loads the .ui file \a filename in the Form \a form. If \a filename is null or not given, + a Open File dialog will be shown to select the file to open. + createToplevelWidget() is used to load the Form's toplevel widget. + \return true if loading succeeded. + \todo Add errors code and error dialog + */ + static bool loadFormFromFile(Form *form, QWidget *container, const QString &filename=QString::null); + + /*! Saves the widget associated to the ObjectTreeItem \a item into DOM document \a domDoc, + with \a parent as parent node. + It calls readPropertyValue() for each object property, readAttribute() for each attribute and + itself to save child widgets.\n + \return true if saving succeeded. + This is used to copy/paste widgets. + */ + static void saveWidget(ObjectTreeItem *item, QDomElement &parent, QDomDocument &domDoc, + bool insideGridLayout=false); + + /*! Cleans the "UI" QDomElement after saving widget. It deletes the "includes" element + not needed when pasting, and make sure all the "widget" elements are at the beginning. + Call this after copying a widget, before pasting.*/ + static void cleanClipboard(QDomElement &uiElement); + + /*! Loads the widget associated to the QDomElement \a el into the Container \a container, + with \a parent as parent widget. + If parent = 0, the Container::widget() is used as parent widget. + This is used to copy/paste widgets. + */ + static void loadWidget(Container *container, + const QDomElement &el, QWidget *parent=0); + + /*! Save an element in the \a domDoc as child of \a parentNode. + The element will be saved like this : + \code <$(tagName) name = "$(property)">< value_as_XML ><$(tagName)/> + \endcode + */ + static void savePropertyElement(QDomElement &parentNode, QDomDocument &domDoc, const QString &tagName, + const QString &property, const QVariant &value); + + /*! Read an object property in the DOM doc. + \param node the QDomNode of the property + \param obj the widget whose property is being read + \param name the name of the property being read + */ + static QVariant readPropertyValue(QDomNode node, QObject *obj, const QString &name); + + /*! Write an object property in the DOM doc. + \param parentNode the DOM document to write to + \param name the name of the property being saved + \param value the value of this property + \param w the widget whose property is being saved + \param lib the widget library for which the property is being saved + + Properties of subwidget are saved with subwidget="true" arribute added + to 'property' XML element. + */ + static void savePropertyValue(QDomElement &parentNode, QDomDocument &parent, const char *name, + const QVariant &value, QWidget *w, WidgetLibrary *lib=0); + + protected: + /*! Saves the QVariant \a value as text to be included in an xml file, with \a parentNode.*/ + static void writeVariant(QDomDocument &parent, QDomElement &parentNode, QVariant value); + + /*! Creates a toplevel widget from the QDomElement \a element in the Form \a form, + with \a parent as parent widget. + It calls readPropertyValue() and loadWidget() to load child widgets. + */ + static void createToplevelWidget(Form *form, QWidget *container, QDomElement &element); + + /*! \return the name of the pixmap saved, to use to access it + This function save the QPixmap \a pixmap into the DOM document \a domDoc. + The pixmap is converted to XPM and compressed for compatibility with Qt Designer. + Encoding code is taken from Designer. + */ + static QString saveImage(QDomDocument &domDoc, const QPixmap &pixmap); + + /*! \return the loaded pixmap + This function loads the pixmap named \a name in the DOM document \a domDoc. + Decoding code is taken from QT Designer. + */ + static QPixmap loadImage(QDomDocument domDoc, const QString& name); + + /*! Reads the child nodes of a "widget" element. */ + static void readChildNodes(ObjectTreeItem *tree, Container *container, + const QDomElement &el, QWidget *w); + + /*! Adds an include file name to be saved in the "includehints" part of .ui file, + which is needed by uic. */ + static void addIncludeFileName(const QString &include, QDomDocument &domDoc); + + private: + // This dict stores buddies associations until the Form is completely loaded. + static QDict<QLabel> *m_buddies; + + /// Instead of having to pass these for every functions, we just store them in the class + //static QWidgdet *m_currentWidget; + static ObjectTreeItem *m_currentItem; + static Form *m_currentForm; + static bool m_savePixmapsInline; +}; + +} + +#endif diff --git a/kexi/formeditor/formmanager.cpp b/kexi/formeditor/formmanager.cpp new file mode 100644 index 00000000..20b40cf9 --- /dev/null +++ b/kexi/formeditor/formmanager.cpp @@ -0,0 +1,1716 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr> + Copyright (C) 2005 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 <kdebug.h> + +#include <qworkspace.h> +#include <qcursor.h> +#include <qstring.h> +#include <qlabel.h> +#include <qobjectlist.h> +#include <qstylefactory.h> +#include <qmetaobject.h> +#include <qregexp.h> +#include <qvaluevector.h> +#include <qvbox.h> + +#include <klocale.h> +#include <kiconloader.h> +#include <kpopupmenu.h> +#include <kstdaction.h> +#include <kaction.h> +#include <kxmlguiclient.h> +#include <kmainwindow.h> +#include <kmessagebox.h> +#include <kconfig.h> +#include <kstyle.h> +#include <kactionclasses.h> +#include <kapplication.h> +#include <kglobal.h> +#include <kglobalsettings.h> +#include <kdialogbase.h> +#include <ktextedit.h> +#include <ktabwidget.h> +#include <kfontdialog.h> + +#include <kdeversion.h> +#if KDE_VERSION >= KDE_MAKE_VERSION(3,1,9) && !defined(Q_WS_WIN) +# include <kactioncollection.h> +#endif + +#include "widgetpropertyset.h" +#include "objecttree.h" +#include "widgetlibrary.h" +#include "form.h" +#include "container.h" +#include "formIO.h" +#include "objecttreeview.h" +#include "commands.h" +#include "tabstopdialog.h" +#include "connectiondialog.h" +#include "pixmapcollection.h" +#include "events.h" +#include "utils.h" +#include "kfdpixmapedit.h" +#include <koproperty/editor.h> +#include <koproperty/property.h> +#include <koproperty/factory.h> + +#include "formmanager.h" + +#define KFD_NO_STYLES //disables; styles support needs improvements + +namespace KFormDesigner { + +//! @internal +class PropertyFactory : public KoProperty::CustomPropertyFactory +{ + public: + PropertyFactory(QObject *parent) + : KoProperty::CustomPropertyFactory(parent) +// m_manager(manager) + { + } + virtual ~PropertyFactory() {} + + KoProperty::CustomProperty* createCustomProperty(KoProperty::Property *) { return 0;} + + KoProperty::Widget* createCustomWidget(KoProperty::Property *prop) + { + return new KFDPixmapEdit(prop); + } +}; + +} + +using namespace KFormDesigner; + +static KStaticDeleter<FormManager> m_managerDeleter; +FormManager* FormManager::_self = 0L; + +FormManager::FormManager(QObject *parent, int options, const char *name) + : QObject(parent, name) +#ifdef KEXI_DEBUG_GUI + , m_uiCodeDialog(0) + , m_options(options) +#endif + , m_objectBlockingPropertyEditorUpdating(0) + , m_isRedoing(false) +{ + Q_UNUSED(options); +#ifdef KEXI_STANDALONE + KGlobal::locale()->insertCatalogue("standalone_kformdesigner"); +#else + KGlobal::locale()->insertCatalogue("kformdesigner"); +#endif + + connect( kapp, SIGNAL( settingsChanged(int) ), SLOT( slotSettingsChanged(int) ) ); + slotSettingsChanged(KApplication::SETTINGS_SHORTCUTS); + +//moved to createWidgetLibrary() m_lib = new WidgetLibrary(this, supportedFactoryGroups); + m_propSet = new WidgetPropertySet(this); + + //unused m_editor = 0; + m_active = 0; + m_inserting = false; + m_drawingSlot = false; + m_collection = 0; + m_connection = 0; + m_popup = 0; + m_treeview = 0; + m_emitSelectionSignalsUpdatesPropertySet = false; + m_domDoc.appendChild(m_domDoc.createElement("UI")); + + m_deleteWidgetLater_list.setAutoDelete(true); + connect( &m_deleteWidgetLater_timer, SIGNAL(timeout()), this, SLOT(deleteWidgetLaterTimeout())); + connect( this, SIGNAL(connectionCreated(KFormDesigner::Form*, KFormDesigner::Connection&)), + this, SLOT(slotConnectionCreated(KFormDesigner::Form*, KFormDesigner::Connection&))); + + // register kfd custom editors + KoProperty::FactoryManager::self()->registerFactoryForEditor(KoProperty::Pixmap, + new PropertyFactory(KoProperty::FactoryManager::self())); +} + +FormManager::~FormManager() +{ + m_managerDeleter.setObject(_self, 0, false); //safe + delete m_popup; + delete m_connection; +#ifdef KEXI_DEBUG_GUI + delete m_uiCodeDialog; +#endif +// delete m_propFactory; +} + + +FormManager* FormManager::self() +{ + return _self; +} + +WidgetLibrary* +FormManager::createWidgetLibrary(FormManager* m, const QStringList& supportedFactoryGroups) +{ + if(!_self) + m_managerDeleter.setObject( _self, m ); + return new WidgetLibrary(_self, supportedFactoryGroups); +} + +void +FormManager::setEditor(KoProperty::Editor *editor) +{ + m_editor = editor; + + if(editor) + editor->changeSet(m_propSet->set()); +} + +void +FormManager::setObjectTreeView(ObjectTreeView *treeview) +{ + m_treeview = treeview; + if (m_treeview) + connect(m_propSet, SIGNAL(widgetNameChanged(const QCString&, const QCString&)), + m_treeview, SLOT(renameItem(const QCString&, const QCString&))); +} + +ActionList +FormManager::createActions(WidgetLibrary *lib, KActionCollection* collection, KXMLGUIClient* client) +{ + m_collection = collection; + + ActionList actions = lib->createWidgetActions(client, m_collection, this, SLOT(insertWidget(const QCString &))); + + if (m_options & HideSignalSlotConnections) + m_dragConnection = 0; + else { + m_dragConnection = new KToggleAction(i18n("Connect Signals/Slots"), + "signalslot", KShortcut(0), this, SLOT(startCreatingConnection()), m_collection, + "drag_connection"); + //to be exclusive with any 'widget' action + m_dragConnection->setExclusiveGroup("LibActionWidgets"); + m_dragConnection->setChecked(false); + actions.append(m_dragConnection); + } + + m_pointer = new KToggleAction(i18n("Pointer"), "mouse_pointer", KShortcut(0), + this, SLOT(slotPointerClicked()), m_collection, "pointer"); + m_pointer->setExclusiveGroup("LibActionWidgets"); //to be exclusive with any 'widget' action + m_pointer->setChecked(true); + actions.append(m_pointer); + + m_snapToGrid = new KToggleAction(i18n("Snap to Grid"), QString::null, KShortcut(0), + 0, 0, m_collection, "snap_to_grid"); + m_snapToGrid->setChecked(true); + actions.append(m_snapToGrid); + + // Create the Style selection action (with a combo box in toolbar and submenu items) + KSelectAction *m_style = new KSelectAction( i18n("Style"), KShortcut(0), + this, SLOT(slotStyle()), m_collection, "change_style"); + m_style->setEditable(false); + + KGlobal::config()->setGroup("General"); + QString currentStyle = QString::fromLatin1(kapp->style().name()).lower(); + const QStringList styles = QStyleFactory::keys(); + m_style->setItems(styles); + m_style->setCurrentItem(0); + + QStringList::ConstIterator endIt = styles.constEnd(); + int idx = 0; + for (QStringList::ConstIterator it = styles.constBegin(); it != endIt; ++it, ++idx) + { + if ((*it).lower() == currentStyle) { + m_style->setCurrentItem(idx); + break; + } + } + + m_style->setToolTip(i18n("Set the current view style.")); + m_style->setMenuAccelsEnabled(true); + actions.append(m_style); + + lib->addCustomWidgetActions(m_collection); + + return actions; +} + +bool +FormManager::isPasteEnabled() +{ + return m_domDoc.namedItem("UI").hasChildNodes(); +} + +void +FormManager::undo() +{ + if(!activeForm() || !activeForm()->objectTree()) + return; + + activeForm()->commandHistory()->undo(); +} + +void +FormManager::redo() +{ + if(!activeForm() || !activeForm()->objectTree()) + return; + + m_isRedoing = true; + activeForm()->commandHistory()->redo(); + m_isRedoing = false; +} + +void +FormManager::insertWidget(const QCString &classname) +{ + if(m_drawingSlot) + stopCreatingConnection(); + + m_inserting = true; + + Form *form; + for(form = m_forms.first(); form; form = m_forms.next()) + { +// form->d->cursors = new QMap<QString, QCursor>(); + if (form->toplevelContainer()) + form->widget()->setCursor(QCursor(CrossCursor)); + QObjectList *l = form->widget()->queryList( "QWidget" ); + for(QObject *o = l->first(); o; o = l->next()) + { + if( ((QWidget*)o)->ownCursor() ) + { +// form->d->cursors->insert(o->name(), ((QWidget*)o)->cursor()); + form->d->cursors.insert(o, static_cast<QWidget*>(o)->cursor()); + static_cast<QWidget*>(o)->setCursor(QCursor(Qt::CrossCursor)); + } + + } + delete l; + } + + m_selectedClass = classname; + m_pointer->setChecked(false); +} + +void +FormManager::stopInsert() +{ + if(m_drawingSlot) + stopCreatingConnection(); + if(!m_inserting) + return; + +//#ifndef KEXI_NO_CURSOR_PROPERTY + Form *form; + for(form = m_forms.first(); form; form = m_forms.next()) + { + form->widget()->unsetCursor(); + QObjectList *l = form->widget()->queryList( "QWidget" ); + for(QObject *o = l->first(); o; o = l->next()) + { + static_cast<QWidget*>(o)->unsetCursor(); +#if 0 + if( ((QWidget*)o)->ownCursor()) { + QMap<QObject*,QCursor>::ConstIterator curIt( form->d->cursors.find(o) ); + if (curIt!=form->d->cursors.constEnd()) + static_cast<QWidget*>(o)->setCursor( *curIt ); +// ((QWidget*)o)->setCursor( (*(form->d->cursors))[o->name()] ) ; + } +#endif + } + delete l; +// delete (form->d->cursors); +// form->d->cursors = 0; + } +//#endif + m_inserting = false; + m_pointer->setChecked(true); +} + +void +FormManager::slotPointerClicked() +{ + if(m_inserting) + stopInsert(); + else if(m_dragConnection) + stopCreatingConnection(); +} + +void +FormManager::startCreatingConnection() +{ + if (m_options & HideSignalSlotConnections) + return; + + if(m_inserting) + stopInsert(); + + // We set a Pointing hand cursor while drawing the connection + Form *form; + for(form = m_forms.first(); form; form = m_forms.next()) + { +// form->d->cursors = new QMap<QString, QCursor>(); + form->d->mouseTrackers = new QStringList(); + if (form->toplevelContainer()) + { + form->widget()->setCursor(QCursor(PointingHandCursor)); + form->widget()->setMouseTracking(true); + } + QObjectList *l = form->widget()->queryList( "QWidget" ); + for(QObject *o = l->first(); o; o = l->next()) + { + QWidget *w = static_cast<QWidget*>(o); + if( w->ownCursor() ) + { + form->d->cursors.insert(w, w->cursor()); +// form->d->cursors->insert(w->name(), w->cursor()); + w->setCursor(QCursor(PointingHandCursor )); + } + if(w->hasMouseTracking()) + form->d->mouseTrackers->append(w->name()); + w->setMouseTracking(true); + } + delete l; + } + delete m_connection; + m_connection = new Connection(); + m_drawingSlot = true; + if (m_dragConnection) + m_dragConnection->setChecked(true); +} + +void +FormManager::resetCreatedConnection() +{ + if (m_options & HideSignalSlotConnections) + return; + + delete m_connection; + m_connection = new Connection(); + + if(m_active && m_active->formWidget()) { + Form *ff = (Form*)m_active; + FormWidget *fw = 0; + if (ff) + fw = ff->formWidget(); + m_active->formWidget()->clearForm(); + } + if (m_active && m_active->widget()) + m_active->widget()->repaint(); +} + +void +FormManager::stopCreatingConnection() +{ + if (m_options & HideSignalSlotConnections) + return; + if(!m_drawingSlot) + return; + + if(m_active && m_active->formWidget()) + m_active->formWidget()->clearForm(); + + Form *form; + for(form = m_forms.first(); form; form = m_forms.next()) + { + form->widget()->unsetCursor(); + form->widget()->setMouseTracking(false); + QObjectList *l = form->widget()->queryList( "QWidget" ); + for(QObject *o = l->first(); o; o = l->next()) + { + QWidget *w = (QWidget*)o; + if( w->ownCursor()) { + QMap<QObject*,QCursor>::ConstIterator curIt( form->d->cursors.find(o) ); + if (curIt!=form->d->cursors.constEnd()) + static_cast<QWidget*>(o)->setCursor( *curIt ); + } +// w->setCursor( (*(form->d->cursors))[o->name()] ) ; + w->setMouseTracking( !form->d->mouseTrackers->grep(w->name()).isEmpty() ); + } + delete l; +// delete (form->d->cursors); +// form->d->cursors = 0; + delete (form->d->mouseTrackers); + form->d->mouseTrackers = 0; + } + + if(m_connection->slot().isNull()) + emit connectionAborted(activeForm()); + delete m_connection; + m_connection = 0; + m_drawingSlot = false; + m_pointer->setChecked(true); +} + +bool +FormManager::snapWidgetsToGrid() +{ + return m_snapToGrid->isChecked(); +} + +void +FormManager::windowChanged(QWidget *w) +{ + kdDebug() << "FormManager::windowChanged(" + << (w ? (QString(w->className())+" "+w->name()) : QString("0")) << ")" << endl; + + if(!w) + { + m_active = 0; + if(m_treeview) + m_treeview->setForm(0); + emit propertySetSwitched(0); + if(isCreatingConnection()) + stopCreatingConnection(); + + emitNoFormSelected(); + return; + } + + Form *previousActive = m_active; + Form *form; + for(form = m_forms.first(); form; form = m_forms.next()) + { + if(form->toplevelContainer() && form->widget() == w) + { + if(m_treeview) + m_treeview->setForm(form); + //if(m_propSet) + // m_propList->setCollection(form->pixmapCollection()); + + kdDebug() << "FormManager::windowChanged() active form is " << form->objectTree()->name() << endl; + + if(m_collection) + { +#ifndef KFD_NO_STYLES + // update the 'style' action + KSelectAction *m_style = (KSelectAction*)m_collection->action("change_style", "KSelectAction"); + const QString currentStyle = form->widget()->style().name(); + const QStringList styles = m_style->items(); + + int idx = 0; + QStringList::ConstIterator endIt = styles.constEnd(); + for (QStringList::ConstIterator it = styles.constBegin(); it != endIt; ++it, ++idx) + { + if ((*it).lower() == currentStyle) { + kdDebug() << "Updating the style to " << currentStyle << endl; + m_style->setCurrentItem(idx); + break; + } + } +#endif + } + + if((form != previousActive) && isCreatingConnection()) + resetCreatedConnection(); + + m_active = form; + + emit dirty(form, form->isModified()); + // update actions state + m_active->emitActionSignals(); + //update the buffer too + form->emitSelectionSignals(); + if (!m_emitSelectionSignalsUpdatesPropertySet) + showPropertySet( propertySet(), true ); + return; + } + } + + for(form = m_preview.first(); form; form = m_preview.next()) + { + kdDebug() << (form->widget() ? form->widget()->name() : "") << endl; + if(form->toplevelContainer() && form->widget() == w) { + kdDebug() << "FormManager::windowChanged() active preview form is " << form->widget()->name() << endl; + + if(m_collection) + { +#ifndef KFD_NO_STYLES + // update the 'style' action + KSelectAction *m_style = (KSelectAction*)m_collection->action("change_style", "KSelectAction"); + const QString currentStyle = form->widget()->style().name(); + const QStringList styles = m_style->items(); + + int idx = 0; + QStringList::ConstIterator endIt = styles.constEnd(); + for (QStringList::ConstIterator it = styles.constBegin(); it != endIt; ++it, ++idx) + { + if ((*it).lower() == currentStyle) { + kdDebug() << "Updating the style to " << currentStyle << endl; + m_style->setCurrentItem(idx); + break; + } + } +#endif + + resetCreatedConnection(); + m_active = form; + + emit dirty(form, false); + emitNoFormSelected(); + showPropertySet(0); + return; + } + } + } + //m_active = 0; +} + +Form* +FormManager::activeForm() const +{ + return m_active; +} + +Form* +FormManager::formForWidget(QWidget *w) +{ + for(Form *form = m_forms.first(); form; form = m_forms.next()) { + if(form->toplevelContainer() && form->widget() == w) + return form; + } + + return 0; // not one of toplevel widgets +} + +void +FormManager::deleteForm(Form *form) +{ + if (!form) + return; + if(m_forms.find(form) == -1) + m_preview.remove(form); + else + m_forms.remove(form); + + if(m_forms.count() == 0) { + m_active = 0; + emit propertySetSwitched(0); + } +} + +void +FormManager::importForm(Form *form, bool preview) +{ + if(!preview) + initForm(form); + else + { + m_preview.append(form); + form->setDesignMode(false); + } +} + +void +FormManager::initForm(Form *form) +{ + m_forms.append(form); + + if(m_treeview) + m_treeview->setForm(form); + + m_active = form; + + connect(form, SIGNAL(selectionChanged(QWidget*, bool, bool)), + m_propSet, SLOT(setSelectedWidgetWithoutReload(QWidget*, bool, bool))); + if(m_treeview) + { + connect(form, SIGNAL(selectionChanged(QWidget*, bool, bool)), + m_treeview, SLOT(setSelectedWidget(QWidget*, bool))); + connect(form, SIGNAL(childAdded(ObjectTreeItem* )), m_treeview, SLOT(addItem(ObjectTreeItem*))); + connect(form, SIGNAL(childRemoved(ObjectTreeItem* )), m_treeview, SLOT(removeItem(ObjectTreeItem*))); + } + connect(m_propSet, SIGNAL(widgetNameChanged(const QCString&, const QCString&)), + form, SLOT(changeName(const QCString&, const QCString&))); + + form->setSelectedWidget(form->widget()); + windowChanged(form->widget()); +} + +void +FormManager::previewForm(Form *form, QWidget *container, Form *toForm) +{ + if(!form || !container || !form->objectTree()) + return; + QDomDocument domDoc; + if (!FormIO::saveFormToDom(form, domDoc)) + return; + + Form *myform; + if(!toForm) + myform = new Form(form->library(), form->objectTree()->name().latin1(), + false/*!designMode, we need to set it early enough*/); + else + myform = toForm; + myform->createToplevel(container); + container->setStyle( &(form->widget()->style()) ); + + if (!FormIO::loadFormFromDom(myform, container, domDoc)) { + delete myform; + return; + } + + myform->setDesignMode(false); + m_preview.append(myform); + container->show(); +} + +/* +bool +FormManager::loadFormFromDomInternal(Form *form, QWidget *container, QDomDocument &inBuf) +{ + return FormIO::loadFormFromDom(myform, container, domDoc); +} + +bool +FormManager::saveFormToStringInternal(Form *form, QString &dest, int indent) +{ + return KFormDesigner::FormIO::saveFormToString(form, dest, indent); +}*/ + +bool +FormManager::isTopLevel(QWidget *w) +{ + if(!activeForm() || !activeForm()->objectTree()) + return false; + +// kdDebug() << "FormManager::isTopLevel(): for: " << w->name() << " = " +// << activeForm()->objectTree()->lookup(w->name())<< endl; + + ObjectTreeItem *item = activeForm()->objectTree()->lookup(w->name()); + if(!item) + return true; + + return (!item->parent()); +} + +void +FormManager::deleteWidget() +{ + if(!activeForm() || !activeForm()->objectTree()) + return; + + QPtrList<QWidget> *list = activeForm()->selectedWidgets(); + if(list->isEmpty()) + return; + + if (activeForm()->widget() == list->first()) { + //toplevel form is selected, cannot delete it + return; + } + + KCommand *com = new DeleteWidgetCommand(*list, activeForm()); + activeForm()->addCommand(com, true); +} + +void +FormManager::copyWidget() +{ + if (!activeForm() || !activeForm()->objectTree()) + return; + + QPtrList<QWidget> *list = activeForm()->selectedWidgets(); + if(list->isEmpty()) + return; + + removeChildrenFromList(*list); + + // We clear the current clipboard + m_domDoc.setContent(QString(), true); + QDomElement parent = m_domDoc.createElement("UI"); + m_domDoc.appendChild(parent); + + QWidget *w; + for(w = list->first(); w; w = list->next()) + { + ObjectTreeItem *it = activeForm()->objectTree()->lookup(w->name()); + if (!it) + continue; + + FormIO::saveWidget(it, parent, m_domDoc); + } + + FormIO::cleanClipboard(parent); + + activeForm()->emitActionSignals(); // to update 'Paste' item state +} + +void +FormManager::cutWidget() +{ + if(!activeForm() || !activeForm()->objectTree()) + return; + + QPtrList<QWidget> *list = activeForm()->selectedWidgets(); + if(list->isEmpty()) + return; + + KCommand *com = new CutWidgetCommand(*list, activeForm()); + activeForm()->addCommand(com, true); +} + +void +FormManager::pasteWidget() +{ + if(!m_domDoc.namedItem("UI").hasChildNodes()) + return; + if(!activeForm() || !activeForm()->objectTree()) + return; + + KCommand *com = new PasteWidgetCommand(m_domDoc, activeForm()->activeContainer(), m_insertPoint); + activeForm()->addCommand(com, true); +} + +void +FormManager::setInsertPoint(const QPoint &p) +{ + m_insertPoint = p; +} + +void +FormManager::createSignalMenu(QWidget *w) +{ + m_sigSlotMenu = new KPopupMenu(); + m_sigSlotMenu->insertTitle(SmallIcon("connection"), i18n("Signals")); + + QStrList list = w->metaObject()->signalNames(true); + QStrListIterator it(list); + for(; it.current() != 0; ++it) + m_sigSlotMenu->insertItem(*it); + + int result = m_sigSlotMenu->exec(QCursor::pos()); + if(result == -1) + resetCreatedConnection(); + else + menuSignalChosen(result); + + delete m_sigSlotMenu; + m_sigSlotMenu = 0; +} + +void +FormManager::createSlotMenu(QWidget *w) +{ + m_sigSlotMenu = new KPopupMenu(); + m_sigSlotMenu->insertTitle(SmallIcon("connection"), i18n("Slots")); + + QString signalArg( m_connection->signal().remove( QRegExp(".*[(]|[)]") ) ); + + QStrList list = w->metaObject()->slotNames(true); + QStrListIterator it(list); + for(; it.current() != 0; ++it) + { + // we add the slot only if it is compatible with the signal + QString slotArg(*it); + slotArg = slotArg.remove( QRegExp(".*[(]|[)]") ); + if(!signalArg.startsWith(slotArg, true)) // args not compatible + continue; + + m_sigSlotMenu->insertItem(*it); + } + + int result = m_sigSlotMenu->exec(QCursor::pos()); + if(result == -1) + resetCreatedConnection(); + else + menuSignalChosen(result); + + delete m_sigSlotMenu; + m_sigSlotMenu = 0; +} + +void +FormManager::createContextMenu(QWidget *w, Container *container, bool popupAtCursor) +{ + if(!activeForm() || !activeForm()->widget()) + return; + const bool toplevelWidgetSelected = activeForm()->widget() == w; + const uint widgetsCount = container->form()->selectedWidgets()->count(); + const bool multiple = widgetsCount > 1; + //const bool enableRemove = w != m_active->widget(); + // We only enablelayout creation if more than one widget with the same parent are selected + const bool enableLayout = multiple || w == container->widget(); + + m_menuWidget = w; + QString n = container->form()->library()->displayName(w->className()); +// QValueVector<int> menuIds(); + + if (!m_popup) { + m_popup = new KPopupMenu(); + } + else { + m_popup->clear(); + } + + //set title + if(!multiple) + { + if(w == container->form()->widget()) + m_popup->insertTitle(SmallIcon("form"), i18n("%1 : Form").arg(w->name()) ); + else + m_popup->insertTitle( SmallIcon( + container->form()->library()->iconName(w->className())), QString(w->name()) + " : " + n ); + } + else + m_popup->insertTitle(SmallIcon("multiple_obj"), i18n("Multiple Widgets") + + QString(" (%1)").arg(widgetsCount)); + + KAction *a; +#define PLUG_ACTION(_name, forceVisible) \ + { a = action(_name); \ + if (a && (forceVisible || a->isEnabled())) { \ + if (separatorNeeded) \ + m_popup->insertSeparator(); \ + separatorNeeded = false; \ + a->plug(m_popup); \ + } \ + } + + 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("layout_menu", enableLayout); + PLUG_ACTION("break_layout", enableLayout); + separatorNeeded = true; + PLUG_ACTION("align_menu", !toplevelWidgetSelected); + PLUG_ACTION("adjust_size_menu", !toplevelWidgetSelected); + separatorNeeded = true; + + // We create the buddy menu + if(!multiple && w->inherits("QLabel") && ((QLabel*)w)->text().contains("&") && (((QLabel*)w)->textFormat() != RichText)) + { + if (separatorNeeded) + m_popup->insertSeparator(); + + KPopupMenu *sub = new KPopupMenu(w); + QWidget *buddy = ((QLabel*)w)->buddy(); + + sub->insertItem(i18n("No Buddy"), MenuNoBuddy); + if(!buddy) + sub->setItemChecked(MenuNoBuddy, true); + sub->insertSeparator(); + + // add all the widgets that can have focus + for(ObjectTreeListIterator it( container->form()->tabStopsIterator() ); it.current(); ++it) + { + int index = sub->insertItem( + SmallIcon(container->form()->library()->iconName(it.current()->className().latin1())), + it.current()->name()); + if(it.current()->widget() == buddy) + sub->setItemChecked(index, true); + } + + /*int id =*/ m_popup->insertItem(i18n("Choose Buddy..."), sub); +// menuIds->append(id); + connect(sub, SIGNAL(activated(int)), this, SLOT(buddyChosen(int))); + + separatorNeeded = true; + } + + //int sigid=0; +#ifdef KEXI_DEBUG_GUI + if(!multiple && !(m_options & HideEventsInPopupMenu)) + { + if (separatorNeeded) + m_popup->insertSeparator(); + + // We create the signals menu + KPopupMenu *sigMenu = new KPopupMenu(); + QStrList list = w->metaObject()->signalNames(true); + QStrListIterator it(list); + for(; it.current() != 0; ++it) + sigMenu->insertItem(*it); + + int id = m_popup->insertItem(SmallIconSet(""), i18n("Events"), sigMenu); +// menuIds->append(id); + if(list.isEmpty()) + m_popup->setItemEnabled(id, false); + connect(sigMenu, SIGNAL(activated(int)), this, SLOT(menuSignalChosen(int))); + separatorNeeded = true; + } +#endif + + // Other items + if(!multiple) + { + int lastID = -1; + if (separatorNeeded) { + lastID = m_popup->insertSeparator(); + } + const uint oldIndex = m_popup->count()-1; + container->form()->library()->createMenuActions(w->className(), w, m_popup, container); + if (oldIndex == (m_popup->count()-1)) { +// for (uint i=oldIndex; i<m_popup->count(); i++) { +// int id = m_popup->idAt( i ); +// if (id!=-1) +// menuIds->append( id ); +// } + //nothing added + if (separatorNeeded) { + m_popup->removeItem( lastID ); +// menuIds->pop_back(); + } + } + } + + //show the popup at the selected widget + QPoint popupPos; + if (popupAtCursor) { + popupPos = QCursor::pos(); + } + else { + WidgetList *lst = container->form()->selectedWidgets(); + QWidget * sel_w = lst ? lst->first() : container->form()->selectedWidget(); + popupPos = sel_w ? sel_w->mapToGlobal(QPoint(sel_w->width()/2, sel_w->height()/2)) : QCursor::pos(); + } + m_insertPoint = container->widget()->mapFromGlobal(popupPos); + m_popup->exec(popupPos);//QCursor::pos()); + m_insertPoint = QPoint(); + +// QValueVector<int>::iterator it; +// for(it = menuIds->begin(); it != menuIds->end(); ++it) +// m_popup->removeItem(*it); +} + +void +FormManager::buddyChosen(int id) +{ + if(!m_menuWidget) + return; + QLabel *label = static_cast<QLabel*>((QWidget*)m_menuWidget); + + if(id == MenuNoBuddy) + { + label->setBuddy(0); + return; + } + + ObjectTreeItem *item = activeForm()->objectTree()->lookup(m_popup->text(id)); + if(!item || !item->widget()) + return; + label->setBuddy(item->widget()); +} + +void +FormManager::menuSignalChosen(int id) +{ + if (m_options & HideSignalSlotConnections) + return; + + //if(!m_menuWidget) + // return; + if(m_drawingSlot && m_sigSlotMenu) + { + if( m_connection->receiver().isNull() ) + m_connection->setSignal(m_sigSlotMenu->text(id)); + else + { + m_connection->setSlot(m_sigSlotMenu->text(id)); + kdDebug() << "Finished creating the connection: sender=" << m_connection->sender() << "; signal=" << m_connection->signal() << + "; receiver=" << m_connection->receiver() << "; slot=" << m_connection->slot() << endl; + emit connectionCreated(activeForm(), *m_connection); + stopCreatingConnection(); + } + } + else if(m_menuWidget) + emit createFormSlot(m_active, m_menuWidget->name(), m_popup->text(id)); +} + +void +FormManager::slotConnectionCreated(Form *form, Connection &connection) +{ + if (m_options & HideSignalSlotConnections) + return; + if(!form) + return; + + Connection *c = new Connection(connection); + form->connectionBuffer()->append(c); +} + +void +FormManager::layoutHBox() +{ + createLayout(Container::HBox); +} + +void +FormManager::layoutVBox() +{ + createLayout(Container::VBox); +} + +void +FormManager::layoutGrid() +{ + createLayout(Container::Grid); +} + +void +FormManager::layoutHSplitter() +{ + createLayout(Container::HSplitter); +} + +void +FormManager::layoutVSplitter() +{ + createLayout(Container::VSplitter); +} + +void +FormManager::layoutHFlow() +{ + createLayout(Container::HFlow); +} + +void +FormManager::layoutVFlow() +{ + createLayout(Container::VFlow); +} + +void +FormManager::createLayout(int layoutType) +{ + WidgetList *list = m_active->selectedWidgets(); + // if only one widget is selected (a container), we modify its layout + if (list->isEmpty()) {//sanity check + kdWarning() << "FormManager::createLayout(): list is empty!" << endl; + return; + } + if(list->count() == 1) + { + ObjectTreeItem *item = m_active->objectTree()->lookup(list->first()->name()); + if(!item || !item->container() || !m_propSet->contains("layout")) + return; + (*m_propSet)["layout"] = Container::layoutTypeToString(layoutType); + return; + } + + QWidget *parent = list->first()->parentWidget(); + for(QWidget *w = list->first(); w; w = list->next()) + { + kdDebug() << "comparing widget " << w->name() << " whose parent is " << w->parentWidget()->name() << " insteaed of " << parent->name() << endl; + if(w->parentWidget() != parent) + { + KMessageBox::sorry(m_active->widget()->topLevelWidget(), i18n("<b>Cannot create the layout.</b>\n" + "All selected widgets must have the same parent.")); + kdDebug() << "FormManager::createLayout() widgets don't have the same parent widget" << endl; + return; + } + } + + KCommand *com = new CreateLayoutCommand(layoutType, *list, m_active); + m_active->addCommand(com, true); +} + +void +FormManager::breakLayout() +{ + if(!activeForm() || !activeForm()->objectTree()) + return; + + Container *container = activeForm()->activeContainer(); + QCString c( container->widget()->className() ); + + if((c == "Grid") || (c == "VBox") || (c == "HBox") || (c == "HFlow") || (c == "VFlow")) + { + KCommand *com = new BreakLayoutCommand(container); + m_active->addCommand(com, true); + } + else // normal container + { + if(activeForm()->selectedWidgets()->count() == 1) + (*m_propSet)["layout"] = "NoLayout"; + else + container->setLayout(Container::NoLayout); + } +} + +void +FormManager::showPropertySet(WidgetPropertySet *set, bool forceReload, const QCString& propertyToSelect) +{ + if (m_objectBlockingPropertyEditorUpdating) + return; + +/*unused if(m_editor) { + if (propertyToSelect.isEmpty() && forceReload) + m_editor->changeSet(set ? set->set() : 0, propertyToSelect); + else + m_editor->changeSet(set ? set->set() : 0); + }*/ + + emit propertySetSwitched(set ? set->set(): 0, /*preservePrevSelection*/forceReload, propertyToSelect); +} + +void +FormManager::blockPropertyEditorUpdating(void *blockingObject) +{ + if (!blockingObject || m_objectBlockingPropertyEditorUpdating) + return; + m_objectBlockingPropertyEditorUpdating = blockingObject; +} + +void +FormManager::unblockPropertyEditorUpdating(void *blockingObject, WidgetPropertySet *set) +{ + if (!blockingObject || m_objectBlockingPropertyEditorUpdating!=blockingObject) + return; + + m_objectBlockingPropertyEditorUpdating = 0; + showPropertySet(set, true/*forceReload*/); +} + +void +FormManager::editTabOrder() +{ + if(!activeForm() || !activeForm()->objectTree()) + return; + QWidget *topLevel = m_active->widget()->topLevelWidget(); + TabStopDialog dlg(topLevel); + //const bool oldAutoTabStops = m_active->autoTabStops(); + if (dlg.exec(m_active) == QDialog::Accepted) { + //inform about changing "autoTabStop" property + // -- this will be received eg. by Kexi, so custom "autoTabStop" property can be updated + emit autoTabStopsSet(m_active, dlg.autoTabStops()); + //force set dirty + emit dirty(m_active, true); + } +} + +void +FormManager::slotStyle() +{ + if(!activeForm()) + return; + + KSelectAction *m_style = (KSelectAction*)m_collection->action("change_style", "KSelectAction"); + QString style = m_style->currentText(); + activeForm()->widget()->setStyle( style); + + QObjectList *l = activeForm()->widget()->queryList( "QWidget" ); + for(QObject *o = l->first(); o; o = l->next()) + (static_cast<QWidget*>(o))->setStyle( style ); + delete l; +} + +void +FormManager::editFormPixmapCollection() +{ + if(!activeForm() || !activeForm()->objectTree()) + return; + + PixmapCollectionEditor dialog(activeForm()->pixmapCollection(), activeForm()->widget()->topLevelWidget()); + dialog.exec(); +} + +void +FormManager::editConnections() +{ + if (m_options & HideSignalSlotConnections) + return; + if(!activeForm() || !activeForm()->objectTree()) + return; + + ConnectionDialog dialog(activeForm()->widget()->topLevelWidget()); + dialog.exec(activeForm()); +} + +void +FormManager::alignWidgets(int type) +{ + if(!activeForm() || !activeForm()->objectTree() || (activeForm()->selectedWidgets()->count() < 2)) + return; + + QWidget *parentWidget = activeForm()->selectedWidgets()->first()->parentWidget(); + + for(QWidget *w = activeForm()->selectedWidgets()->first(); w; w = activeForm()->selectedWidgets()->next()) + { + if(w->parentWidget() != parentWidget) + { + kdDebug() << "FormManager::alignWidgets() type ==" << type << " widgets don't have the same parent widget" << endl; + return; + } + } + + KCommand *com = new AlignWidgetsCommand(type, *(activeForm()->selectedWidgets()), activeForm()); + activeForm()->addCommand(com, true); +} + +void +FormManager::alignWidgetsToLeft() +{ + alignWidgets(AlignWidgetsCommand::AlignToLeft); +} + +void +FormManager::alignWidgetsToRight() +{ + alignWidgets(AlignWidgetsCommand::AlignToRight); +} + +void +FormManager::alignWidgetsToTop() +{ + alignWidgets(AlignWidgetsCommand::AlignToTop); +} + +void +FormManager::alignWidgetsToBottom() +{ + alignWidgets(AlignWidgetsCommand::AlignToBottom); +} + +void +FormManager::adjustWidgetSize() +{ + if(!activeForm() || !activeForm()->objectTree()) + return; + + KCommand *com = new AdjustSizeCommand(AdjustSizeCommand::SizeToFit, *(activeForm()->selectedWidgets()), activeForm()); + activeForm()->addCommand(com, true); +} + +void +FormManager::alignWidgetsToGrid() +{ + if(!activeForm() || !activeForm()->objectTree()) + return; + + KCommand *com = new AlignWidgetsCommand(AlignWidgetsCommand::AlignToGrid, *(activeForm()->selectedWidgets()), activeForm()); + activeForm()->addCommand(com, true); +} + +void +FormManager::adjustSizeToGrid() +{ + if(!activeForm() || !activeForm()->objectTree()) + return; + + KCommand *com = new AdjustSizeCommand(AdjustSizeCommand::SizeToGrid, *(activeForm()->selectedWidgets()), activeForm()); + activeForm()->addCommand(com, true); +} + +void +FormManager::adjustWidthToSmall() +{ + if(!activeForm() || !activeForm()->objectTree()) + return; + + KCommand *com = new AdjustSizeCommand(AdjustSizeCommand::SizeToSmallWidth, *(activeForm()->selectedWidgets()), activeForm()); + activeForm()->addCommand(com, true); +} + +void +FormManager::adjustWidthToBig() +{ + if(!activeForm() || !activeForm()->objectTree()) + return; + + KCommand *com = new AdjustSizeCommand(AdjustSizeCommand::SizeToBigWidth, *(activeForm()->selectedWidgets()), activeForm()); + activeForm()->addCommand(com, true); +} + +void +FormManager::adjustHeightToSmall() +{ + if(!activeForm() || !activeForm()->objectTree()) + return; + + KCommand *com = new AdjustSizeCommand(AdjustSizeCommand::SizeToSmallHeight, *(activeForm()->selectedWidgets()), activeForm()); + activeForm()->addCommand(com, true); +} + +void +FormManager::adjustHeightToBig() +{ + if(!activeForm() || !activeForm()->objectTree()) + return; + + KCommand *com = new AdjustSizeCommand(AdjustSizeCommand::SizeToBigHeight, *(activeForm()->selectedWidgets()), activeForm()); + activeForm()->addCommand(com, true); +} + +void +FormManager::bringWidgetToFront() +{ + if(!activeForm() || !activeForm()->objectTree()) + return; + + for(QWidget *w = activeForm()->selectedWidgets()->first(); w; w = activeForm()->selectedWidgets()->next()) + w->raise(); +} + +void +FormManager::sendWidgetToBack() +{ + if(!activeForm() || !activeForm()->objectTree()) + return; + + for(QWidget *w = activeForm()->selectedWidgets()->first(); w; w = activeForm()->selectedWidgets()->next()) + w->lower(); +} + +void +FormManager::selectAll() +{ + if(!activeForm() || !activeForm()->objectTree()) + return; + + activeForm()->selectFormWidget(); + uint count = activeForm()->objectTree()->children()->count(); + for(ObjectTreeItem *it = activeForm()->objectTree()->children()->first(); it; + it = activeForm()->objectTree()->children()->next(), count--) + { + activeForm()->setSelectedWidget(it->widget(), /*add*/true, /*raise*/false, /*moreWillBeSelected*/count>1); + } +} + +void +FormManager::clearWidgetContent() +{ + if(!activeForm() || !activeForm()->objectTree()) + return; + + for(QWidget *w = activeForm()->selectedWidgets()->first(); w; w = activeForm()->selectedWidgets()->next()) + activeForm()->library()->clearWidgetContent(w->className(), w); +} + +void +FormManager::deleteWidgetLater( QWidget *w ) +{ + w->hide(); + w->reparent(0, WType_TopLevel, QPoint(0,0)); + m_deleteWidgetLater_list.append( w ); + m_deleteWidgetLater_timer.start( 100, true ); +} + +void +FormManager::deleteWidgetLaterTimeout() +{ + m_deleteWidgetLater_list.clear(); +} + +void +FormManager::showFormUICode() +{ +#ifdef KEXI_DEBUG_GUI + if(!activeForm()) + return; + + QString uiCode; + if (!FormIO::saveFormToString(activeForm(), uiCode, 3)) { + //! @todo show err? + return; + } + + if (!m_uiCodeDialog) { + m_uiCodeDialog = new KDialogBase(0, "uiwindow", true, i18n("Form's UI Code"), + KDialogBase::Close, KDialogBase::Close); + m_uiCodeDialog->resize(700, 600); + QVBox *box = m_uiCodeDialog->makeVBoxMainWidget(); + KTabWidget* tab = new KTabWidget(box); + + m_currentUICodeDialogEditor = new KTextEdit(QString::null, QString::null, tab); + tab->addTab( m_currentUICodeDialogEditor, i18n("Current")); + m_currentUICodeDialogEditor->setReadOnly(true); + QFont f( m_currentUICodeDialogEditor->font() ); + f.setFamily("courier"); + m_currentUICodeDialogEditor->setFont(f); + m_currentUICodeDialogEditor->setTextFormat(Qt::PlainText); + + m_originalUICodeDialogEditor = new KTextEdit(QString::null, QString::null, tab); + tab->addTab( m_originalUICodeDialogEditor, i18n("Original")); + m_originalUICodeDialogEditor->setReadOnly(true); + m_originalUICodeDialogEditor->setFont(f); + m_originalUICodeDialogEditor->setTextFormat(Qt::PlainText); + } + m_currentUICodeDialogEditor->setText( uiCode ); + //indent and set our original doc as well: + QDomDocument doc; + doc.setContent( activeForm()->m_recentlyLoadedUICode ); + m_originalUICodeDialogEditor->setText( doc.toString( 3 ) ); + m_uiCodeDialog->show(); +#endif +} + +void +FormManager::slotSettingsChanged(int category) +{ + if (category==KApplication::SETTINGS_SHORTCUTS) { + m_contextMenuKey = KGlobalSettings::contextMenuKey(); + } +} + +void +FormManager::emitWidgetSelected( KFormDesigner::Form* form, bool multiple ) +{ + enableFormActions(); + // Enable edit actions + enableAction("edit_copy", true); + enableAction("edit_cut", true); + enableAction("edit_delete", true); + enableAction("clear_contents", true); + + // 'Align Widgets' menu + enableAction("align_menu", multiple); + enableAction("align_to_left", multiple); + enableAction("align_to_right", multiple); + enableAction("align_to_top", multiple); + enableAction("align_to_bottom", multiple); + + enableAction("adjust_size_menu", true); + enableAction("adjust_width_small", multiple); + enableAction("adjust_width_big", multiple); + enableAction("adjust_height_small", multiple); + enableAction("adjust_height_big", multiple); + + enableAction("format_raise", true); + enableAction("format_lower", true); + + WidgetList *wlist = form->selectedWidgets(); + bool fontEnabled = false; + for (WidgetListIterator it(*wlist); it.current(); ++it) { + if (-1 != it.current()->metaObject()->findProperty("font", true)) { + fontEnabled = true; + break; + } + } + enableAction("format_font", fontEnabled); + + // If the widgets selected is a container, we enable layout actions + bool containerSelected = false; + if(!multiple) + { + KFormDesigner::ObjectTreeItem *item = 0; + if (form->selectedWidgets()->first()) + form->objectTree()->lookup( form->selectedWidgets()->first()->name() ); + if(item && item->container()) + containerSelected = true; + } + const bool twoSelected = form->selectedWidgets()->count()==2; + // Layout actions + enableAction("layout_menu", multiple || containerSelected); + enableAction("layout_hbox", multiple || containerSelected); + enableAction("layout_vbox", multiple || containerSelected); + enableAction("layout_grid", multiple || containerSelected); + enableAction("layout_hsplitter", twoSelected); + enableAction("layout_vsplitter", twoSelected); + + KFormDesigner::Container *container = activeForm() ? activeForm()->activeContainer() : 0; + if (container) + enableAction("break_layout", (container->layoutType() != KFormDesigner::Container::NoLayout)); + + emit widgetSelected(form, true); +} + +void +FormManager::emitFormWidgetSelected( KFormDesigner::Form* form ) +{ +// disableWidgetActions(); + enableAction("edit_copy", false); + enableAction("edit_cut", false); + enableAction("edit_delete", false); + enableAction("clear_contents", false); + + // Disable format functions + enableAction("align_menu", false); + enableAction("align_to_left", false); + enableAction("align_to_right", false); + enableAction("align_to_top", false); + enableAction("align_to_bottom", false); + enableAction("adjust_size_menu", false); + enableAction("format_raise", false); + enableAction("format_lower", false); + + enableAction("format_font", false); + + enableFormActions(); + + const bool twoSelected = form->selectedWidgets()->count()==2; + const bool hasChildren = !form->objectTree()->children()->isEmpty(); + + // Layout actions + enableAction("layout_menu", hasChildren); + enableAction("layout_hbox", hasChildren); + enableAction("layout_vbox", hasChildren); + enableAction("layout_grid", hasChildren); + enableAction("layout_hsplitter", twoSelected); + enableAction("layout_vsplitter", twoSelected); + enableAction("break_layout", (form->toplevelContainer()->layoutType() != KFormDesigner::Container::NoLayout)); + + emit formWidgetSelected( form ); +} + +void +FormManager::emitNoFormSelected() +{ + disableWidgetActions(); + + // Disable edit actions +// enableAction("edit_paste", false); +// enableAction("edit_undo", false); +// enableAction("edit_redo", false); + + // Disable 'Tools' actions + enableAction("pixmap_collection", false); + if (!(m_options & HideSignalSlotConnections)) + enableAction("form_connections", false); + enableAction("taborder", false); + enableAction("change_style", activeForm()!=0); + + // Disable items in 'File' + if (!(m_options & SkipFileActions)) { + enableAction("file_save", false); + enableAction("file_save_as", false); + enableAction("preview_form", false); + } + + emit noFormSelected(); +} + +void +FormManager::enableFormActions() +{ + // Enable 'Tools' actions + enableAction("pixmap_collection", true); + if (!(m_options & HideSignalSlotConnections)) + enableAction("form_connections", true); + enableAction("taborder", true); + enableAction("change_style", true); + + // Enable items in 'File' + if (!(m_options & SkipFileActions)) { + enableAction("file_save", true); + enableAction("file_save_as", true); + enableAction("preview_form", true); + } + + enableAction("edit_paste", isPasteEnabled()); + enableAction("edit_select_all", true); +} + +void +FormManager::disableWidgetActions() +{ + // Disable edit actions + enableAction("edit_copy", false); + enableAction("edit_cut", false); + enableAction("edit_delete", false); + enableAction("clear_contents", false); + + // Disable format functions + enableAction("align_menu", false); + enableAction("align_to_left", false); + enableAction("align_to_right", false); + enableAction("align_to_top", false); + enableAction("align_to_bottom", false); + enableAction("adjust_size_menu", false); + enableAction("format_raise", false); + enableAction("format_lower", false); + + enableAction("layout_menu", false); + enableAction("layout_hbox", false); + enableAction("layout_vbox", false); + enableAction("layout_grid", false); + enableAction("layout_hsplitter", false); + enableAction("layout_vsplitter", false); + enableAction("break_layout", false); +} + +void +FormManager::emitUndoEnabled(bool enabled, const QString &text) +{ + enableAction("edit_undo", enabled); + emit undoEnabled(enabled, text); +} + +void +FormManager::emitRedoEnabled(bool enabled, const QString &text) +{ + enableAction("edit_redo", enabled); + emit redoEnabled(enabled, text); +} + +void +FormManager::changeFont() +{ + if (!m_active) + return; + WidgetList *wlist = m_active->selectedWidgets(); + WidgetList widgetsWithFontProperty; + QWidget *widget; + QFont font; + bool oneFontSelected = true; + for (WidgetListIterator it(*wlist); (widget = it.current()); ++it) { + if (m_active->library()->isPropertyVisible(widget->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 = m_active->widget()->font(); + + if (1==widgetsWithFontProperty.count()) { + //single widget's settings + widget = widgetsWithFontProperty.first(); + KoProperty::Property &fontProp = m_propSet->property("font"); + if (QDialog::Accepted != KFontDialog::getFont(font, false, m_active->widget())) + return; + fontProp = font; + return; + } + //multiple widgets + int diffFlags=0; + if (QDialog::Accepted != KFontDialog::getFontDiff(font, diffFlags, false, m_active->widget()) + || 0==diffFlags) + return; + //update font + for (WidgetListIterator it(widgetsWithFontProperty); (widget = it.current()); ++it) { + 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 KoProperty::Set supports multiple selections */ + widget->setFont( prevFont ); + //temporary fix for dirty flag: + emit dirty(m_active, true); + } +} + +#include "formmanager.moc" diff --git a/kexi/formeditor/formmanager.h b/kexi/formeditor/formmanager.h new file mode 100644 index 00000000..48e00b0f --- /dev/null +++ b/kexi/formeditor/formmanager.h @@ -0,0 +1,496 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr> + Copyright (C) 2005 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. +*/ + +#ifndef FORMMANAGER_H +#define FORMMANAGER_H + +#include <qobject.h> +#include <qdom.h> +#include <qptrlist.h> +#include <qtimer.h> +#include <qguardedptr.h> +#include <qstringlist.h> + +class QWidget; +class QWorkspace; +class KPopupMenu; +class KActionCollection; +class KAction; +class KToggleAction; +class KDialogBase; +class KTextEdit; +class KXMLGUIClient; +class KMainWindow; + +namespace KoProperty { + class Editor; + class Set; + class Property; + class Widget; +} + +namespace KFormDesigner { + +class WidgetPropertySet; +class Form; +class Container; +class WidgetLibrary; +class ObjectTreeView; +class Connection; +class FormManager; +typedef QPtrList<KAction> ActionList; + +//! @internal +//static FormManager* FormManager_static = 0; + +//! A class to manage (create/load/save) Forms +/** This is Form Designer's main class, which is used by external APIs to access FormDesigner. + This is the class you have to use to integrate FormDesigner into another program. + It deals with creating, saving and loading Form, as well as widget insertion and copying. + It also ensures all the components (ObjectTreeView, Form and PropertyEditor) are synced, + and link them. + It holds the WidgetLibrary, the WidgetPropertySet, links to ObjectTreeView and PropertyEditor, + as well as the copied widget and the insert state. + **/ +class KFORMEDITOR_EXPORT FormManager : public QObject +{ + Q_OBJECT + + public: + /*! Constructs FormManager object. + Using \a options you can control manager's behaviour, see Options. */ + FormManager(QObject *parent = 0, int options = 0, const char *name = 0); + + virtual ~FormManager(); + + //! Creates widget library for supportedFactoryGroups + //! and initializes FormManager singleton. \a m should be always the same for every call. + static WidgetLibrary* createWidgetLibrary(FormManager* m, + const QStringList& supportedFactoryGroups); + + //! Access to FormManager singleton + static FormManager* self(); + + /*! Options for creating FormManager objects. + * These are really bit-flags and may be or-ed together. + */ + enum Options { HideEventsInPopupMenu = 1, SkipFileActions = 2, + HideSignalSlotConnections = 4 }; //todo + + /*! Creates all the KActions related to widget insertion, and plug them + into the \a collection. \a client XML GUI client is used to call + lib->addCustomWidgetActions(client). + These actions are automatically connected to \ref insertWidget() slot. + \return a QPtrList of the created actions. + */ + ActionList createActions(WidgetLibrary *lib, KActionCollection* collection, KXMLGUIClient *client); + + /*! Enables or disables actions \a name. + KFD uses KPart's, action collection here. + Kexi implements this to get (shared) actions defined elsewhere. */ + virtual void enableAction( const char* name, bool enable ) = 0; + + /*! \return action for \a name. @see enableAction() */ + virtual KAction* action(const char* name) = 0; + + bool isPasteEnabled(); + +// //! \return A pointer to the WidgetLibrary owned by this Manager. +// WidgetLibrary* lib() const { return m_lib; } + + //! \return A pointer to the WidgetPropertySet owned by this Manager. + WidgetPropertySet* propertySet() const { return m_propSet; } + + /*! \return true if one of the insert buttons was pressed and the forms + are ready to create a widget. */ + bool isInserting() const { return m_inserting; } + + /*! \return The name of the class being inserted, corresponding + to the menu item or the toolbar button clicked. */ + QCString selectedClass() const { return m_selectedClass; } + + /*! Sets the point where the pasted widget should be moved to. */ + void setInsertPoint(const QPoint &p); + + //! \return If we are creating a Connection by drag-and-drop or not. + bool isCreatingConnection() { return m_drawingSlot; } + + //! \return the Connection being created. + Connection* createdConnection() { return m_connection; } + + /*! Resets the Connection being created. We stay in Connection creation mode, + but we start a new connection (when the user clicks + outside of signals/slots menu). */ + void resetCreatedConnection(); + + //! Creates and display a menu with all the signals of widget \a w. + void createSignalMenu(QWidget *w); + + //! Creates and display a menu with all the slots of widget \a w. + void createSlotMenu(QWidget *w); + + //! Emits the signal \ref createFormSlot(). Used by WidgetPropertySet. + void emitCreateSlot(const QString &widget, const QString &value) + { emit createFormSlot(m_active, widget, value); } + + /*! \return The Form actually active and focused. + */ + Form* activeForm() const; + + /*! \return the Form whose toplevel widget is \a w, or 0 + if there is not or the Form is in preview mode. */ + Form* formForWidget(QWidget *w); + + /*! \return true if \a w is a toplevel widget, + ie. it is the main widget of a Form (so it should have a caption , + an icon ...) */ + bool isTopLevel(QWidget *w); + + //! \return A pointer to the KoProperty::Editor we use. + //unused KoProperty::Editor* propertyEditor() const { return m_editor; } + + /*! Shows a property set \a set in a Property Editor. + If \a buff is 0, Property Editor will be cleared. + If \a forceReload is true, the set will be reloaded even + if it's the same as previous one. + If \a propertyToSelect is not empty, an item for this name will be selected + (usable when previously there was no set visible). */ + virtual void showPropertySet(WidgetPropertySet *set, bool forceReload = false, + const QCString& propertyToSelect = QCString()); + + void blockPropertyEditorUpdating(void *blockingObject); + + void unblockPropertyEditorUpdating(void *blockingObject, WidgetPropertySet *set); + + /*! Sets the external property editor pane used by FormDesigner (it may be docked).*/ + void setEditor(KoProperty::Editor *editor); + + /*! Sets the external object tree view used by FormDesigner (it may be docked). + This function also connects appropriate signals and slots to ensure + sync with the current Form. */ + void setObjectTreeView(ObjectTreeView *treeview); + + /*! Previews the Form \a form using the widget \a w as toplevel container for this Form. */ + void previewForm(Form *form, QWidget *w, Form *toForm=0); + + /*! Adds a existing form w and changes it to a container */ + void importForm(Form *form=0, bool preview=false); + + /*! Deletes the Form \a form and removes it from our list. */ + void deleteForm(Form *form); + + /*! This function creates and displays the context menu corresponding to the widget \a w. + The menu item are disabled if necessary, and + the widget specific part is added (menu from the factory and buddy selection). */ + void createContextMenu(QWidget *w, Container *container, bool popupAtCursor = true); + + //! \return If we align widgets to grid or not. + bool snapWidgetsToGrid(); + + //! @internal used by Container + int contextMenuKey() const { return m_contextMenuKey; } + + //! @internal + void emitWidgetSelected( KFormDesigner::Form* form, bool multiple ); + //! @internal + void emitFormWidgetSelected( KFormDesigner::Form* form ); + //! @internal + void emitNoFormSelected(); + + /*! @internal + \return true is redo action is being executed. + Used in WidgetPropertySet::slotPropertyChanged() */ + bool isRedoing() const { return m_isRedoing; } + + public slots: + /*! Deletes the selected widget in active Form and all of its children. */ + void deleteWidget(); + + /*! Copies the slected widget and all its children of the active Form using an XML representation. */ + void copyWidget(); + + /*! Cuts (ie Copies and deletes) the selected widget and all its children of + the active Form using an XML representation. */ + void cutWidget(); + + /*! Pastes the XML representation of the copied or cut widget. The widget is + pasted when the user clicks the Form to + indicate the new position of the widget, or at the position of the contextual menu if there is one. */ + void pasteWidget(); + + /*! Selects all toplevel widgets in trhe current form. */ + void selectAll(); + + /*! Clears the contents of the selected widget(s) (eg for a line edit or a listview). */ + void clearWidgetContent(); + + void undo(); + void redo(); + + /*! Displays a dialog where the user can modify the tab order of the active Form, + by drag-n-drop or using up/down buttons. */ + void editTabOrder(); + + /*! Adjusts the size of the selected widget, ie resize it to its size hint. */ + void adjustWidgetSize(); + + /*! Creates a dialog to edit the \ref activeForm() PixmapCollection. */ + void editFormPixmapCollection(); + + /*! Creates a dialog to edit the Connection of \ref activeForm(). */ + void editConnections(); + + //! Lay out selected widgets using HBox layout (calls \ref CreateLayoutCommand). + void layoutHBox(); + //! Lay out selected widgets using VBox layout. + void layoutVBox(); + //! Lay out selected widgets using Grid layout. + void layoutGrid(); + //! Lay out selected widgets in an horizontal splitter + void layoutHSplitter(); + //! Lay out selected widgets in a verticak splitter + void layoutVSplitter(); + //! Lay out selected widgets using HFlow layout + void layoutHFlow(); + //! Lay out selected widgets using VFlow layout. + void layoutVFlow(); + + //! Breaks selected layout(calls \ref BreakLayoutCommand). + void breakLayout(); + + void alignWidgetsToLeft(); + void alignWidgetsToRight(); + void alignWidgetsToTop(); + void alignWidgetsToBottom(); + void alignWidgetsToGrid(); + + void adjustSizeToGrid(); + + //! Resize all selected widgets to the width of the narrowest widget. + void adjustWidthToSmall(); + + //! Resize all selected widgets to the width of the widest widget. + void adjustWidthToBig(); + + //! Resize all selected widgets to the height of the shortest widget. + void adjustHeightToSmall(); + + //! Resize all selected widgets to the height of the tallest widget. + void adjustHeightToBig(); + + void bringWidgetToFront(); + void sendWidgetToBack(); + + /*! This slot is called when the user presses a "Widget" toolbar button + or a "Widget" menu item. Prepares all Forms for + creation of a new widget (ie changes cursor ...). + */ + void insertWidget(const QCString &classname); + + /*! Stops the current widget insertion (ie unset the cursor ...). */ + void stopInsert(); + + //! Slot called when the user presses 'Pointer' icon. Switch to Default mode. + void slotPointerClicked(); + + //! Enter the Connection creation mode. + void startCreatingConnection(); + + //! Leave the Connection creation mode. + void stopCreatingConnection(); + + /*! Calls this slot when the window activated changes (eg connect + to QWorkspace::windowActivated(QWidget*)). You <b>need</b> to connect + to this slot, it will crash otherwise. + */ + void windowChanged(QWidget *w); + + //! Used to delayed widgets' deletion (in Container::deleteItem()) + void deleteWidgetLater( QWidget *w ); + + /*! For debugging purposes only: + shows a text window containing contents of .ui XML definition of the current form. */ + void showFormUICode(); + + /*! Executes font dialog and changes it for currently selected widget(s). */ + void changeFont(); + + signals: + /*! This signal is emitted as the property set switched. + If \a forceReload is true, the set needs to be reloaded even + if it's the same as previous one. */ + void propertySetSwitched(KoProperty::Set *set, bool forceReload = false, const QCString& propertyToSelect = QCString()); + + /*! This signal is emitted when any change is made to the Form \a form, + so it will need to be saved. */ + void dirty(KFormDesigner::Form *form, bool isDirty=true); + + /*! Signal emitted when a normal widget is selected inside \a form + (ie not form widget). If \a multiple is true, + then more than one widget is selected. Use this to update actions state. */ + void widgetSelected(KFormDesigner::Form *form, bool multiple); + + /*! Signal emitted when the form widget is selected inside \a form. + Use this to update actions state. */ + void formWidgetSelected(KFormDesigner::Form *form); + + /*! Signal emitted when no form (or a preview form) is selected. + Use this to update actions state. */ + void noFormSelected(); + + /*! Signal emitted when undo action activation changes. + \a text is the full text of the action (including command name). */ + void undoEnabled(bool enabled, const QString &text = QString::null); + + /*! Signal emitted when redo action activation changes. + \a text is the full text of the action (including command name). */ + void redoEnabled(bool enabled, const QString &text = QString::null); + + /*! Signal emitted when the user choose a signal in 'Events' menu + in context menu, or in 'Events' in property editor. + The code editor should then create the slot connected to this signal. */ + void createFormSlot(KFormDesigner::Form *form, const QString &widget, const QString &signal); + + /*! Signal emitted when the Connection creation by drag-and-drop ends. + \a connection is the created Connection. You should copy it, + because it is deleted just after the signal is emitted. */ + void connectionCreated(KFormDesigner::Form *form, KFormDesigner::Connection &connection); + + /*! Signal emitted when the Connection creation by drag-and-drop is aborted by user. */ + void connectionAborted(KFormDesigner::Form *form); + + /*! Signal emitted when "autoTabStops" is changed. */ + void autoTabStopsSet(KFormDesigner::Form *form, bool set); + + /*! Signal emitted before the form gets finally deleted. \a form is still a valid pointer, + but the widgets inside the form are in unknown state. */ + void aboutToDeleteForm(KFormDesigner::Form *form); + + /*! Signal emitted when new form gets created. */ + void formCreated(KFormDesigner::Form *form); + + protected slots: + void deleteWidgetLaterTimeout(); + + /*! Slot called when a buddy is chosen in the buddy list. Sets the label buddy. */ + void buddyChosen(int id); + + /*! Slot called when the user chooses an item in signal (or slot) menu. + The \ref createdConnection() is updated, and the connection created + (for the signal menu). */ + void menuSignalChosen(int id); + + /*! Slot called when the user changes current style using combbox in toolbar or menu. */ + void slotStyle(); + + void slotConnectionCreated(KFormDesigner::Form*, KFormDesigner::Connection&); + + void slotSettingsChanged(int category); + + protected: + /*! Inits the Form, adds it to m_forms, and conects slots. */ + void initForm(Form *form); + +#if 0 + /*! Default implementation just calls FormIO::loadFormFromDom(). + Change this if you need to handle, eg. custom UI XML tags, as in Kexi's Form Designer. */ + virtual bool loadFormFromDomInternal(Form *form, QWidget *container, QDomDocument &inBuf); + + /*! Default implementation just calls FormIO::saveFormToString(). + Change this if you need to handle, eg. custom UI XML tags, as in Kexi's Form Designer. */ + virtual bool saveFormToStringInternal(Form *form, QString &dest, int indent = 0); +#endif + /*! Function called by the "Lay out in..." menu items. It creates a layout from the + currently selected widgets (that must have the same parent). + Calls \ref CreateLayoutCommand. */ + void createLayout(int layoutType); + + /*! Function called by all other AlignWidgets*() function. Calls \ref AlignWidgetsCommand. */ + void alignWidgets(int type); + + void enableFormActions(); + void disableWidgetActions(); + void emitUndoEnabled(bool enabled, const QString &text); + void emitRedoEnabled(bool enabled, const QString &text); + + /*! True if emitSelectionSignals() updates property set so showPropertySet() will + not be needed in windowChanged(). False by default. Set to true in KexiFormManager. */ + bool m_emitSelectionSignalsUpdatesPropertySet : 1; + + private: + static FormManager* _self; + + //! Enum for menu items indexes + enum { MenuTitle = 200, MenuCopy, MenuCut, MenuPaste, MenuDelete, MenuHBox = 301, + MenuVBox, MenuGrid, MenuHSplitter, MenuVSplitter, MenuNoBuddy = 501 }; + + WidgetPropertySet *m_propSet; +// WidgetLibrary *m_lib; + QGuardedPtr<KoProperty::Editor> m_editor; + QGuardedPtr<ObjectTreeView> m_treeview; + // Forms + QPtrList<Form> m_forms; + QPtrList<Form> m_preview; + QGuardedPtr<Form> m_active; + + // Copy/Paste + QDomDocument m_domDoc; + KPopupMenu *m_popup; + QPoint m_insertPoint; + QGuardedPtr<QWidget> m_menuWidget; + + // Insertion + bool m_inserting; + QCString m_selectedClass; + + // Connection stuff + bool m_drawingSlot; + Connection *m_connection; + KPopupMenu *m_sigSlotMenu; + + // Actions + KActionCollection *m_collection; + KToggleAction *m_pointer, *m_dragConnection, *m_snapToGrid; + + //! Used to delayed widgets deletion + QTimer m_deleteWidgetLater_timer; + QPtrList<QWidget> m_deleteWidgetLater_list; + +#ifdef KEXI_DEBUG_GUI + KDialogBase *m_uiCodeDialog; + KTextEdit *m_currentUICodeDialogEditor; + KTextEdit *m_originalUICodeDialogEditor; +#endif + + int m_options; //!< @see Options enum + int m_contextMenuKey; //!< Id of context menu key (cached) + + void *m_objectBlockingPropertyEditorUpdating; + bool m_isRedoing : 1; + + friend class PropertyCommand; + friend class GeometryPropertyCommand; + friend class CutWidgetCommand; + friend class Form; +}; + +} + +#endif diff --git a/kexi/formeditor/kdevelop_plugin/Makefile.am b/kexi/formeditor/kdevelop_plugin/Makefile.am new file mode 100644 index 00000000..62507e9c --- /dev/null +++ b/kexi/formeditor/kdevelop_plugin/Makefile.am @@ -0,0 +1,21 @@ +include $(top_srcdir)/kexi/Makefile.global + +INCLUDES = -I$(top_srcdir)/kexi/formeditor -I$(top_srcdir)/kexi/core $(all_includes) +METASOURCES = AUTO + +# KFormDesigner KDevelop plugin +kde_module_LTLIBRARIES = libkformdesigner_kdev_part.la + +libkformdesigner_kdev_part_la_SOURCES = kfd_kdev_part.cpp +libkformdesigner_kdev_part_la_LDFLAGS = -module $(KDE_PLUGIN) $(VER_INFO) $(all_libraries) +libkformdesigner_kdev_part_la_LIBADD = $(top_builddir)/kexi/formeditor/libkformdesigner.la \ + -lkinterfacedesigner $(LIB_KFILE) + +# this is where the desktop file will go +partdesktopdir = $(kde_servicesdir) +partdesktop_DATA = kformdesigner_kdev_part.desktop + +# this is where the part's XML-GUI resource file goes +partrcdir = $(kde_datadir)/kformdesigner_kdev_part +partrc_DATA = kformdesigner_part.rc kformdesigner_part_shell.rc + diff --git a/kexi/formeditor/kdevelop_plugin/kfd_kdev_part.cpp b/kexi/formeditor/kdevelop_plugin/kfd_kdev_part.cpp new file mode 100644 index 00000000..c3b6e448 --- /dev/null +++ b/kexi/formeditor/kdevelop_plugin/kfd_kdev_part.cpp @@ -0,0 +1,694 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr> + + 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 <qworkspace.h> +#include <qdockarea.h> +#include <qdockwindow.h> +#include <qhbox.h> +#include <qpainter.h> +#include <qevent.h> +#include <qobjectlist.h> + +#include <kdeversion.h> +#include <kaction.h> +#include <kinstance.h> +#include <klocale.h> +#include <kaboutdata.h> +#include <kdebug.h> +#include <kstdaction.h> +#include <kapplication.h> +#include <kiconloader.h> +#include <kfiledialog.h> +#include <klibloader.h> +#include <kmessagebox.h> + +#include "form.h" +#include "formIO.h" +#include "objecttree.h" +#include "container.h" +#include "formmanager.h" +#include "objecttreeview.h" + +#include "kfd_kdev_part.h" + +#define ENABLE_ACTION(name, enable) \ + if(actionCollection()->action( name )) \ + actionCollection()->action( name )->setEnabled( enable ) + +KInstance *KFDFactory::m_instance = 0L; + +KFDFactory::KFDFactory() +: KParts::Factory(0, "libkformdesigner_kdev_part") +{} + +KFDFactory::~KFDFactory() +{ + if (m_instance) + { + delete m_instance->aboutData(); + delete m_instance; + } + + m_instance = 0; +} + +KParts::Part* +KFDFactory::createPartObject( QWidget *parentWidget, const char *, QObject *, const char *name, + const char *classname, const QStringList &args) +{ + bool readOnly = (classname == "KParts::ReadOnlyPart"); + KFormDesignerKDevPart *part = new KFormDesignerKDevPart(parentWidget, name, readOnly, args); + return part; +} + +KInstance* +KFDFactory::instance() +{ + if (!m_instance) + m_instance = new KInstance(aboutData()); + return m_instance; +} + +KAboutData* +KFDFactory::aboutData() +{ + KAboutData *about = new KAboutData("kformdesigner_kdev_part", I18N_NOOP("Form Designer Part"), "0.3"); + return about; +} + +// copied from kfd_part.cpp +class KFDPart_FormManager : public KFormDesigner::FormManager +{ + public: + /*! Constructs FormManager object. + See WidgetLibrary's constructor documentation for information about + \a supportedFactoryGroups parameter. + Using \a options you can control manager's behaviour, see \ref Options. */ + KFDPart_FormManager(KFormDesignerPart *part, int options = 0, const char *name = 0) + : KFormDesigner::FormManager(part, options, name) + , m_part(part) + { + } + + virtual KAction* action( const char* name) + { + return m_part->actionCollection()->action( name ); + } + + virtual void enableAction( const char* name, bool enable ) { + if(m_part->actionCollection()->action( name )) + m_part->actionCollection()->action( name )->setEnabled( enable ); + } + + KFormDesignerPart *m_part; +}; + +////////////// + +KFormDesigner::WidgetLibrary* KFormDesignerKDevPart::static_formsLibrary = 0L; + +KFormDesignerKDevPart::KFormDesignerKDevPart(QWidget *parent, const char *name, bool readOnly, const QStringList &args) +: Designer(parent, name), m_count(0) +{ + setInstance(KFDFactory::instance()); + instance()->iconLoader()->addAppDir("kexi"); + instance()->iconLoader()->addAppDir("kformdesigner"); + + setReadWrite(!readOnly); + m_uniqueFormMode = true; + m_openingFile = false; + + if(!args.grep("multipleMode").isEmpty()) + setUniqueFormMode(false); + m_inShell = (!args.grep("shell").isEmpty()); + + QHBox *container = new QHBox(parent, "kfd_container_widget"); + container->setFocusPolicy(QWidget::ClickFocus); + + m_workspace = new QWorkspace(container, "kfd_workspace"); + m_workspace->show(); + QStringList supportedFactoryGroups; +/* @todo add configuration for supported factory groups */ + static_formsLibrary = KFormDesigner::FormManager::createWidgetLibrary( + new KFDPart_FormManager(this, 0, "kfd_manager"), supportedFactoryGroups ); + + if(!readOnly) + { + QDockArea *dockArea = new QDockArea(Vertical, QDockArea::Reverse, container, "kfd_part_dockarea"); + + QDockWindow *dockTree = new QDockWindow(dockArea); + KFormDesigner::ObjectTreeView *view = new KFormDesigner::ObjectTreeView(dockTree); + dockTree->setWidget(view); + dockTree->setCaption(i18n("Objects")); + dockTree->setResizeEnabled(true); + dockTree->setFixedExtentWidth(256); + + QDockWindow *dockEditor = new QDockWindow(dockArea); + KoProperty::Editor *editor = new KoProperty::Editor(dockEditor); + dockEditor->setWidget(editor); + dockEditor->setCaption(i18n("Properties")); + dockEditor->setResizeEnabled(true); + + KFormDesigner::FormManager::self()->setEditor(editor); + KFormDesigner::FormManager::self()->setObjectTreeView(view); + + setupActions(); + setModified(false); + + // action stuff + connect(KFormDesigner::FormManager::self(), SIGNAL(widgetSelected(KFormDesigner::Form*, bool)), SLOT(slotWidgetSelected(KFormDesigner::Form*, bool))); + connect(KFormDesigner::FormManager::self(), SIGNAL(formWidgetSelected(KFormDesigner::Form*)), SLOT(slotFormWidgetSelected(KFormDesigner::Form*))); + connect(KFormDesigner::FormManager::self(), SIGNAL(noFormSelected()), SLOT(slotNoFormSelected())); + connect(KFormDesigner::FormManager::self(), SIGNAL(undoEnabled(bool, const QString&)), SLOT(setUndoEnabled(bool, const QString&))); + connect(KFormDesigner::FormManager::self(), SIGNAL(redoEnabled(bool, const QString&)), SLOT(setRedoEnabled(bool, const QString&))); + + connect(KFormDesigner::FormManager::self(), SIGNAL(dirty(KFormDesigner::Form*, bool)), this, SLOT(slotFormModified(KFormDesigner::Form*, bool))); + + connect(KFormDesigner::FormManager::self(), SIGNAL(createFormSlot(KFormDesigner::Form*, const QString&, const QString&)), + this, SLOT(slotCreateFormSlot(KFormDesigner::Form*, const QString&, const QString &))); + } + + container->show(); + setWidget(container); + connect(m_workspace, SIGNAL(windowActivated(QWidget*)), KFormDesigner::FormManager::self(), SLOT(windowChanged(QWidget*))); + slotNoFormSelected(); +} + +KFormDesigner::WidgetLibrary* KFormDesignerKDevPart::formsLibrary() +{ + return static_formsLibrary; +} + +void +KFormDesignerKDevPart::setupActions() +{ + KStdAction::open(this, SLOT(open()), actionCollection()); + KStdAction::openNew(this, SLOT(createBlankForm()), actionCollection()); + KStdAction::save(this, SLOT(save()), actionCollection()); + KStdAction::saveAs(this, SLOT(saveAs()), actionCollection()); + KStdAction::cut(KFormDesigner::FormManager::self(), SLOT(cutWidget()), actionCollection()); + KStdAction::copy(KFormDesigner::FormManager::self(), SLOT(copyWidget()), actionCollection()); + KStdAction::paste(KFormDesigner::FormManager::self(), SLOT(pasteWidget()), actionCollection()); + KStdAction::undo(KFormDesigner::FormManager::self(), SLOT(undo()), actionCollection()); + KStdAction::redo(KFormDesigner::FormManager::self(), SLOT(redo()), actionCollection()); + KStdAction::selectAll(KFormDesigner::FormManager::self(), SLOT(selectAll()), actionCollection()); + new KAction(i18n("Clear Widget Contents"), "editclear", KShortcut(0), KFormDesigner::FormManager::self(), SLOT(clearWidgetContent()), actionCollection(), "clear_contents"); + new KAction(i18n("Delete Widget"), "editdelete", KShortcut(0), KFormDesigner::FormManager::self(), SLOT(deleteWidget()), actionCollection(), "edit_delete"); + new KAction(i18n("Preview Form"), "filequickprint", "Ctrl+T", this, SLOT(slotPreviewForm()), actionCollection(), "preview_form"); + new KAction(i18n("Edit Tab Order"), "tab_order", KShortcut(0), KFormDesigner::FormManager::self(), SLOT(editTabOrder()), actionCollection(), "taborder"); + new KAction(i18n("Edit Pixmap Collection"), "icons", KShortcut(0), KFormDesigner::FormManager::self(), SLOT(editFormPixmapCollection()), actionCollection(), "pixmap_collection"); + new KAction(i18n("Edit Form Connections"), "connections", KShortcut(0), KFormDesigner::FormManager::self(), SLOT(editConnections()), actionCollection(), "form_connections"); + + new KAction(i18n("Lay Out Widgets &Horizontally"), QString::null, KShortcut(0), KFormDesigner::FormManager::self(), SLOT(layoutHBox()), actionCollection(), "layout_hbox"); + new KAction(i18n("Lay Out Widgets &Vertically"), QString::null, KShortcut(0), KFormDesigner::FormManager::self(), SLOT(layoutVBox()), actionCollection(), "layout_vbox"); + new KAction(i18n("Lay Out Widgets in &Grid"), QString::null, KShortcut(0), KFormDesigner::FormManager::self(), SLOT(layoutGrid()), actionCollection(), "layout_grid"); + new KAction(i18n("&Break Layout"), QString::null, KShortcut(0), KFormDesigner::FormManager::self(), SLOT(breakLayout()), actionCollection(), "break_layout"); + + new KAction(i18n("Bring Widget to Front"), "raise", KShortcut(0), KFormDesigner::FormManager::self(), SLOT(bringWidgetToFront()), actionCollection(), "format_raise"); + new KAction(i18n("Send Widget to Back"), "lower", KShortcut(0), KFormDesigner::FormManager::self(), SLOT(sendWidgetToBack()), actionCollection(), "format_lower"); + + KActionMenu *alignMenu = new KActionMenu(i18n("Align Widgets' Positions"), "aopos2grid", actionCollection(), "align_menu"); + alignMenu->insert( new KAction(i18n("To Left"), "aoleft", KShortcut(0), KFormDesigner::FormManager::self(), SLOT(alignWidgetsToLeft()), actionCollection(), "align_to_left") ); + alignMenu->insert( new KAction(i18n("To Right"), "aoright", KShortcut(0), KFormDesigner::FormManager::self(), SLOT(alignWidgetsToRight()), actionCollection(), "align_to_right") ); + alignMenu->insert( new KAction(i18n("To Top"), "aotop", KShortcut(0), KFormDesigner::FormManager::self(), SLOT(alignWidgetsToTop()), actionCollection(), "align_to_top") ); + alignMenu->insert( new KAction(i18n("To Bottom"), "aobottom", KShortcut(0), KFormDesigner::FormManager::self(), SLOT(alignWidgetsToBottom()), actionCollection(), "align_to_bottom") ); + alignMenu->insert( new KAction(i18n("To Grid"), "aopos2grid", KShortcut(0), KFormDesigner::FormManager::self(), SLOT(alignWidgetsToGrid()), actionCollection(), "align_to_grid") ); + + KActionMenu *sizeMenu = new KActionMenu(i18n("Adjust Widgets' Sizes"), "aogrid", actionCollection(), "adjust_size_menu"); + sizeMenu->insert( new KAction(i18n("To Fit"), "aofit", KShortcut(0), KFormDesigner::FormManager::self(), SLOT(adjustWidgetSize()), actionCollection(), "adjust_to_fit") ); + sizeMenu->insert( new KAction(i18n("To Grid"), "aogrid", KShortcut(0), KFormDesigner::FormManager::self(), SLOT(adjustSizeToGrid()), actionCollection(), "adjust_size_grid") ); + sizeMenu->insert( new KAction(i18n("To Shortest"), "aoshortest", KShortcut(0), KFormDesigner::FormManager::self(), SLOT(adjustHeightToSmall()), actionCollection(), "adjust_height_small") ); + sizeMenu->insert( new KAction(i18n("To Tallest"), "aotallest", KShortcut(0), KFormDesigner::FormManager::self(), SLOT(adjustHeightToBig()), actionCollection(), "adjust_height_big") ); + sizeMenu->insert( new KAction(i18n("To Narrowest"), "aonarrowest", KShortcut(0), KFormDesigner::FormManager::self(), SLOT(adjustWidthToSmall()), actionCollection(), "adjust_width_small") ); + sizeMenu->insert( new KAction(i18n("To Widest"), "aowidest", KShortcut(0), KFormDesigner::FormManager::self(), SLOT(adjustWidthToBig()), actionCollection(), "adjust_width_big") ); + + if(m_inShell) + setXMLFile("kformdesigner_part_shell.rc"); + else + setXMLFile("kformdesigner_part.rc"); + KFormDesigner::FormManager::self()->createActions(formsLibrary(), actionCollection(), this); +} + +void +KFormDesignerKDevPart::createBlankForm() +{ + if(KFormDesigner::FormManager::self()->activeForm() && m_uniqueFormMode) + { + m_openingFile = true; + closeURL(); + m_openingFile = false; + } + + if(m_uniqueFormMode && KFormDesigner::FormManager::self()->activeForm() && !KFormDesigner::FormManager::self()->activeForm()->isModified() && KFormDesigner::FormManager::self()->activeForm()->filename().isNull()) + return; // active form is already a blank one + + QString n = i18n("Form") + QString::number(++m_count); + Form *form = new Form(formsLibrary(), n.latin1()); + FormWidgetBase *w = new FormWidgetBase(this, m_workspace, n.latin1()); + + w->setCaption(n); + w->setIcon(SmallIcon("form")); + w->resize(350, 300); + w->show(); + w->setFocus(); + + form->createToplevel(w, w); + KFormDesigner::FormManager::self()->importForm(form); +} + +void +KFormDesignerKDevPart::open() +{ + m_openingFile = true; + KURL url = KFileDialog::getOpenURL("::kformdesigner", i18n("*.ui|Qt Designer UI Files"), m_workspace->topLevelWidget()); + if(!url.isEmpty()) + ReadWritePart::openURL(url); + m_openingFile = false; +} + +bool +KFormDesignerKDevPart::openFile() +{ + Form *form = new Form(formsLibrary()); + FormWidgetBase *w = new FormWidgetBase(this, m_workspace); + form->createToplevel(w, w); + + if(!KFormDesigner::FormIO::loadForm(form, w, m_file)) + { + delete form; + delete w; + return false; + } + + w->show(); + KFormDesigner::FormManager::self()->importForm(form, !isReadWrite()); + return true; +} + +bool +KFormDesignerKDevPart::saveFile() +{ + KFormDesigner::FormIO::saveForm(KFormDesigner::FormManager::self()->activeForm(), m_file); + return true; +} + +void +KFormDesignerKDevPart::saveAs() +{ + KURL url = KFileDialog::getSaveURL("::kformdesigner", i18n("*.ui|Qt Designer UI Files"), m_workspace); + if(url.isEmpty()) + return; + else + ReadWritePart::saveAs(url); +} + +bool +KFormDesignerKDevPart::closeForm(Form *form) +{ + int res = KMessageBox::warningYesNoCancel( m_workspace->topLevelWidget(), + i18n( "The form \"%1\" has been modified.\n" + "Do you want to save your changes or discard them?" ).arg( form->objectTree()->name() ), + i18n( "Close Form" ), KStdGuiItem::save(), KStdGuiItem::discard() ); + + if(res == KMessageBox::Yes) + save(); + + return (res != KMessageBox::Cancel); +} + +bool +KFormDesignerKDevPart::closeForms() +{ + QWidgetList list = m_workspace->windowList(QWorkspace::CreationOrder); + for(QWidget *w = list.first(); w; w = list.next()) + if(w->close() == false) + return false; + + return true; +} + +bool +KFormDesignerKDevPart::closeURL() +{ + if(!KFormDesigner::FormManager::self()->activeForm()) + return true; + + if(m_uniqueFormMode || !m_openingFile) + return closeForms(); + + return true; +} + +void +KFormDesignerKDevPart::slotFormModified(Form *, bool isDirty) +{ + setModified(isDirty); +} + +void +KFormDesignerKDevPart::slotPreviewForm() +{ + if(!KFormDesigner::FormManager::self()->activeForm()) + return; + + FormWidgetBase *w = new FormWidgetBase(this, m_workspace); + KFormDesigner::FormManager::self()->previewForm(KFormDesigner::FormManager::self()->activeForm(), w); +} + +void +KFormDesignerKDevPart::slotWidgetSelected(Form *form, bool multiple) +{ + enableFormActions(); + // Enable edit actions + ENABLE_ACTION("edit_copy", true); + ENABLE_ACTION("edit_cut", true); + ENABLE_ACTION("edit_delete", true); + ENABLE_ACTION("clear_contents", true); + + // 'Align Widgets' menu + ENABLE_ACTION("align_menu", multiple); + ENABLE_ACTION("align_to_left", multiple); + ENABLE_ACTION("align_to_right", multiple); + ENABLE_ACTION("align_to_top", multiple); + ENABLE_ACTION("align_to_bottom", multiple); + + ENABLE_ACTION("adjust_size_menu", true); + ENABLE_ACTION("adjust_width_small", multiple); + ENABLE_ACTION("adjust_width_big", multiple); + ENABLE_ACTION("adjust_height_small", multiple); + ENABLE_ACTION("adjust_height_big", multiple); + + ENABLE_ACTION("format_raise", true); + ENABLE_ACTION("format_lower", true); + + // If the widgets selected is a container, we enable layout actions + if(!multiple) + { + KFormDesigner::ObjectTreeItem *item = form->objectTree()->lookup( form->selectedWidgets()->first()->name() ); + if(item && item->container()) + multiple = true; + } + // Layout actions + ENABLE_ACTION("layout_hbox", multiple); + ENABLE_ACTION("layout_vbox", multiple); + ENABLE_ACTION("layout_grid", multiple); + + KFormDesigner::Container *container = KFormDesigner::FormManager::self()->activeForm()->activeContainer(); + ENABLE_ACTION("break_layout", (container->layoutType() != KFormDesigner::Container::NoLayout)); +} + +void +KFormDesignerKDevPart::slotFormWidgetSelected(Form *form) +{ + disableWidgetActions(); + enableFormActions(); + + // Layout actions + ENABLE_ACTION("layout_hbox", true); + ENABLE_ACTION("layout_vbox", true); + ENABLE_ACTION("layout_grid", true); + ENABLE_ACTION("break_layout", (form->toplevelContainer()->layoutType() != KFormDesigner::Container::NoLayout)); +} + +void +KFormDesignerKDevPart::slotNoFormSelected() +{ + disableWidgetActions(); + + // Disable paste action + ENABLE_ACTION("edit_paste", false); + + ENABLE_ACTION("edit_undo", false); + ENABLE_ACTION("edit_redo", false); + + // Disable 'Tools' actions + ENABLE_ACTION("pixmap_collection", false); + ENABLE_ACTION("form_connections", false); + ENABLE_ACTION("taborder", false); + ENABLE_ACTION("change_style", false); + + // Disable items in 'File' + ENABLE_ACTION("file_save", false); + ENABLE_ACTION("file_save_as", false); + ENABLE_ACTION("preview_form", false); +} + +void +KFormDesignerKDevPart::enableFormActions() +{ + // Enable 'Tools' actions + ENABLE_ACTION("pixmap_collection", true); + ENABLE_ACTION("form_connections", true); + ENABLE_ACTION("taborder", true); + ENABLE_ACTION("change_style", true); + + // Enable items in 'File' + ENABLE_ACTION("file_save", true); + ENABLE_ACTION("file_save_as", true); + ENABLE_ACTION("preview_form", true); + + ENABLE_ACTION("edit_paste", KFormDesigner::FormManager::self()->isPasteEnabled()); + ENABLE_ACTION("edit_select_all", true); +} + +void +KFormDesignerKDevPart::disableWidgetActions() +{ + // Disable edit actions + ENABLE_ACTION("edit_copy", false); + ENABLE_ACTION("edit_cut", false); + ENABLE_ACTION("edit_delete", false); + ENABLE_ACTION("clear_contents", false); + + // Disable format functions + ENABLE_ACTION("align_menu", false); + ENABLE_ACTION("align_to_left", false); + ENABLE_ACTION("align_to_right", false); + ENABLE_ACTION("align_to_top", false); + ENABLE_ACTION("align_to_bottom", false); + ENABLE_ACTION("adjust_size_menu", false); + ENABLE_ACTION("format_raise", false); + ENABLE_ACTION("format_lower", false); + + ENABLE_ACTION("layout_hbox", false); + ENABLE_ACTION("layout_vbox", false); + ENABLE_ACTION("layout_grid", false); + ENABLE_ACTION("break_layout", false); +} + +void +KFormDesignerKDevPart::setUndoEnabled(bool enabled, const QString &text) +{ + KAction *undoAction = actionCollection()->action("edit_undo"); + if(undoAction) + { + undoAction->setEnabled(enabled); + if(!text.isNull()) + undoAction->setText(text); + } +} + +void +KFormDesignerKDevPart::setRedoEnabled(bool enabled, const QString &text) +{ + KAction *redoAction = actionCollection()->action("edit_redo"); + if(redoAction) + { + redoAction->setEnabled(enabled); + if(!text.isNull()) + redoAction->setText(text); + } +} + +void +KFormDesignerKDevPart::slotCreateFormSlot(Form *form, const QString &widget, const QString &signal) +{ + Function f; + f.returnType = "void"; + f.function = widget + "_" + signal; + f.specifier = "non virtual"; + f.access = "public"; + f.type = ftQtSlot; + emit addedFunction(designerType(), form->objectTree()->name(), f); +} + +KFormDesignerKDevPart::~KFormDesignerKDevPart() +{ +} + + +////// FormWidgetBase : helper widget to draw rects on top of widgets + +//repaint all children widgets +static void repaintAll(QWidget *w) +{ + QObjectList *list = w->queryList("QWidget"); + QObjectListIt it(*list); + for (QObject *obj; (obj=it.current()); ++it ) { + static_cast<QWidget*>(obj)->repaint(); + } + delete list; +} + +void +FormWidgetBase::drawRects(const QValueList<QRect> &list, int type) +{ + QPainter p; + p.begin(this, true); + bool unclipped = testWFlags( WPaintUnclipped ); + setWFlags( WPaintUnclipped ); + + if (prev_rect.isValid()) { + //redraw prev. selection's rectangle + p.drawPixmap( QPoint(prev_rect.x()-2, prev_rect.y()-2), buffer, QRect(prev_rect.x()-2, prev_rect.y()-2, prev_rect.width()+4, prev_rect.height()+4)); + } + p.setBrush(QBrush::NoBrush); + if(type == 1) // selection rect + p.setPen(QPen(white, 1, Qt::DotLine)); + else if(type == 2) // insert rect + p.setPen(QPen(white, 2)); + p.setRasterOp(XorROP); + + prev_rect = QRect(); + QValueList<QRect>::ConstIterator endIt = list.constEnd(); + for(QValueList<QRect>::ConstIterator it = list.constBegin(); it != endIt; ++it) { + p.drawRect(*it); + prev_rect = prev_rect.unite(*it); + } + + if (!unclipped) + clearWFlags( WPaintUnclipped ); + p.end(); +} + +void +FormWidgetBase::drawRect(const QRect& r, int type) +{ + QValueList<QRect> l; + l.append(r); + drawRects(l, type); +} + +void +FormWidgetBase::initRect() +{ + repaintAll(this); + buffer.resize( width(), height() ); + buffer = QPixmap::grabWindow( winId() ); + prev_rect = QRect(); +} + +void +FormWidgetBase::clearRect() +{ + QPainter p; + p.begin(this, true); + bool unclipped = testWFlags( WPaintUnclipped ); + setWFlags( WPaintUnclipped ); + + //redraw entire form surface + p.drawPixmap( QPoint(0,0), buffer, QRect(0,0,buffer.width(), buffer.height()) ); + + if (!unclipped) + clearWFlags( WPaintUnclipped ); + p.end(); + + repaintAll(this); +} + +void +FormWidgetBase::highlightWidgets(QWidget *from, QWidget *to)//, const QPoint &point) +{ + QPoint fromPoint, toPoint; + if(from && from->parentWidget() && (from != this)) + fromPoint = from->parentWidget()->mapTo(this, from->pos()); + if(to && to->parentWidget() && (to != this)) + toPoint = to->parentWidget()->mapTo(this, to->pos()); + + QPainter p; + p.begin(this, true); + bool unclipped = testWFlags( WPaintUnclipped ); + setWFlags( WPaintUnclipped ); + + if (prev_rect.isValid()) { + //redraw prev. selection's rectangle + p.drawPixmap( QPoint(prev_rect.x(), prev_rect.y()), buffer, QRect(prev_rect.x(), prev_rect.y(), prev_rect.width(), prev_rect.height())); + } + + p.setPen( QPen(Qt::red, 2) ); + + if(to) + { + QPixmap pix1 = QPixmap::grabWidget(from); + QPixmap pix2 = QPixmap::grabWidget(to); + + if((from != this) && (to != this)) + p.drawLine( from->parentWidget()->mapTo(this, from->geometry().center()), to->parentWidget()->mapTo(this, to->geometry().center()) ); + + p.drawPixmap(fromPoint.x(), fromPoint.y(), pix1); + p.drawPixmap(toPoint.x(), toPoint.y(), pix2); + + if(to == this) + p.drawRoundRect(2, 2, width()-4, height()-4, 4, 4); + else + p.drawRoundRect(toPoint.x(), toPoint.y(), to->width(), to->height(), 5, 5); + } + + if(from == this) + p.drawRoundRect(2, 2, width()-4, height()-4, 4, 4); + else + p.drawRoundRect(fromPoint.x(), fromPoint.y(), from->width(), from->height(), 5, 5); + + if((to == this) || (from == this)) + prev_rect = QRect(0, 0, buffer.width(), buffer.height()); + else if(to) + { + prev_rect.setX( (fromPoint.x() < toPoint.x()) ? (fromPoint.x() - 5) : (toPoint.x() - 5) ); + prev_rect.setY( (fromPoint.y() < toPoint.y()) ? (fromPoint.y() - 5) : (toPoint.y() - 5) ); + prev_rect.setRight( (fromPoint.x() < toPoint.x()) ? (toPoint.x() + to->width() + 10) : (fromPoint.x() + from->width() + 10) ); + prev_rect.setBottom( (fromPoint.y() < toPoint.y()) ? (toPoint.y() + to->height() + 10) : (fromPoint.y() + from->height() + 10) ) ; + } + else + prev_rect = QRect(fromPoint.x()- 5, fromPoint.y() -5, from->width() + 10, from->height() + 10); + + if (!unclipped) + clearWFlags( WPaintUnclipped ); + p.end(); +} + +void +FormWidgetBase::closeEvent(QCloseEvent *ev) +{ + Form *form = KFormDesigner::FormManager::self()->formForWidget(this); + if(!form || !form->isModified() || !form->objectTree()) // == preview form + ev->accept(); + else + { + bool close = m_part->closeForm(form); + if(close) + ev->accept(); + else + ev->ignore(); + } +} + +K_EXPORT_COMPONENT_FACTORY(libkformdesigner_kdev_part, KFDFactory) + +#include "kfd_kdev_part.moc" + diff --git a/kexi/formeditor/kdevelop_plugin/kfd_kdev_part.h b/kexi/formeditor/kdevelop_plugin/kfd_kdev_part.h new file mode 100644 index 00000000..52ce5f27 --- /dev/null +++ b/kexi/formeditor/kdevelop_plugin/kfd_kdev_part.h @@ -0,0 +1,139 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef KFORMDESIGNER_KDEVELOP_PART_H +#define KFORMDESIGNER_KDEVELOP_PART_H + +#include <qwidget.h> +#include <qpixmap.h> + +#include <kinterfacedesigner/designer.h> +#include <kparts/factory.h> + +#include "form.h" + +class KAboutData; +class KInstance; +class QWorkspace; +class QCloseEvent; + +using KFormDesigner::Form; +using namespace KInterfaceDesigner; + +class KFORMEDITOR_EXPORT KFDFactory : public KParts::Factory +{ + Q_OBJECT + + public: + KFDFactory(); + virtual ~KFDFactory(); + + virtual KParts::Part* createPartObject(QWidget *parentWidget=0, const char *widgetName=0, QObject *parent=0, const char *name=0, + const char *classname="KParts::Part", const QStringList &args=QStringList()); + + static KInstance *instance(); + static KAboutData *aboutData(); + + private: + static KInstance *m_instance; +}; + +class KFORMEDITOR_EXPORT KFormDesignerKDevPart : public Designer +{ + Q_OBJECT + + public: + KFormDesignerKDevPart(QWidget *parent, const char *name, bool readOnly=true, const QStringList &args=QStringList()); + virtual ~KFormDesignerKDevPart(); + + virtual DesignerType designerType() { return QtDesigner; } + virtual void openProject(const QString &) {} + +// KFormDesigner::FormManager* manager() { return m_manager; } + void setUniqueFormMode(bool enable) { m_uniqueFormMode = enable; } + + bool closeForm(Form *form); + bool closeForms(); + + virtual bool closeURL(); + + static KFormDesigner::WidgetLibrary* formsLibrary(); + + public slots: + /*! Creates a new blank Form. The new Form is shown and become the active Form. */ + void createBlankForm(); + /*! Loads a Form from a UI file. A "Open File" dialog is shown to select the file. The loaded Form is shown and becomes + the active Form. */ + void open(); + void slotPreviewForm(); + void saveAs(); + void slotCreateFormSlot(KFormDesigner::Form *form, const QString &widget, const QString &signal); + + protected slots: + void slotWidgetSelected(KFormDesigner::Form *form, bool multiple); + void slotFormWidgetSelected(KFormDesigner::Form *form); + void slotNoFormSelected(); + void slotFormModified(KFormDesigner::Form *form, bool isDirty); + void setUndoEnabled(bool enabled, const QString &text); + void setRedoEnabled(bool enabled, const QString &text); + + protected: + virtual bool openFile(); + virtual bool saveFile(); + void disableWidgetActions(); + void enableFormActions(); + void setupActions(); + + private: + static KFormDesigner::WidgetLibrary* static_formsLibrary; +// KFormDesigner::FormManager *m_manager; + QWorkspace *m_workspace; + int m_count; + bool m_uniqueFormMode; + bool m_openingFile; + bool m_inShell; +}; + +//! Helper: this widget is used to create form's surface +class KFORMEDITOR_EXPORT FormWidgetBase : public QWidget, public KFormDesigner::FormWidget +{ + Q_OBJECT + + public: + FormWidgetBase(KFormDesignerKDevPart *part, QWidget *parent = 0, const char *name = 0, int WFlags = WDestructiveClose) + : QWidget(parent, name, WFlags), m_part(part) {} + ~FormWidgetBase() {;} + + void drawRect(const QRect& r, int type); + void drawRects(const QValueList<QRect> &list, int type); + void initRect(); + void clearRect(); + void highlightWidgets(QWidget *from, QWidget *to);//, const QPoint &p); + + protected: + void closeEvent(QCloseEvent *ev); + + private: + QPixmap buffer; //!< stores grabbed entire form's area for redraw + QRect prev_rect; //!< previously selected rectangle + KFormDesignerKDevPart *m_part; +}; + +#endif + diff --git a/kexi/formeditor/kdevelop_plugin/kformdesigner_kdev_part.desktop b/kexi/formeditor/kdevelop_plugin/kformdesigner_kdev_part.desktop new file mode 100644 index 00000000..c94e628f --- /dev/null +++ b/kexi/formeditor/kdevelop_plugin/kformdesigner_kdev_part.desktop @@ -0,0 +1,50 @@ +[Desktop Entry] +Name=Form Designer KDevelop Plugin +Name[bg]=Приставка на KDevelop за проектиране на форми +Name[ca]=Endollat de disseny de formulari de KDevelop +Name[cy]=Ategyn KDevelop: Dylunydd Ffurflenni +Name[da]=Form Designer KDEvelop-plugin +Name[de]=KDevelop-Modul zum Formularentwurf +Name[el]=Πρόσθετο σχεδίασης φόρμας του KDevelop +Name[eo]=Formulardesegnila kromaĵo por KDevelop +Name[es]=Complemento de diseñador de formularios de KDevelop +Name[et]=Vormikujundaja KDevelopi plugin +Name[eu]=Formularioak diseinatzeko KDevelop-en plugina +Name[fa]=وصلۀ KDevelop طراح برگه +Name[fi]=KDevelopin lomakkeen suunnittelija -liitännäinen +Name[fr]=Module externe KDevelop de composition d'interfaces graphiques +Name[fy]=KDevelop-plugin FormDesigner +Name[gl]=Plugin de Deseño de Formularios para Kdevelop +Name[he]=תוסף מעצב טפסים ל־KDevelop +Name[hr]=KDevelop dodatak dizajnera obrazaca +Name[hu]=Űrlaptervező modul a KDevelophoz +Name[is]=Form hönnunar KDevelop íforrit +Name[it]=Plugin di KDevelop per il progetto dei moduli +Name[ja]=フォームデザイナー Kdevelop プラグイン +Name[km]=កម្មវិធីជំនួយកម្មវិធីរចនាសំណុំបែបបទសម្រាប់ KDevelop +Name[lv]=KDevelop formu veidošanas spraudnis +Name[ms]=Plugin KDevelop Pereka Bentuk Borang +Name[nb]=KDevelop-tillegg for skjemautforming +Name[nds]=KDevelop-Moduul för't Opstellen vun Kiekwarken +Name[ne]=फारम डिजाइनकर्ता केडीई विकास प्लगइन +Name[nl]=KDevelop-plugin FormDesigner +Name[nn]=KDevelop-tillegg for skjemautforming +Name[pl]=Wtyczka projektanta formularzy dla KDevelop +Name[pt]='Plugin' para o KDevelop de Desenho de Formulários +Name[pt_BR]=Plugin do Desenhista de Formulário do KDevelop +Name[ru]=Модуль форм KDevelop +Name[sk]=KDevelop modul na vytváranie formulárov (Form Designer) +Name[sl]=Vstavek za oblikovanje obrazcev za KDevelop +Name[sr]=Прикључак дизајнера форми за KDevelop +Name[sr@Latn]=Priključak dizajnera formi za KDevelop +Name[sv]=KDevelop insticksprogram för formulärkonstruktion +Name[ta]=படிவ வடிவமைப்பாளர் கேடெவெலப் சொருகுப்பொருள் +Name[tr]=FormTasarımcısı KDevelop Eklentisi +Name[uk]=Втулок дизайнера форм KDevelop +Name[zh_CN]=表单设计器 KDevelop 插件 +Name[zh_TW]=表單設計師 KDevelop 外掛程式 +MimeType=application/x-designer; +ServiceTypes=KParts/ReadOnlyPart,KParts/ReadWritePart +X-KDE-Library=libkformdesigner_kdev_part +Type=Service +InitialPreference=0 diff --git a/kexi/formeditor/kdevelop_plugin/kformdesigner_part.rc b/kexi/formeditor/kdevelop_plugin/kformdesigner_part.rc new file mode 100644 index 00000000..3cadda53 --- /dev/null +++ b/kexi/formeditor/kdevelop_plugin/kformdesigner_part.rc @@ -0,0 +1,129 @@ +<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd"> +<kpartgui name="kfd_part" version="1"> + +<MenuBar> + <Menu name="edit" noMerge="1"> + <Action name="edit_undo" group="edit_undo_merge"/> + <Action name="edit_redo" group="edit_undo_merge"/> + <Separator group="edit_undo_merge"/> + <Action name="edit_cut" group="edit_paste_merge"/> + <Action name="edit_copy" group="edit_paste_merge"/> + <Action name="edit_paste" group="edit_paste_merge"/> + <Action name="delete_widget" group="edit_paste_merge"/> + <Action name="clear_contents" group="edit_paste_merge"/> + <Action name="edit_select_all" group="edit_select_merge"/> + <Separator group="edit_paste_merge"/> + </Menu> + <Menu name="tools" noMerge="1"> + <Merge/> + <Separator/> + <Action name="taborder"/> + <Action name="change_style"/> + <Action name="pixmap_collection"/> + <Action name="form_connections"/> + </Menu> + <Menu name="widgets" noMerge="1"> + <text>&Widgets</text> + <Merge/> + <Action name="pointer"/> + <Action name="drag_connection"/> + <Separator/> + <Action name="library_widget_SubForm"/> + <Action name="library_widget_QGroupBox"/> + <Action name="library_widget_MyTabWidget"/> + <Action name="library_widget_QWidgetStack"/> + <Action name="library_widget_QFrame"/> + <Separator/> + <Action name="library_widget_KLineEdit"/> + <Action name="library_widget_QLabel"/> + <Action name="library_widget_KexiPictureLabel"/> + <Action name="library_widget_KPushButton"/> + <Action name="library_widget_QRadioButton"/> + <Action name="library_widget_QCheckBox"/> + <Action name="library_widget_KIntSpinBox"/> + <Action name="library_widget_KComboBox"/> + <Action name="library_widget_KListBox"/> + <Action name="library_widget_KTextEdit"/> + <Action name="library_widget_KListView"/> + <Action name="library_widget_QSlider"/> + <Action name="library_widget_KProgress"/> + <Action name="library_widget_KTimeWidget"/> + <Action name="library_widget_KDateWidget"/> + <Action name="library_widget_KDateTimeWidget"/> + <Action name="library_widget_Line"/> + <Action name="library_widget_Spring"/> + </Menu> + <Menu name="format" noMerge="1"> + <text>&Format</text> + <Action name="snap_to_grid"/> + <Separator/> + <Action name="layout_hbox"/> + <Action name="layout_vbox"/> + <Action name="layout_grid"/> + <Action name="break_layout"/> + <Separator/> + <Action name="align_menu"/> + <Action name="adjust_size_menu"/> + <Separator/> + <Action name="format_raise"/> + <Action name="format_lower"/> + </Menu> +</MenuBar> + +<ToolBar name="fileToolBar" noMerge="1"><text>Main Toolbar</text> + <Action name="preview_form"/> + <Separator/> + <Action name="edit_undo"/> + <Action name="edit_redo"/> + <Separator/> + <Action name="edit_cut"/> + <Action name="edit_copy"/> + <Action name="edit_paste"/> + <Action name="delete_widget"/> + <Action name="clear_contents"/> +</ToolBar> +<ToolBar name="containers" fullWidth="false" noMerge="1"> + <text>Containers</text> + <Action name="library_widget_SubForm"/> + <Action name="library_widget_QGroupBox"/> + <Action name="library_widget_MyTabWidget"/> + <Action name="library_widget_QWidgetStack"/> + <Action name="library_widget_QFrame"/> +</ToolBar> +<ToolBar name="widgets" fullWidth="false" noMerge="1"> + <text>Widgets</text> + <Action name="pointer"/> + <Action name="drag_connection"/> + <Separator/> + <Action name="library_widget_KLineEdit"/> + <Action name="library_widget_QLabel"/> + <Action name="library_widget_KexiPictureLabel"/> + <Action name="library_widget_KPushButton"/> + <Action name="library_widget_QRadioButton"/> + <Action name="library_widget_QCheckBox"/> + <Action name="library_widget_KIntSpinBox"/> + <Action name="library_widget_KComboBox"/> + <Action name="library_widget_KListBox"/> + <Action name="library_widget_KTextEdit"/> + <Action name="library_widget_KListView"/> + <Action name="library_widget_QSlider"/> + <Action name="library_widget_KProgress"/> + <Action name="library_widget_KTimeWidget"/> + <Action name="library_widget_KDateWidget"/> + <Action name="library_widget_KDateTimeWidget"/> + <Action name="library_widget_Line"/> + <Action name="library_widget_Spring"/> + <ActionList name="undo_actions" /> +</ToolBar> +<ToolBar name="tools" fullWidth="false" noMerge="1"> +<text>Tools Toolbar</text> + <Action name="pixmap_collection"/> + <Action name="change_style"/> +</ToolBar> +<ToolBar name="format" fullWidth="false" noMerge="1"> +<text>Format Toolbar</text> + <Action name="align_menu"/> + <Action name="adjust_size_menu"/> +</ToolBar> + +</kpartgui>
\ No newline at end of file diff --git a/kexi/formeditor/kdevelop_plugin/kformdesigner_part_shell.rc b/kexi/formeditor/kdevelop_plugin/kformdesigner_part_shell.rc new file mode 100644 index 00000000..8b592752 --- /dev/null +++ b/kexi/formeditor/kdevelop_plugin/kformdesigner_part_shell.rc @@ -0,0 +1,142 @@ +<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd"> +<kpartgui name="kfd_part" version="1"> + +<MenuBar> + <Menu name="file" noMerge="1"> + <Action name="file_new"/> + <Action name="file_open"/> + <Separator /> + <Action name="file_save"/> + <Action name="file_save_as"/> + <Separator/> + </Menu> + <Menu name="edit" noMerge="1"> + <Action name="edit_undo" group="edit_undo_merge"/> + <Action name="edit_redo" group="edit_undo_merge"/> + <Separator group="edit_undo_merge"/> + <Action name="edit_cut" group="edit_paste_merge"/> + <Action name="edit_copy" group="edit_paste_merge"/> + <Action name="edit_paste" group="edit_paste_merge"/> + <Action name="delete_widget" group="edit_paste_merge"/> + <Action name="clear_contents" group="edit_paste_merge"/> + <Separator group="edit_select_merge"/> + <Action name="edit_select_all" group="edit_select_merge"/> + <Separator group="edit_paste_merge"/> + </Menu> + <Menu name="view" noMerge="1"> + <Action name="preview_form"/> + </Menu> + <Menu name="tools" noMerge="1"> + <Action name="taborder"/> + <Action name="change_style"/> + <Action name="pixmap_collection"/> + <Action name="form_connections"/> + </Menu> + <Menu name="widgets" noMerge="1"> + <text>&Widgets</text> + <Merge/> + <Action name="pointer"/> + <Action name="drag_connection"/> + <Separator/> + <Action name="library_widget_SubForm"/> + <Action name="library_widget_QGroupBox"/> + <Action name="library_widget_MyTabWidget"/> + <Action name="library_widget_QWidgetStack"/> + <Action name="library_widget_QFrame"/> + <Separator/> + <Action name="library_widget_KLineEdit"/> + <Action name="library_widget_QLabel"/> + <Action name="library_widget_KexiPictureLabel"/> + <Action name="library_widget_KPushButton"/> + <Action name="library_widget_QRadioButton"/> + <Action name="library_widget_QCheckBox"/> + <Action name="library_widget_KIntSpinBox"/> + <Action name="library_widget_KComboBox"/> + <Action name="library_widget_KListBox"/> + <Action name="library_widget_KTextEdit"/> + <Action name="library_widget_KListView"/> + <Action name="library_widget_QSlider"/> + <Action name="library_widget_KProgress"/> + <Action name="library_widget_KTimeWidget"/> + <Action name="library_widget_KDateWidget"/> + <Action name="library_widget_KDateTimeWidget"/> + <Action name="library_widget_Line"/> + <Action name="library_widget_Spring"/> + </Menu> + <Menu name="format" noMerge="1"> + <text>&Format</text> + <Action name="snap_to_grid"/> + <Separator/> + <Action name="layout_hbox"/> + <Action name="layout_vbox"/> + <Action name="layout_grid"/> + <Action name="break_layout"/> + <Separator/> + <Action name="align_menu"/> + <Action name="adjust_size_menu"/> + <Separator/> + <Action name="format_raise"/> + <Action name="format_lower"/> + </Menu> +</MenuBar> + +<ToolBar name="fileToolBar" noMerge="1"><text>Main Toolbar</text> + <Action name="file_new"/> + <Action name="file_open"/> + <Action name="file_save"/> + <Action name="preview_form"/> + <Separator/> + <Action name="edit_undo"/> + <Action name="edit_redo"/> + <Separator/> + <Action name="edit_cut"/> + <Action name="edit_copy"/> + <Action name="edit_paste"/> + <Action name="delete_widget"/> + <Action name="clear_contents"/> +</ToolBar> +<ToolBar name="containers" fullWidth="false" noMerge="1"> + <text>Containers</text> + <Action name="library_widget_SubForm"/> + <Action name="library_widget_QGroupBox"/> + <Action name="library_widget_MyTabWidget"/> + <Action name="library_widget_QWidgetStack"/> + <Action name="library_widget_QFrame"/> +</ToolBar> +<ToolBar name="widgets" fullWidth="false" noMerge="1"> + <text>Widgets</text> + <Action name="pointer"/> + <Action name="drag_connection"/> + <Separator/> + <Action name="library_widget_KLineEdit"/> + <Action name="library_widget_QLabel"/> + <Action name="library_widget_KexiPictureLabel"/> + <Action name="library_widget_KPushButton"/> + <Action name="library_widget_QRadioButton"/> + <Action name="library_widget_QCheckBox"/> + <Action name="library_widget_KIntSpinBox"/> + <Action name="library_widget_KComboBox"/> + <Action name="library_widget_KListBox"/> + <Action name="library_widget_KTextEdit"/> + <Action name="library_widget_KListView"/> + <Action name="library_widget_QSlider"/> + <Action name="library_widget_KProgress"/> + <Action name="library_widget_KTimeWidget"/> + <Action name="library_widget_KDateWidget"/> + <Action name="library_widget_KDateTimeWidget"/> + <Action name="library_widget_Line"/> + <Action name="library_widget_Spring"/> + <ActionList name="undo_actions" /> +</ToolBar> +<ToolBar name="tools" fullWidth="false" noMerge="1"> +<text>Tools Toolbar</text> + <Action name="pixmap_collection"/> + <Action name="change_style"/> +</ToolBar> +<ToolBar name="format" fullWidth="false" noMerge="1"> +<text>Format Toolbar</text> + <Action name="align_menu"/> + <Action name="adjust_size_menu"/> +</ToolBar> + +</kpartgui>
\ No newline at end of file diff --git a/kexi/formeditor/kfdpixmapedit.cpp b/kexi/formeditor/kfdpixmapedit.cpp new file mode 100644 index 00000000..e8a96761 --- /dev/null +++ b/kexi/formeditor/kfdpixmapedit.cpp @@ -0,0 +1,59 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr> + + 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 "kfdpixmapedit.h" + +#include <kdebug.h> + +#include <koproperty/property.h> +#include "pixmapcollection.h" +#include "formmanager.h" +#include "form.h" +#include "objecttree.h" + +using namespace KFormDesigner; + +KFDPixmapEdit::KFDPixmapEdit(KoProperty::Property *property, QWidget *parent, const char *name) + : KoProperty::PixmapEdit(property, parent, name) +{ +// m_manager = manager; +} + +KFDPixmapEdit::~KFDPixmapEdit() +{} + +void +KFDPixmapEdit::selectPixmap() +{ + KoProperty::PixmapEdit::selectPixmap(); +#if 0 //will be reenabled for new image collection + if(!m_manager->activeForm() || !property()) + return; + + ObjectTreeItem *item = m_manager->activeForm()->objectTree()->lookup(m_manager->activeForm()->selectedWidget()->name()); + QString name = item ? item->pixmapName(property()->name()) : ""; + PixmapCollectionChooser dialog( m_manager->activeForm()->pixmapCollection(), name, topLevelWidget() ); + if(dialog.exec() == QDialog::Accepted) { + setValue(dialog.pixmap(), true); + item->setPixmapName(property()->name(), dialog.pixmapName()); + } +#endif +} + +#include "kfdpixmapedit.moc" diff --git a/kexi/formeditor/kfdpixmapedit.h b/kexi/formeditor/kfdpixmapedit.h new file mode 100644 index 00000000..0d183ea4 --- /dev/null +++ b/kexi/formeditor/kfdpixmapedit.h @@ -0,0 +1,44 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef KFORMEDITOR_PIXMAPEDIT_H +#define KFORMEDITOR_PIXMAPEDIT_H + +#include <koproperty/editors/pixmapedit.h> + +namespace KFormDesigner { + +class KFORMEDITOR_EXPORT KFDPixmapEdit : public KoProperty::PixmapEdit +{ + Q_OBJECT + + public: + KFDPixmapEdit(KoProperty::Property *property, QWidget *parent=0, const char *name=0); + virtual ~KFDPixmapEdit(); + + public slots: + virtual void selectPixmap(); + + private: +// FormManager *m_manager; +}; + +} + +#endif diff --git a/kexi/formeditor/libactionwidget.cpp b/kexi/formeditor/libactionwidget.cpp new file mode 100644 index 00000000..8a17c3c3 --- /dev/null +++ b/kexi/formeditor/libactionwidget.cpp @@ -0,0 +1,52 @@ +/* This file is part of the KDE project + Copyright (C) 2003 Lucijan Busch <lucijan@gmx.at> + Copyright (C) 2004 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 <kdebug.h> + +#include "libactionwidget.h" +#include "widgetfactory.h" + +using namespace KFormDesigner; + +LibActionWidget::LibActionWidget(WidgetInfo *w, KActionCollection *c) + : KToggleAction(w->name(), w->pixmap(), 0/*Key_F5*/, 0, 0 /*SLOT(slotWidget())*/, + c, QString("library_widget_" + w->className()).latin1()) +{ +// kdDebug() << "LibActionWidget::LibActionWidget(): " << QString("library_widget_" + w->className()).latin1() << endl; + m_className = w->className(); + setExclusiveGroup("LibActionWidgets"); + setToolTip(w->name()); + setWhatsThis(w->description()); +// connect(this, SIGNAL(activated()), this, SLOT(slotWidget())); +} + +void +LibActionWidget::slotActivated() +{ + KToggleAction::slotActivated(); + if (isChecked()) + emit prepareInsert(m_className); +} + +LibActionWidget::~LibActionWidget() +{ +} + +#include "libactionwidget.moc" diff --git a/kexi/formeditor/libactionwidget.h b/kexi/formeditor/libactionwidget.h new file mode 100644 index 00000000..bfa45f6a --- /dev/null +++ b/kexi/formeditor/libactionwidget.h @@ -0,0 +1,60 @@ +/* This file is part of the KDE project + Copyright (C) 2003 Lucijan Busch <lucijan@gmx.at> + Copyright (C) 2004 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. +*/ + +#ifndef LIBACTIONWIDGET_H +#define LIBACTIONWIDGET_H + + +#include <kactionclasses.h> + +namespace KFormDesigner +{ + +class WidgetInfo; + +/** + * KToggleAction subclass which remembers the matching class name. + */ +class KFORMEDITOR_EXPORT LibActionWidget : public KToggleAction +{ + Q_OBJECT + public: + /** LibActionWidget object is initialized to be mutually + exclusive with all other LibActionWidget objects */ + LibActionWidget(WidgetInfo *, KActionCollection *collection); + virtual ~LibActionWidget(); + + signals: + /** + * emits a signal containing the class name + */ + void prepareInsert(const QCString &className); + + protected slots: + /** reimplemented from KToggleAction */ + virtual void slotActivated(); + + private: + QCString m_className; +}; + +} + +#endif diff --git a/kexi/formeditor/objecttree.cpp b/kexi/formeditor/objecttree.cpp new file mode 100644 index 00000000..d5708773 --- /dev/null +++ b/kexi/formeditor/objecttree.cpp @@ -0,0 +1,244 @@ +/* This file is part of the KDE project + Copyright (C) 2003 Lucijan Busch <lucijan@gmx.at> + Copyright (C) 2006 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 <kdebug.h> +#include <qwidget.h> +#include <qvariant.h> +#include <qdom.h> +#include <qtextstream.h> + +#include "form.h" +#include "container.h" +#include "objecttree.h" + + +using namespace KFormDesigner; + +///////////////////////////////////////////////////////////////////////////////////////// +///////////////// ObjectTreeItem ///////////// +//////////////////////////////////////////////////////////////////////////////////////// + + +ObjectTreeItem::ObjectTreeItem(const QString &classn, const QString &name, QWidget *widget, + Container *parentContainer, Container *container) + : m_enabled(true), m_row(-1), m_col(-1), m_rowspan(-1), m_colspan(-1), m_span(false) +{ + m_className = classn; + m_name = name; + m_widget = widget; + m_container = container; + m_eater = new EventEater(widget, parentContainer); + m_parent = 0; + m_subprops = 0; +} + +ObjectTreeItem::~ObjectTreeItem() +{ +// kdDebug() << "ObjectTreeItem deleted: " << name() << endl; + delete m_subprops; +} + +void +ObjectTreeItem::rename(const QString &name) +{ + m_name = name; +} + +void +ObjectTreeItem::addChild(ObjectTreeItem *c) +{ + m_children.append(c); + c->setParent(this); +} + +void +ObjectTreeItem::removeChild(ObjectTreeItem *c) +{ + m_children.remove(c); +} + +void +ObjectTreeItem::addModifiedProperty(const QCString &property, const QVariant &oldValue) +{ + if(property == "name") + return; + + if(!m_props.contains(property)) { + m_props.insert(property, oldValue); + kdDebug() << "ObjectTree::adModProperty(): Added this property in the list: " << property << " oldValue: " << oldValue << endl; + } +} + +void +ObjectTreeItem::addSubproperty(const QCString &property, const QVariant& value) +{ + if (!m_subprops) + m_subprops = new QMap<QString, QVariant>(); + if (!m_props.contains(property)) + m_subprops->insert( property, value ); +} + +void +ObjectTreeItem::storeUnknownProperty(QDomElement &el) +{ + if(!el.isNull()) { + QTextStream ts(m_unknownProps, IO_WriteOnly|IO_Append ); + el.save(ts, 0); + } +} + +void +ObjectTreeItem::setPixmapName(const QCString &property, const QString &name) +{ + m_pixmapNames[property] = name; +} + +QString +ObjectTreeItem::pixmapName(const QCString &property) +{ + if(m_pixmapNames.contains(property)) + return m_pixmapNames[property]; + return QString::null; +} + +void +ObjectTreeItem::setGridPos(int row, int col, int rowspan, int colspan) +{ + m_row = row; m_col = col; + m_rowspan = rowspan; + m_colspan = colspan; + if(colspan || rowspan) + m_span = true; + else + m_span = false; +} + +///////////////////////////////////////////////////////////////////////////////////////// +/// ObjectTree ///////// +//////////////////////////////////////////////////////////////////////////////////////// + +ObjectTree::ObjectTree(const QString &classn, const QString &name, QWidget *widget, Container *container) + : ObjectTreeItem(classn, name, widget, container, container) +{ +} + +ObjectTree::~ObjectTree() +{ +// for(ObjectTreeItem *it = children()->first(); it; it = children()->next()) +// removeItem(it->name()); + while (children()->first()) { + removeItem(children()->first()); + } +} + +bool +ObjectTree::rename(const QString &oldname, const QString &newname) +{ + if(oldname == m_name) + { + ObjectTreeItem::rename(newname); + return true; + } + + ObjectTreeItem *it = lookup(oldname); + if(!it) + return false; + + it->rename(newname); + m_treeDict.remove(oldname); + m_treeDict.insert(newname, it); + + return true; +} + +bool +ObjectTree::reparent(const QString &name, const QString &newparent) +{ + ObjectTreeItem *item = lookup(name); + if(!item) return false; + ObjectTreeItem *parent = lookup(newparent); + if(!parent) return false; + + item->parent()->removeChild(item); + parent->addChild(item); + return true; +} + +ObjectTreeItem* +ObjectTree::lookup(const QString &name) +{ + if(name == this->name()) + return this; + else + return m_treeDict[name]; +} + +void +ObjectTree::addItem(ObjectTreeItem *parent, ObjectTreeItem *c) +{ + m_treeDict.insert(c->name(), c); + + if(!parent) + parent = this; + parent->addChild(c); + m_container->form()->emitChildAdded(c); + + kdDebug() << "ObjectTree::addItem(): adding " << c->name() << " to " << parent->name() << endl; +} + +void +ObjectTree::removeItem(const QString &name) +{ + ObjectTreeItem *c = lookup(name); + removeItem(c); +} + +void +ObjectTree::removeItem(ObjectTreeItem *c) +{ + if (m_container && m_container->form()) + m_container->form()->emitChildRemoved(c); + + for(ObjectTreeItem *it = c->children()->first(); it; it = c->children()->next()) + removeItem(it->name()); + + m_treeDict.remove(c->name()); + c->parent()->removeChild(c); + delete c; +} + +QCString +ObjectTree::generateUniqueName(const QCString &prefix, bool numberSuffixRequired) +{ + /* old way of naming widgets + int appendix = m_names[c] + 1; + QString name(c); + name.append(QString::number(appendix)); + m_names[c] = appendix;*/ + if (!numberSuffixRequired && !lookup(prefix)) + return prefix; + QString name( prefix ); + int i = 2; //start from 2, i.e. we have: "widget", "widget2", etc. + while(lookup(name + QString::number(i))) + i++; + + return (name + QString::number(i)).latin1(); +} + diff --git a/kexi/formeditor/objecttree.h b/kexi/formeditor/objecttree.h new file mode 100644 index 00000000..252ee54c --- /dev/null +++ b/kexi/formeditor/objecttree.h @@ -0,0 +1,184 @@ +/* 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) 2006 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. +*/ + +#ifndef KFORMDESIGNEROBJECTTREE_H +#define KFORMDESIGNEROBJECTTREE_H + +#include <qptrlist.h> +#include <qmap.h> +#include <qdict.h> +#include <qvariant.h> +#include <qstring.h> +#include <qguardedptr.h> + +class QWidget; +class QDomElement; + +namespace KFormDesigner { + +class ObjectTreeItem; +class Container; +class EventEater; + +//! @short An list of ObjectTreeItem pointers. +typedef QPtrList<ObjectTreeItem> ObjectTreeList; + +//! @short An iterator for ObjectTreeList. +typedef QPtrListIterator<ObjectTreeItem> ObjectTreeListIterator; + +//! @short A QString-based disctionary of ObjectTreeItem pointers. +typedef QDict<ObjectTreeItem> ObjectTreeDict; + +//! @short An iterator for ObjectTreeDict. +typedef QDictIterator<ObjectTreeItem> ObjectTreeDictIterator; + +//! @short A QString -> QVarinat map. +typedef QMap<QString, QVariant> QVariantMap; + +//! @short A const iterator for QVariantMap. +typedef QMapConstIterator<QString, QVariant> QVariantMapConstIterator; + +/*! + @short An item representing a widget + Holds the properties of a widget (classname, name, parent, children ..). + @author Lucijan Busch <lucijan@kde.org> + */ +class KFORMEDITOR_EXPORT ObjectTreeItem +{ + public: + ObjectTreeItem(const QString &className, const QString &name, QWidget *widget, Container *parentContainer, Container *container=0); + virtual ~ObjectTreeItem(); + + QString name() const { return m_name; } + QString className() const { return m_className; } + QWidget* widget() const { return m_widget; } + EventEater* eventEater() const { return m_eater; } + ObjectTreeItem* parent() const { return m_parent; } + ObjectTreeList* children() { return &m_children; } + + /*! \return a QMap<QString, QVariant> of all modified properties for this widget. + The QVariant is the old value (ie first value) of the property whose name is the QString. */ + const QVariantMap* modifiedProperties() const { return &m_props;} + + //! \return the widget's Container, or 0 if the widget is not a Container. + Container* container() const { return m_container;} + + void setWidget(QWidget *w) { m_widget = w; } + void setParent(ObjectTreeItem *parent) { m_parent = parent;} + + void debug(int ident); + void rename(const QString &name); + + void addChild(ObjectTreeItem *it); + void removeChild(ObjectTreeItem *it); + + /*! Adds \a property in the list of the modified properties for this object. + These modified properties are written in the .ui files when saving the form. + */ + void addModifiedProperty(const QCString &property, const QVariant &oldValue); + void storeUnknownProperty(QDomElement &el); + + /*! Adds subproperty \a property value \a value (a property of subwidget). + Remembering it for delayed setting is needed because on loading + the subwidget could be not created yet (true e.g. for KexiDBAutoField). */ + void addSubproperty(const QCString &property, const QVariant& value); + + /*! \return subproperties for this item, added by addSubproperty() + or 0 is there are no subproperties. */ + QMap<QString, QVariant>* subproperties() const { return m_subprops; } + + void setPixmapName(const QCString &property, const QString &name); + QString pixmapName(const QCString &property); + + void setEnabled(bool enabled) { m_enabled = enabled; } + bool isEnabled() const { return m_enabled; } + + int gridRow() const { return m_row; } + int gridCol() const { return m_col; } + int gridRowSpan() const { return m_rowspan; } + int gridColSpan() const { return m_colspan; } + bool spanMultipleCells() const { return m_span; } + void setGridPos(int row, int col, int rowspan, int colspan); + + protected: + QString m_className; + QString m_name; + ObjectTreeList m_children; + QGuardedPtr<Container> m_container; + QMap<QString, QVariant> m_props; + QMap<QString, QVariant> *m_subprops; + QString m_unknownProps; + QMap<QCString, QString> m_pixmapNames; + ObjectTreeItem* m_parent; + QGuardedPtr<QWidget> m_widget; + QGuardedPtr<EventEater> m_eater; + + bool m_enabled; + + int m_row, m_col, m_rowspan, m_colspan; + bool m_span; + + friend class ObjectTree; + friend class FormIO; +}; + +/*! @short Represents all the objects available within a form. + This class holds ObjectTreeItem for each widget in a Form. */ +class KFORMEDITOR_EXPORT ObjectTree : public ObjectTreeItem +{ + public: + ObjectTree(const QString &className=QString::null, const QString &name=QString::null, + QWidget *widget=0, Container *container=0); + virtual ~ObjectTree(); + + /*! Renames the item named \a oldname to \a newname. \return false if widget named \a newname + already exists and renaming failed. */ + bool rename(const QString &oldname, const QString &newname ); + /*! Sets \a newparent as new parent for the item whose name is \a name. */ + bool reparent(const QString &name, const QString &newparent); + + /*! \return the ObjectTreeItem named \a name, or 0 if doesn't exist. */ + ObjectTreeItem* lookup(const QString &name); + + /*! \return a dict containing all ObjectTreeItem in this ObjectTree. If you want to iterate on + this dict, use ObjectTreeDictIterator. */ + ObjectTreeDict* dict() { return &m_treeDict; } + + void addItem(ObjectTreeItem *parent, ObjectTreeItem *c); + void removeItem(const QString &name); + void removeItem(ObjectTreeItem *c); + + /*! Generates a new, unique name for a new widget using prefix \a prefix + (e.g. if \a prefix is "lineEdit", "lineEdit1" is returned). + \a prefix must be a valid identifier. + If \a numberSuffixRequired is true (the default) a number suffix is mandatory. + If \a numberSuffixRequired is false and there's a widget prefix \a prefix, + then \a prefix is returned (e.g. if \a prefix is "lineEdit", and "lineEdit" doesn't exist yet, + "lineEdit" is returned). */ + QCString generateUniqueName(const QCString &prefix, bool numberSuffixRequired = true); + + private: + ObjectTreeDict m_treeDict; +}; + +} + +#endif diff --git a/kexi/formeditor/objecttreeview.cpp b/kexi/formeditor/objecttreeview.cpp new file mode 100644 index 00000000..22ed6d7f --- /dev/null +++ b/kexi/formeditor/objecttreeview.cpp @@ -0,0 +1,377 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr> + + 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 <kdebug.h> + +#include <qpainter.h> + +#include <kiconloader.h> +#include <klocale.h> + +#include "objecttree.h" +#include "form.h" +#include "container.h" +#include "formmanager.h" +#include "widgetlibrary.h" + +#include "objecttreeview.h" + +using namespace KFormDesigner; + +ObjectTreeViewItem::ObjectTreeViewItem(ObjectTreeViewItem *parent, ObjectTreeItem *item) + : KListViewItem(parent, item->name(), item->className()) +{ + m_item = item; +} + +ObjectTreeViewItem::ObjectTreeViewItem(KListView *list, ObjectTreeItem *item) + : KListViewItem(list, item ? item->name() : QString::null, item ? item->className() : QString::null) +{ + m_item = item; +} + +ObjectTreeViewItem::~ObjectTreeViewItem() +{ +} + +QString +ObjectTreeViewItem::name() const +{ + if(m_item) + return m_item->name(); + else + return QString::null; +} + +void +ObjectTreeViewItem::paintCell(QPainter *p, const QColorGroup & cg, int column, int width, int align) +{ + int margin = listView()->itemMargin(); + if(column == 1) + { + if(!m_item) + return; + KListViewItem::paintCell(p, cg, column, width, align); + } + else + { + if(!m_item) + return; + + p->fillRect(0,0,width, height(), QBrush(backgroundColor())); + + if(isSelected()) + { + p->fillRect(0,0,width, height(), QBrush(cg.highlight())); + p->setPen(cg.highlightedText()); + } + + QFont f = listView()->font(); + p->save(); + if(isSelected()) + f.setBold(true); + p->setFont(f); + if(depth() == 0) // for edit tab order dialog + { + QString iconName + = ((ObjectTreeView*)listView())->iconNameForClass(m_item->widget()->className()); + p->drawPixmap(margin, (height() - IconSize(KIcon::Small))/2 , SmallIcon(iconName)); + p->drawText( + QRect(2*margin + IconSize(KIcon::Small),0,width, height()-1), + Qt::AlignVCenter, m_item->name()); + } + else + p->drawText(QRect(margin,0,width, height()-1), Qt::AlignVCenter, m_item->name()); + p->restore(); + + p->setPen( QColor(200,200,200) ); //like in t.v. + p->drawLine(width-1, 0, width-1, height()-1); + } + + p->setPen( QColor(200,200,200) ); //like in t.v. + p->drawLine(-150, height()-1, width, height()-1 ); +} + +void +ObjectTreeViewItem::paintBranches(QPainter *p, const QColorGroup &cg, int w, int y, int h) +{ + p->eraseRect(0,0,w,h); + ObjectTreeViewItem *item = (ObjectTreeViewItem*)firstChild(); + if(!item || !item->m_item || !item->m_item->widget()) + return; + + p->save(); + p->translate(0,y); + while(item) + { + p->fillRect(0,0,w, item->height(), QBrush(item->backgroundColor())); + p->fillRect(-150,0,150, item->height(), QBrush(item->backgroundColor())); + p->save(); + p->setPen( QColor(200,200,200) ); //like in t.v. + p->drawLine(-150, item->height()-1, w, item->height()-1 ); + p->restore(); + + if(item->isSelected()) + { + p->fillRect(0,0,w, item->height(), QBrush(cg.highlight())); + p->fillRect(-150,0,150, item->height(), QBrush(cg.highlight())); + } + + QString iconName + = ((ObjectTreeView*)listView())->iconNameForClass(item->m_item->widget()->className()); + p->drawPixmap( + (w - IconSize(KIcon::Small))/2, (item->height() - IconSize(KIcon::Small))/2 , + SmallIcon(iconName)); + + p->translate(0, item->totalHeight()); + item = (ObjectTreeViewItem*)item->nextSibling(); + } + p->restore(); +} + +void +ObjectTreeViewItem::setup() +{ + KListViewItem::setup(); + if(!m_item) + setHeight(0); +} + +void +ObjectTreeViewItem::setOpen( bool o ) +{ + //don't allow to collapse the node, user may be tricked because we're not displaying [+] marks + if (o) + KListViewItem::setOpen(o); +} + +// ObjectTreeView itself ---------------- + +ObjectTreeView::ObjectTreeView(QWidget *parent, const char *name, bool tabStop) + : KListView(parent, name) + , m_form(0) +{ + addColumn(i18n("Name"), 130); + addColumn(i18n("Widget's type", "Type"), 100); + + installEventFilter(this); + + connect((QObject*)header(), SIGNAL(sectionHandleDoubleClicked(int)), this, SLOT(slotColumnSizeChanged(int))); + if(!tabStop) + { + setSelectionModeExt(Extended); + connect(this, SIGNAL(selectionChanged()), this, SLOT(slotSelectionChanged())); + connect(this, SIGNAL(contextMenu(KListView *, QListViewItem *, const QPoint&)), this, SLOT(displayContextMenu(KListView*, QListViewItem*, const QPoint&))); + } + + setFullWidth(true); + setAllColumnsShowFocus(true); + setItemMargin(3); + setSorting(-1); +} + +ObjectTreeView::~ObjectTreeView() +{ +} + +QSize +ObjectTreeView::sizeHint() const +{ + return QSize( QFontMetrics(font()).width(columnText(0)+columnText(1)+" "), + KListView::sizeHint().height()); +} + +QString +ObjectTreeView::iconNameForClass(const QCString &classname) +{ + return m_form->library()->iconName(classname); +} + +void +ObjectTreeView::slotColumnSizeChanged(int) +{ + setColumnWidth(1, viewport()->width() - columnWidth(0)); +} + +void +ObjectTreeView::displayContextMenu(KListView *list, QListViewItem *item, const QPoint &) +{ + if(list != this || !m_form || !item) + return; + + QWidget *w = ((ObjectTreeViewItem*)item)->m_item->widget(); + if(!w) + return; + + FormManager::self()->createContextMenu(w, m_form->activeContainer()); +} + +ObjectTreeViewItem* +ObjectTreeView::findItem(const QString &name) +{ + QListViewItemIterator it(this); + while(it.current()) + { + ObjectTreeViewItem *item = static_cast<ObjectTreeViewItem*>(it.current()); + if(item->name() == name) + { + return item; + } + it++; + } + return 0; +} + +void +ObjectTreeView::setSelectedWidget(QWidget *w, bool add) +{ + blockSignals(true); // to avoid recursion + + if(!w) + { + clearSelection(); + blockSignals(false); + return; + } + + if(selectedItems().count() == 0) + add = false; + + if(!add) + clearSelection(); + + + QListViewItem *item = (QListViewItem*) findItem(w->name()); + if(!add) + { + setCurrentItem(item); + setSelectionAnchor(item); + setSelected(item, true); + } + else + setSelected(item, true); + + blockSignals(false); +} + +void +ObjectTreeView::slotSelectionChanged() +{ + const bool hadFocus = hasFocus(); + QPtrList<QListViewItem> list = selectedItems(); + m_form->selectFormWidget(); + for(QListViewItem *item = list.first(); item; item = list.next()) + { + ObjectTreeViewItem *it = static_cast<ObjectTreeViewItem*>(item); + QWidget *w = it->objectTree()->widget(); + if(w && (m_form->selectedWidgets()->findRef(w) == -1)) + m_form->setSelectedWidget(w, true, true); + } + if (hadFocus) + setFocus(); //restore focus +} + +void +ObjectTreeView::addItem(ObjectTreeItem *item) +{ + ObjectTreeViewItem *parent=0; + + parent = findItem(item->parent()->name()); + if(!parent) + return; + + loadTree(item, parent); +} + +void +ObjectTreeView::removeItem(ObjectTreeItem *item) +{ + if(!item) + return; + ObjectTreeViewItem *it = findItem(item->name()); + delete it; +} + +void +ObjectTreeView::renameItem(const QCString &oldname, const QCString &newname) +{ + if(findItem(newname)) + return; + ObjectTreeViewItem *item = findItem(oldname); + if(!item) + return; + item->setText(0, newname); +} + +void +ObjectTreeView::setForm(Form *form) +{ + if (m_form) + disconnect(m_form, SIGNAL(destroying()), this, SLOT(slotBeforeFormDestroyed())); + m_form = form; + m_topItem = 0; + clear(); + + if(!m_form) + return; + + connect(m_form, SIGNAL(destroying()), this, SLOT(slotBeforeFormDestroyed())); + + // Creates the hidden top Item + m_topItem = new ObjectTreeViewItem(this); + m_topItem->setSelectable(false); + m_topItem->setOpen(true); + + ObjectTree *tree = m_form->objectTree(); + loadTree(tree, m_topItem); + + if(!form->selectedWidgets()->isEmpty()) + setSelectedWidget(form->selectedWidgets()->first()); + else + setSelectedWidget(form->widget()); +} + +void +ObjectTreeView::slotBeforeFormDestroyed() +{ + setForm(0); +} + +ObjectTreeViewItem* +ObjectTreeView::loadTree(ObjectTreeItem *item, ObjectTreeViewItem *parent) +{ + if(!item) + return 0; + ObjectTreeViewItem *treeItem = new ObjectTreeViewItem(parent, item); + treeItem->setOpen(true); + + // The item is inserted by default at the beginning, but we want it to be at the end, so we move it + QListViewItem *last = parent->firstChild(); + while(last->nextSibling()) + last = last->nextSibling(); + treeItem->moveItem(last); + + ObjectTreeList *list = item->children(); + for(ObjectTreeItem *it = list->first(); it; it = list->next()) + loadTree(it, treeItem); + + return treeItem; +} + +#include "objecttreeview.moc" diff --git a/kexi/formeditor/objecttreeview.h b/kexi/formeditor/objecttreeview.h new file mode 100644 index 00000000..393f3ba0 --- /dev/null +++ b/kexi/formeditor/objecttreeview.h @@ -0,0 +1,129 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef OBJECTTREEVIEW_H +#define OBJECTTREEVIEW_H + +#include <klistview.h> + +namespace KFormDesigner { + +class ObjectTreeItem; +class Form; + +//! @short An item in ObjectTreeView associated with an ObjectTreeItem. +class KFORMEDITOR_EXPORT ObjectTreeViewItem : public KListViewItem +{ + public: + ObjectTreeViewItem(ObjectTreeViewItem *parent, ObjectTreeItem *item); + ObjectTreeViewItem(KListView *list, ObjectTreeItem *item=0); + virtual ~ObjectTreeViewItem(); + + //! \return the item name, ie the ObjectTreeItem name + QString name() const; + + //! \return the ObjectTreeItem associated to this item. + ObjectTreeItem* objectTree() const { return m_item; } + + virtual void setOpen( bool o ); + + protected: + //! Reimplemented to draw custom contents (copied from Property Editor) + virtual void paintCell(QPainter *p, const QColorGroup & cg, int column, int width, int align); + + //! Reimplemented to draw custom contents (copied from Property Editor) + virtual void paintBranches(QPainter *p, const QColorGroup &cg, int w, int y, int h); + + //! Reimplemented to draw custom contents (copied from Property Editor) + virtual void setup(); + + private: + ObjectTreeItem *m_item; + + friend class ObjectTreeView; +}; + +/*! @short A graphical view of Form's ObjectTree. + This is a KListView which represents an item for each widget in the form. + The actually selected widget is written bold + and selected. Clicking on a list item selects the corresponding widget in the Form. + */ +class KFORMEDITOR_EXPORT ObjectTreeView : public KListView +{ + Q_OBJECT + + public: + ObjectTreeView(QWidget *parent=0, const char *name=0, bool tabStop = false); + virtual ~ObjectTreeView(); + + virtual QSize sizeHint() const; + + /*! Sets \a form as the current Form in the list. The list will automatically + be filled with an item for each widget in the Form, and selection will be synced. + Nothing happens if \a form is already the current Form. + */ + void setForm(Form *form); + + //! \return the pixmap name for a given class, to be shown next to the widget name. + QString iconNameForClass(const QCString &classname); + + public slots: + /*! Sets the widget \a w as selected item, so it will be written bold. + It is added to current selection if \a add is true. */ + void setSelectedWidget(QWidget *w, bool add=false); + + /*! Adds the ObjectTreeItem \a item in the list, with the appropriate parent. */ + void addItem(ObjectTreeItem *item); + + /*! Removess the ObjectTreeItem \a item from the list. */ + void removeItem(ObjectTreeItem *item); + + /*! Just renames the list item from \a oldname to \a newname. */ + void renameItem(const QCString &oldname, const QCString &newname); + + protected slots: + /*! This slot is called when the user right-click a list item. + The widget context menu is shown, as inisde the Form. */ + void displayContextMenu(KListView *list, QListViewItem *item, const QPoint &p); + + void slotColumnSizeChanged(int); + + /*! The selected list item has changed, so we emit a signal to update the Form. */ + void slotSelectionChanged(); + + /*! Called before Form object is destroyed. */ + void slotBeforeFormDestroyed(); + + protected: + //! Internal function to fill the list. + ObjectTreeViewItem* loadTree(ObjectTreeItem *item, ObjectTreeViewItem *parent); + + //! \return The item whose name is \a name. + ObjectTreeViewItem* findItem(const QString &name); + + private: + Form *m_form; + ObjectTreeViewItem *m_topItem; + + friend class TabStopDialog; +}; + +} + +#endif diff --git a/kexi/formeditor/resizehandle.cpp b/kexi/formeditor/resizehandle.cpp new file mode 100644 index 00000000..0731926f --- /dev/null +++ b/kexi/formeditor/resizehandle.cpp @@ -0,0 +1,339 @@ +/* This file is part of the KDE libraries + Copyright (C) 2002 Joseph Wenninger <jowenn@kde.org> + Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + 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 <kdebug.h> +#include <klocale.h> + +#include <qpainter.h> +#include <qcursor.h> + +#include "form.h" +#include "formmanager.h" +#include "resizehandle.h" +#include "container.h" +#include "widgetfactory.h" +#include "widgetlibrary.h" + +#define MINIMUM_WIDTH 10 +#define MINIMUM_HEIGHT 10 + +using namespace KFormDesigner; + +ResizeHandle::ResizeHandle(ResizeHandleSet *set, HandlePos pos, bool editing) + : QWidget(set->m_widget->parentWidget()), m_set(set) +{ +// setBackgroundMode(Qt::NoBackground); + m_dragging = false; + //m_editing = editing; + setEditingMode(editing); + setFixedWidth(6); + setFixedHeight(6); + m_pos = pos; + //m_buddy = buddy; + //buddy->installEventFilter(this); + m_set->m_widget->installEventFilter(this); +//js installEventFilter(this); + + updatePos(); + show(); +} + +ResizeHandle::~ResizeHandle() +{ +} + +void ResizeHandle::setEditingMode(bool editing) +{ + if(editing) + setBackgroundColor(blue); + else + setBackgroundColor(black); +} + +void ResizeHandle::updatePos() +{ + switch (m_pos) + { + case TopLeft: + move(m_set->m_widget->x() - 3, m_set->m_widget->y() - 3); + setCursor(QCursor(SizeFDiagCursor)); + break; + case TopCenter: + move(m_set->m_widget->x() + m_set->m_widget->width()/2 - 3, m_set->m_widget->y() - 3); + setCursor(QCursor(SizeVerCursor)); + break; + case TopRight: + move(m_set->m_widget->x() + m_set->m_widget->width() - 3, m_set->m_widget->y() - 3); + setCursor(QCursor(SizeBDiagCursor)); + break; + case LeftCenter: + move(m_set->m_widget->x() - 3, m_set->m_widget->y() + m_set->m_widget->height()/2 - 3); + setCursor(QCursor(SizeHorCursor)); + break; + case RightCenter: + move(m_set->m_widget->x() + m_set->m_widget->width() - 3, m_set->m_widget->y() + m_set->m_widget->height()/2 - 3); + setCursor(QCursor(SizeHorCursor)); + break; + case BottomLeft: + move(m_set->m_widget->x() - 3, m_set->m_widget->y() + m_set->m_widget->height() - 3); + setCursor(QCursor(SizeBDiagCursor)); + break; + case BottomCenter: + move(m_set->m_widget->x() + m_set->m_widget->width()/2 - 3, m_set->m_widget->y() + m_set->m_widget->height() - 3); + setCursor(QCursor(SizeVerCursor)); + break; + case BottomRight: + move(m_set->m_widget->x() + m_set->m_widget->width() - 3, m_set->m_widget->y() + m_set->m_widget->height() - 3); + setCursor(QCursor(SizeFDiagCursor)); + break; + } +} + +bool ResizeHandle::eventFilter(QObject *o, QEvent *ev) +{ + if (((ev->type() == QEvent::Move) || (ev->type() == QEvent::Resize)) && o == m_set->m_widget) + { + //QTimer::singleShot(0,this,SLOT(updatePos())); + updatePos(); + } +/* else if (ev->type() == QEvent::Paint && o == this) { + QPainter p; + p.begin(m_set->m_widget, true); + const bool unclipped = testWFlags( WPaintUnclipped ); + setWFlags( WPaintUnclipped ); + + p.setPen(QPen(white, 10)); + p.setRasterOp(XorROP); + p.drawRect( 20, 20, 100, 100 );//m_set->m_widget->x(), m_set->m_widget->y(), 150, 150 ); + p.drawRect( m_set->m_widget->x(), m_set->m_widget->y(), 150, 150 ); + if (!unclipped) + clearWFlags( WPaintUnclipped ); + p.end(); + + return true; + }*/ + return false; +} + +void ResizeHandle::mousePressEvent(QMouseEvent *ev) +{ + const bool startDragging = !m_dragging; + m_dragging = true; + m_x = ev->x(); + m_y = ev->y(); + if (startDragging) { +// m_form->resizeHandleDraggingStarted(m_set->widget()); + WidgetFactory *wfactory = m_set->m_form->library()->factoryForClassName(m_set->widget()->className()); + if (wfactory) + wfactory->resetEditor(); + } +} + +void ResizeHandle::mouseMoveEvent(QMouseEvent *ev) +{ + int gridX = m_set->m_form->gridSize(); + int gridY = m_set->m_form->gridSize(); + + if (!m_dragging) return; + //if(m_editing) return; + + int tmpx = m_set->m_widget->x(); + int tmpy = m_set->m_widget->y(); + int tmpw = m_set->m_widget->width(); + int tmph = m_set->m_widget->height(); + + int dummyx = ev->x() - m_x; + int dummyy = ev->y() - m_y; + + if(FormManager::self()->snapWidgetsToGrid() && (ev->state() != (LeftButton|ControlButton|AltButton))) + { + dummyy = (int) ( ((float)dummyy) / ((float)gridY) + 0.5 ); + dummyy *= gridY; + dummyx = (int) ( ((float)dummyx) / ((float)gridX) + 0.5 ); + dummyx *= gridX; + } + + switch (m_pos) + { + case TopRight: + tmpw += dummyx; + tmpy += dummyy; + tmph -= dummyy; + break; + case RightCenter: + tmpw += dummyx; + break; + case BottomRight: + tmpw += dummyx; + tmph += dummyy; + break; + case TopCenter: + tmpy += dummyy; + tmph -= dummyy; + break; + case BottomCenter: + tmph=tmph+dummyy; + break; + case TopLeft: + tmpx += dummyx; + tmpw -= dummyx; + tmpy += dummyy; + tmph -= dummyy; + break; + case LeftCenter: + tmpx += dummyx; + tmpw -= dummyx; + break; + case BottomLeft: + tmpx += dummyx; + tmpw -= dummyx; + tmph += dummyy; + break; + } + + // Not move the top-left corner further than the bottom-right corner + if(tmpx >= m_set->m_widget->x() + m_set->m_widget->width()) + { + tmpx = m_set->m_widget->x() + m_set->m_widget->width() - MINIMUM_WIDTH; + tmpw = MINIMUM_WIDTH; + } + + if(tmpy >= m_set->m_widget->y() + m_set->m_widget->height()) + { + tmpy = m_set->m_widget->y() + m_set->m_widget->height() - MINIMUM_HEIGHT; + tmph = MINIMUM_HEIGHT; + } + + // Do not resize a widget outside of parent boundaries + if(tmpx < 0) + { + tmpw += tmpx; + tmpx = 0; + } + else if(tmpx + tmpw > m_set->m_widget->parentWidget()->width()) + tmpw = m_set->m_widget->parentWidget()->width() - tmpx; + + if(tmpy < 0) + { + tmph += tmpy; + tmpy = 0; + } + else if(tmpy + tmph > m_set->m_widget->parentWidget()->height()) + tmph = m_set->m_widget->parentWidget()->height() - tmpy; + + const bool shouldBeMoved = (tmpx != m_set->m_widget->x()) || (tmpy != m_set->m_widget->y()); + const bool shouldBeResized = (tmpw != m_set->m_widget->width()) || (tmph != m_set->m_widget->height()); + + if (shouldBeMoved && shouldBeResized) + m_set->m_widget->hide(); + + // Resize it + if (shouldBeResized) + { + // Keep a QSize(10, 10) minimum size + tmpw = (tmpw < MINIMUM_WIDTH) ? MINIMUM_WIDTH : tmpw; + tmph = (tmph < MINIMUM_HEIGHT) ? MINIMUM_HEIGHT : tmph; + m_set->m_widget->resize(tmpw,tmph); + } + + // Move the widget if necessary + if (shouldBeMoved) + m_set->m_widget->move(tmpx,tmpy); + + if (shouldBeMoved && shouldBeResized) + m_set->m_widget->show(); +} + +void ResizeHandle::mouseReleaseEvent(QMouseEvent *) +{ + m_dragging = false; +} + +void ResizeHandle::paintEvent( QPaintEvent * ) +{ + //draw XORed background + + /*QPainter p(this); + p.setRasterOp(XorROP); + p.fillRect(QRect(0, 0, 6, 6),white); + bitBlt( this, QPoint(0,0), parentWidget(), rect(), XorROP);*/ +} + +/////////////// ResizeHandleSet ////////////////// + +ResizeHandleSet::ResizeHandleSet(QWidget *modify, Form *form, bool editing) +: QObject(modify->parentWidget()), /*m_widget(modify),*/ m_form(form) +{ + m_widget = 0; + /*QWidget *parent = modify->parentWidget(); + + handles[0] = new ResizeHandle( modify, ResizeHandle::TopLeft, editing); + handles[1] = new ResizeHandle( modify, ResizeHandle::TopCenter, editing); + handles[2] = new ResizeHandle( modify, ResizeHandle::TopRight, editing); + handles[3] = new ResizeHandle( modify, ResizeHandle::LeftCenter, editing); + handles[4] = new ResizeHandle( modify, ResizeHandle::RightCenter, editing); + handles[5] = new ResizeHandle( modify, ResizeHandle::BottomLeft, editing); + handles[6] = new ResizeHandle( modify, ResizeHandle::BottomCenter, editing); + handles[7] = new ResizeHandle( modify, ResizeHandle::BottomRight, editing);*/ + setWidget(modify, editing); +} + +ResizeHandleSet::~ResizeHandleSet() +{ + for (int i = 0; i < 8; i++) + delete m_handles[i]; +} + +void +ResizeHandleSet::setWidget(QWidget *modify, bool editing) +{ + if(modify == m_widget) + return; + + if(m_widget) { + for(int i = 0; i < 8; i++) + delete m_handles[i]; + } + + m_widget = modify; + + m_handles[0] = new ResizeHandle(this, ResizeHandle::TopLeft, editing); + m_handles[1] = new ResizeHandle(this, ResizeHandle::TopCenter, editing); + m_handles[2] = new ResizeHandle(this, ResizeHandle::TopRight, editing); + m_handles[3] = new ResizeHandle(this, ResizeHandle::LeftCenter, editing); + m_handles[4] = new ResizeHandle(this, ResizeHandle::RightCenter, editing); + m_handles[5] = new ResizeHandle(this, ResizeHandle::BottomLeft, editing); + m_handles[6] = new ResizeHandle(this, ResizeHandle::BottomCenter, editing); + m_handles[7] = new ResizeHandle(this, ResizeHandle::BottomRight, editing); +} + +void +ResizeHandleSet::raise() +{ + for(int i = 0; i < 8; i++) + m_handles[i]->raise(); +} + +void ResizeHandleSet::setEditingMode(bool editing) +{ + for(int i = 0; i < 8; i++) + m_handles[i]->setEditingMode(editing); +} + +#include "resizehandle.moc" diff --git a/kexi/formeditor/resizehandle.h b/kexi/formeditor/resizehandle.h new file mode 100644 index 00000000..15ddbf36 --- /dev/null +++ b/kexi/formeditor/resizehandle.h @@ -0,0 +1,102 @@ +/* This file is part of the KDE libraries + Copyright (C) 2002 Joseph Wenninger <jowenn@kde.org> + Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef KFE_RESIZEHANDLER_H +#define KFE_RESIZEHANDLER_H + +#include <qdict.h> +#include <qguardedptr.h> +#include <qwidget.h> + +/** + *@author Joseph Wenninger + */ + +namespace KFormDesigner +{ + +class Form; +class ResizeHandleSet; + +/** +* a single widget which represents a dot for resizing a widget +* @author Joseph Wenninger +*/ +class KFORMEDITOR_EXPORT ResizeHandle : public QWidget +{ + Q_OBJECT + + public: + enum HandlePos { TopLeft = 0, TopCenter = 2, TopRight = 4, LeftCenter = 8, RightCenter = 16, + BottomLeft = 32, BottomCenter = 64, BottomRight = 128 }; + ResizeHandle(ResizeHandleSet *set, HandlePos pos, bool editing=false); + virtual ~ResizeHandle(); + void setEditingMode(bool editing); + + protected: + virtual void mousePressEvent(QMouseEvent *ev); + virtual void mouseMoveEvent(QMouseEvent *ev); + virtual void mouseReleaseEvent(QMouseEvent *ev); + virtual void paintEvent( QPaintEvent *ev ); + + protected slots: + bool eventFilter(QObject *obj, QEvent *ev); + void updatePos(); + + private: + ResizeHandleSet *m_set; + HandlePos m_pos; + //QWidget *m_buddy; + bool m_dragging; + //bool m_editing; + int m_x; + int m_y; +}; + +/** +* a set of resize handles (for resizing widgets) +* @author Joseph Wenninger +*/ +class KFORMEDITOR_EXPORT ResizeHandleSet: public QObject +{ + Q_OBJECT + + public: + typedef QDict<ResizeHandleSet> Dict; + + ResizeHandleSet(QWidget *modify, Form *form, bool editing = false); + ~ResizeHandleSet(); + + void setWidget(QWidget *modify, bool editing = false); + QWidget *widget() const { return m_widget; } + void raise(); + void setEditingMode(bool editing); + + private: + QGuardedPtr<ResizeHandle> m_handles[8]; + QGuardedPtr<QWidget> m_widget; + QGuardedPtr<Form> m_form; + bool m_editing; + + friend class ResizeHandle; +}; + +} + +#endif diff --git a/kexi/formeditor/richtextdialog.cpp b/kexi/formeditor/richtextdialog.cpp new file mode 100644 index 00000000..f4596ed5 --- /dev/null +++ b/kexi/formeditor/richtextdialog.cpp @@ -0,0 +1,210 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr> + + 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 <qlayout.h> + +#include <ktoolbar.h> +#include <kfontcombo.h> +#include <kcolorcombo.h> +#include <ktoolbarradiogroup.h> +#include <kdebug.h> +#include <klocale.h> + +#include "richtextdialog.h" + +namespace KFormDesigner { + +////////////////////////////////////////////////////////////////////////////////// +//////////////// A simple dialog to edit rich text //////////////////////////// +////////////////////////////////////////////////////////////////////////////////// + +RichTextDialog::RichTextDialog(QWidget *parent, const QString &text) +: KDialogBase(parent, "richtext_dialog", true, i18n("Edit Rich Text"), Ok|Cancel, Ok, false) +{ + QFrame *frame = makeMainWidget(); + QVBoxLayout *l = new QVBoxLayout(frame); + l->setAutoAdd(true); + + m_toolbar = new KToolBar(frame); + m_toolbar->setFlat(true); + m_toolbar->show(); + + m_fcombo = new KFontCombo(m_toolbar); + m_toolbar->insertWidget(TBFont, 40, m_fcombo); + connect(m_fcombo, SIGNAL(textChanged(const QString&)), this, SLOT(changeFont(const QString &))); + + m_toolbar->insertSeparator(); + + m_colCombo = new KColorCombo(m_toolbar); + m_toolbar->insertWidget(TBColor, 30, m_colCombo); + connect(m_colCombo, SIGNAL(activated(const QColor&)), this, SLOT(changeColor(const QColor&))); + + m_toolbar->insertButton("text_bold", TBBold, true, i18n("Bold")); + m_toolbar->insertButton("text_italic", TBItalic, true, i18n("Italic")); + m_toolbar->insertButton("text_under", TBUnder, true, i18n("Underline")); + m_toolbar->setToggle(TBBold, true); + m_toolbar->setToggle(TBItalic, true); + m_toolbar->setToggle(TBUnder, true); + m_toolbar->insertSeparator(); + + m_toolbar->insertButton("text_super", TBSuper, true, i18n("Superscript")); + m_toolbar->insertButton("text_sub", TBSub, true, i18n("Subscript")); + m_toolbar->setToggle(TBSuper, true); + m_toolbar->setToggle(TBSub, true); + m_toolbar->insertSeparator(); + + KToolBarRadioGroup *group = new KToolBarRadioGroup(m_toolbar); + m_toolbar->insertButton("text_left", TBLeft, true, i18n("Left Align")); + m_toolbar->setToggle(TBLeft, true); + group->addButton(TBLeft); + m_toolbar->insertButton("text_center", TBCenter, true, i18n("Centered")); + m_toolbar->setToggle(TBCenter, true); + group->addButton(TBCenter); + m_toolbar->insertButton("text_right", TBRight, true, i18n("Right Align")); + m_toolbar->setToggle(TBRight, true); + group->addButton(TBRight); + m_toolbar->insertButton("text_block", TBJustify, true, i18n("Justified")); + m_toolbar->setToggle(TBJustify, true); + group->addButton(TBJustify); + + connect(m_toolbar, SIGNAL(toggled(int)), this, SLOT(buttonToggled(int))); + + m_edit = new KTextEdit(text, QString::null, frame, "richtext_edit"); + m_edit->setTextFormat(RichText); + m_edit->setFocus(); + + connect(m_edit, SIGNAL(cursorPositionChanged(int, int)), this, SLOT(cursorPositionChanged(int, int))); + connect(m_edit, SIGNAL(clicked(int, int)), this, SLOT(cursorPositionChanged(int, int))); + connect(m_edit, SIGNAL(currentVerticalAlignmentChanged(VerticalAlignment)), this, SLOT(slotVerticalAlignmentChanged(VerticalAlignment))); + + m_edit->moveCursor(QTextEdit::MoveEnd, false); + cursorPositionChanged(0, 0); + m_edit->show(); + frame->show(); +} + +QString +RichTextDialog::text() +{ + return m_edit->text(); +} + +void +RichTextDialog::changeFont(const QString &font) +{ + m_edit->setFamily(font); +} + +void +RichTextDialog::changeColor(const QColor &color) +{ + m_edit->setColor(color); +} + +void +RichTextDialog::buttonToggled(int id) +{ + bool isOn = m_toolbar->isButtonOn(id); + + switch(id) + { + case TBBold: m_edit->setBold(isOn); break; + case TBItalic: m_edit->setItalic(isOn); break; + case TBUnder: m_edit->setUnderline(isOn); break; + case TBSuper: + { + if(isOn && m_toolbar->isButtonOn(TBSub)) + m_toolbar->setButton(TBSub, false); + m_edit->setVerticalAlignment(isOn ? QTextEdit::AlignSuperScript : QTextEdit::AlignNormal); + break; + } + case TBSub: + { + if(isOn && m_toolbar->isButtonOn(TBSuper)) + m_toolbar->setButton(TBSuper, false); + m_edit->setVerticalAlignment(isOn ? QTextEdit::AlignSubScript : QTextEdit::AlignNormal); + break; + } + case TBLeft: case TBCenter: + case TBRight: case TBJustify: + { + if(!isOn) break; + switch(id) + { + case TBLeft: m_edit->setAlignment(Qt::AlignLeft); break; + case TBCenter: m_edit->setAlignment(Qt::AlignCenter); break; + case TBRight: m_edit->setAlignment(Qt::AlignRight); break; + case TBJustify: m_edit->setAlignment(Qt::AlignJustify); break; + default: break; + } + } + default: break; + } + +} + +void +RichTextDialog::cursorPositionChanged(int, int) +{ + m_fcombo->setCurrentFont(m_edit->currentFont().family()); + m_colCombo->setColor(m_edit->color()); + m_toolbar->setButton(TBBold, m_edit->bold()); + m_toolbar->setButton(TBItalic, m_edit->italic()); + m_toolbar->setButton(TBUnder, m_edit->underline()); + + int id = 0; + switch(m_edit->alignment()) + { + case Qt::AlignLeft: id = TBLeft; break; + case Qt::AlignCenter: id = TBCenter; break; + case Qt::AlignRight: id = TBRight; break; + case Qt::AlignJustify: id = TBJustify; break; + default: id = TBLeft; break; + } + m_toolbar->setButton(id, true); +} + +void +RichTextDialog::slotVerticalAlignmentChanged(VerticalAlignment align) +{ + switch(align) + { + case QTextEdit::AlignSuperScript: + { + m_toolbar->setButton(TBSuper, true); + m_toolbar->setButton(TBSub, false); + break; + } + case QTextEdit::AlignSubScript: + { + m_toolbar->setButton(TBSub, true); + m_toolbar->setButton(TBSuper, false); + break; + } + default: + { + m_toolbar->setButton(TBSuper, false); + m_toolbar->setButton(TBSub, false); + } + } +} + + +} + +#include "richtextdialog.moc" diff --git a/kexi/formeditor/richtextdialog.h b/kexi/formeditor/richtextdialog.h new file mode 100644 index 00000000..83c96464 --- /dev/null +++ b/kexi/formeditor/richtextdialog.h @@ -0,0 +1,63 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef RICHTEXTEDIT_DIALOG_H +#define RICHTEXTEDIT_DIALOG_H + +#include <kdialogbase.h> +#include <ktextedit.h> + +class KToolBar; +class KFontCombo; +class KColorCombo; + +namespace KFormDesigner { + +//! A simple dialog to edit rich text +/*! It allows to change font name, style and color, alignment. */ +class KFORMEDITOR_EXPORT RichTextDialog : public KDialogBase +{ + Q_OBJECT + + public: + RichTextDialog(QWidget *parent, const QString &text); + ~RichTextDialog(){;} + + QString text(); + + enum VerticalAlignment{AlignNormal = QTextEdit::AlignNormal, AlignSuperScript = QTextEdit::AlignSuperScript, AlignSubScript = QTextEdit::AlignSubScript}; + + public slots: + void changeFont(const QString &); + void changeColor(const QColor&); + void buttonToggled(int); + void cursorPositionChanged(int, int); + void slotVerticalAlignmentChanged(VerticalAlignment align); + + private: + enum { TBFont = 100, TBColor, TBBold, TBItalic, TBUnder, TBSuper, TBSub, TBLeft = 201, TBCenter, TBRight, TBJustify }; + KToolBar *m_toolbar; + KTextEdit *m_edit; + KFontCombo *m_fcombo; + KColorCombo *m_colCombo; +}; + +} + +#endif diff --git a/kexi/formeditor/scripting/Makefile.am b/kexi/formeditor/scripting/Makefile.am new file mode 100644 index 00000000..f03ccfdf --- /dev/null +++ b/kexi/formeditor/scripting/Makefile.am @@ -0,0 +1,18 @@ +include $(top_srcdir)/kexi/Makefile.global + +noinst_LTLIBRARIES = libkformdesigner_scripting.la + +libkformdesigner_scripting_la_SOURCES = formscript.cpp scriptIO.cpp scriptmanager.cpp + +libkformdesigner_scripting_la_LDFLAGS = $(all_libraries) -Wnounresolved +libkformdesigner_scripting_la_LIBADD = $(top_builddir)/kexi/formeditor/libkformdesigner.la + +libkformdesigner_scripting_la_METASOURCES = AUTO + +SUBDIRS = . + +INCLUDES= -I$(top_srcdir)/kexi/formeditor \ + -I$(top_srcdir)/kexi \ + -I$(top_srcdir)/kexi/scripting \ + -I$(top_srcdir)/kexi/core $(all_includes) + diff --git a/kexi/formeditor/scripting/formscript.cpp b/kexi/formeditor/scripting/formscript.cpp new file mode 100644 index 00000000..b598066e --- /dev/null +++ b/kexi/formeditor/scripting/formscript.cpp @@ -0,0 +1,109 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr> + + 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 "formscript.h" +#include "scriptmanager.h" + +#include "form.h" +#include "objecttree.h" +// Kross Includes +#include "main/manager.h" +#include "main/scriptcontainer.h" +#include "api/exception.h" + +#include <kdebug.h> + +using namespace KFormDesigner; + +FormScript::FormScript(Form *form, ScriptManager *manager, const char *name) + : QObject(manager, name), m_manager(manager), m_form(form) +{ + m_script = manager->krossManager()->getScriptContainer(form->widget()->name()); +} + +FormScript::~FormScript() +{ +} + +QString +FormScript::getCode(const QString &) +{ + /// \todo Allow to select only one function + return m_script->getCode(); +} + +void +FormScript::setCode(const QString &code) +{ + m_script->setCode(code); +} + +void +FormScript::appendCode(const QString &code) +{ + QString s = m_script->getCode(); + s.append("\n"); + s.append(code); + m_script->setCode(s); +} + +bool +FormScript::execute(const QString &functionName) +{ + /// \todo support return value and arguments + try { + m_script->callFunction(functionName); + } + catch(Kross::Api::Exception& e) { + kdDebug() << QString("EXCEPTION type='%1' description='%2'").arg(e.type()).arg(e.description()) << endl; + return false; + } + return true; +} + +void +FormScript::connectEvents() +{ + if(!m_form || !m_form->objectTree()) + return; + // first call addQObject for each widget in the Form + ObjectTreeDict *dict = m_form->objectTree()->dict(); + ObjectTreeDictIterator it(*dict); + for(; it.current(); ++it) + m_script->addQObject(it.current()->widget()); + m_script->addQObject(m_form->widget()); + + // Then we connect all signals + QValueListConstIterator<Event*> endIt = m_list.constEnd(); + for(QValueListConstIterator<Event*> it = m_list.constBegin(); it != endIt; ++it) { + if( (*it)->type() == Event::Slot) { + connect((*it)->sender(), (*it)->signal(), (*it)->receiver(), (*it)->slot()); + } + else if( (*it)->type() == Event::UserFunction) { + m_script->connect((*it)->sender(), (*it)->signal(), (*it)->slot()); + } + else if( (*it)->type() == Event::Action) { + /// \todo connect signals with actions + } + } +} + + +#include "formscript.moc" + diff --git a/kexi/formeditor/scripting/formscript.h b/kexi/formeditor/scripting/formscript.h new file mode 100644 index 00000000..e3d63d74 --- /dev/null +++ b/kexi/formeditor/scripting/formscript.h @@ -0,0 +1,78 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef FORMSCRIPT_H +#define FORMSCRIPT_H + +#include "kexievents.h" + +#include <qobject.h> +#include <qstring.h> +#include <ksharedptr.h> + +class ScriptManager; + +namespace KFormDesigner { + class Form; +} + +namespace Kross { + namespace Api { + class ScriptContainer; + } +} + +using namespace KFormDesigner; + +//! A class that stores the code and events related to a single form +class FormScript : public QObject +{ + Q_OBJECT + + public: + FormScript(Form *form, ScriptManager *manager, const char *name=0); + ~FormScript(); + + EventList* eventList() { return &m_list; } + Kross::Api::ScriptContainer* scriptContainer() { return m_script; } + + /*! \return The code of funtionName. If parameter is empty, it returns the full code of this form.*/ + QString getCode(const QString &functionName=QString::null); + /*! Replaces the actual form code with the string \a code. + Called eg by (future) script editor. */ + void setCode(const QString &code); + /*! Adds the string \a code at the end of current code. Used to add a function in the script. */ + void appendCode(const QString &code); + + /*! Executes the \a functionName. + \todo how do we give parameters? */ + bool execute(const QString &functionName); + /*! Really connects all events in the list. + Also calls Kross;;Api::Manager::addObject for each widget in the form to allow the user to + use these widgets in the script. */ + void connectEvents(); + + private: + ScriptManager *m_manager; + Form *m_form; + KSharedPtr<Kross::Api::ScriptContainer> m_script; + EventList m_list; +}; + +#endif diff --git a/kexi/formeditor/scripting/scriptIO.cpp b/kexi/formeditor/scripting/scriptIO.cpp new file mode 100644 index 00000000..8620a8ae --- /dev/null +++ b/kexi/formeditor/scripting/scriptIO.cpp @@ -0,0 +1,186 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr> + + 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 "scriptIO.h" +#include "formscript.h" +#include "kexievents.h" + +#include "form.h" +#include "objecttree.h" +// Kross includes +#include "main/scriptcontainer.h" + +bool +ScriptIO::saveFormEvents(QDomNode &parentNode, FormScript *formScript) +{ + QDomDocument domDoc = parentNode.ownerDocument(); + + // Save the form's code + if(!formScript->getCode().isEmpty()) { + QDomElement script = domDoc.createElement("script"); + script.setAttribute("language", formScript->scriptContainer()->getInterpreterName()); + parentNode.appendChild(script); + QDomText scriptCode = domDoc.createTextNode(formScript->getCode()); + script.appendChild(scriptCode); + } + + // Save all form events + if(!formScript->eventList()->isEmpty()) + saveEventList(formScript->eventList(), parentNode); + return true; +} + +bool +ScriptIO::loadFormEvents(QDomNode &parentNode, Form *form, ScriptManager *manager) +{ + QDomElement script = parentNode.namedItem("script").toElement(); + QDomElement events = parentNode.namedItem("events").toElement(); + + // Load script code + FormScript *formScript = new FormScript(form, manager); + if(!script.isNull()) { + formScript->scriptContainer()->setInterpreterName(script.attribute("language")); + formScript->setCode(script.text()); + } + + // Load all events in the EventList + if(!events.isNull()) { + for(QDomNode n = events.firstChild(); !n.isNull(); n = n.nextSibling()) + loadEvent(n, formScript->eventList(), form); + } + return true; +} + +bool +ScriptIO::saveAllEventsForWidget(QObject *widget, FormScript *formScript, QDomNode &node) +{ + EventList *l = formScript->eventList()->allEventsForObject(widget); + saveEventList(l, node); + return true; +} + +void +ScriptIO::saveEvent(Event *event, QDomNode &parentNode) +{ + if(!event) + return; + + QDomDocument domDoc = parentNode.ownerDocument(); + QDomElement eventNode = domDoc.createElement("event"); + eventNode.setAttribute("type", event->type()); + parentNode.appendChild(eventNode); + + switch(event->type()) { + case Event::Slot: { + QDomElement sender = domDoc.createElement("sender"); + eventNode.appendChild(sender); + QDomText senderText = domDoc.createTextNode(event->sender() ? event->sender()->name() : ""); + sender.appendChild(senderText); + + QDomElement signal = domDoc.createElement("signal"); + eventNode.appendChild(signal); + QDomText signalText = domDoc.createTextNode(event->signal()); + signal.appendChild(signalText); + + QDomElement receiver = domDoc.createElement("receiver"); + eventNode.appendChild(receiver); + QDomText receiverText = domDoc.createTextNode(event->receiver() ? event->receiver()->name() : ""); + receiver.appendChild(receiverText); + + QDomElement slot = domDoc.createElement("slot"); + eventNode.appendChild(slot); + QDomText slotText = domDoc.createTextNode(event->slot()); + slot.appendChild(slotText); + break; + } + + case Event::UserFunction: { + QDomElement sender = domDoc.createElement("sender"); + eventNode.appendChild(sender); + QDomText senderText = domDoc.createTextNode(event->sender() ? event->sender()->name() : ""); + sender.appendChild(senderText); + + QDomElement signal = domDoc.createElement("signal"); + eventNode.appendChild(signal); + QDomText signalText = domDoc.createTextNode(event->signal()); + signal.appendChild(signalText); + + QDomElement function = domDoc.createElement("function"); + eventNode.appendChild(function); + QDomText functionText = domDoc.createTextNode(event->slot()); + function.appendChild(functionText); + break; + } + + case Event::Action: + // !\todo + break; + + default: + break; + } +} + +void +ScriptIO::saveEventList(EventList *list, QDomNode &parentNode) +{ + if(!list || list->isEmpty()) + return; + + QDomDocument domDoc = parentNode.ownerDocument(); + QDomElement events = domDoc.createElement("events"); + parentNode.appendChild(events); + + QValueListConstIterator<Event*> endIt = list->constEnd(); + for(QValueListConstIterator<Event*> it = list->constBegin(); it != endIt; ++it) + saveEvent((*it), events); +} + +void +ScriptIO::loadEvent(QDomNode &node, EventList *list, Form *form) +{ + int type = node.toElement().attribute("type").toInt(); + Event *event = new Event(); + + switch(type) { + case Event::Slot: { + ObjectTreeItem *sender = form->objectTree()->lookup(node.namedItem("sender").toElement().text()); + event->setSender(sender ? sender->widget() : 0); + event->setSignal(node.namedItem("signal").toElement().text().local8Bit()); + ObjectTreeItem *receiver = form->objectTree()->lookup(node.namedItem("receiver").toElement().text()); + event->setReceiver(receiver ? receiver->widget() : 0); + event->setSlot(node.namedItem("slot").toElement().text().local8Bit()); + event->setType(Event::Slot); + break; + } + + case Event::UserFunction: { + ObjectTreeItem *sender = form->objectTree()->lookup(node.namedItem("sender").toElement().text()); + event->setSender(sender ? sender->widget() : 0); + event->setSignal(node.namedItem("signal").toElement().text().local8Bit()); + event->setSlot(node.namedItem("function").toElement().text().local8Bit()); + event->setType(Event::UserFunction); + break; + } + default: break; + } + + list->addEvent(event); +} + diff --git a/kexi/formeditor/scripting/scriptIO.h b/kexi/formeditor/scripting/scriptIO.h new file mode 100644 index 00000000..9fed06a6 --- /dev/null +++ b/kexi/formeditor/scripting/scriptIO.h @@ -0,0 +1,64 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef SCRIPTIO_H +#define SCRIPTIO_H + +#include <qdom.h> + +class QString; +class QObject; +class Event; +class EventList; +class ScriptManager; +class FormScript; + +namespace KFormDesigner { + class Form; +} + +using namespace KFormDesigner; + +//! A static class to deal with loading/saving events from/to XML +class ScriptIO +{ + public: + /*! Save the evnts of a form. + Creates an \<events\> tag, and then one \<event\> tag for each event. + Each event contains \<sender\> and \<receiver\> tags, with attributes depending on event type. */ + static bool saveFormEvents(QDomNode &parentNode, FormScript *script); + /*! Reads the \<events\> tag (\a parentNode), then creates and fills a FormScript object linked to this \a form. + The new FormScript object is then added to ScriptManager list.*/ + static bool loadFormEvents(QDomNode &parentNode, Form *form, ScriptManager *manager); + + /*! Save only the events related to widget \a name in the FormScript \a fscript. + Used eg when copying/pasting widgets to keep also events related to it.*/ + static bool saveAllEventsForWidget(QObject *widget, FormScript *fscript, QDomNode &node); + + static void saveEvent(Event *event, QDomNode &parentNode); + static void saveEventList(EventList *list, QDomNode &parentNode); + static void loadEvent(QDomNode &node, EventList *list, Form *form); + + protected: + ScriptIO() {;} + ~ScriptIO() {;} +}; + +#endif + diff --git a/kexi/formeditor/scripting/scriptmanager.cpp b/kexi/formeditor/scripting/scriptmanager.cpp new file mode 100644 index 00000000..c572c685 --- /dev/null +++ b/kexi/formeditor/scripting/scriptmanager.cpp @@ -0,0 +1,70 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr> + + 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 "scriptmanager.h" +#include "formscript.h" + +#include "form.h" +#include "formmanager.h" +// Kross includes +#include "main/manager.h" + +using KFormDesigner::Form; + +ScriptManager::ScriptManager(QObject *parent, const char *name) + : QObject(parent, name) +{ + m_manager = Kross::Api::Manager::scriptManager(); + m_dict.setAutoDelete(true); +} + +ScriptManager::~ScriptManager() +{ +} + +FormScript* +ScriptManager::newFormScript(Form *form) +{ + FormScript *script = new FormScript(form, this); + m_dict.insert(form, script); + return script; +} + +FormScript* +ScriptManager::scriptForForm(Form *form) +{ + return m_dict[form]; +} + +void +ScriptManager::setFormManager(FormManager *manager) +{ + m_formManager = manager; + connect(m_formManager, SIGNAL(aboutToDeleteForm(KFormDesigner::Form*)), this, SLOT(slotFormDeleted(KFormDesigner::Form*))); + connect(m_formManager, SIGNAL(formCreated(KFormDesigner::Form*)), this, SLOT(newFormScript(KFormDesigner::Form*))); +} + +void +ScriptManager::slotFormDeleted(KFormDesigner::Form *form) +{ + m_dict.remove(form); +} + +#include "scriptmanager.moc" + diff --git a/kexi/formeditor/scripting/scriptmanager.h b/kexi/formeditor/scripting/scriptmanager.h new file mode 100644 index 00000000..258ce2cc --- /dev/null +++ b/kexi/formeditor/scripting/scriptmanager.h @@ -0,0 +1,69 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef SCRIPTMANAGER_H +#define SCRIPTMANAGER_H + +#include <qobject.h> +#include <qptrdict.h> + +class FormScript; + +namespace Kross { + namespace Api { + class Manager; + } +} + +namespace KFormDesigner { + class FormManager; + class Form; +} + +using namespace KFormDesigner; + +class ScriptManager : public QObject +{ + Q_OBJECT + + public: + ScriptManager(QObject *parent=0, const char *name=0); + ~ScriptManager(); + + /*! \return The FormScript object associated to this Form. */ + FormScript* scriptForForm(Form *form); + + void setFormManager(FormManager *manager); + FormManager* formManager() { return m_formManager; } + Kross::Api::Manager* krossManager() { return m_manager; } + + public slots: + /*! Called when a form is deleted. It is removed from the dict. */ + void slotFormDeleted(KFormDesigner::Form *form); + /*! \return A new FormScript object associated to the Form \a form. */ + FormScript* newFormScript(KFormDesigner::Form *form); + + private: + Kross::Api::Manager *m_manager; + KFormDesigner::FormManager *m_formManager; + QPtrDict<FormScript> m_dict; +}; + +#endif + diff --git a/kexi/formeditor/spring.cpp b/kexi/formeditor/spring.cpp new file mode 100644 index 00000000..e09cc2d7 --- /dev/null +++ b/kexi/formeditor/spring.cpp @@ -0,0 +1,158 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr> + Copyright (C) 2005 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 <qsizepolicy.h> +#include <qpainter.h> +#include <qdom.h> +#include <qvariant.h> + +#include <kdebug.h> + +#include "objecttree.h" +#include "container.h" +#include "form.h" +#include "formIO.h" +#include "widgetlibrary.h" + +#include "spring.h" + +Spring::Spring(QWidget *parent, const char *name) + : QWidget(parent, name) +{ + m_edit = true; + m_orient = Horizontal; + setSizeType((SizeType)QSizePolicy::Expanding); +} + +void +Spring::setOrientation(Orientation orient) +{ + SizeType type = sizeType(); + m_orient = orient; + setSizeType(type); + repaint(); +} + +Spring::SizeType +Spring::sizeType() const +{ + if(m_orient == Vertical) + return (SizeType)sizePolicy().verData(); + else + return (SizeType)sizePolicy().horData(); +} + +void +Spring::setSizeType(SizeType size) +{ + if(m_orient == Vertical) + setSizePolicy(QSizePolicy::Minimum, (QSizePolicy::SizeType)size); + else + setSizePolicy( (QSizePolicy::SizeType)size, QSizePolicy::Minimum); +} + +void +Spring::paintEvent(QPaintEvent *ev) +{ + if(!m_edit) + return; + + QPainter p(this); + if(!ev->erased()) + p.eraseRect(0,0,width(), height()); + p.setPen(QPen(Qt::white, 1)); + p.setRasterOp(Qt::XorROP); + QPointArray pa(4); + if(m_orient == Vertical) { + uint part = (height()+16) / 16; + if (part<3) + part=3; + uint w = width()-1; + uint offset = 0; + for (uint i=0; i<4; i++, offset+=(part*4)) { + pa.putPoints(0, 4, + w/2,offset, w,offset+part, w,offset+part, w/2,offset+part*2); + p.drawCubicBezier(pa, 0); + pa.putPoints(0, 4, + w/2,offset+part*2, 0,offset+part*3, 0,offset+part*3, w/2,offset+part*4); + p.drawCubicBezier(pa, 0); + } + } + else { + uint part = (width()+16) / 16; + if (part<3) + part=3; + uint h = height()-1; + uint offset = 0; + for (uint i=0; i<4; i++, offset+=(part*4)) { + pa.putPoints(0, 4, + offset,h/2, offset+part,0, offset+part,0, offset+part*2,h/2); + p.drawCubicBezier(pa, 0); + pa.putPoints(0, 4, + offset+part*2,h/2, offset+part*3,h, offset+part*3,h, offset+part*4,h/2); + p.drawCubicBezier(pa, 0); + } + } +} + +bool +Spring::isPropertyVisible(const QCString &name) +{ + if((name == "name") || (name == "sizeType") || (name == "orientation") || (name == "geometry")) + return true; + + return false; +} + + +void +Spring::saveSpring(KFormDesigner::ObjectTreeItem *item, QDomElement &parentNode, QDomDocument &domDoc, bool insideGridLayout) +{ + QDomElement tclass = domDoc.createElement("spacer"); + parentNode.appendChild(tclass); + + if(insideGridLayout) + { + tclass.setAttribute("row", item->gridRow()); + tclass.setAttribute("column", item->gridCol()); + if(item->spanMultipleCells()) + { + tclass.setAttribute("rowspan", item->gridRowSpan()); + tclass.setAttribute("colspan", item->gridColSpan()); + } + } + + KFormDesigner::FormIO::savePropertyValue(tclass, domDoc, "name", item->widget()->property("name"), item->widget()); + + if(parentNode.tagName() == "widget") + KFormDesigner::FormIO::savePropertyValue(tclass, domDoc, "geometry", item->widget()->property("geometry"), item->widget()); + + if(!item->widget()->sizeHint().isValid()) + KFormDesigner::FormIO::savePropertyValue(tclass, domDoc, "sizeHint", item->widget()->property("size"), item->widget()); + else + KFormDesigner::FormIO::savePropertyValue(tclass, domDoc, "sizeHint", item->widget()->property("sizeHint"), item->widget()); + + KFormDesigner::FormIO::savePropertyValue(tclass, domDoc, "orientation", item->widget()->property("orientation"), item->widget()); + KFormDesigner::FormIO::savePropertyValue(tclass, domDoc, "sizeType", item->widget()->property("sizeType"), item->widget()); +} + + +#include "spring.moc" + diff --git a/kexi/formeditor/spring.h b/kexi/formeditor/spring.h new file mode 100644 index 00000000..7ca3d35f --- /dev/null +++ b/kexi/formeditor/spring.h @@ -0,0 +1,74 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef SPACER_H +#define SPACER_H + +#include <qwidget.h> + +class QDomElement; +class QDomDocument; + +namespace KFormDesigner { + +class ObjectTreeItem; +class Container; +class WidgetLibrary; + +} + +class KFORMEDITOR_EXPORT Spring : public QWidget +{ + Q_OBJECT + Q_ENUMS(SizeType) + Q_PROPERTY(Orientation orientation READ orientation WRITE setOrientation) + Q_PROPERTY(SizeType sizeType READ sizeType WRITE setSizeType) + + private: + enum {HSize = 6, HMask = 0x3f, VMask = HMask << HSize, MayGrow = 1, ExpMask = 2, MayShrink = 4 }; + public: + enum SizeType {Fixed = 0, Minimum = MayGrow, Maximum = MayShrink, Preferred = MayGrow|MayShrink , MinimumExpanding = Minimum|ExpMask, + Expanding = MinimumExpanding|MayShrink }; + + public: + Spring(QWidget *parent, const char *name); + ~Spring() {;} + + static bool isPropertyVisible(const QCString &name); + static void saveSpring(KFormDesigner::ObjectTreeItem *item, QDomElement &parent, QDomDocument &domDoc, bool insideGridLayout); + + void setOrientation(Orientation orient); + Orientation orientation() const { return m_orient;} + void setSizeType(SizeType size); + SizeType sizeType() const; + + void setPreviewMode() { m_edit = false; } + + private: + void paintEvent(QPaintEvent *ev); + + private: + Orientation m_orient; + bool m_edit; +}; + + +#endif + + diff --git a/kexi/formeditor/tabstopdialog.cpp b/kexi/formeditor/tabstopdialog.cpp new file mode 100644 index 00000000..067c333a --- /dev/null +++ b/kexi/formeditor/tabstopdialog.cpp @@ -0,0 +1,168 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr> + Copyright (C) 2005 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 <qlayout.h> +#include <qcheckbox.h> +#include <qtooltip.h> + +#include <kiconloader.h> +#include <kdebug.h> +#include <klocale.h> +#include <kpushbutton.h> + +#include "form.h" +#include "objecttreeview.h" + +#include "tabstopdialog.h" + +using namespace KFormDesigner; + +////////////////////////////////////////////////////////////////////////////////// +////////// The Tab Stop Dialog to edit tab order /////////////////////////////// +////////////////////////////////////////////////////////////////////////////////// + +TabStopDialog::TabStopDialog(QWidget *parent) +: KDialogBase(parent, "tabstop_dialog", true, i18n("Edit Tab Order"), Ok|Cancel, Ok, false) +{ + QFrame *frame = makeMainWidget(); + QGridLayout *l = new QGridLayout(frame, 2, 2, 0, 6); + m_treeview = new ObjectTreeView(frame, "tabstops_treeview", true); + m_treeview->setItemsMovable(true); + m_treeview->setDragEnabled(true); + m_treeview->setDropVisualizer(true); + m_treeview->setAcceptDrops(true); + m_treeview->setFocus(); + l->addWidget(m_treeview, 0, 0); + + m_treeview->m_form = 0; + connect(m_treeview, SIGNAL(currentChanged(QListViewItem*)), this, SLOT(updateButtons(QListViewItem*))); + connect(m_treeview, SIGNAL(moved(QListViewItem*, QListViewItem*, QListViewItem*)), this, SLOT(updateButtons(QListViewItem*))); + + QVBoxLayout *vbox = new QVBoxLayout(); + l->addLayout(vbox, 0, 1); + m_btnUp = new KPushButton(SmallIconSet("1uparrow"), i18n("Move Up"), frame); + QToolTip::add( m_btnUp, i18n("Move widget up") ); + vbox->addWidget(m_btnUp); + connect(m_btnUp, SIGNAL(clicked()), this, SLOT(moveItemUp())); + + m_btnDown = new KPushButton(SmallIconSet("1downarrow"), i18n("Move Down"), frame); + QToolTip::add( m_btnDown, i18n("Move widget down") ); + vbox->addWidget(m_btnDown); + connect(m_btnDown, SIGNAL(clicked()), this, SLOT(moveItemDown())); + vbox->addStretch(); + + m_check = new QCheckBox(i18n("Handle tab order automatically"), frame, "tabstops_check"); + connect(m_check, SIGNAL(toggled(bool)), this, SLOT(slotRadioClicked(bool))); + l->addMultiCellWidget(m_check, 1, 1, 0, 1); + + updateGeometry(); + setInitialSize(QSize(500+m_btnUp->width(), QMAX(400,m_treeview->height()))); +} + +TabStopDialog::~TabStopDialog() +{ +} + +int TabStopDialog::exec(Form *form) +{ + m_treeview->clear(); + m_treeview->m_form = form; + + if(form->autoTabStops()) + form->autoAssignTabStops(); + form->updateTabStopsOrder(); + ObjectTreeListIterator it( form->tabStopsIterator() ); + it.toLast(); + for(;it.current(); --it) + new ObjectTreeViewItem(m_treeview, it.current()); + + m_check->setChecked(form->autoTabStops()); + + if (m_treeview->firstChild()) { + m_treeview->setCurrentItem(m_treeview->firstChild()); + m_treeview->setSelected(m_treeview->firstChild(), true); + } + + if (QDialog::Rejected == KDialogBase::exec()) + return QDialog::Rejected; + + //accepted + form->setAutoTabStops(m_check->isChecked()); + if(form->autoTabStops()) + { + form->autoAssignTabStops(); + return QDialog::Accepted; + } + + //add items to the order list + form->tabStops()->clear(); + ObjectTreeViewItem *item = (ObjectTreeViewItem*)m_treeview->firstChild(); + while(item) + { + ObjectTreeItem *tree = item->objectTree(); + if(tree) + form->tabStops()->append(tree); + item = (ObjectTreeViewItem*)item->nextSibling(); + } + return QDialog::Accepted; +} + +void +TabStopDialog::moveItemUp() +{ + if (!m_treeview->selectedItem()) + return; + QListViewItem *before = m_treeview->selectedItem()->itemAbove(); + before->moveItem(m_treeview->selectedItem()); + updateButtons(m_treeview->selectedItem()); +} + +void +TabStopDialog::moveItemDown() +{ + QListViewItem *item = m_treeview->selectedItem(); + if (!item) + return; + item->moveItem( item->nextSibling()); + updateButtons(item); +} + +void +TabStopDialog::updateButtons(QListViewItem *item) +{ + m_btnUp->setEnabled( item && (item->itemAbove() && m_treeview->isEnabled() + /*&& (item->itemAbove()->parent() == item->parent()))*/ )); + m_btnDown->setEnabled( item && item->nextSibling() && m_treeview->isEnabled() ); +} + +void +TabStopDialog::slotRadioClicked(bool isOn) +{ + m_treeview->setEnabled(!isOn); + updateButtons( m_treeview->selectedItem() ); +} + +bool +TabStopDialog::autoTabStops() const +{ + return m_check->isChecked(); +} + +#include "tabstopdialog.moc" + diff --git a/kexi/formeditor/tabstopdialog.h b/kexi/formeditor/tabstopdialog.h new file mode 100644 index 00000000..9ae6a88a --- /dev/null +++ b/kexi/formeditor/tabstopdialog.h @@ -0,0 +1,63 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef TABSTOPEDIT_DIALOG_H +#define TABSTOPEDIT_DIALOG_H + +#include <kdialogbase.h> + +class QListViewItem; +class QCheckBox; +class QToolButton; +class KPushButton; + +namespace KFormDesigner { + +class Form; +class ObjectTreeView; + +//! A dialog to edit Form tab stops +/*! The user can change the order by dragging list items or using buttons at the right. + The tab stops can be arranged automatically (see \ref Form::autoAssignTabStops()). */ +class KFORMEDITOR_EXPORT TabStopDialog : public KDialogBase +{ + Q_OBJECT + + public: + TabStopDialog(QWidget *parent); + virtual ~TabStopDialog(); + + public slots: + int exec(KFormDesigner::Form *form); + void moveItemUp(); + void moveItemDown(); + void updateButtons(QListViewItem*); + void slotRadioClicked(bool isOn); + + bool autoTabStops() const; + + protected: + ObjectTreeView *m_treeview; + KPushButton *m_btnUp, *m_btnDown; + QCheckBox *m_check; +}; + +} + +#endif diff --git a/kexi/formeditor/test/Makefile.am b/kexi/formeditor/test/Makefile.am new file mode 100644 index 00000000..f039daeb --- /dev/null +++ b/kexi/formeditor/test/Makefile.am @@ -0,0 +1,46 @@ +## Makefile.am for kformdesigner + +include $(top_srcdir)/kexi/Makefile.global + +# this is the program that gets installed. it's name is used for all +# of the other Makefile.am variables +bin_PROGRAMS = kformdesigner + +# set the include path for X, qt and KDE +INCLUDES = -I$(top_srcdir)/kexi -I$(top_srcdir)/kexi/formeditor \ + -I$(top_srcdir)/kexi/widget -I$(top_srcdir)/kexi/core \ + -I$(top_srcdir)/lib -I$(top_srcdir)/lib/kofficecore $(all_includes) + +kformdesigner_LDFLAGS = $(KDE_RPATH) $(all_libraries) + +kformdesigner_LDADD = $(top_builddir)/kexi/formeditor/libkformdesigner.la + +kformdesigner_SOURCES = main.cpp kfd_mainwindow.cpp + +# client stuff + +METASOURCES = AUTO + +kdelnkdir = $(kde_appsdir)/Development +kdelnk_DATA = kformdesigner.desktop + +rcdir = $(kde_datadir)/kformdesigner +rc_DATA = kfd_mainwindow.rc + + +# KFormDesigner KPart +kde_module_LTLIBRARIES = libkformdesigner_part.la + +libkformdesigner_part_la_SOURCES = kfd_part.cpp +libkformdesigner_part_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries) $(VER_INFO) +libkformdesigner_part_la_LIBADD = $(top_builddir)/kexi/formeditor/libkformdesigner.la $(LIB_KFILE) + +# this is where the desktop file will go +partdesktopdir = $(kde_servicesdir) +partdesktop_DATA = kformdesigner_part.desktop + +# this is where the part's XML-GUI resource file goes +partrcdir = $(kde_datadir)/kformdesigner_part +partrc_DATA = kformdesigner_part.rc kformdesigner_part_shell.rc + +KDE_ICON = kformdesigner diff --git a/kexi/formeditor/test/cr16-app-kformdesigner.png b/kexi/formeditor/test/cr16-app-kformdesigner.png Binary files differnew file mode 100644 index 00000000..fee34b1a --- /dev/null +++ b/kexi/formeditor/test/cr16-app-kformdesigner.png diff --git a/kexi/formeditor/test/cr22-app-kformdesigner.png b/kexi/formeditor/test/cr22-app-kformdesigner.png Binary files differnew file mode 100644 index 00000000..50f608cc --- /dev/null +++ b/kexi/formeditor/test/cr22-app-kformdesigner.png diff --git a/kexi/formeditor/test/cr32-app-kformdesigner.png b/kexi/formeditor/test/cr32-app-kformdesigner.png Binary files differnew file mode 100644 index 00000000..e98d13d9 --- /dev/null +++ b/kexi/formeditor/test/cr32-app-kformdesigner.png diff --git a/kexi/formeditor/test/kfd_mainwindow.cpp b/kexi/formeditor/test/kfd_mainwindow.cpp new file mode 100644 index 00000000..e9d52e47 --- /dev/null +++ b/kexi/formeditor/test/kfd_mainwindow.cpp @@ -0,0 +1,88 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr> + + 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 <kaction.h> +#include <kstdaction.h> +#include <kurl.h> +#include <kdebug.h> +#include <klibloader.h> +#include <kmessagebox.h> +#include <klocale.h> +#include <kapplication.h> + +#include "kfd_mainwindow.h" + +KFDMainWindow::KFDMainWindow() + : KParts::MainWindow() +{ + setXMLFile("kfd_mainwindow.rc"); + + setupActions(); + //statusBar()->show(); + + KLibFactory *factory = KLibLoader::self()->factory("libkformdesigner_part"); + if (factory) + { + QStringList list; + list << "shell" << "multipleMode"; + m_part = static_cast<KParts::ReadWritePart *>( factory->create(this, "kformdesigner_part", "KParts::ReadWritePart", list) ); + + if(m_part) + { + setCentralWidget(m_part->widget()); + createGUI(m_part); + } + } + else + { + KMessageBox::error(this, i18n("Could not find the KFormDesigner part. Please check your installation.")); + kapp->quit(); + return; + } + + setAutoSaveSettings(); +} + +void +KFDMainWindow::loadUIFile(const QString &filename) +{ + loadUIFile(KURL::fromPathOrURL(filename)); +} + +void +KFDMainWindow::loadUIFile(const KURL &url) +{ + m_part->openURL(url); +} + +void +KFDMainWindow::setupActions() +{ + KStdAction::quit(kapp, SLOT(quit()), actionCollection()); +} + +bool +KFDMainWindow::queryClose() +{ + if(!m_part) + return true; + + return m_part->closeURL(); +} + +#include "kfd_mainwindow.moc" diff --git a/kexi/formeditor/test/kfd_mainwindow.h b/kexi/formeditor/test/kfd_mainwindow.h new file mode 100644 index 00000000..03892a1e --- /dev/null +++ b/kexi/formeditor/test/kfd_mainwindow.h @@ -0,0 +1,46 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef KFORMDESIGNER_MAINWINDOW_H +#define KFORMDESIGNER_MAINWINDOW_H + +#include <kparts/mainwindow.h> + +class KFDMainWindow : public KParts::MainWindow +{ + Q_OBJECT + + public: + KFDMainWindow(); + ~KFDMainWindow() {;} + + /** @todo change it to bool */ + void loadUIFile(const QString &filename); + /** @todo change it to bool */ + void loadUIFile(const KURL &url); + virtual bool queryClose(); + + private: + void setupActions(); + + private: + KParts::ReadWritePart *m_part; +}; + +#endif diff --git a/kexi/formeditor/test/kfd_mainwindow.rc b/kexi/formeditor/test/kfd_mainwindow.rc new file mode 100644 index 00000000..66f48600 --- /dev/null +++ b/kexi/formeditor/test/kfd_mainwindow.rc @@ -0,0 +1,26 @@ +<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd"> +<kpartgui name="kfd_mainwindow" version="1"> +<MenuBar> + <Menu noMerge="1" name="file"><text>&File</text> + <Merge/> + <Separator/> + <Action name="file_quit"/> + </Menu> + <Menu noMerge="1" name="edit"><text>&Edit</text> + <Merge/> + </Menu> + <Menu noMerge="1" name="view"><text>&View</text> + <Merge/> + </Menu> + <Menu noMerge="1" name="tools"><text>&Tools</text> + <Merge/> + </Menu> + <Menu noMerge="1" name="widgets"><text>&Widgets</text> + <Merge/> + </Menu> + <Menu noMerge="1" name="format"><text>&Format</text> + <Merge/> + </Menu> + <Merge/> +</MenuBar> +</kpartgui>
\ No newline at end of file diff --git a/kexi/formeditor/test/kfd_part.cpp b/kexi/formeditor/test/kfd_part.cpp new file mode 100644 index 00000000..48832be8 --- /dev/null +++ b/kexi/formeditor/test/kfd_part.cpp @@ -0,0 +1,729 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr> + + 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 <qworkspace.h> +#include <qdockarea.h> +#include <qdockwindow.h> +#include <qhbox.h> +#include <qpainter.h> +#include <qevent.h> +#include <qobjectlist.h> + +#include <kdeversion.h> +#include <kaction.h> +#include <kinstance.h> +#include <klocale.h> +#include <kaboutdata.h> +#include <kdebug.h> +#include <kstdaction.h> +#include <kapplication.h> +#include <kiconloader.h> +#include <kfiledialog.h> +#include <klibloader.h> +#include <kmessagebox.h> + +#include "form.h" +#include "formIO.h" +#include "objecttree.h" +#include "container.h" +#include "formmanager.h" +#include "objecttreeview.h" +#include <koproperty/set.h> +#include <koproperty/editor.h> + +#include "kfd_part.h" + +/* +#define ENABLE_ACTION(name, enable) + if(actionCollection()->action( name )) + actionCollection()->action( name )->setEnabled( enable ) +*/ + +class KFDPart_FormManager : public KFormDesigner::FormManager +{ + public: + /*! Constructs FormManager object. + See WidgetLibrary's constructor documentation for information about + \a supportedFactoryGroups parameter. + Using \a options you can control manager's behaviour, see \ref Options. */ + KFDPart_FormManager(KFormDesignerPart *part, int options = 0, const char *name = 0) + : KFormDesigner::FormManager(part, options, name) + , m_part(part) + { + } + + virtual KAction* action( const char* name) + { + return m_part->actionCollection()->action( name ); + } + + virtual void enableAction( const char* name, bool enable ) { + if(m_part->actionCollection()->action( name )) + m_part->actionCollection()->action( name )->setEnabled( enable ); + } + + KFormDesignerPart *m_part; +}; + +KInstance *KFDFactory::m_instance = 0L; + +KFDFactory::KFDFactory() +{} + +KFDFactory::~KFDFactory() +{ + if (m_instance) + { + delete m_instance->aboutData(); + delete m_instance; + } + + m_instance = 0; +} + +KParts::Part* +KFDFactory::createPartObject( QWidget *parentWidget, const char *, QObject *, const char *name, + const char *classname, const QStringList &args) +{ + bool readOnly = (classname == "KParts::ReadOnlyPart"); + KFormDesignerPart *part = new KFormDesignerPart(parentWidget, name, readOnly, args); + return part; +} + +KInstance* +KFDFactory::instance() +{ + if (!m_instance) + m_instance = new KInstance(aboutData()); + return m_instance; +} + +KAboutData* +KFDFactory::aboutData() +{ + KAboutData *about = new KAboutData("kformdesigner_part", I18N_NOOP("Form Designer Part"), "0.3"); + return about; +} + +KFormDesigner::WidgetLibrary* KFormDesignerPart::static_formsLibrary = 0L; + +KFormDesignerPart::KFormDesignerPart(QWidget *parent, const char *name, bool readOnly, const QStringList &args) +: KParts::ReadWritePart(parent, name), m_count(0) +{ + setInstance(KFDFactory::instance()); + instance()->iconLoader()->addAppDir("kexi"); + instance()->iconLoader()->addAppDir("kformdesigner"); + + setReadWrite(!readOnly); + m_uniqueFormMode = true; + m_openingFile = false; + + if(!args.grep("multipleMode").isEmpty()) + setUniqueFormMode(false); + m_inShell = (!args.grep("shell").isEmpty()); + + QHBox *container = new QHBox(parent, "kfd_container_widget"); + + m_workspace = new QWorkspace(container, "kfd_workspace"); + m_workspace->show(); + + QStringList supportedFactoryGroups; +/* @todo add configuration for supported factory groups */ + static_formsLibrary = KFormDesigner::FormManager::createWidgetLibrary( + new KFDPart_FormManager(this, 0, "kfd_manager"), supportedFactoryGroups ); + + if(!readOnly) + { + QDockArea *dockArea = new QDockArea(Vertical, QDockArea::Reverse, container, "kfd_part_dockarea"); + + QDockWindow *dockTree = new QDockWindow(dockArea); + KFormDesigner::ObjectTreeView *view = new KFormDesigner::ObjectTreeView(dockTree); + dockTree->setWidget(view); + dockTree->setCaption(i18n("Objects")); + dockTree->setResizeEnabled(true); + dockTree->setFixedExtentWidth(256); + + QDockWindow *dockEditor = new QDockWindow(dockArea); + m_editor = new KoProperty::Editor(dockEditor); + dockEditor->setWidget(m_editor); + dockEditor->setCaption(i18n("Properties")); + dockEditor->setResizeEnabled(true); + + KFormDesigner::FormManager::self()->setEditor(m_editor); + KFormDesigner::FormManager::self()->setObjectTreeView(view); + + setupActions(); + setModified(false); + + // action stuff +// connect(m_manager, SIGNAL(widgetSelected(KFormDesigner::Form*, bool)), SLOT(slotWidgetSelected(KFormDesigner::Form*, bool))); +// connect(m_manager, SIGNAL(formWidgetSelected(KFormDesigner::Form*)), SLOT(slotFormWidgetSelected(KFormDesigner::Form*))); +// connect(m_manager, SIGNAL(noFormSelected()), SLOT(slotNoFormSelected())); + connect(KFormDesigner::FormManager::self(), SIGNAL(undoEnabled(bool, const QString&)), SLOT(setUndoEnabled(bool, const QString&))); + connect(KFormDesigner::FormManager::self(), SIGNAL(redoEnabled(bool, const QString&)), SLOT(setRedoEnabled(bool, const QString&))); + connect(KFormDesigner::FormManager::self(), SIGNAL(dirty(KFormDesigner::Form*, bool)), this, SLOT(slotFormModified(KFormDesigner::Form*, bool))); + } + + container->show(); + setWidget(container); + connect(m_workspace, SIGNAL(windowActivated(QWidget*)), KFormDesigner::FormManager::self(), SLOT(windowChanged(QWidget*))); + connect(KFormDesigner::FormManager::self(), SIGNAL(propertySetSwitched(KoProperty::Set*, bool, const QCString&)), + this, SLOT(slotPropertySetSwitched(KoProperty::Set*, bool, const QCString&))); + +// slotNoFormSelected(); + KFormDesigner::FormManager::self()->emitNoFormSelected(); +} + +KFormDesignerPart::~KFormDesignerPart() +{ +} + +KFormDesigner::WidgetLibrary* KFormDesignerPart::formsLibrary() +{ + return static_formsLibrary; +} + +void +KFormDesignerPart::setupActions() +{ + KStdAction::open(this, SLOT(open()), actionCollection()); + KStdAction::openNew(this, SLOT(createBlankForm()), actionCollection()); + KStdAction::save(this, SLOT(save()), actionCollection()); + KStdAction::saveAs(this, SLOT(saveAs()), actionCollection()); + KStdAction::cut(KFormDesigner::FormManager::self(), SLOT(cutWidget()), actionCollection()); + KStdAction::copy(KFormDesigner::FormManager::self(), SLOT(copyWidget()), actionCollection()); + KStdAction::paste(KFormDesigner::FormManager::self(), SLOT(pasteWidget()), actionCollection()); + KStdAction::undo(KFormDesigner::FormManager::self(), SLOT(undo()), actionCollection()); + KStdAction::redo(KFormDesigner::FormManager::self(), SLOT(redo()), actionCollection()); + KStdAction::selectAll(KFormDesigner::FormManager::self(), SLOT(selectAll()), actionCollection()); + new KAction(i18n("Clear Widget Contents"), "editclear", KShortcut(0), KFormDesigner::FormManager::self(), SLOT(clearWidgetContent()), actionCollection(), "clear_contents"); + new KAction(i18n("Delete Widget"), "editdelete", KShortcut(0), KFormDesigner::FormManager::self(), SLOT(deleteWidget()), actionCollection(), "edit_delete"); + new KAction(i18n("Preview Form"), "filequickprint", CTRL+Key_T, this, SLOT(slotPreviewForm()), actionCollection(), "preview_form"); + new KAction(i18n("Edit Tab Order"), "tab_order", KShortcut(0), KFormDesigner::FormManager::self(), SLOT(editTabOrder()), actionCollection(), "taborder"); + new KAction(i18n("Edit Pixmap Collection"), "icons", KShortcut(0), KFormDesigner::FormManager::self(), SLOT(editFormPixmapCollection()), actionCollection(), "pixmap_collection"); + new KAction(i18n("Edit Form Connections"), "connections", KShortcut(0), KFormDesigner::FormManager::self(), SLOT(editConnections()), actionCollection(), "form_connections"); + + KActionMenu *layoutMenu = new KActionMenu(i18n("Group Widgets"), "", actionCollection(), "layout_menu"); + layoutMenu->insert(new KAction(i18n("&Horizontally"), QString::null, KShortcut(0), KFormDesigner::FormManager::self(), SLOT(layoutHBox()), actionCollection(), "layout_hbox")); + layoutMenu->insert(new KAction(i18n("&Vertically"), QString::null, KShortcut(0), KFormDesigner::FormManager::self(), SLOT(layoutVBox()), actionCollection(), "layout_vbox")); + layoutMenu->insert(new KAction(i18n("In &Grid"), QString::null, KShortcut(0), KFormDesigner::FormManager::self(), SLOT(layoutGrid()), actionCollection(), "layout_grid")); + layoutMenu->insert(new KAction(i18n("By &Rows"), QString::null, KShortcut(0), KFormDesigner::FormManager::self(), SLOT(layoutHFlow()), actionCollection(), "layout_hflow")); + layoutMenu->insert(new KAction(i18n("By &Columns"), QString::null, KShortcut(0), KFormDesigner::FormManager::self(), SLOT(layoutVFlow()), actionCollection(), "layout_vflow")); + layoutMenu->insert(new KAction(i18n("Horizontally in &Splitter"), QString::null, KShortcut(0), KFormDesigner::FormManager::self(), SLOT(layoutHSplitter()), actionCollection(), "layout_hsplitter")); + layoutMenu->insert(new KAction(i18n("Verti&cally in Splitter"), QString::null, KShortcut(0), KFormDesigner::FormManager::self(), SLOT(layoutVSplitter()), actionCollection(), "layout_vsplitter")); + new KAction(i18n("&Ungroup Widgets"), QString::null, KShortcut(0), KFormDesigner::FormManager::self(), SLOT(breakLayout()), actionCollection(), "break_layout"); + +/* + new KAction(i18n("Lay Out Widgets &Horizontally"), QString::null, KShortcut(0), KFormDesigner::FormManager::self(), SLOT(layoutHBox()), actionCollection(), "layout_hbox"); + new KAction(i18n("Lay Out Widgets &Vertically"), QString::null, KShortcut(0), KFormDesigner::FormManager::self(), SLOT(layoutVBox()), actionCollection(), "layout_vbox"); + new KAction(i18n("Lay Out Widgets in &Grid"), QString::null, KShortcut(0), KFormDesigner::FormManager::self(), SLOT(layoutGrid()), actionCollection(), "layout_grid"); + new KAction(i18n("Lay Out Widgets H&orizontally in Splitter"), QString::null, KShortcut(0), KFormDesigner::FormManager::self(), SLOT(layoutHSplitter()), actionCollection(), "layout_hsplitter"); + new KAction(i18n("Lay Out Widgets Verti&cally in Splitter"), QString::null, KShortcut(0), KFormDesigner::FormManager::self(), SLOT(layoutVSplitter()), actionCollection(), "layout_vsplitter"); + new KAction(i18n("&Break Layout"), QString::null, KShortcut(0), KFormDesigner::FormManager::self(), SLOT(breakLayout()), actionCollection(), "break_layout"); +*/ + new KAction(i18n("Bring Widget to Front"), "raise", KShortcut(0), KFormDesigner::FormManager::self(), SLOT(bringWidgetToFront()), actionCollection(), "format_raise"); + new KAction(i18n("Send Widget to Back"), "lower", KShortcut(0), KFormDesigner::FormManager::self(), SLOT(sendWidgetToBack()), actionCollection(), "format_lower"); + + KActionMenu *alignMenu = new KActionMenu(i18n("Align Widgets' Positions"), "aopos2grid", actionCollection(), "align_menu"); + alignMenu->insert( new KAction(i18n("To Left"), "aoleft", KShortcut(0), KFormDesigner::FormManager::self(), SLOT(alignWidgetsToLeft()), actionCollection(), "align_to_left") ); + alignMenu->insert( new KAction(i18n("To Right"), "aoright", KShortcut(0), KFormDesigner::FormManager::self(), SLOT(alignWidgetsToRight()), actionCollection(), "align_to_right") ); + alignMenu->insert( new KAction(i18n("To Top"), "aotop", KShortcut(0), KFormDesigner::FormManager::self(), SLOT(alignWidgetsToTop()), actionCollection(), "align_to_top") ); + alignMenu->insert( new KAction(i18n("To Bottom"), "aobottom", KShortcut(0), KFormDesigner::FormManager::self(), SLOT(alignWidgetsToBottom()), actionCollection(), "align_to_bottom") ); + alignMenu->insert( new KAction(i18n("To Grid"), "aopos2grid", KShortcut(0), KFormDesigner::FormManager::self(), SLOT(alignWidgetsToGrid()), actionCollection(), "align_to_grid") ); + + KActionMenu *sizeMenu = new KActionMenu(i18n("Adjust Widgets' Sizes"), "aogrid", actionCollection(), "adjust_size_menu"); + sizeMenu->insert( new KAction(i18n("To Fit"), "aofit", KShortcut(0), KFormDesigner::FormManager::self(), SLOT(adjustWidgetSize()), actionCollection(), "adjust_to_fit") ); + sizeMenu->insert( new KAction(i18n("To Grid"), "aogrid", KShortcut(0), KFormDesigner::FormManager::self(), SLOT(adjustSizeToGrid()), actionCollection(), "adjust_size_grid") ); + sizeMenu->insert( new KAction(i18n("To Shortest"), "aoshortest", KShortcut(0), KFormDesigner::FormManager::self(), SLOT(adjustHeightToSmall()), actionCollection(), "adjust_height_small") ); + sizeMenu->insert( new KAction(i18n("To Tallest"), "aotallest", KShortcut(0), KFormDesigner::FormManager::self(), SLOT(adjustHeightToBig()), actionCollection(), "adjust_height_big") ); + sizeMenu->insert( new KAction(i18n("To Narrowest"), "aonarrowest", KShortcut(0), KFormDesigner::FormManager::self(), SLOT(adjustWidthToSmall()), actionCollection(), "adjust_width_small") ); + sizeMenu->insert( new KAction(i18n("To Widest"), "aowidest", KShortcut(0), KFormDesigner::FormManager::self(), SLOT(adjustWidthToBig()), actionCollection(), "adjust_width_big") ); + + if(m_inShell) + setXMLFile("kformdesigner_part_shell.rc"); + else + setXMLFile("kformdesigner_part.rc"); + KFormDesigner::FormManager::self()->createActions(formsLibrary(), actionCollection(), this); +} + +void +KFormDesignerPart::createBlankForm() +{ + if(KFormDesigner::FormManager::self()->activeForm() && m_uniqueFormMode) + { + m_openingFile = true; + closeURL(); + m_openingFile = false; + } + + if(m_uniqueFormMode && KFormDesigner::FormManager::self()->activeForm() + && !KFormDesigner::FormManager::self()->activeForm()->isModified() + && KFormDesigner::FormManager::self()->activeForm()->filename().isNull()) + return; // active form is already a blank one + + QString n = i18n("Form") + QString::number(++m_count); + Form *form = new Form(formsLibrary(), n.latin1(), + false/*!designMode, we need to set it early enough*/); + FormWidgetBase *w = new FormWidgetBase(this, m_workspace, n.latin1()); + + w->setCaption(n); + w->setIcon(SmallIcon("form")); + w->resize(350, 300); + w->show(); + w->setFocus(); + + form->createToplevel(w, w); + KFormDesigner::FormManager::self()->importForm(form); +} + +void +KFormDesignerPart::open() +{ + m_openingFile = true; + KURL url = KFileDialog::getOpenURL("::kformdesigner", i18n("*.ui|Qt Designer UI Files"), m_workspace->topLevelWidget()); + if(!url.isEmpty()) + ReadWritePart::openURL(url); + m_openingFile = false; +} + +bool +KFormDesignerPart::openFile() +{ + Form *form = new Form(formsLibrary()); + FormWidgetBase *w = new FormWidgetBase(this, m_workspace); + form->createToplevel(w, w); + + if(!KFormDesigner::FormIO::loadFormFromFile(form, w, m_file)) + { + delete form; + delete w; + return false; + } + + w->show(); + KFormDesigner::FormManager::self()->importForm(form, !isReadWrite()); + return true; +} + +bool +KFormDesignerPart::saveFile() +{ + KFormDesigner::FormIO::saveFormToFile(KFormDesigner::FormManager::self()->activeForm(), m_file); + return true; +} + +void +KFormDesignerPart::saveAs() +{ + KURL url = KFileDialog::getSaveURL("::kformdesigner", i18n("*.ui|Qt Designer UI Files"), + m_workspace->topLevelWidget()); + if(url.isEmpty()) + return; + else + ReadWritePart::saveAs(url); +} + +bool +KFormDesignerPart::closeForm(Form *form) +{ + int res = KMessageBox::questionYesNoCancel( m_workspace->topLevelWidget(), + i18n( "The form \"%1\" has been modified.\n" + "Do you want to save your changes or discard them?" ).arg( form->objectTree()->name() ), + i18n( "Close Form" ), KStdGuiItem::save(), KStdGuiItem::discard() ); + + if(res == KMessageBox::Yes) + save(); + + return (res != KMessageBox::Cancel); +} + +bool +KFormDesignerPart::closeForms() +{ + QWidgetList list = m_workspace->windowList(QWorkspace::CreationOrder); + for(QWidget *w = list.first(); w; w = list.next()) + if(w->close() == false) + return false; + + return true; +} + +bool +KFormDesignerPart::closeURL() +{ + if(!KFormDesigner::FormManager::self()->activeForm()) + return true; + + if(m_uniqueFormMode || !m_openingFile) { + if (!closeForms()) + return false; + } + + delete (KoProperty::Editor*)m_editor; + return true; +} + +void +KFormDesignerPart::slotFormModified(Form *, bool isDirty) +{ + setModified(isDirty); +} + +void +KFormDesignerPart::slotPreviewForm() +{ + if(!KFormDesigner::FormManager::self()->activeForm()) + return; + + FormWidgetBase *w = new FormWidgetBase(this, m_workspace); + KFormDesigner::FormManager::self()->previewForm(KFormDesigner::FormManager::self()->activeForm(), w); +} + +#if 0 + +void +KFormDesignerPart::slotWidgetSelected(Form *form, bool multiple) +{ + enableFormActions(); + // Enable edit actions + ENABLE_ACTION("edit_copy", true); + ENABLE_ACTION("edit_cut", true); + ENABLE_ACTION("edit_delete", true); + ENABLE_ACTION("clear_contents", true); + + // 'Align Widgets' menu + ENABLE_ACTION("align_menu", multiple); + ENABLE_ACTION("align_to_left", multiple); + ENABLE_ACTION("align_to_right", multiple); + ENABLE_ACTION("align_to_top", multiple); + ENABLE_ACTION("align_to_bottom", multiple); + + ENABLE_ACTION("adjust_size_menu", true); + ENABLE_ACTION("adjust_width_small", multiple); + ENABLE_ACTION("adjust_width_big", multiple); + ENABLE_ACTION("adjust_height_small", multiple); + ENABLE_ACTION("adjust_height_big", multiple); + + ENABLE_ACTION("format_raise", true); + ENABLE_ACTION("format_lower", true); + + // If the widgets selected is a container, we enable layout actions + bool containerSelected = false; + if(!multiple) + { + KFormDesigner::ObjectTreeItem *item = form->objectTree()->lookup( form->selectedWidgets()->first()->name() ); + if(item && item->container()) + containerSelected = true; + } + const bool twoSelected = form->selectedWidgets()->count()==2; + // Layout actions + ENABLE_ACTION("layout_menu", multiple || containerSelected); + ENABLE_ACTION("layout_hbox", multiple || containerSelected); + ENABLE_ACTION("layout_vbox", multiple || containerSelected); + ENABLE_ACTION("layout_grid", multiple || containerSelected); + ENABLE_ACTION("layout_hsplitter", twoSelected); + ENABLE_ACTION("layout_vsplitter", twoSelected); + + KFormDesigner::Container *container = KFormDesigner::FormManager::self()->activeForm()->activeContainer(); + ENABLE_ACTION("break_layout", (container->layoutType() != KFormDesigner::Container::NoLayout)); +} + +void +KFormDesignerPart::slotFormWidgetSelected(Form *form) +{ + disableWidgetActions(); + enableFormActions(); + + const bool twoSelected = form->selectedWidgets()->count()==2; + const bool hasChildren = !form->objectTree()->children()->isEmpty(); + + // Layout actions + ENABLE_ACTION("layout_menu", hasChildren); + ENABLE_ACTION("layout_hbox", hasChildren); + ENABLE_ACTION("layout_vbox", hasChildren); + ENABLE_ACTION("layout_grid", hasChildren); + ENABLE_ACTION("layout_hsplitter", twoSelected); + ENABLE_ACTION("layout_vsplitter", twoSelected); + ENABLE_ACTION("break_layout", (form->toplevelContainer()->layoutType() != KFormDesigner::Container::NoLayout)); +} + +void +KFormDesignerPart::slotNoFormSelected() +{ + disableWidgetActions(); + + // Disable paste action + ENABLE_ACTION("edit_paste", false); + + ENABLE_ACTION("edit_undo", false); + ENABLE_ACTION("edit_redo", false); + + // Disable 'Tools' actions + ENABLE_ACTION("pixmap_collection", false); + ENABLE_ACTION("form_connections", false); + ENABLE_ACTION("taborder", false); + ENABLE_ACTION("change_style", KFormDesigner::FormManager::self()->activeForm()); + + // Disable items in 'File' + ENABLE_ACTION("file_save", false); + ENABLE_ACTION("file_save_as", false); + ENABLE_ACTION("preview_form", false); +} + +void +KFormDesignerPart::enableFormActions() +{ + // Enable 'Tools' actions + ENABLE_ACTION("pixmap_collection", true); + ENABLE_ACTION("form_connections", true); + ENABLE_ACTION("taborder", true); + ENABLE_ACTION("change_style", true); + + // Enable items in 'File' + ENABLE_ACTION("file_save", true); + ENABLE_ACTION("file_save_as", true); + ENABLE_ACTION("preview_form", true); + + ENABLE_ACTION("edit_paste", KFormDesigner::FormManager::self()->isPasteEnabled()); + ENABLE_ACTION("edit_select_all", true); +} + +void +KFormDesignerPart::disableWidgetActions() +{ + // Disable edit actions + ENABLE_ACTION("edit_copy", false); + ENABLE_ACTION("edit_cut", false); + ENABLE_ACTION("edit_delete", false); + ENABLE_ACTION("clear_contents", false); + + // Disable format functions + ENABLE_ACTION("align_menu", false); + ENABLE_ACTION("align_to_left", false); + ENABLE_ACTION("align_to_right", false); + ENABLE_ACTION("align_to_top", false); + ENABLE_ACTION("align_to_bottom", false); + ENABLE_ACTION("adjust_size_menu", false); + ENABLE_ACTION("format_raise", false); + ENABLE_ACTION("format_lower", false); + + ENABLE_ACTION("layout_menu", false); + ENABLE_ACTION("layout_hbox", false); + ENABLE_ACTION("layout_vbox", false); + ENABLE_ACTION("layout_grid", false); + ENABLE_ACTION("layout_hsplitter", false); + ENABLE_ACTION("layout_vsplitter", false); + ENABLE_ACTION("break_layout", false); +} +#endif + +void +KFormDesignerPart::setUndoEnabled(bool enabled, const QString &text) +{ + KAction *undoAction = actionCollection()->action("edit_undo"); + if(undoAction) + { + if(!text.isNull()) + undoAction->setText(text); + } +} + +void +KFormDesignerPart::setRedoEnabled(bool enabled, const QString &text) +{ + KAction *redoAction = actionCollection()->action("edit_redo"); + if(redoAction) + { + if(!text.isNull()) + redoAction->setText(text); + } +} + + +////// FormWidgetBase : helper widget to draw rects on top of widgets + +//repaint all children widgets +static void repaintAll(QWidget *w) +{ + w->repaint(); + QObjectList *list = w->queryList("QWidget"); + QObjectListIt it(*list); + for (QObject *obj; (obj=it.current()); ++it ) { + static_cast<QWidget*>(obj)->repaint(); + } + delete list; +} + +void +FormWidgetBase::drawRects(const QValueList<QRect> &list, int type) +{ + QPainter p; + p.begin(this, true); + bool unclipped = testWFlags( WPaintUnclipped ); + setWFlags( WPaintUnclipped ); + + if (prev_rect.isValid()) { + //redraw prev. selection's rectangle + p.drawPixmap( QPoint(prev_rect.x()-2, prev_rect.y()-2), buffer, QRect(prev_rect.x()-2, prev_rect.y()-2, prev_rect.width()+4, prev_rect.height()+4)); + } + p.setBrush(QBrush::NoBrush); + if(type == 1) // selection rect + p.setPen(QPen(white, 1, Qt::DotLine)); + else if(type == 2) // insert rect + p.setPen(QPen(white, 2)); + p.setRasterOp(XorROP); + + prev_rect = QRect(); + QValueList<QRect>::ConstIterator endIt = list.constEnd(); + for(QValueList<QRect>::ConstIterator it = list.constBegin(); it != endIt; ++it) { + p.drawRect(*it); + prev_rect = prev_rect.unite(*it); + } + + if (!unclipped) + clearWFlags( WPaintUnclipped ); + p.end(); +} + +void +FormWidgetBase::drawRect(const QRect& r, int type) +{ + QValueList<QRect> l; + l.append(r); + drawRects(l, type); +} + +void +FormWidgetBase::initBuffer() +{ + repaintAll(this); + buffer.resize( width(), height() ); + buffer = QPixmap::grabWindow( winId() ); + prev_rect = QRect(); +} + +void +FormWidgetBase::clearForm() +{ + QPainter p; + p.begin(this, true); + bool unclipped = testWFlags( WPaintUnclipped ); + setWFlags( WPaintUnclipped ); + + //redraw entire form surface + p.drawPixmap( QPoint(0,0), buffer, QRect(0,0,buffer.width(), buffer.height()) ); + + if (!unclipped) + clearWFlags( WPaintUnclipped ); + p.end(); + + repaintAll(this); +} + +void +FormWidgetBase::highlightWidgets(QWidget *from, QWidget *to)//, const QPoint &point) +{ + QPoint fromPoint, toPoint; + if(from && from->parentWidget() && (from != this)) + fromPoint = from->parentWidget()->mapTo(this, from->pos()); + if(to && to->parentWidget() && (to != this)) + toPoint = to->parentWidget()->mapTo(this, to->pos()); + + QPainter p; + p.begin(this, true); + bool unclipped = testWFlags( WPaintUnclipped ); + setWFlags( WPaintUnclipped ); + + if (prev_rect.isValid()) { + //redraw prev. selection's rectangle + p.drawPixmap( QPoint(prev_rect.x(), prev_rect.y()), buffer, QRect(prev_rect.x(), prev_rect.y(), prev_rect.width(), prev_rect.height())); + } + + p.setPen( QPen(Qt::red, 2) ); + + if(to) + { + QPixmap pix1 = QPixmap::grabWidget(from); + QPixmap pix2 = QPixmap::grabWidget(to); + + if((from != this) && (to != this)) + p.drawLine( from->parentWidget()->mapTo(this, from->geometry().center()), to->parentWidget()->mapTo(this, to->geometry().center()) ); + + p.drawPixmap(fromPoint.x(), fromPoint.y(), pix1); + p.drawPixmap(toPoint.x(), toPoint.y(), pix2); + + if(to == this) + p.drawRoundRect(2, 2, width()-4, height()-4, 4, 4); + else + p.drawRoundRect(toPoint.x(), toPoint.y(), to->width(), to->height(), 5, 5); + } + + if(from == this) + p.drawRoundRect(2, 2, width()-4, height()-4, 4, 4); + else + p.drawRoundRect(fromPoint.x(), fromPoint.y(), from->width(), from->height(), 5, 5); + + if((to == this) || (from == this)) + prev_rect = QRect(0, 0, buffer.width(), buffer.height()); + else if(to) + { + prev_rect.setX( (fromPoint.x() < toPoint.x()) ? (fromPoint.x() - 5) : (toPoint.x() - 5) ); + prev_rect.setY( (fromPoint.y() < toPoint.y()) ? (fromPoint.y() - 5) : (toPoint.y() - 5) ); + prev_rect.setRight( (fromPoint.x() < toPoint.x()) ? (toPoint.x() + to->width() + 10) : (fromPoint.x() + from->width() + 10) ); + prev_rect.setBottom( (fromPoint.y() < toPoint.y()) ? (toPoint.y() + to->height() + 10) : (fromPoint.y() + from->height() + 10) ) ; + } + else + prev_rect = QRect(fromPoint.x()- 5, fromPoint.y() -5, from->width() + 10, from->height() + 10); + + if (!unclipped) + clearWFlags( WPaintUnclipped ); + p.end(); +} + +void +FormWidgetBase::closeEvent(QCloseEvent *ev) +{ + Form *form = KFormDesigner::FormManager::self()->formForWidget(this); + if(!form || !form->isModified() || !form->objectTree()) // == preview form + ev->accept(); + else + { + bool close = m_part->closeForm(form); + if(close) + ev->accept(); + else + ev->ignore(); + } +} + +void KFormDesignerPart::slotPropertySetSwitched(KoProperty::Set *set, bool forceReload, + const QCString& propertyToSelect) +{ + if (m_editor) { + if (propertyToSelect.isEmpty() && forceReload) + m_editor->changeSet(set, propertyToSelect); + else + m_editor->changeSet(set); + } +} + +K_EXPORT_COMPONENT_FACTORY(libkformdesigner_part, KFDFactory) + +#include "kfd_part.moc" + diff --git a/kexi/formeditor/test/kfd_part.h b/kexi/formeditor/test/kfd_part.h new file mode 100644 index 00000000..77b809ca --- /dev/null +++ b/kexi/formeditor/test/kfd_part.h @@ -0,0 +1,142 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef KFORMDESIGNER_PART_H +#define KFORMDESIGNER_PART_H + +#include <qwidget.h> +#include <qpixmap.h> + +#include <kparts/part.h> +#include <kparts/factory.h> + +#include "form.h" + +class KAboutData; +class KInstance; +class QWorkspace; +class QCloseEvent; + +using KFormDesigner::Form; + +class KFORMEDITOR_EXPORT KFDFactory : public KParts::Factory +{ + Q_OBJECT + + public: + KFDFactory(); + virtual ~KFDFactory(); + + virtual KParts::Part* createPartObject(QWidget *parentWidget=0, const char *widgetName=0, QObject *parent=0, const char *name=0, + const char *classname="KParts::Part", const QStringList &args=QStringList()); + + static KInstance *instance(); + static KAboutData *aboutData(); + + private: + static KInstance *m_instance; +}; + +class KFORMEDITOR_EXPORT KFormDesignerPart: public KParts::ReadWritePart +{ + Q_OBJECT + + public: + KFormDesignerPart(QWidget *parent, const char *name, bool readOnly=true, const QStringList &args=QStringList()); + virtual ~KFormDesignerPart(); + + static KFormDesigner::WidgetLibrary* formsLibrary(); + +// KFormDesigner::FormManager* manager() { return m_manager; } + void setUniqueFormMode(bool enable) { m_uniqueFormMode = enable; } + + bool closeForm(Form *form); + bool closeForms(); + + virtual bool closeURL(); + + public slots: + /*! Creates a new blank Form. The new Form is shown and become the active Form. */ + void createBlankForm(); + + /*! Loads a Form from a UI file. A "Open File" dialog is shown to select the file. The loaded Form is shown and becomes + the active Form. */ + void open(); + + void slotPreviewForm(); + void saveAs(); + //void slotCreateFormSlot(KFormDesigner::Form *form, const QString &widget, const QString &signal); + + protected slots: + void slotFormModified(KFormDesigner::Form *form, bool isDirty); +//moved to manager void slotWidgetSelected(KFormDesigner::Form *form, bool multiple); +//moved to manager void slotFormWidgetSelected(KFormDesigner::Form *form); +//moved to manager void slotNoFormSelected(); + void setUndoEnabled(bool enabled, const QString &text); + void setRedoEnabled(bool enabled, const QString &text); + + /*! Shows a property set \a set in a Property Editor. */ + void slotPropertySetSwitched(KoProperty::Set *set, bool forceReload = false, + const QCString& propertyToSelect = QCString()); + + protected: + virtual bool openFile(); + virtual bool saveFile(); + void disableWidgetActions(); + void enableFormActions(); + void setupActions(); + + private: + static KFormDesigner::WidgetLibrary* static_formsLibrary; +// KFormDesigner::FormManager *m_manager; + QWorkspace *m_workspace; + QGuardedPtr<KoProperty::Editor> m_editor; + int m_count; + bool m_uniqueFormMode; + bool m_openingFile; + bool m_inShell; +}; + +//! Helper: this widget is used to create form's surface +class KFORMEDITOR_EXPORT FormWidgetBase : public QWidget, public KFormDesigner::FormWidget +{ + Q_OBJECT + + public: + FormWidgetBase(KFormDesignerPart *part, QWidget *parent = 0, const char *name = 0, int WFlags = WDestructiveClose) + : QWidget(parent, name, WFlags), m_part(part) {} + ~FormWidgetBase() {;} + + void drawRect(const QRect& r, int type); + void drawRects(const QValueList<QRect> &list, int type); + void initBuffer(); + void clearForm(); + void highlightWidgets(QWidget *from, QWidget *to);//, const QPoint &p); + + protected: + void closeEvent(QCloseEvent *ev); + + private: + QPixmap buffer; //!< stores grabbed entire form's area for redraw + QRect prev_rect; //!< previously selected rectangle + KFormDesignerPart *m_part; +}; + +#endif + diff --git a/kexi/formeditor/test/kformdesigner.desktop b/kexi/formeditor/test/kformdesigner.desktop new file mode 100644 index 00000000..4a018915 --- /dev/null +++ b/kexi/formeditor/test/kformdesigner.desktop @@ -0,0 +1,61 @@ +[Desktop Entry] +Name=KFormDesigner +Name[cs]=Návrhář formulářů +Name[cy]=DylunyddKForm +Name[fa]=طراح KForm +Name[ne]=केडीई फाराम डिजाइनकर्ता +Name[sv]=Kformdesigner +Name[ta]=படிவம் வடிவமைப்பவர் +Name[tg]=KДизайнгари шакл +Name[tr]=KFormTasarımcısı +Name[zh_CN]=KForm 设计器 +Exec=kformdesigner %i %m -caption "%c" +Icon=kformdesigner +Type=Application +DocPath=kformdesigner/kformdesigner.html +GenericName=Form Designer +GenericName[bg]=Проектиране на форми +GenericName[br]=Ergrafer ar paperenn-reol +GenericName[ca]=Dissenyador de formulari +GenericName[cy]=Dylunydd Ffurflenni +GenericName[de]=Formular-Designer +GenericName[el]=Σχεδιαστής φόρμας +GenericName[eo]=Formulardesegnilo +GenericName[es]=Diseñador de formularios +GenericName[et]=Vormikujundaja +GenericName[eu]=Formularioen diseinatzailea +GenericName[fa]=طراح برگه +GenericName[fi]=Lomakkeen suunnittleija +GenericName[fr]=Concepteur d'interfaces graphiques +GenericName[gl]=Deseño de Formularios +GenericName[he]=מעצב טפסים +GenericName[hr]=Dizajner obrazaca +GenericName[hu]=Űrlaptervező +GenericName[is]=Form hönnuður +GenericName[it]=Progetto dei moduli +GenericName[ja]=フォームデザイナー +GenericName[km]=កម្មវិធីរចនាសំណុំបែបបទ +GenericName[lv]=Formu veidotājs +GenericName[ms]=Pereka Bentuk Borang +GenericName[nb]=Skjemautforming +GenericName[nds]=Kiekwark-Maker +GenericName[ne]=फारम डिजाइनकर्ता +GenericName[nn]=Skjemautforming +GenericName[pl]=Projektant formularzy +GenericName[pt]=Desenho de Formulários +GenericName[pt_BR]=Desenhista de Formulário +GenericName[ru]=Редактор форм +GenericName[se]=Skovvehápmejeaddji +GenericName[sl]=Oblikovalec obrazcev +GenericName[sr]=Дизајнер форми +GenericName[sr@Latn]=Dizajner formi +GenericName[sv]=Formulärkonstruktion +GenericName[uk]=Дизайнер форм +GenericName[uz]=Shakl dizayneri +GenericName[uz@cyrillic]=Шакл дизайнери +GenericName[zh_CN]=表单设计器 +GenericName[zh_TW]=表單設計師 +MimeType=application/x-designer; +Terminal=false +Categories=Qt;KDE;Development; + diff --git a/kexi/formeditor/test/kformdesigner_part.desktop b/kexi/formeditor/test/kformdesigner_part.desktop new file mode 100644 index 00000000..b0f38454 --- /dev/null +++ b/kexi/formeditor/test/kformdesigner_part.desktop @@ -0,0 +1,52 @@ +[Desktop Entry] +Name=Form Designer +Name[bg]=Проектиране на форми +Name[br]=Ergrafer ar paperenn-reol +Name[ca]=Dissenyador de formulari +Name[cs]=Návrhář formulářů +Name[cy]=Dylunydd Ffurflenni +Name[de]=Formular-Designer +Name[el]=Σχεδιαστής φόρμας +Name[eo]=Formulardesegnilo +Name[es]=Diseñador de formularios +Name[et]=Vormikujundaja +Name[eu]=Formularioen diseinatzailea +Name[fa]=طراح برگه +Name[fi]=Lomakkeen suunnittelija +Name[fr]=Composeur d'interfaces graphiques +Name[gl]=Deseño de Formularios +Name[he]=מעצב טפסים +Name[hr]=Dizajner obrazaca +Name[hu]=Űrlaptervező +Name[is]=Form hönnuður +Name[it]=Progetto dei moduli +Name[ja]=フォームデザイナー +Name[km]=កម្មវិធីរចនាសំណុំបែបបទ +Name[lt]=Formų kūrimo programa +Name[lv]=Formu veidotājs +Name[ms]=Pereka Bentuk Borang +Name[nb]=Skjemautforming +Name[nds]=Kiekwark-Maker +Name[ne]=फारम डिजाइनकर्ता +Name[nn]=Skjemautforming +Name[pl]=Projektant formularzy +Name[pt]=Desenho de Formulários +Name[pt_BR]=Desenhista de Formulário +Name[ru]=Редактор форм +Name[se]=Skovvehápmejeaddji +Name[sl]=Oblikovalec obrazcev +Name[sr]=Дизајнер форми +Name[sr@Latn]=Dizajner formi +Name[sv]=Formulärkonstruktion +Name[ta]=படிவ வடிவமைப்பாளர் +Name[tg]=Дизайнгари шакл +Name[tr]=FormTasarımcısı +Name[uk]=Дизайнер форм +Name[uz]=Shakl dizayneri +Name[uz@cyrillic]=Шакл дизайнери +Name[zh_CN]=表单设计器 +Name[zh_TW]=表單設計師 +MimeType=application/x-designer; +ServiceTypes=KParts/ReadOnlyPart,KParts/ReadWritePart +X-KDE-Library=libkformdesigner_part +Type=Service diff --git a/kexi/formeditor/test/kformdesigner_part.rc b/kexi/formeditor/test/kformdesigner_part.rc new file mode 100644 index 00000000..03c25f19 --- /dev/null +++ b/kexi/formeditor/test/kformdesigner_part.rc @@ -0,0 +1,125 @@ +<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd"> +<kpartgui name="kfd_part" version="2"> + +<MenuBar> + <Menu name="edit" noMerge="1"> + <Action name="edit_undo" group="edit_undo_merge"/> + <Action name="edit_redo" group="edit_undo_merge"/> + <Separator group="edit_undo_merge"/> + <Action name="edit_cut" group="edit_paste_merge"/> + <Action name="edit_copy" group="edit_paste_merge"/> + <Action name="edit_paste" group="edit_paste_merge"/> + <Action name="edit_delete" group="edit_paste_merge"/> + <Action name="clear_contents" group="edit_paste_merge"/> + <Action name="edit_select_all" group="edit_select_merge"/> + </Menu> + <Menu name="tools" noMerge="1"> + <Merge/> + <Separator/> + <Action name="taborder"/> + <Action name="change_style"/> + <Action name="pixmap_collection"/> + <Action name="form_connections"/> + </Menu> + <Menu name="widgets" noMerge="1"> + <text>&Widgets</text> + <Merge/> + <Action name="pointer"/> + <Action name="drag_connection"/> + <Separator/> + <Action name="library_widget_SubForm"/> + <Action name="library_widget_QGroupBox"/> + <Action name="library_widget_KFDTabWidget"/> + <Action name="library_widget_QWidgetStack"/> + <Action name="library_widget_QFrame"/> + <Separator/> + <Action name="library_widget_KLineEdit"/> + <Action name="library_widget_QLabel"/> + <Action name="library_widget_KexiPictureLabel"/> + <Action name="library_widget_KPushButton"/> + <Action name="library_widget_QRadioButton"/> + <Action name="library_widget_QCheckBox"/> + <Action name="library_widget_KIntSpinBox"/> + <Action name="library_widget_KComboBox"/> + <Action name="library_widget_KListBox"/> + <Action name="library_widget_KTextEdit"/> + <Action name="library_widget_KListView"/> + <Action name="library_widget_QSlider"/> + <Action name="library_widget_KProgress"/> + <Action name="library_widget_KTimeWidget"/> + <Action name="library_widget_KDateWidget"/> + <Action name="library_widget_KDateTimeWidget"/> + <Action name="library_widget_Line"/> + <Action name="library_widget_Spring"/> + </Menu> + <Menu name="format" noMerge="1"> + <text>&Format</text> + <Action name="snap_to_grid"/> + <Separator/> + <Action name="layout_menu"/> + <Action name="break_layout"/> + <Separator/> + <Action name="formpart_break_layout"/> + <Action name="align_menu"/> + <Action name="adjust_size_menu"/> + <Separator/> + <Action name="format_raise"/> + <Action name="format_lower"/> + </Menu> +</MenuBar> + +<ToolBar name="fileToolBar" noMerge="1"><text>Main Toolbar</text> + <Action name="preview_form"/> + <Separator/> + <Action name="edit_undo"/> + <Action name="edit_redo"/> + <Separator/> + <Action name="edit_cut"/> + <Action name="edit_copy"/> + <Action name="edit_paste"/> +</ToolBar> +<ToolBar name="containers" fullWidth="false" noMerge="1"> + <text>Containers</text> + <Action name="library_widget_SubForm"/> + <Action name="library_widget_QGroupBox"/> + <Action name="library_widget_KFDTabWidget"/> + <Action name="library_widget_QWidgetStack"/> + <Action name="library_widget_QFrame"/> +</ToolBar> +<ToolBar name="widgets" fullWidth="false" noMerge="1"> + <text>Widgets</text> + <Action name="pointer"/> + <Action name="drag_connection"/> + <Separator/> + <Action name="library_widget_KLineEdit"/> + <Action name="library_widget_QLabel"/> + <Action name="library_widget_KexiPictureLabel"/> + <Action name="library_widget_KPushButton"/> + <Action name="library_widget_QRadioButton"/> + <Action name="library_widget_QCheckBox"/> + <Action name="library_widget_KIntSpinBox"/> + <Action name="library_widget_KComboBox"/> + <Action name="library_widget_KListBox"/> + <Action name="library_widget_KTextEdit"/> + <Action name="library_widget_KListView"/> + <Action name="library_widget_QSlider"/> + <Action name="library_widget_KProgress"/> + <Action name="library_widget_KTimeWidget"/> + <Action name="library_widget_KDateWidget"/> + <Action name="library_widget_KDateTimeWidget"/> + <Action name="library_widget_Line"/> + <Action name="library_widget_Spring"/> + <ActionList name="undo_actions" /> +</ToolBar> +<ToolBar name="tools" fullWidth="false" noMerge="1"> +<text>Tools Toolbar</text> + <Action name="pixmap_collection"/> + <!-- <Action name="change_style"/> !--> +</ToolBar> +<ToolBar name="format" fullWidth="false" noMerge="1"> +<text>Format Toolbar</text> + <Action name="align_menu"/> + <Action name="adjust_size_menu"/> +</ToolBar> + +</kpartgui>
\ No newline at end of file diff --git a/kexi/formeditor/test/kformdesigner_part_shell.rc b/kexi/formeditor/test/kformdesigner_part_shell.rc new file mode 100644 index 00000000..8163cab3 --- /dev/null +++ b/kexi/formeditor/test/kformdesigner_part_shell.rc @@ -0,0 +1,138 @@ +<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd"> +<kpartgui name="kfd_part" version="2"> + +<MenuBar> + <Menu name="file" noMerge="1"> + <Action name="file_new"/> + <Action name="file_open"/> + <Separator /> + <Action name="file_save"/> + <Action name="file_save_as"/> + <Separator/> + </Menu> + <Menu name="edit" noMerge="1"> + <Action name="edit_undo" group="edit_undo_merge"/> + <Action name="edit_redo" group="edit_undo_merge"/> + <Separator group="edit_undo_merge"/> + <Action name="edit_cut" group="edit_paste_merge"/> + <Action name="edit_copy" group="edit_paste_merge"/> + <Action name="edit_paste" group="edit_paste_merge"/> + <Action name="edit_delete" group="edit_paste_merge"/> + <Action name="clear_contents" group="edit_paste_merge"/> + <Separator group="edit_select_merge"/> + <Action name="edit_select_all" group="edit_select_merge"/> + <Separator group="edit_paste_merge"/> + </Menu> + <Menu name="view" noMerge="1"> + <Action name="preview_form"/> + </Menu> + <Menu name="tools" noMerge="1"> + <Action name="taborder"/> + <Action name="change_style"/> + <Action name="pixmap_collection"/> + <Action name="form_connections"/> + </Menu> + <Menu name="widgets" noMerge="1"> + <text>&Widgets</text> + <Merge/> + <Action name="pointer"/> + <Action name="drag_connection"/> + <Separator/> + <Action name="library_widget_SubForm"/> + <Action name="library_widget_QGroupBox"/> + <Action name="library_widget_KFDTabWidget"/> + <Action name="library_widget_QWidgetStack"/> + <Action name="library_widget_QFrame"/> + <Separator/> + <Action name="library_widget_KLineEdit"/> + <Action name="library_widget_QLabel"/> + <Action name="library_widget_KexiPictureLabel"/> + <Action name="library_widget_KPushButton"/> + <Action name="library_widget_QRadioButton"/> + <Action name="library_widget_QCheckBox"/> + <Action name="library_widget_KIntSpinBox"/> + <Action name="library_widget_KComboBox"/> + <Action name="library_widget_KListBox"/> + <Action name="library_widget_KTextEdit"/> + <Action name="library_widget_KListView"/> + <Action name="library_widget_QSlider"/> + <Action name="library_widget_KProgress"/> + <Action name="library_widget_KTimeWidget"/> + <Action name="library_widget_KDateWidget"/> + <Action name="library_widget_KDateTimeWidget"/> + <Action name="library_widget_Line"/> + <Action name="library_widget_Spring"/> + </Menu> + <Menu name="format" noMerge="1"> + <text>&Format</text> + <Action name="snap_to_grid"/> + <Separator/> + <Action name="layout_menu"/> + <Action name="break_layout"/> + <Separator/> + <Action name="align_menu"/> + <Action name="adjust_size_menu"/> + <Separator/> + <Action name="format_raise"/> + <Action name="format_lower"/> + </Menu> +</MenuBar> + +<ToolBar name="fileToolBar" noMerge="1"><text>Main Toolbar</text> + <Action name="file_new"/> + <Action name="file_open"/> + <Action name="file_save"/> + <Action name="preview_form"/> + <Separator/> + <Action name="edit_undo"/> + <Action name="edit_redo"/> + <Separator/> + <Action name="edit_cut"/> + <Action name="edit_copy"/> + <Action name="edit_paste"/> +</ToolBar> +<ToolBar name="containers" fullWidth="false" noMerge="1"> + <text>Containers</text> + <Action name="library_widget_SubForm"/> + <Action name="library_widget_QGroupBox"/> + <Action name="library_widget_KFDTabWidget"/> + <Action name="library_widget_QWidgetStack"/> + <Action name="library_widget_QFrame"/> +</ToolBar> +<ToolBar name="widgets" fullWidth="false" noMerge="1"> + <text>Widgets</text> + <Action name="pointer"/> + <Action name="drag_connection"/> + <Separator/> + <Action name="library_widget_KLineEdit"/> + <Action name="library_widget_QLabel"/> + <Action name="library_widget_KexiPictureLabel"/> + <Action name="library_widget_KPushButton"/> + <Action name="library_widget_QRadioButton"/> + <Action name="library_widget_QCheckBox"/> + <Action name="library_widget_KIntSpinBox"/> + <Action name="library_widget_KComboBox"/> + <Action name="library_widget_KListBox"/> + <Action name="library_widget_KTextEdit"/> + <Action name="library_widget_KListView"/> + <Action name="library_widget_QSlider"/> + <Action name="library_widget_KProgress"/> + <Action name="library_widget_KTimeWidget"/> + <Action name="library_widget_KDateWidget"/> + <Action name="library_widget_KDateTimeWidget"/> + <Action name="library_widget_Line"/> + <Action name="library_widget_Spring"/> + <ActionList name="undo_actions" /> +</ToolBar> +<ToolBar name="tools" fullWidth="false" noMerge="1"> +<text>Tools Toolbar</text> + <Action name="pixmap_collection"/> + <!-- <Action name="change_style"/> !--> +</ToolBar> +<ToolBar name="format" fullWidth="false" noMerge="1"> +<text>Format Toolbar</text> + <Action name="align_menu"/> + <Action name="adjust_size_menu"/> +</ToolBar> + +</kpartgui>
\ No newline at end of file diff --git a/kexi/formeditor/test/main.cpp b/kexi/formeditor/test/main.cpp new file mode 100644 index 00000000..f4866f29 --- /dev/null +++ b/kexi/formeditor/test/main.cpp @@ -0,0 +1,81 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr> + + 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 "kfd_mainwindow.h" + +#include <kapplication.h> +#include <kiconloader.h> +#include <kaboutdata.h> +#include <kcmdlineargs.h> +#include <klocale.h> + +static const char *description = + I18N_NOOP("KFormDesigner"); + +static const char *version = "0.3"; + +static KCmdLineOptions options[] = +{ + { "+[URL]", I18N_NOOP( "Document to open" ), 0 }, + KCmdLineLastOption +}; + +int main(int argc, char **argv) +{ + KAboutData about("kformdesigner", I18N_NOOP("KFormDesigner"), version, description, + KAboutData::License_LGPL, "(C) 2003-2005 Kexi Team", 0, 0); + about.addCredit( "Lucijan Busch", "Original author", 0, "lucijan@kde.org" ); + about.addAuthor( "Cedric Pasteur", 0, "cedric.pasteur@free.fr"); + about.addCredit( "Jarosław Staniek", "Win32 version, some icons, many fixes, ideas and bug reports", "js@iidea.pl", 0); + about.addCredit( "Kristof Borrey ", "Icons", 0, "kristof.borrey@skynet.be" ); + KCmdLineArgs::init(argc, argv, &about); + KCmdLineArgs::addCmdLineOptions(options); + KApplication app; + + KGlobal::iconLoader()->addAppDir("kexi"); + + KFDMainWindow *v = new KFDMainWindow(); + if (!v->centralWidget()) { //KFD part could be not found + delete v; + return 1; + } + app.setMainWidget(v); + v->show(); + + + + // see if we are starting with session management + if (app.isRestored()) + { + RESTORE(KFDMainWindow); + } + else + { + // no session.. just start up normally + KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); + if (args->count() >= 1) + { + for (int i = 0; i < args->count(); i++) + /** @todo report loading errors here */ + v->loadUIFile(args->url(i)); + } + args->clear(); + } + + return app.exec(); +} diff --git a/kexi/formeditor/utils.cpp b/kexi/formeditor/utils.cpp new file mode 100644 index 00000000..0c3acf59 --- /dev/null +++ b/kexi/formeditor/utils.cpp @@ -0,0 +1,184 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr> + Copyright (C) 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 <qcursor.h> +#include <qobjectlist.h> +#include <qtabwidget.h> +#include <qtabbar.h> + +#include <kdebug.h> +#include <kexiutils/utils.h> + +#include "form.h" +#include "objecttree.h" +#include "utils.h" + +using namespace KFormDesigner; + +void +KFormDesigner::removeChildrenFromList(WidgetList &list) +{ + for(WidgetListIterator it(list); it.current() != 0; ++it) { + QWidget *w = it.current(); + + // If any widget in the list is a child of this widget, we remove it from the list + for(WidgetListIterator it2(list); it2.current() != 0; ++it2) { + QWidget *widg = it2.current(); + if((w != widg) && (w->child(widg->name()))) + { + kdDebug() << "Removing the widget " << widg->name() << "which is a child of " << w->name() << endl; + list.remove(widg); + } + } + } +} + +void +KFormDesigner::installRecursiveEventFilter(QObject *object, QObject *container) +{ + if(!object || !container|| !object->isWidgetType()) + return; + + kdDebug() << "Installing event filter on widget: " << object->name() << " directed to " << container->name() << endl; + object->installEventFilter(container); + if(((QWidget*)object)->ownCursor()) + ((QWidget*)object)->setCursor(QCursor(Qt::ArrowCursor)); + + if(!object->children()) + return; + + QObjectList list = *(object->children()); + for(QObject *obj = list.first(); obj; obj = list.next()) + installRecursiveEventFilter(obj, container); +} + +void +KFormDesigner::removeRecursiveEventFilter(QObject *object, QObject *container) +{ + object->removeEventFilter(container); + if(!object->isWidgetType()) + return; + if(!object->children()) + return; + + QObjectList list = *(object->children()); + for(QObject *obj = list.first(); obj; obj = list.next()) + removeRecursiveEventFilter(obj, container); +} + +void +KFormDesigner::setRecursiveCursor(QWidget *w, Form *form) +{ + ObjectTreeItem *tree = form->objectTree()->lookup(w->name()); + if(tree && ((tree->modifiedProperties()->contains("cursor")) || !tree->children()->isEmpty()) + && !w->inherits("QLineEdit") && !w->inherits("QTextEdit") + ) //fix weird behaviour + return; // if the user has set a cursor for this widget or this is a container, don't change it + + if(w->ownCursor()) + w->setCursor(Qt::ArrowCursor); + + QObjectList *l = w->queryList( "QWidget" ); + for(QObject *o = l->first(); o; o = l->next()) + ((QWidget*)o)->setCursor(Qt::ArrowCursor); + delete l; +} + +QSize +KFormDesigner::getSizeFromChildren(QWidget *w, const char *inheritClass) +{ + int tmpw = 0, tmph = 0; + QObjectList *list = w->queryList(inheritClass, 0, false, false); + for(QObject *o = list->first(); o; o = list->next()) { + QRect r = ((QWidget*)o)->geometry(); + tmpw = QMAX(tmpw, r.right()); + tmph = QMAX(tmph, r.bottom()); + } + + delete list; + return QSize(tmpw, tmph) + QSize(10, 10); +} + +// ----------------- + +HorWidgetList::HorWidgetList(QWidget *topLevelWidget) + : WidgetList() + , m_topLevelWidget(topLevelWidget) +{ +} + +HorWidgetList::~HorWidgetList() +{ +} + +int HorWidgetList::compareItems(QPtrCollection::Item item1, QPtrCollection::Item item2) +{ + QWidget *w1 = static_cast<QWidget*>(item1); + QWidget *w2 = static_cast<QWidget*>(item2); + return w1->mapTo(m_topLevelWidget, QPoint(0,0)).x() - w2->mapTo(m_topLevelWidget, QPoint(0,0)).x(); +} + +// ----------------- + +VerWidgetList::VerWidgetList(QWidget *topLevelWidget) + : WidgetList() + , m_topLevelWidget(topLevelWidget) +{ +} + +VerWidgetList::~VerWidgetList() +{ +} + +int VerWidgetList::compareItems(QPtrCollection::Item item1, QPtrCollection::Item item2) +{ + QWidget *w1 = static_cast<QWidget*>(item1); + QWidget *w2 = static_cast<QWidget*>(item2); + + int y1, y2; + QObject *page1 = 0; + TabWidget *tw1 = KFormDesigner::findParent<KFormDesigner::TabWidget>(w1, "KFormDesigner::TabWidget", page1); + if (tw1) // special case + y1 = w1->mapTo(m_topLevelWidget, QPoint(0,0)).y() + tw1->tabBarHeight() -2 -2; + else + y1 = w1->mapTo(m_topLevelWidget, QPoint(0,0)).y(); + + QObject *page2 = 0; + TabWidget *tw2 = KFormDesigner::findParent<KFormDesigner::TabWidget>(w2, "KFormDesigner::TabWidget", page2); + if (tw1 && tw2 && tw1 == tw2 && page1 != page2) { + // this sorts widgets by tabs there're put in + return tw1->indexOf(static_cast<QWidget*>(page1)) - tw2->indexOf(static_cast<QWidget*>(page2)); + } + + if (tw2) // special case + y2 = w2->mapTo(m_topLevelWidget, QPoint(0,0)).y() + tw2->tabBarHeight() -2 -2; + else + y2 = w2->mapTo(m_topLevelWidget, QPoint(0,0)).y(); + + kdDebug() << w1->name() << ": " << y1 << " " + << " | " << w2->name() << ": " << y2 << endl; + + + //kdDebug() << w1->name() << ": " << w1->mapTo(m_topLevelWidget, QPoint(0,0)) << " " << w1->y() + //<< " | " << w2->name() << ":" /*<< w2->mapFrom(m_topLevelWidget, QPoint(0,w2->y()))*/ << " " << w2->y() << endl; + return y1 - y2; +} + +#include "utils.moc" diff --git a/kexi/formeditor/utils.h b/kexi/formeditor/utils.h new file mode 100644 index 00000000..d5384e45 --- /dev/null +++ b/kexi/formeditor/utils.h @@ -0,0 +1,113 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr> + Copyright (C) 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. +*/ + +#ifndef FORMEDITORUTILS_H +#define FORMEDITORUTILS_H + +#include <qptrlist.h> +#include <qtabbar.h> +#include <qtabwidget.h> + +//! @todo replace QTabWidget by KTabWidget after the bug with & is fixed: +#define TabWidgetBase QTabWidget +//#define USE_KTabWidget //todo: uncomment + +namespace KFormDesigner { + +class Form; + +/*! \return parent object of \a o that inherits \a className or NULL if no such parent + If the parent is found, \a prevPrev is set to a child of child of the parent, + what for TabWidget means the page widget. */ +template<class type> +type* findParent(QObject* o, const char* className, QObject* &prevPrev) +{ + if (!o || !className || className[0]=='\0') + return 0; + QObject *prev = o; + while ( ((o=o->parent())) && !o->inherits(className) ) { + prevPrev = prev; + prev = o; + } + return static_cast<type*>(o); +} + +//! A tab widget providing information about height of the tab bar. +class KFORMEDITOR_EXPORT TabWidget : public TabWidgetBase +{ + Q_OBJECT + public: + TabWidget(QWidget *parent, const char *name) + : TabWidgetBase(parent, name) {} + virtual ~TabWidget() {} + int tabBarHeight() const { return tabBar()->height(); } +}; + +//! @short A list of widget pointers. +typedef QPtrList<QWidget> WidgetList; + +//! @short An iterator for WidgetList. +typedef QPtrListIterator<QWidget> WidgetListIterator; + +//! @short A helper for sorting widgets horizontally +class HorWidgetList : public WidgetList +{ + public: + HorWidgetList(QWidget *topLevelWidget); + virtual ~HorWidgetList(); + protected: + virtual int compareItems(QPtrCollection::Item item1, QPtrCollection::Item item2); + QWidget *m_topLevelWidget; +}; + +//! @short A helper for sorting widgets vertically +class VerWidgetList : public WidgetList +{ + public: + VerWidgetList(QWidget *topLevelWidget); + virtual ~VerWidgetList(); + protected: + virtual int compareItems(QPtrCollection::Item item1, QPtrCollection::Item item2); + QWidget *m_topLevelWidget; +}; + +/*! This function is used to remove all the child widgets from a list, and + keep only the "toplevel" ones. */ +KFORMEDITOR_EXPORT void removeChildrenFromList(WidgetList &list); + +/*! This helper function install an event filter on \a object and all of its + children, directed to \a container. + This is necessary to filter events for composed widgets. */ +KFORMEDITOR_EXPORT void installRecursiveEventFilter(QObject *object, QObject *container); + +/*! This helper function removes an event filter installed before + on \a object and all of its children. + This is necessary to filter events for composed widgets. */ +KFORMEDITOR_EXPORT void removeRecursiveEventFilter(QObject *object, QObject *container); + +KFORMEDITOR_EXPORT void setRecursiveCursor(QWidget *w, Form *form); + +/*! \return the size of \a w children. This can be used eg to get widget's sizeHint. */ +KFORMEDITOR_EXPORT QSize getSizeFromChildren(QWidget *widget, const char *inheritClass="QWidget"); + +} + +#endif + diff --git a/kexi/formeditor/widgetfactory.cpp b/kexi/formeditor/widgetfactory.cpp new file mode 100644 index 00000000..8122013c --- /dev/null +++ b/kexi/formeditor/widgetfactory.cpp @@ -0,0 +1,725 @@ +/* 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-2006 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 "widgetfactory.h" + +#include <qcursor.h> +#include <qobjectlist.h> +#include <qdict.h> +#include <qmetaobject.h> + +#include <kdebug.h> +#include <klocale.h> +//#ifdef KEXI_KTEXTEDIT +#include <ktextedit.h> +//#else +#include <klineedit.h> +//#endif +#include <kdialogbase.h> +#include <keditlistbox.h> +#include <kxmlguiclient.h> +#include <kactioncollection.h> + +#include "richtextdialog.h" +#include "editlistviewdialog.h" +#include "resizehandle.h" +#include "formmanager.h" +#include "form.h" +#include "container.h" +#include "objecttree.h" +#include "widgetlibrary.h" +#include "utils.h" +#include "widgetpropertyset.h" +#include "widgetwithsubpropertiesinterface.h" +#include <koproperty/property.h> + +using namespace KFormDesigner; + +///// Widget Info ////////////////////////// + +WidgetInfo::WidgetInfo(WidgetFactory *f) + : m_inheritedClass(0) + , m_overriddenAlternateNames(0) + , m_factory(f) + , m_propertiesWithDisabledAutoSync(0) + , m_customTypesForProperty(0) +{ +} + +WidgetInfo::WidgetInfo(WidgetFactory *f, const char* parentFactoryName, + const char* inheritedClassName) + : m_parentFactoryName( QCString("kformdesigner_")+parentFactoryName ) + , m_inheritedClassName(inheritedClassName) + , m_inheritedClass(0) + , m_overriddenAlternateNames(0) + , m_factory(f) + , m_propertiesWithDisabledAutoSync(0) + , m_customTypesForProperty(0) +{ + m_class = inheritedClassName; +} + +WidgetInfo::~WidgetInfo() +{ + delete m_overriddenAlternateNames; + delete m_propertiesWithDisabledAutoSync; + delete m_customTypesForProperty; +} + +void WidgetInfo::addAlternateClassName(const QCString& alternateName, bool override) +{ + m_alternateNames += alternateName; + if (override) { + if (!m_overriddenAlternateNames) + m_overriddenAlternateNames = new QAsciiDict<char>(101); + m_overriddenAlternateNames->insert(alternateName, (char*)1); + } + else { + if (m_overriddenAlternateNames) + m_overriddenAlternateNames->take(alternateName); + } +} + +bool WidgetInfo::isOverriddenClassName(const QCString& alternateName) const +{ + return m_overriddenAlternateNames && (m_overriddenAlternateNames->find(alternateName) != 0); +} + +void WidgetInfo::setAutoSyncForProperty(const char *propertyName, tristate flag) +{ + if (!m_propertiesWithDisabledAutoSync) { + if (~flag) + return; + m_propertiesWithDisabledAutoSync = new QAsciiDict<char>(101); + } + + if (~flag) { + m_propertiesWithDisabledAutoSync->remove(propertyName); + } + else { + m_propertiesWithDisabledAutoSync->insert(propertyName, flag==true ? (char*)1 : (char*)2); + } +} + +tristate WidgetInfo::autoSyncForProperty(const char *propertyName) const +{ + char* flag = m_propertiesWithDisabledAutoSync ? m_propertiesWithDisabledAutoSync->find(propertyName) : 0; + if (!flag) + return cancelled; + return flag==(char*)1 ? true : false; +} + +void WidgetInfo::setCustomTypeForProperty(const char *propertyName, int type) +{ + if (!propertyName || type==KoProperty::Auto) + return; + if (!m_customTypesForProperty) { + m_customTypesForProperty = new QMap<QCString,int>(); + } + m_customTypesForProperty->replace(propertyName, type); +} + +int WidgetInfo::customTypeForProperty(const char *propertyName) const +{ + if (!m_customTypesForProperty || !m_customTypesForProperty->contains(propertyName)) + return KoProperty::Auto; + return (*m_customTypesForProperty)[propertyName]; +} + + +///// Widget Factory ////////////////////////// + +WidgetFactory::WidgetFactory(QObject *parent, const char *name) + : QObject(parent, (const char*)(QCString("kformdesigner_")+name)) +{ + m_showAdvancedProperties = true; + m_classesByName.setAutoDelete(true); + m_hiddenClasses = 0; + m_guiClient = 0; +} + +WidgetFactory::~WidgetFactory() +{ + delete m_hiddenClasses; +} + +void WidgetFactory::addClass(WidgetInfo *w) +{ + WidgetInfo *oldw = m_classesByName[w->className()]; + if (oldw==w) + return; + if (oldw) { + kdWarning() << "WidgetFactory::addClass(): class with name '" << w->className() + << "' already exists for factory '" << name() << "'" << endl; + return; + } + m_classesByName.insert( w->className(), w ); +} + +void WidgetFactory::hideClass(const char *classname) +{ + if (!m_hiddenClasses) + m_hiddenClasses = new QAsciiDict<char>(101, false); + m_hiddenClasses->insert(classname, (char*)1); +} + +void +WidgetFactory::createEditor(const QCString &classname, const QString &text, + QWidget *w, Container *container, QRect geometry, + int align, bool useFrame, bool multiLine, BackgroundMode background) +{ +//#ifdef KEXI_KTEXTEDIT + if (multiLine) { + KTextEdit *textedit = new KTextEdit(text, QString::null, w->parentWidget()); + textedit->setTextFormat(Qt::PlainText); + textedit->setAlignment(align); + if (dynamic_cast<QTextEdit*>(w)) { + textedit->setWordWrap(dynamic_cast<QTextEdit*>(w)->wordWrap()); + textedit->setWrapPolicy(dynamic_cast<QTextEdit*>(w)->wrapPolicy()); + } + textedit->setPalette(w->palette()); + textedit->setFont(w->font()); + textedit->setResizePolicy(QScrollView::Manual); + textedit->setGeometry(geometry); + if(background == Qt::NoBackground) + textedit->setBackgroundMode(w->backgroundMode()); + else + textedit->setBackgroundMode(background); +// textedit->setPaletteBackgroundColor(textedit->colorGroup().color( QColorGroup::Base )); + textedit->setPaletteBackgroundColor(w->paletteBackgroundColor()); + for(int i =0; i <= textedit->paragraphs(); i++) + textedit->setParagraphBackgroundColor(i, w->paletteBackgroundColor()); + textedit->selectAll(true); + textedit->setColor(w->paletteForegroundColor()); + textedit->selectAll(false); + textedit->moveCursor(QTextEdit::MoveEnd, false); + textedit->setParagraphBackgroundColor(0, w->paletteBackgroundColor()); + textedit->setVScrollBarMode(QScrollView::AlwaysOff); //ok? + textedit->setHScrollBarMode(QScrollView::AlwaysOff); //ok? + textedit->installEventFilter(this); + textedit->setFrameShape(useFrame ? QFrame::LineEditPanel : QFrame::NoFrame); + textedit->setMargin(2); //to move away from resize handle + textedit->show(); + textedit->setFocus(); + textedit->selectAll(); + setEditor(w, textedit); + + connect(textedit, SIGNAL(textChanged()), this, SLOT(slotTextChanged())); + connect(w, SIGNAL(destroyed()), this, SLOT(widgetDestroyed())); + connect(textedit, SIGNAL(destroyed()), this, SLOT(editorDeleted())); +//#else + } + else { + KLineEdit *editor = new KLineEdit(text, w->parentWidget()); + editor->setAlignment(align); + editor->setPalette(w->palette()); + editor->setFont(w->font()); + editor->setGeometry(geometry); + if(background == Qt::NoBackground) + editor->setBackgroundMode(w->backgroundMode()); + else + editor->setBackgroundMode(background); + editor->installEventFilter(this); + editor->setFrame(useFrame); + editor->setMargin(2); //to move away from resize handle + editor->show(); + editor->setFocus(); + editor->selectAll(); + connect(editor, SIGNAL(textChanged(const QString&)), this, SLOT(changeTextInternal(const QString&))); + connect(w, SIGNAL(destroyed()), this, SLOT(widgetDestroyed())); + connect(editor, SIGNAL(destroyed()), this, SLOT(editorDeleted())); + + setEditor(w, editor); +// m_editor = editor; + } + //copy properties if available + WidgetWithSubpropertiesInterface* subpropIface = dynamic_cast<WidgetWithSubpropertiesInterface*>(w); + QWidget *subwidget = (subpropIface && subpropIface->subwidget()) ? subpropIface->subwidget() : w; + if (-1!=m_editor->metaObject()->findProperty("margin", true) && -1!=subwidget->metaObject()->findProperty("margin", true)) + m_editor->setProperty("margin", subwidget->property("margin")); +//#endif +//js m_handles = new ResizeHandleSet(w, container->form(), true); + m_handles = container->form()->resizeHandlesForWidget(w); + if (m_handles) { + m_handles->setEditingMode(true); + m_handles->raise(); + } + + ObjectTreeItem *tree = container->form()->objectTree()->lookup(w->name()); + if(!tree) + return; + tree->eventEater()->setContainer(this); + + //m_widget = w; + setWidget(w, container); + m_editedWidgetClass = classname; + m_firstText = text; +// m_container = container; + + changeTextInternal(text); // to update size of the widget +} + +void +WidgetFactory::disableFilter(QWidget *w, Container *container) +{ + ObjectTreeItem *tree = container->form()->objectTree()->lookup(w->name()); + if(!tree) + return; + tree->eventEater()->setContainer(this); + + w->setFocus(); +//js m_handles = new ResizeHandleSet(w, container->form(), true); + m_handles = container->form()->resizeHandlesForWidget(w); + if (m_handles) { + m_handles->setEditingMode(true); + m_handles->raise(); + } + + //m_widget = w; + setWidget(w, container); +// m_container = container; + setEditor(w, 0); +// m_editor = 0; + + // widget is disabled, so we re-enable it while editing + if(!tree->isEnabled()) { + QPalette p = w->palette(); + QColorGroup cg = p.active(); + p.setActive(p.disabled()); + p.setDisabled(cg); + w->setPalette(p); + } + + connect(w, SIGNAL(destroyed()), this, SLOT(widgetDestroyed())); +} + +bool +WidgetFactory::editList(QWidget *w, QStringList &list) +{ + KDialogBase dialog(w->topLevelWidget(), "stringlist_dialog", true, i18n("Edit List of Items"), + KDialogBase::Ok|KDialogBase::Cancel, KDialogBase::Ok, false); + + KEditListBox *edit = new KEditListBox(i18n("Contents of %1").arg(w->name()), &dialog, "editlist"); + dialog.setMainWidget(edit); + edit->insertStringList(list); +// edit->show(); + + if(dialog.exec() == QDialog::Accepted) + { + list = edit->items(); + return true; + } + return false; +} + +bool +WidgetFactory::editRichText(QWidget *w, QString &text) +{ + RichTextDialog dlg(w, text); + if(dlg.exec()== QDialog::Accepted) + { + text = dlg.text(); + return true; + } + return false; +} + +void +WidgetFactory::editListView(QListView *listview) +{ + EditListViewDialog dlg(((QWidget*)listview)->topLevelWidget()); + //dlg.exec(listview); +} + +bool +WidgetFactory::eventFilter(QObject *obj, QEvent *ev) +{ + if( ((ev->type() == QEvent::Resize) || (ev->type() == QEvent::Move) ) && (obj == m_widget) && editor(m_widget)) { + // resize widget using resize handles + QWidget *ed = editor(m_widget); + resizeEditor(ed, m_widget, m_widget->className()); + } + else if((ev->type() == QEvent::Paint) && (obj == m_widget) && editor(m_widget)) { + // paint event for container edited (eg button group) + return m_container->eventFilter(obj, ev); + } + else if((ev->type() == QEvent::MouseButtonPress) && (obj == m_widget) && editor(m_widget)) { + // click outside editor --> cancel editing + Container *cont = m_container; + resetEditor(); + return cont->eventFilter(obj, ev); + } + + if(ev->type() == QEvent::FocusOut) + { + QWidget *w = editor(m_widget); + if (!w) + w = (QWidget *)m_widget; + if(obj != (QObject *)w) + return false; + + QWidget *focus = w->topLevelWidget()->focusWidget(); + if(focus && w != focus && !w->child(focus->name(), focus->className())) + resetEditor(); + } + else if(ev->type() == QEvent::KeyPress) + { + QWidget *w = editor(m_widget); + if (!w) + w = (QWidget *)m_widget; + if(obj != (QObject *)w) + return false; + + QKeyEvent *e = static_cast<QKeyEvent*>(ev); + if(((e->key() == Qt::Key_Return) || (e->key() == Qt::Key_Enter)) && (e->state() != AltButton)) + resetEditor(); + if(e->key() == Qt::Key_Escape) + { + setEditorText(m_firstText); + //changeText(m_firstText); + resetEditor(); + } + } + else if(ev->type() == QEvent::ContextMenu) { + QWidget *w = editor(m_widget); + if (!w) + w = (QWidget *)m_widget; + if(obj != (QObject *)w) + return false; + + return true; + } +// if(obj == m_widget) +// return m_container->eventFilter(obj, ev); +// else + return false; +} + +void +WidgetFactory::resetEditor() +{ + if (m_container) + m_container->stopInlineEditing(); + + QWidget *ed = editor(m_widget); + if(m_widget) + { + ObjectTreeItem *tree = m_container ? m_container->form()->objectTree()->lookup(m_widget->name()) : 0; + if(!tree) + { + kdDebug() << "WidgetFactory::resetEditor() : error cannot found a tree item " << endl; + return; + } + tree->eventEater()->setContainer(m_container); + if(m_widget) {// && !ed) + setRecursiveCursor(m_widget, m_container->form()); + if (m_widget->inherits("QLineEdit") || m_widget->inherits("QTextEdit")) { //fix weird behaviour + m_widget->unsetCursor(); + m_widget->setCursor(Qt::ArrowCursor); + } + } + + // disable again the widget + if(!ed && !tree->isEnabled()) { + QPalette p = m_widget->palette(); + QColorGroup cg = p.active(); + p.setActive(p.disabled()); + p.setDisabled(cg); + m_widget->setPalette(p); + } + } + if(ed) + { + changeTextInternal(editorText()); + disconnect(ed, 0, this, 0); + ed->deleteLater(); + } + + if(m_widget) + { + disconnect(m_widget, 0, this, 0); + m_widget->repaint(); + } + +//js delete m_handles; + if (m_handles) { + m_handles->setEditingMode(false); + } + setEditor(m_widget, 0); +// m_editor = 0; + setWidget(0, 0); + //m_widget = 0; + m_handles = 0; +// m_container = 0; +} + +void +WidgetFactory::widgetDestroyed() +{ + if(m_editor) + { + m_editor->deleteLater(); + m_editor = 0; + } + +//js delete m_handles; + if (m_handles) { + m_handles->setEditingMode(false); + + } + m_widget = 0; + m_handles = 0; + m_container = 0; +} + +void +WidgetFactory::editorDeleted() +{ +//js delete m_handles; + if (m_handles) { + m_handles->setEditingMode(false); + } + setEditor(m_widget, 0); + setWidget(0, 0); +// m_widget = 0; + m_handles = 0; +// m_container = 0; +} + +void +WidgetFactory::changeProperty(const char *name, const QVariant &value, Form *form) +//WidgetFactory::changeProperty(const char *name, const QVariant &value, Container *container) +{ +// if (!form->manager()) +// return; + if(form->selectedWidgets()->count() > 1) + { // If eg multiple labels are selected, we only want to change the text of one of them (the one the user cliked on) + if(m_widget) + m_widget->setProperty(name, value); + else + form->selectedWidgets()->first()->setProperty(name, value); + } + else + { + WidgetPropertySet *set = KFormDesigner::FormManager::self()->propertySet(); + if(set->contains(name)) + (*set)[name] = value; + } +} + +/* +void +WidgetFactory::addPropertyDescription(Container *container, const char *prop, const QString &desc) +{ + WidgetPropertySet *buff = container->form()->manager()->buffer(); + buff->addPropertyDescription(prop, desc); +} + +void +WidgetFactory::addValueDescription(Container *container, const char *value, const QString &desc) +{ + WidgetPropertySet *buff = container->form()->manager()->buffer(); + buff->addValueDescription(value, desc); +}*/ + +bool +WidgetFactory::isPropertyVisible(const QCString &classname, QWidget *w, + const QCString &property, bool multiple, bool isTopLevel) +{ + if (multiple) + { + return property=="font" || property=="paletteBackgroundColor" || property=="enabled" + || property=="paletteForegroundColor" || property=="cursor" || property=="paletteBackgroundPixmap"; + } + +// if(d->properties.isEmpty() && !isTopLevel) +// d->properties << "caption" << "icon" << "sizeIncrement" << "iconText"; +// if(! (d->properties.grep(property)).isEmpty() ) +// return false; + + return isPropertyVisibleInternal(classname, w, property, isTopLevel); +// return !multiple && isPropertyVisibleInternal(classname, w, property); +} + +bool +WidgetFactory::isPropertyVisibleInternal(const QCString &, QWidget *w, + const QCString &property, bool isTopLevel) +{ + Q_UNUSED( w ); + +#ifdef KEXI_NO_CURSOR_PROPERTY +//! @todo temporary unless cursor works properly in the Designer + if (property=="cursor") + return false; +#endif + + if (!isTopLevel + && (property=="caption" || property=="icon" || property=="sizeIncrement" || property=="iconText")) { + // don't show these properties for a non-toplevel widget + return false; + } + return true; +} + +bool +WidgetFactory::propertySetShouldBeReloadedAfterPropertyChange(const QCString& classname, QWidget *w, + const QCString& property) +{ + Q_UNUSED(classname); + Q_UNUSED(w); + Q_UNUSED(property); + return false; +} + +void +WidgetFactory::resizeEditor(QWidget *, QWidget *, const QCString&) +{ +} + +void +WidgetFactory::slotTextChanged() +{ + changeTextInternal(editorText()); +} + +bool +WidgetFactory::clearWidgetContent(const QCString &, QWidget *) +{ + return false; +} + +void +WidgetFactory::changeTextInternal(const QString& text) +{ + if (changeText( text )) + return; + //try in inherited + if (!m_editedWidgetClass.isEmpty()) { + WidgetInfo *wi = m_classesByName[ m_editedWidgetClass ]; + if (wi && wi->inheritedClass()) { +// wi->inheritedClass()->factory()->m_container = m_container; + wi->inheritedClass()->factory()->changeText( text ); + } + } +} + +bool +WidgetFactory::changeText(const QString& text) +{ + changeProperty( "text", text, m_container->form() ); + return true; +} + +bool +WidgetFactory::readSpecialProperty(const QCString &, QDomElement &, QWidget *, ObjectTreeItem *) +{ + return false; +} + +bool +WidgetFactory::saveSpecialProperty(const QCString &, const QString &, const QVariant&, QWidget *, QDomElement &, QDomDocument &) +{ + return false; +} + +bool WidgetFactory::inheritsFactories() +{ + for (QAsciiDictIterator<WidgetInfo> it(m_classesByName); it.current(); ++it) { + if (!it.current()->parentFactoryName().isEmpty()) + return true; + } + return false; +} + +QString WidgetFactory::editorText() const { + QWidget *ed = editor(m_widget); + return dynamic_cast<KTextEdit*>(ed) ? dynamic_cast<KTextEdit*>(ed)->text() : dynamic_cast<KLineEdit*>(ed)->text(); +} + +void WidgetFactory::setEditorText(const QString& text) { + QWidget *ed = editor(m_widget); + if (dynamic_cast<KTextEdit*>(ed)) + dynamic_cast<KTextEdit*>(ed)->setText(text); + else + dynamic_cast<KLineEdit*>(ed)->setText(text); +} + +void WidgetFactory::setEditor(QWidget *widget, QWidget *editor) +{ + if (!widget) + return; + WidgetInfo *winfo = m_classesByName[widget->className()]; + if (!winfo || winfo->parentFactoryName().isEmpty()) { + m_editor = editor; + } + else { + WidgetFactory *f = m_library->factory(winfo->parentFactoryName()); + if (f!=this) + f->setEditor(widget, editor); + m_editor = editor; //keep a copy + } +} + +QWidget *WidgetFactory::editor(QWidget *widget) const +{ + if (!widget) + return 0; + WidgetInfo *winfo = m_classesByName[widget->className()]; + if (!winfo || winfo->parentFactoryName().isEmpty()) { + return m_editor; + } + else { + WidgetFactory *f = m_library->factoryForClassName(widget->className()); + if (f!=this) + return f->editor(widget); + return m_editor; + } +} + +void WidgetFactory::setWidget(QWidget *widget, Container* container) +{ + WidgetInfo *winfo = widget ? m_classesByName[widget->className()] : 0; + if (winfo && !winfo->parentFactoryName().isEmpty()) { + WidgetFactory *f = m_library->factory(winfo->parentFactoryName()); + if (f!=this) + f->setWidget(widget, container); + } + m_widget = widget; //keep a copy + m_container = container; +} + +QWidget *WidgetFactory::widget() const +{ + return m_widget; +} + +void WidgetFactory::setInternalProperty(const QCString& classname, const QCString& property, + const QString& value) +{ + m_internalProp[classname+":"+property]=value; +} + +void WidgetFactory::setPropertyOptions( WidgetPropertySet& /*buf*/, const WidgetInfo& /*info*/, QWidget * /*w*/ ) +{ + //nothing +} + +#include "widgetfactory.moc" diff --git a/kexi/formeditor/widgetfactory.desktop b/kexi/formeditor/widgetfactory.desktop new file mode 100644 index 00000000..1bf68d9e --- /dev/null +++ b/kexi/formeditor/widgetfactory.desktop @@ -0,0 +1,53 @@ +[Desktop Entry] +Type=ServiceType +X-KDE-ServiceType=KFormDesigner/WidgetFactory +Comment=Widget Factory Base +Comment[bg]=Създаване на графични обекти +Comment[ca]=Factoria base d'estris +Comment[cy]=Bâs Ffatri Celfigion +Comment[de]=Basis für die Erstellung von Bedienelementen +Comment[el]=Βάση μηχανής γραφικών συστατικών +Comment[eo]=Fenestraĵfabrikbazo +Comment[es]=Base de fábrica de elementos +Comment[et]=Vidinate põhibaas +Comment[eu]=Trepeten faktoriaren oinarria +Comment[fa]=پایۀ کارخانۀ عنصر +Comment[fi]=Käyttöliittymäelementtien pohja +Comment[fr]=Base de création de widgets +Comment[gl]=Fábrica de Elementos +Comment[he]=בסיס למפעל פריטים +Comment[hr]=Baza widget tvornice +Comment[hu]=Widgetkészítő-alap +Comment[is]=Hluta smiðjugrunnur +Comment[it]=Base della fabbrica di oggetti +Comment[ja]=ウィジェットファクトリーベース +Comment[lv]=Logdaļas fabrikas bāze +Comment[ms]=Pangkalan Kilang Widget +Comment[nb]=Base for elementfabrikk +Comment[nds]=Basis för't Opstellen vun Stüerelementen +Comment[ne]=विजेट फ्याक्ट्री आधार +Comment[nn]=Base for elementfabrikk +Comment[pl]=Podstawowa fabryka kontrolek +Comment[pt]=Fábrica de Elementos +Comment[pt_BR]=Widget de Fábrica Base +Comment[ru]=Базовое приложение, содержащее офисный элемент управления +Comment[se]=Áhtafabrihka vuođđu +Comment[sk]=Základ pre vytváracie rozhranie prvkov +Comment[sl]=Tovarna gradnikov +Comment[sr]=Основа фабрике контрола +Comment[sr@Latn]=Osnova fabrike kontrola +Comment[sv]=Bas för att skapa grafiska komponenter +Comment[ta]= widget தொழிற்சாலை தளம் +Comment[tg]=Гузориши база, ки таркиботи идораи офис дар дохил дорад +Comment[uk]=База фабрики віджетів +Comment[zh_CN]=基础组件工厂 +Comment[zh_TW]=視窗元件工廠基地 + +[PropertyDef::X-KFormDesigner-FactoryGroup] +Type=QString + +[PropertyDef::X-KFormDesigner-WidgetFactoryVersion] +Type=int + +[PropertyDef::X-KFormDesigner-XMLGUIFileName] +Type=QString diff --git a/kexi/formeditor/widgetfactory.h b/kexi/formeditor/widgetfactory.h new file mode 100644 index 00000000..43736ebc --- /dev/null +++ b/kexi/formeditor/widgetfactory.h @@ -0,0 +1,518 @@ +/* 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-2006 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. +*/ + +#ifndef KFORMDESIGNERWIDGETFACTORY_H +#define KFORMDESIGNERWIDGETFACTORY_H + + +#include <qobject.h> +#include <qguardedptr.h> +#include <qpixmap.h> +#include <qpopupmenu.h> +#include <qasciidict.h> + +#include <kexiutils/tristate.h> + +// class QPixmap; +template<class type> class QValueVector; +template<class type> class QPtrList; +template<class type> class QDict; +class QWidget; +class QDomElement; +class QDomDocument; +class QVariant; +class QListView; +class KActionCollection; +class KTextEdit; +class KLineEdit; +class KXMLGUIClient; + +namespace KoProperty { + class Set; +} + +namespace KFormDesigner { + +class WidgetFactory; +class WidgetLibrary; +class Container; +class ResizeHandleSet; +class ObjectTreeItem; +class WidgetPropertySet; +class Form; + +/** + * This class holds properties of widget classes provided by a factory. + */ +class KFORMEDITOR_EXPORT WidgetInfo +{ + public: + typedef QPtrList<WidgetInfo> List; + typedef QAsciiDict<WidgetInfo> Dict; + + WidgetInfo(WidgetFactory *f); + + WidgetInfo(WidgetFactory *f, const char* parentFactoryName, const char* inheritedClassName = 0); + + virtual ~WidgetInfo(); + + //! \return a pixmap associated with the widget + QString pixmap() const { return m_pixmap; } + + //! \return the class name of a widget e.g. 'QLineEdit' + QCString className() const { return m_class; } + + /*! \return the name used to name widget, that will appear eg in scripts (must not contain spaces + nor non-latin1 characters) */ + QString namePrefix() const { return m_prefixName; } + + //! \return the real name e.g. 'Line Edit', showed eg in ObjectTreeView + QString name() const { return m_name; } + + QString description() const { return m_desc; } + QString includeFileName() const { return m_include; } + QValueList<QCString> alternateClassNames() const { return m_alternateNames; } + QString savingName() const { return m_saveName; } + WidgetFactory *factory() const { return m_factory; } + + void setPixmap(const QString &p) { m_pixmap = p; } + void setClassName(const QCString &s) { m_class = s; } + void setName(const QString &n) { m_name = n; } + void setNamePrefix(const QString &n) { m_prefixName = n; } + void setDescription(const QString &desc) { m_desc = desc;} + + /*! Sets the C++ include file corresponding to this class, + that uic will need to add when creating the file. You don't have to set this for Qt std widgets.*/ + void setIncludeFileName(const QString &name) { m_include = name;} + + /*! Sets alternate names for this class. + If this name is found when loading a .ui file, the className() will be used instead. + It allows to support both KDE and Qt versions of widget, without duplicating code. + As a rule, className() should always return a class name which is inherited from + alternate class. For example KListView class has alternate QListView class. + + \a override parameter overrides class name of a widget, + even if it was implemented in other factory. + By default it's set to false, what means that no other class is overridden + by this widget class if there is already a class implementing it + (no matter in which factory). + By forced overriding existing class with other - custom, user + will be able to see more or less properties and experience different behaviour. + For example, in Kexi application, KLineEdit class contains additional + "datasource" property for binding to database sources. + */ + void addAlternateClassName(const QCString& alternateName, bool override = false); + + /*! \return true is a class \a alternateName is defined as alternate name with + 'override' flag set to true, using addAlternateClassName(). + If this flag is set to false (the default) or there's no such alternate class + name defined. */ + bool isOverriddenClassName(const QCString& alternateName) const; + + /*! Sets the name that will be written in the .ui file when saving. + This name must be one of alternate names (or loading will be impossible). + + On form data saving to XML .ui format, saveName is used instead, + so .ui format is not broken and still usable with other software as Qt Designer. + Custom properties are saved as well with 'stdset' attribute set to 0. */ + void setSavingName(const QString &saveName) { m_saveName = saveName; } + + /*! Sets autoSync flag for property \a propertyName. + This allows to override autoSync flag for certain widget's property, because + e.g. KoProperty::Editor can have autoSync flag set to false or true, but + not all properties have to comply with that. + \a flag equal to cancelled value means there is no overriding (the default). */ + void setAutoSyncForProperty(const char *propertyName, tristate flag); + + /*! \return autoSync override value (true or false) for \a propertyName. + If cancelled value is returned, there is no overriding (the default). */ + tristate autoSyncForProperty(const char *propertyName) const; + + QCString parentFactoryName() const { return m_parentFactoryName; } + + WidgetInfo* inheritedClass() const { return m_inheritedClass; } + + /*! Sets custom type \a type for property \a propertyName. + This allows to override default type, especially when custom property + and custom property editor item has to be used. */ + void setCustomTypeForProperty(const char *propertyName, int type); + + /*! \return custom type for property \a propertyName. If no specific custom type has been assigned, + KoProperty::Auto is returned. + @see setCustomTypeForProperty() */ + int customTypeForProperty(const char *propertyName) const; + + protected: + QCString m_parentFactoryName, m_inheritedClassName; //!< Used for inheriting widgets between factories + WidgetInfo* m_inheritedClass; + + private: + QString m_pixmap; + QCString m_class; + QString m_name; + QString m_prefixName; + QString m_desc; + QString m_include; + QValueList<QCString> m_alternateNames; + QAsciiDict<char> *m_overriddenAlternateNames; + QString m_saveName; + QGuardedPtr<WidgetFactory> m_factory; + QAsciiDict<char> *m_propertiesWithDisabledAutoSync; + QMap<QCString,int> *m_customTypesForProperty; + + friend class WidgetLibrary; +}; + +//! The base class for all widget Factories +/*! This is the class you need to inherit to create a new Factory. There are few + virtuals you need to implement, and some other functions + to implement if you want more features.\n \n + + <b>Widget Creation</b>\n + To be able to create widgets, you need to implement the create() function, an classes(), + which should return all the widgets supported by this factory.\n \n + + <b>GUI Integration</b>\n + The following functions allow you to customize even more the look-n-feel of your widgets inside KFormDesigner. + You can use createMenuActions() to add custom items in widget's context menu. The previewWidget() + is called when the Form gets in Preview mode, and you have a last opportunity to remove all editing-related + stuff (see eg \ref Spring class).\n + You can also choose which properties to show in the Property Editor. + By default, most all properties are shown (see implementation for details), + but you can hide some reimplementing isPropertyVisibleInternal() (don't forget to call superclass' method) + To add new properties, just define new Q_PROPERTY in widget class definition.\n \n + + <b>Inline editing</b>\n + KFormDesigner allow you to edit the widget's contents inside Form, without using a dialog. + You can of course customize the behaviour of your widgets, using startEditing(). There are some editing + modes already implemented in WidgetFactroy, but you can create your own if you want: + \li Editing using a line edit (createEditor()): a line edit is created on top of widget, + where the user inputs text. As the text changes, changeText() is called + (where you should set your widget's text and resize widget to fit the text if needed) and resizeEditor() + to update editor's position when widget is moved/resized.\n + \li Editing by disabling event filter: if you call disableFilter(), the event filter + on the object is temporarily disabled, so the widget behaves as usual. This + can be used for more complex widgets, such as spinbox, date/time edit, etc. + \li Other modes: there are 3 other modes, to edit a string list: editList() + (for combo box, listbox), to edit rich text: editRichText() (for labels, etc.) + and to edit a listview: editListView(). \n \n + + <b>Widget saving/loading</b>\n + You can also control how your widget are saved/loaded. You can choose which properties to save + (see autoSaveProperties()), and save/load custom properties, ie + properties that are not Q_PROPERTY but you want to save in the UI file. This is used eg to + save combo box or listview contents (see saveSpecialProperty() and + readSpecialProperty()). \n \n + + <b>Special internal properties</b>\n + Use void setInternalProperty(const QCString& classname, const QCString& property, const QString& value); + to set values of special internal properties. + Currently these properties are used for customizing popup menu items used for orientation selection. + Customization for class ClassName should look like: + <code> void setInternalProperty("ClassName", "orientationSelectionPopup", "myicon"); </code> + Available internal properties: + * "orientationSelectionPopup" - set it to "1" if you want a given class to offer orientation selection, + so orientation selection popup will be displayed when needed. + * "orientationSelectionPopup:horizontalIcon" - sets a name of icon for "Horizontal" item + for objects of class 'ClassName'. Set this property only for classes supporting orientations. + * "orientationSelectionPopup:verticalIcon" - the same for "Vertical" item. + Set this property only for classes supporting orientations. + * "orientationSelectionPopup:horizontalText" - sets a i18n'd text for "Horizontal" item + for objects of class 'ClassName', e.g. i18n("Insert Horizontal Line"). + Set this property only for classes supporting orientations. + * "orientationSelectionPopup:verticalText" - the same for "Vertical" item, + e.g. i18n("Insert Vertical Line"). Set this property only for classes supporting orientations. + * "dontStartEditingOnInserting" - if not empty, WidgetFactory::startEditing() will not be executed upon + widget inseting by a user. + * "forceShowAdvancedProperty:{propertyname}" - set it to "1" for "{propertyname}" advanced property + if you want to force it to be visible even if WidgetLibrary::setAdvancedPropertiesVisible(false) + has been called. For example, setting "forceShowAdvancedProperty:pixmap" to "1" + unhides "pixmap" property for a given class. + + See StdWidgetFactory::StdWidgetFactory() for properties like + "Line:orientationSelectionPopup:horizontalIcon". + + \n\n + See the standard factories in formeditor/factories for an example of factories, + and how to deal with complex widgets (eg tabwidget). + */ +class KFORMEDITOR_EXPORT WidgetFactory : public QObject +{ + Q_OBJECT + public: + //! Options used in createWidget() + enum CreateWidgetOptions { + AnyOrientation = 1, //!< any orientation hint + HorizontalOrientation = 2, //!< horizontal orientation hint + VerticalOrientation = 4, //!< vertical orientation hint + DesignViewMode = 8, //!< create widget in design view mode, otherwise preview mode + DefaultOptions = AnyOrientation | DesignViewMode + }; + + WidgetFactory(QObject *parent=0, const char *name=0); + virtual ~WidgetFactory(); + + /*! Adds a new class described by \a w. */ + void addClass(WidgetInfo *w); + + /*! This method allows to force a class \a classname to hidden. + It is useful if you do not want a class to be available + (e.g. because it is not implemented well yet for our purposes). + All widget libraries are affected by this setting. */ + void hideClass(const char *classname); + + /** + * \return all classes which are provided by this factory + */ + const WidgetInfo::Dict classes() const { return m_classesByName; } + + /** + * Creates a widget (and if needed a KFormDesigner::Container) + * \return the created widget + * \param classname the classname of the widget, which should get created + * \param parent the parent for the created widget + * \param name the name of the created widget + * \param container the toplevel Container (if a container should get created) + * \param options options for the created widget: orientation and view mode (see CreateWidgetOptions) + */ + virtual QWidget* createWidget(const QCString &classname, QWidget *parent, const char *name, + KFormDesigner::Container *container, + int options = DefaultOptions) = 0; + + /*! Creates custom actions. Reimplement this if you need to add some + actions coming from the factory. */ + virtual void createCustomActions(KActionCollection *col) { Q_UNUSED(col); }; + + /*! This function can be used to add custom items in widget \a w context + menu \a menu. */ + virtual bool createMenuActions(const QCString &classname, QWidget *w, QPopupMenu *menu, + KFormDesigner::Container *container)=0; + + /*! Creates (if necessary) an editor to edit the contents of the widget directly in the Form + (eg creates a line edit to change the text of a label). \a classname is + the class the widget belongs to, \a w is the widget to edit + and \a container is the parent container of this widget (to access Form etc.). + */ + virtual bool startEditing(const QCString &classname, QWidget *w, Container *container)=0; + + /*! This function is called just before the Form is previewed. It allows widgets + to make changes before switching (ie for a Spring, hiding the cross) */ + virtual bool previewWidget(const QCString &classname, QWidget *widget, Container *container)=0; + + virtual bool clearWidgetContent(const QCString &classname, QWidget *w); + + /*! This function is called when FormIO finds a property, at save time, + that it cannot handle (ie not a normal property). + This way you can save special properties, for example the contents of a listbox. + \sa readSpecialProperty() + */ + virtual bool saveSpecialProperty(const QCString &classname, const QString &name, + const QVariant &value, QWidget *w, + QDomElement &parentNode, QDomDocument &parent); + + /*! This function is called when FormIO finds a property or an unknown + element in a .ui file. You can this way load a special property, for + example the contents of a listbox. + \sa saveSpecialProperty() + */ + virtual bool readSpecialProperty(const QCString &classname, QDomElement &node, + QWidget *w, ObjectTreeItem *item); + + /*! This function is used to know whether the \a property for the widget \a w + should be shown or not in the PropertyEditor. If \a multiple is true, + then multiple widgets of the same class are selected, and you should + only show properties shared by widgets (eg font, color). By default, + all properties are shown if multiple == true, and none if multiple == false. */ + bool isPropertyVisible(const QCString &classname, QWidget *w, + const QCString &property, bool multiple, bool isTopLevel); + + /*! You need to return here a list of the properties that should automatically be saved + for a widget belonging to \a classname, and your custom properties (eg "text" + for label or button, "contents" for combobox...). */ + virtual QValueList<QCString> autoSaveProperties(const QCString &classname)=0; + + /*! \return The i18n'ed name of the property whose name is \a name, + that will be displayed in PropertyEditor. */ + inline QString propertyDescForName(const QCString &name) { return m_propDesc[name]; }; + + /*! \return The i18n'ed name of the property's value whose name is \a name. */ + inline QString propertyDescForValue(const QCString &name) { return m_propValDesc[name]; }; + + /*! This method is called after WidgetPropertySet was filled with properties + of a widget \a w, of class defined by \a info. + Default implementation does nothing. + Implement this if you need to set options for properties within the set \a buf. */ + virtual void setPropertyOptions( WidgetPropertySet& buf, const WidgetInfo& info, QWidget *w ); + + /*! \return internal property \a property for a class \a classname. + Internal properties are not stored within objects, but can be just provided + to describe classes' details. */ + inline QString internalProperty(const QCString& classname, const QCString& property) const { + return m_internalProp[classname+":"+property]; + } + + protected: + /*! This function is called when we want to know whether the property should be visible. + Implement it in the factory; don't forget to call implementation in the superclass. + Default implementation hides "caption", "icon", "sizeIncrement" and "iconText" properties. */ + virtual bool isPropertyVisibleInternal(const QCString &classname, QWidget *w, + const QCString &property, bool isTopLevel); + + /*! Sometimes property sets should be reloaded when a given property value changed. + Implement it in the factory. Default implementation always returns false. */ + virtual bool propertySetShouldBeReloadedAfterPropertyChange(const QCString& classname, QWidget *w, + const QCString& property); + + /*! This function creates a KLineEdit to input some text and edit a widget's contents. + This can be used in startEditing(). \a text is the text to display by default + in the line edit, \a w is the edited widget, \a geometry is the geometry the new line + edit should have, and \a align is Qt::AlignmentFlags of the new line edit. */ + void createEditor(const QCString &classname, const QString &text, + QWidget *w, Container *container, QRect geometry, + int align, bool useFrame=false, bool multiLine = false, + BackgroundMode background = Qt::NoBackground); + + /*! This function provides a simple editing mode : it justs disable event filtering + for the widget, and it install it again when + the widget loose focus or Enter is pressed. + */ + void disableFilter(QWidget *w, Container *container); + + /*! This function creates a little dialog (a KEditListBox) to modify the contents + of a list (of strings). It can be used to modify the contents + of a combo box for instance. The modified list is copied + into \a list when the user presses "Ok".*/ + bool editList(QWidget *w, QStringList &list); + + /*! This function creates a little editor to modify rich text. It supports alignment, + subscript and superscript and all basic formatting properties. + If the user presses "Ok", the edited text is put in \a text. + If he presses "Cancel", nothing happens. */ + bool editRichText(QWidget *w, QString &text); + + /*! This function creates a dialog to modify the contents of a ListView. You can modify both + columns and list items. The listview is automatically updated if the user presses "Ok".*/ + void editListView(QListView *listview); + + /*! This function destroys the editor when it loses focus or Enter is pressed. */ + virtual bool eventFilter(QObject *obj, QEvent *ev); + + /*! This function is used to modify a property of a widget (eg after editing it). + Please use it instead of w->setProperty() to allow sync inside PropertyEditor. + */ + void changeProperty(const char *name, const QVariant &value, Form *form); + + /*! This function is called when the widget is resized, + and the \a editor size needs to be updated. */ + virtual void resizeEditor(QWidget *editor, QWidget *widget, const QCString &classname); + +// /*! Adds the i18n'ed description of a property, which will be shown in PropertyEditor. */ +// void addPropertyDescription(Container *container, const char *prop, const QString &desc); + +// /*! Adds the i18n'ed description of a property value, which will be shown in PropertyEditor. */ +// void addValueDescription(Container *container, const char *value, const QString &desc); + + /*! \return true if at least one class defined by this factory inherits + a class from other factory. Used in WidgetLibrary::loadFactories() + to load factories in proper order. */ + bool inheritsFactories(); + + public slots: + + /*! @internal. This slot is called when the editor has lost focus or the user pressed Enter. + It destroys the editor or installs again the event filter on the widget. */ + void resetEditor(); + + protected slots: + /*! + Default implementation changes "text" property. + You have to reimplement this function for editing inside the Form to work if your widget's + property you want to change isn't named "text". + This slot is called when the line edit text changes, and you have to make + it really change the good property of the widget using changeProperty() (text, or title, etc.). + */ + virtual bool changeText(const QString &newText); + + void changeTextInternal(const QString& text); + + void slotTextChanged(); + + /*! This slot is called when the editor is destroyed.*/ + void editorDeleted(); + void widgetDestroyed(); + + protected: + QString editorText() const; + void setEditorText(const QString& text); + void setEditor(QWidget *widget, QWidget *editor); + QWidget *editor(QWidget *widget) const; + void setWidget(QWidget *widget, Container *container); + QWidget *widget() const; + + /*! Assigns \a value for internal property \a property for a class \a classname. + Internal properties are not stored within objects, but can be provided + to describe classes' details. */ + void setInternalProperty(const QCString& classname, const QCString& property, const QString& value); + + WidgetLibrary *m_library; + QCString m_editedWidgetClass; +//#ifdef KEXI_KTEXTEDIT +// QGuardedPtr<KTextEdit> m_editor; +//#else +// QGuardedPtr<KLineEdit> m_editor; +//#endif + QString m_firstText; + QGuardedPtr<ResizeHandleSet> m_handles; + QGuardedPtr<Container> m_container; +// WidgetInfo::List m_classes; + WidgetInfo::Dict m_classesByName; + QAsciiDict<char>* m_hiddenClasses; + + //! i18n stuff + QMap<QCString, QString> m_propDesc; + QMap<QCString, QString> m_propValDesc; + //! internal properties + QMap<QCString, QString> m_internalProp; + + /*! flag useful to decide whether to hide some properties. + It's value is inherited from WidgetLibrary. */ + bool m_showAdvancedProperties; + + /*! Contains name of an XMLGUI file providing toolbar buttons + (and menu items in the future?) for the factory. + Can be empty, e.g. for the main factory which has XMLGUI defined in the shell window itself + (e.g. kexiformpartinstui.rc for Kexi Forms). This name is set in WidgetLibrary::loadFactories() */ + QString m_xmlGUIFileName; + + KXMLGUIClient *m_guiClient; + + QGuardedPtr<QWidget> m_widget; + QGuardedPtr<QWidget> m_editor; + + friend class WidgetLibrary; +}; + +//! macro to declare KFormDesigner-compatible widget factory as a KDE Component factory +#define KFORMDESIGNER_WIDGET_FACTORY(factoryClassName, libraryName) \ + K_EXPORT_COMPONENT_FACTORY(kformdesigner_ ## libraryName, KGenericFactory<factoryClassName>("kformdesigner_" # libraryName)) + +} +#endif diff --git a/kexi/formeditor/widgetlibrary.cpp b/kexi/formeditor/widgetlibrary.cpp new file mode 100644 index 00000000..1a198195 --- /dev/null +++ b/kexi/formeditor/widgetlibrary.cpp @@ -0,0 +1,769 @@ +/* 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 <qdom.h> +#include <qstrlist.h> + +#include <kdebug.h> +#include <klocale.h> +#include <klibloader.h> +#include <kparts/componentfactory.h> +#include <ktrader.h> +#include <kiconloader.h> +#include <kpopupmenu.h> + +#include "widgetfactory.h" +#include "widgetlibrary.h" +#include "libactionwidget.h" +#include "container.h" +#include "form.h" +#include "formIO.h" + +namespace KFormDesigner { + +//! @internal +class XMLGUIClient : public QObject, public KXMLGUIClient +{ + public: + XMLGUIClient(KXMLGUIClient* parent, const QString& xmlFileName) + : QObject(parent->actionCollection()), KXMLGUIClient(parent) + { + setXMLFile( xmlFileName, true /*merge*/ ); + } +}; + +//! @internal +class WidgetLibraryPrivate +{ + public: + WidgetLibraryPrivate() + : widgets(101) +// , alternateWidgets(101) + , services(101, false) + , supportedFactoryGroups(17, false) + , factories(101, false) + , advancedProperties(1009, true) + , hiddenClasses(101, true) + , showAdvancedProperties(true) + , factoriesLoaded(false) + { + services.setAutoDelete(true); + advancedProperties.insert("autoMask", (char*)1); + advancedProperties.insert("baseSize", (char*)1); + advancedProperties.insert("mouseTracking", (char*)1); + advancedProperties.insert("acceptDrops", (char*)1); + advancedProperties.insert("cursorPosition", (char*)1); + advancedProperties.insert("contextMenuEnabled", (char*)1); + advancedProperties.insert("trapEnterKeyEvent", (char*)1); + advancedProperties.insert("dragEnabled", (char*)1); + advancedProperties.insert("enableSqueezedText", (char*)1); + advancedProperties.insert("sizeIncrement", (char*)1); +/*! @todo: reenable */ advancedProperties.insert("palette", (char*)1); + advancedProperties.insert("backgroundOrigin", (char*)1); + advancedProperties.insert("backgroundMode", (char*)1);//this is rather useless + advancedProperties.insert("layout", (char*)1);// too large risk to break things + // by providing this in propeditor + advancedProperties.insert("minimumSize", (char*)1); + advancedProperties.insert("maximumSize", (char*)1); +#ifdef KEXI_NO_UNFINISHED +/*! @todo reenable */ + advancedProperties.insert("paletteBackgroundPixmap", (char*)1); + advancedProperties.insert("icon", (char*)1); + advancedProperties.insert("pixmap", (char*)1); + advancedProperties.insert("accel", (char*)1); +#endif + } + // dict which associates a class name with a Widget class + WidgetInfo::Dict widgets;//, alternateWidgets; + QAsciiDict<KService::Ptr> services; + QAsciiDict<char> supportedFactoryGroups; + QAsciiDict<WidgetFactory> factories; + QAsciiDict<char> advancedProperties; + QAsciiDict<char> hiddenClasses; + bool showAdvancedProperties : 1; + bool factoriesLoaded : 1; +}; +} + +using namespace KFormDesigner; + +//------------------------------------------- + +WidgetLibrary::WidgetLibrary(QObject *parent, const QStringList& supportedFactoryGroups) + : QObject(parent) + , d(new WidgetLibraryPrivate()) +{ + for (QStringList::ConstIterator it = supportedFactoryGroups.constBegin(); + it!=supportedFactoryGroups.constEnd(); ++it) + { + d->supportedFactoryGroups.insert( (*it).lower().latin1(), (char*)1); + } + lookupFactories(); +} + +WidgetLibrary::~WidgetLibrary() +{ + delete d; +} + +void +WidgetLibrary::loadFactoryWidgets(WidgetFactory *f) +{ + const WidgetInfo::Dict widgets = f->classes(); + WidgetInfo *w; + for(QAsciiDictIterator<WidgetInfo> it(widgets); (w = it.current()); ++it) + { + if (0 != d->hiddenClasses[ w->className() ]) + continue; //this class is hidden + // check if we want to inherit a widget from a different factory + if (!w->m_parentFactoryName.isEmpty() && !w->m_inheritedClassName.isEmpty()) { + WidgetFactory *parentFactory = d->factories[w->m_parentFactoryName]; + if (!parentFactory) { + kdWarning() << "WidgetLibrary::loadFactoryWidgets(): class '" << w->className() + << "' - no such parent factory '" << w->m_parentFactoryName << "'" << endl; + continue; + } + WidgetInfo* inheritedClass = parentFactory->m_classesByName[ w->m_inheritedClassName ]; + if (!inheritedClass) { + kdWarning() << "WidgetLibrary::loadFactoryWidgets(): class '" << w->m_inheritedClassName + << "' - no such class to inherit in factory '" << w->m_parentFactoryName << "'" << endl; + continue; + } + //ok: inherit properties: + w->m_inheritedClass = inheritedClass; + if (w->pixmap().isEmpty()) + w->setPixmap( inheritedClass->pixmap() ); + //ok? + foreach (QValueList<QCString>::ConstIterator, it_alt, inheritedClass->m_alternateNames) { + w->addAlternateClassName( *it_alt, inheritedClass->isOverriddenClassName( *it_alt ) ); + } + if (w->includeFileName().isEmpty()) + w->setIncludeFileName( inheritedClass->includeFileName() ); + if (w->name().isEmpty()) + w->setName( inheritedClass->name() ); + if (w->namePrefix().isEmpty()) + w->setNamePrefix( inheritedClass->namePrefix() ); + if (w->description().isEmpty()) + w->setDescription( inheritedClass->description() ); + } + +// kdDebug() << "WidgetLibrary::addFactory(): adding class " << w->className() << endl; + QValueList<QCString> l = w->alternateClassNames(); + l.prepend( w->className() ); + //d->widgets.insert(w->className(), w); +// if(!w->alternateClassName().isEmpty()) { +// QStringList l = QStringList::split("|", w->alternateClassName()); + QValueList<QCString>::ConstIterator endIt = l.constEnd(); + for(QValueList<QCString>::ConstIterator it = l.constBegin(); it != endIt; ++it) { + WidgetInfo *widgetForClass = d->widgets.find( *it ); + if (!widgetForClass || (widgetForClass && !widgetForClass->isOverriddenClassName(*it))) { + //insert a widgetinfo, if: + //1) this class has no alternate class assigned yet, or + //2) this class has alternate class assigned but without 'override' flag + d->widgets.replace( *it, w); + } + +/* WidgetInfo *widgetForClass = d->alternateWidgets.find(*it); + if (!widgetForClass || (widgetForClass && !widgetForClass->isOverriddenClassName(*it))) { + //insert a widgetinfo, if: + //1) this class has no alternate class assigned yet, or + //2) this class has alternate class assigned but without 'override' flag + d->alternateWidgets.replace(*it, w); + }*/ + } + } +} + +void +WidgetLibrary::lookupFactories() +{ + KTrader::OfferList tlist = KTrader::self()->query("KFormDesigner/WidgetFactory"); + KTrader::OfferList::ConstIterator it, end( tlist.constEnd() ); + for( it = tlist.constBegin(); it != end; ++it) + { + KService::Ptr ptr = (*it); + KService::Ptr* existingService = (d->services)[ptr->library().latin1()]; + if (existingService) { + kdWarning() << "WidgetLibrary::lookupFactories(): factory '" << ptr->name() + << "' already found (library="<< (*existingService)->library() + <<")! skipping this one: library=" << ptr->library() << endl; + continue; + } + kdDebug() << "WidgetLibrary::lookupFactories(): found factory: " << ptr->name() << endl; + + QCString groupName = ptr->property("X-KFormDesigner-FactoryGroup").toCString(); + if (!groupName.isEmpty() && !d->supportedFactoryGroups[groupName]) { + kdDebug() << "WidgetLibrary::lookupFactories(): factory group '" << groupName + << "' is unsupported by this application (library=" << ptr->library() << ")"<< endl; + continue; + } + const uint factoryVersion = ptr->property("X-KFormDesigner-WidgetFactoryVersion").toUInt(); + if (KFormDesigner::version()!=factoryVersion) { + kdWarning() << QString("WidgetLibrary::lookupFactories(): factory '%1'" + " has version '%2' but required Widget Factory version is '%3'\n" + " -- skipping this factory!").arg(ptr->library()).arg(factoryVersion) + .arg(KFormDesigner::version()) << endl; + continue; + } + d->services.insert(ptr->library().latin1(), new KService::Ptr( ptr )); + } +} + +void +WidgetLibrary::loadFactories() +{ + if (d->factoriesLoaded) + return; + d->factoriesLoaded = true; + for (QAsciiDictIterator<KService::Ptr> it(d->services); it.current(); ++it) { + WidgetFactory *f = KParts::ComponentFactory::createInstanceFromService<WidgetFactory>( + *it.current(), this, (*it.current())->library().latin1(), QStringList()); + if (!f) { + kdWarning() << "WidgetLibrary::loadFactories(): creating factory failed! " + << (*it.current())->library() << endl; + continue; + } + f->m_library = this; + f->m_showAdvancedProperties = d->showAdvancedProperties; //inherit this flag from the library + f->m_xmlGUIFileName = (*it.current())->property("X-KFormDesigner-XMLGUIFileName").toString(); + d->factories.insert( f->name(), f ); + + //collect information about classes to be hidden + if (f->m_hiddenClasses) { + for (QAsciiDictIterator<char> it2(*f->m_hiddenClasses); it2.current(); ++it2) { + d->hiddenClasses.replace( it2.currentKey(), (char*)1 ); + } + } + } + + //now we have factories instantiated: load widgets + QPtrList<WidgetFactory> loadLater; + for (QAsciiDictIterator<WidgetFactory> it(d->factories); it.current(); ++it) { + //ONE LEVEL, FLAT INHERITANCE, but works! + //if this factory inherits from something, load its witgets later +//! @todo improve + if (it.current()->inheritsFactories()) + loadLater.append( it.current() ); + else + loadFactoryWidgets(it.current()); + } + //load now the rest + for (QPtrListIterator<WidgetFactory> it(loadLater); it.current(); ++it) { + loadFactoryWidgets(it.current()); + } +} + +/* old +QString +WidgetLibrary::createXML() +{ + loadFactories(); + + QDomDocument doc("kpartgui"); + QDomElement root = doc.createElement("kpartgui"); + + root.setAttribute("name", "kformdesigner"); + root.setAttribute("version", "0.3"); + doc.appendChild(root); + + QDomElement toolbar = doc.createElement("ToolBar"); + toolbar.setAttribute("name", "widgets"); + root.appendChild(toolbar); + + QDomElement texttb = doc.createElement("text"); + toolbar.appendChild(texttb); + QDomText ttext = doc.createTextNode("Widgets"); + texttb.appendChild(ttext); + + QDomElement menubar = doc.createElement("MenuBar"); + toolbar.setAttribute("name", "widgets"); + root.appendChild(menubar); + + QDomElement Mtextb = doc.createElement("text"); + toolbar.appendChild(Mtextb); + QDomText Mtext = doc.createTextNode("Widgets"); + Mtextb.appendChild(Mtext); + QDomElement menu = doc.createElement("Menu"); + menu.setAttribute("name", "widgets"); + + QAsciiDictIterator<WidgetInfo> it(d->widgets); + int i = 0; + for(; it.current(); ++it) + { + QDomElement action = doc.createElement("Action"); + action.setAttribute("name", "library_widget" + it.current()->className()); + toolbar.appendChild(action); + + i++; + } + + return doc.toString(); +}*/ + +ActionList +WidgetLibrary::createWidgetActions(KXMLGUIClient* client, KActionCollection *parent, + QObject *receiver, const char *slot) +{ + loadFactories(); + + // init XML gui clients (custom factories have their own .rc files) + for (QAsciiDictIterator<WidgetFactory> it(d->factories); it.current(); ++it) + { + if (it.current()->m_xmlGUIFileName.isEmpty()) { // probably a built-in factory, with GUI file like kexiformpartinstui.rc + it.current()->m_guiClient = 0; + } + else { // a custom factory with its own .rc file + it.current()->m_guiClient = new XMLGUIClient(client, it.current()->m_xmlGUIFileName); + } + } + + ActionList actions; + for (QAsciiDictIterator<WidgetInfo> it(d->widgets); it.current(); ++it) + { + LibActionWidget *a = new LibActionWidget(it.current(), + it.current()->factory()->m_guiClient + ? it.current()->factory()->m_guiClient->actionCollection() : parent); + connect(a, SIGNAL(prepareInsert(const QCString &)), receiver, slot); + actions.append(a); + } + return actions; +} + +void +WidgetLibrary::addCustomWidgetActions(KActionCollection *col) +{ + for (QAsciiDictIterator<WidgetFactory> it(d->factories); it.current(); ++it) + { + it.current()->createCustomActions( + it.current()->m_guiClient + ? it.current()->m_guiClient->actionCollection() : col); + } +} + +QWidget* +WidgetLibrary::createWidget(const QCString &classname, QWidget *parent, const char *name, Container *c, + int options) +{ + loadFactories(); + WidgetInfo *wclass = d->widgets[classname]; + if(!wclass) + return 0; + + QWidget *widget = wclass->factory()->createWidget(wclass->className(), parent, name, c, options); + if (!widget) { + //try to instantiate from inherited class + if (wclass->inheritedClass()) + widget = wclass->inheritedClass()->factory()->createWidget( + wclass->className(), parent, name, c, options); + if (!widget) + return 0; + } + widget->setAcceptDrops(true); + emit widgetCreated(widget); + return widget; +} + +bool +WidgetLibrary::createMenuActions(const QCString &c, QWidget *w, QPopupMenu *menu, + KFormDesigner::Container *container) +{ + loadFactories(); + WidgetInfo *wclass = d->widgets[c]; + if(!wclass) + return false; + + wclass->factory()->m_widget = w; + wclass->factory()->m_container = container; + if (wclass->factory()->createMenuActions(c, w, menu, container)) + return true; + //try from inherited class + if (wclass->inheritedClass()) + return wclass->inheritedClass()->factory() + ->createMenuActions(wclass->className(), w, menu, container); + return false; +} + +bool +WidgetLibrary::startEditing(const QCString &classname, QWidget *w, Container *container) +{ + loadFactories(); + WidgetInfo *wclass = d->widgets[classname]; + if(!wclass) + return false; + + if (wclass->factory()->startEditing(classname, w, container)) + return true; + //try from inherited class + if (wclass->inheritedClass()) + return wclass->inheritedClass()->factory()->startEditing(wclass->className(), w, container); + return false; +} + +bool +WidgetLibrary::previewWidget(const QCString &classname, QWidget *widget, Container *container) +{ + loadFactories(); + WidgetInfo *wclass = d->widgets[classname]; + if(!wclass) + return false; + + if (wclass->factory()->previewWidget(classname, widget, container)) + return true; + //try from inherited class + if (wclass->inheritedClass()) + return wclass->inheritedClass()->factory()->previewWidget(wclass->className(), widget, container); + return false; +} + +bool +WidgetLibrary::clearWidgetContent(const QCString &classname, QWidget *w) +{ + loadFactories(); + WidgetInfo *wclass = d->widgets[classname]; + if(!wclass) + return false; + + if (wclass->factory()->clearWidgetContent(classname, w)) + return true; + //try from inherited class + if (wclass->inheritedClass()) + return wclass->inheritedClass()->factory()->clearWidgetContent(wclass->className(), w); + return false; +} + +QString +WidgetLibrary::displayName(const QCString &classname) +{ + loadFactories(); + WidgetInfo *wi = d->widgets.find(classname); + if(wi) + return wi->name(); + + return classname; +} + +QString +WidgetLibrary::savingName(const QCString &classname) +{ + loadFactories(); + QString s; + WidgetInfo *wi = d->widgets.find(classname); + if(wi && !wi->savingName().isEmpty()) + return wi->savingName(); + + return classname; +} + +QString +WidgetLibrary::namePrefix(const QCString &classname) +{ + loadFactories(); + WidgetInfo *wi = d->widgets.find(classname); + if(wi) + return wi->namePrefix(); + + return classname; +} + +QString +WidgetLibrary::textForWidgetName(const QCString &name, const QCString &className) +{ + loadFactories(); + WidgetInfo *widget = d->widgets[className]; + if(!widget) + return QString::null; + + QString newName = name; + newName.remove(widget->namePrefix()); + newName = widget->name() + " " + newName; + return newName; +} + +QCString +WidgetLibrary::classNameForAlternate(const QCString &classname) +{ + loadFactories(); + if(d->widgets.find(classname)) + return classname; + + WidgetInfo *wi = d->widgets[classname]; + if (wi) { + return wi->className(); + } + + // widget not supported + return "CustomWidget"; +} + +QString +WidgetLibrary::includeFileName(const QCString &classname) +{ + loadFactories(); + WidgetInfo *wi = d->widgets.find(classname); + if(wi) + return wi->includeFileName(); + + return QString::null; +} + +QString +WidgetLibrary::iconName(const QCString &classname) +{ + loadFactories(); + WidgetInfo *wi = d->widgets.find(classname); + if(wi) + return wi->pixmap(); + + return QString::fromLatin1("unknown_widget"); +} + +bool +WidgetLibrary::saveSpecialProperty(const QCString &classname, const QString &name, const QVariant &value, QWidget *w, QDomElement &parentNode, QDomDocument &parent) +{ + loadFactories(); + WidgetInfo *wi = d->widgets.find(classname); + if (!wi) + return false; + + if (wi->factory()->saveSpecialProperty(classname, name, value, w, parentNode, parent)) + return true; + //try from inherited class + if (wi->inheritedClass()) + return wi->inheritedClass()->factory()->saveSpecialProperty(wi->className(), name, value, w, parentNode, parent); + return false; +} + +bool +WidgetLibrary::readSpecialProperty(const QCString &classname, QDomElement &node, QWidget *w, ObjectTreeItem *item) +{ + loadFactories(); + WidgetInfo *wi = d->widgets.find(classname); + if (!wi) + return false; + if (wi->factory()->readSpecialProperty(classname, node, w, item)) + return true; + //try from inherited class + if (wi->inheritedClass()) + return wi->inheritedClass()->factory()->readSpecialProperty(wi->className(), node, w, item); + return false; +} + +void WidgetLibrary::setAdvancedPropertiesVisible(bool set) +{ + d->showAdvancedProperties = set; +} + +bool WidgetLibrary::advancedPropertiesVisible() const +{ + return d->showAdvancedProperties; +} + +bool +WidgetLibrary::isPropertyVisible(const QCString &classname, QWidget *w, + const QCString &property, bool multiple, bool isTopLevel) +{ + if (isTopLevel) { + // no focus policy for top-level form widget... + if (!d->showAdvancedProperties && property == "focusPolicy") + return false; + } + + loadFactories(); + WidgetInfo *wi = d->widgets.find(classname); + if (!wi) + return false; + if (!d->showAdvancedProperties && d->advancedProperties[ property ]) { + //this is advanced property, should we hide it? + if (wi->factory()->internalProperty(classname, "forceShowAdvancedProperty:"+property).isEmpty() + && (!wi->inheritedClass() || wi->inheritedClass()->factory()->internalProperty(classname, "forceShowAdvancedProperty:"+property).isEmpty())) + { + return false; //hide it + } + } + + if (!wi->factory()->isPropertyVisible(classname, w, property, multiple, isTopLevel)) + return false; + //try from inherited class + if (wi->inheritedClass() + && !wi->inheritedClass()->factory()->isPropertyVisible(wi->className(), w, property, multiple, isTopLevel)) + return false; + + return true; +} + +QValueList<QCString> +WidgetLibrary::autoSaveProperties(const QCString &classname) +{ + loadFactories(); + WidgetInfo *wi = d->widgets.find(classname); + if(!wi) + return QValueList<QCString>(); + QValueList<QCString> lst; + //prepend from inherited class + if (wi->inheritedClass()) + lst = wi->inheritedClass()->factory()->autoSaveProperties(wi->className()); + lst += wi->factory()->autoSaveProperties(classname); + return lst; +} + +WidgetInfo* +WidgetLibrary::widgetInfoForClassName(const char* classname) +{ + loadFactories(); + return d->widgets.find(classname); +} + +WidgetFactory* +WidgetLibrary::factoryForClassName(const char* classname) +{ + WidgetInfo *wi = widgetInfoForClassName(classname); + return wi ? wi->factory() : 0; +} + +QString WidgetLibrary::propertyDescForName(WidgetInfo *winfo, const QCString& propertyName) +{ + if (!winfo || !winfo->factory()) + return QString::null; + QString desc( winfo->factory()->propertyDescForName(propertyName) ); + if (!desc.isEmpty()) + return desc; + if (winfo->m_parentFactoryName.isEmpty()) + return QString::null; + + //try in parent factory, if exists + WidgetFactory *parentFactory = d->factories[winfo->m_parentFactoryName]; + if (!parentFactory) + return QString::null; + + return parentFactory->propertyDescForName(propertyName); +} + +QString WidgetLibrary::propertyDescForValue(WidgetInfo *winfo, const QCString& name) +{ + if (!winfo->factory()) + return QString::null; + QString desc( winfo->factory()->propertyDescForValue(name) ); + if (!desc.isEmpty()) + return desc; + if (winfo->m_parentFactoryName.isEmpty()) + return QString::null; + + //try in parent factory, if exists + WidgetFactory *parentFactory = d->factories[winfo->m_parentFactoryName]; + if (!parentFactory) + return QString::null; + + return parentFactory->propertyDescForValue(name); +} + +void WidgetLibrary::setPropertyOptions( WidgetPropertySet& buf, const WidgetInfo& winfo, QWidget* w ) +{ + if (!winfo.factory()) + return; + winfo.factory()->setPropertyOptions(buf, winfo, w); + if (winfo.m_parentFactoryName.isEmpty()) + return; + WidgetFactory *parentFactory = d->factories[winfo.m_parentFactoryName]; + if (!parentFactory) + return; + parentFactory->setPropertyOptions(buf, winfo, w); +} + +WidgetFactory* WidgetLibrary::factory(const char* factoryName) const +{ + return d->factories[factoryName]; +} + +QString WidgetLibrary::internalProperty(const QCString& classname, const QCString& property) +{ + loadFactories(); + WidgetInfo *wclass = d->widgets[classname]; + if(!wclass) + return QString::null; + QString value( wclass->factory()->internalProperty(classname, property) ); + if (value.isEmpty() && wclass->inheritedClass()) + return wclass->inheritedClass()->factory()->internalProperty(classname, property); + return value; +} + +WidgetFactory::CreateWidgetOptions WidgetLibrary::showOrientationSelectionPopup( + const QCString &classname, QWidget* parent, const QPoint& pos) +{ + loadFactories(); + WidgetInfo *wclass = d->widgets[classname]; + if(!wclass) + return WidgetFactory::AnyOrientation; + + //get custom icons and strings + QPixmap iconHorizontal, iconVertical; + QString iconName( wclass->factory()->internalProperty(classname, "orientationSelectionPopup:horizontalIcon") ); + if (iconName.isEmpty() && wclass->inheritedClass()) + iconName = wclass->inheritedClass()->factory()->internalProperty(classname, "orientationSelectionPopup:horizontalIcon"); + if (!iconName.isEmpty()) + iconHorizontal = SmallIcon(iconName); + + iconName = wclass->factory()->internalProperty(classname, "orientationSelectionPopup:verticalIcon"); + if (iconName.isEmpty() && wclass->inheritedClass()) + iconName = wclass->inheritedClass()->factory()->internalProperty(classname, "orientationSelectionPopup:verticalIcon"); + if (!iconName.isEmpty()) + iconVertical = SmallIcon(iconName); + + QString textHorizontal = wclass->factory()->internalProperty(classname, "orientationSelectionPopup:horizontalText"); + if (textHorizontal.isEmpty() && wclass->inheritedClass()) + iconName = wclass->inheritedClass()->factory()->internalProperty(classname, "orientationSelectionPopup:horizontalText"); + if (textHorizontal.isEmpty()) //default + textHorizontal = i18n("Insert Horizontal Widget", "Insert Horizontal"); + + QString textVertical = wclass->factory()->internalProperty(classname, "orientationSelectionPopup:verticalText"); + if (textVertical.isEmpty() && wclass->inheritedClass()) + iconName = wclass->inheritedClass()->factory()->internalProperty(classname, "orientationSelectionPopup:verticalText"); + if (textVertical.isEmpty()) //default + textVertical = i18n("Insert Vertical Widget", "Insert Vertical"); + + KPopupMenu* popup = new KPopupMenu(parent, "orientationSelectionPopup"); + popup->insertTitle(SmallIcon(wclass->pixmap()), i18n("Insert Widget: %1").arg(wclass->name())); + popup->insertItem(iconHorizontal, textHorizontal, 1); + popup->insertItem(iconVertical, textVertical, 2); + popup->insertSeparator(); + popup->insertItem(SmallIcon("button_cancel"), i18n("Cancel"), 3); + WidgetFactory::CreateWidgetOptions result; + switch (popup->exec(pos)) { + case 1: + result = WidgetFactory::HorizontalOrientation; break; + case 2: + result = WidgetFactory::VerticalOrientation; break; + default: + result = WidgetFactory::AnyOrientation; //means "cancelled" + } + delete popup; + return result; +} + +bool WidgetLibrary::propertySetShouldBeReloadedAfterPropertyChange( + const QCString& classname, QWidget *w, const QCString& property) +{ + WidgetInfo *winfo = widgetInfoForClassName(classname); + if (!winfo) + return false; + return winfo->factory()->propertySetShouldBeReloadedAfterPropertyChange(classname, w, property); +} + +#include "widgetlibrary.moc" diff --git a/kexi/formeditor/widgetlibrary.h b/kexi/formeditor/widgetlibrary.h new file mode 100644 index 00000000..f4f8c1f3 --- /dev/null +++ b/kexi/formeditor/widgetlibrary.h @@ -0,0 +1,212 @@ +/* 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. +*/ + +#ifndef KFORMDESIGNERWIDGETLIBRARY_H +#define KFORMDESIGNERWIDGETLIBRARY_H + +#include <qobject.h> +#include <qmap.h> +#include <qdict.h> + +#include "widgetfactory.h" + +template<class type> class QPtrList; +template<class type> class QValueVector; +class KActionCollection; +class KAction; +class QWidget; +class QPopupMenu; +class QVariant; +class QDomDocument; +class QDomElement; + +namespace KFormDesigner { + +class Container; +class ObjectTreeItem; +class WidgetLibraryPrivate; +class WidgetPropertySet; + +typedef QPtrList<KAction> ActionList; + +/** + * This class searches for factories and provides KActions for widget creation. + * Every widget can be located using this library. + * You call WidgetLibrary functions instead of calling directly factories. + * See WidgetFactory for a description of the functions. + */ +class KFORMEDITOR_EXPORT WidgetLibrary : public QObject +{ + Q_OBJECT + + public: + /*! Constructs WidgetLibrary object. + In \a supportedFactoryGroups you can provide + factory group list to be supported. Factory groups are defined by + "X-KFormDesigner-FactoryGroup" field in every factory serviece's .desktop file. + By default (when supportedFactoryGroups is empty) only factories having empty + "X-KFormDesigner-FactoryGroup" field will be loaded. + Factory group names are case-insensitive. */ + WidgetLibrary(QObject *parent=0, const QStringList& supportedFactoryGroups = QStringList()); + + virtual ~WidgetLibrary(); + + /** + * creates actions for widget creating + */ + ActionList createWidgetActions(KXMLGUIClient* client, KActionCollection *parent, + QObject *receiver, const char *slot); + + void addCustomWidgetActions(KActionCollection *col); + +//old /** +//old * creates the XML for widget actions +//old */ +//old QString createXML(); + + /** + * searches the right factory and creates a widget. + * \return the widget or 0 if something falid + */ + QWidget *createWidget(const QCString &classname, QWidget *parent, const char *name, Container *c, + int options = WidgetFactory::DefaultOptions); + + bool createMenuActions(const QCString &c, QWidget *w, QPopupMenu *menu, + KFormDesigner::Container *container); + + /** + * Shows orientation selection popup. + * \return one of the following values: + * - WidgetFactory::AnyOrientation (means no selection has been made, i.e. it was cancelled) + * - WidgetFactory::HorizontalOrientation + * - WidgetFactory::VerticalOrientation + */ + WidgetFactory::CreateWidgetOptions showOrientationSelectionPopup( + const QCString &classname, QWidget* parent, const QPoint& pos); + + QString internalProperty(const QCString& classname, const QCString& property); + + QString displayName(const QCString &classname); + QString namePrefix(const QCString &classname); + QString textForWidgetName(const QCString &name, const QCString &className); + + /*! Checks if the \a classname is an alternate classname, + and returns the good classname. + If \a classname is not alternate, \a classname is returned. */ + QCString classNameForAlternate(const QCString &classname); + QString iconName(const QCString &classname); + QString includeFileName(const QCString &classname); + QString savingName(const QCString &classname); + + bool startEditing(const QCString &classname, QWidget *w, Container *container); + bool previewWidget(const QCString &classname, QWidget *widget, Container *container); + bool clearWidgetContent(const QCString &classname, QWidget *w); + + bool saveSpecialProperty(const QCString &classname, const QString &name, + const QVariant &value, QWidget *w, QDomElement &parentNode, QDomDocument &parent); + bool readSpecialProperty(const QCString &classname, QDomElement &node, QWidget *w, + ObjectTreeItem *item); + bool isPropertyVisible(const QCString &classname, QWidget *w, + const QCString &property, bool multiple = false, bool isTopLevel = false); + + QValueList<QCString> autoSaveProperties(const QCString &classname); + + WidgetInfo* widgetInfoForClassName(const char* classname); + + WidgetFactory* factoryForClassName(const char* className); + + WidgetFactory* factory(const char* factoryName) const; + + /*! \return true if advanced properties like "mouseTracking" should + be user-visible. True by default (in KFD), but Kexi set's this to false. + See WidgetLibraryPrivate class implementation for complete list + of advanced properties. */ + bool advancedPropertiesVisible() const; + + /*! Sets advanced properties to be visible or not. */ + void setAdvancedPropertiesVisible(bool set); + + /*! \return The i18n'ed name of the property \a propertyName + for a class described by \a winfo. The name can be displayed in + PropertyEditor. The name is retrieved from class' widget library. + If this library doesn't define description for such property, + and there is a parent library for \a winfo defined, parent library + is asked for returning description string. + Eventually, if even this failed, empty string is returned. + @see WidgetFactory::propertyDescForName() */ + QString propertyDescForName(WidgetInfo *winfo, const QCString& propertyName); + + /*! \return The i18n'ed name of the property's value whose name is \a name. + Works in the same way as propertyDescForName(): if actual library + does not define a description we are looking for, parent factory is asked + to return such description. + Eventually, if even this failed, empty string is returned. + @see WidgetFactory::propertyDescForValue() */ + QString propertyDescForValue(WidgetInfo *winfo, const QCString& name); + + /*! Used by WidgetPropertySet::setWidget() after creating properties. */ + void setPropertyOptions( WidgetPropertySet &list, const WidgetInfo& winfo, QWidget* w ); + + /*! \return true if property sets should be reloaded for \a property property, + \a classname class and widget \a w when a given property value changed. */ + bool propertySetShouldBeReloadedAfterPropertyChange(const QCString& classname, QWidget *w, + const QCString& property); + + signals: + void prepareInsert(const QCString &c); + + //! Received by KexiFormPart::slotWidgetCreatedByFormsLibrary() so we can add drag/drop + //! connection for the new widget + void widgetCreated(QWidget *widget); + + protected: + /** + * Adds a factory to the library, creates actions for widgets in the added factory. + * This function is not called directly but by the factory locater. + */ + void loadFactoryWidgets(WidgetFactory *f); + +#if 0 //UNIMPLEMENTED + /** + * you can restrict the loaded factories by setting the filter to a pattern + * like 'kexi|containers' in that case only factory containing 'kexi' or containers will be loaded. + * this is useful if you want to embedd formeditor and provide e.g. a LineEdit with special features + * but don't want to confuse the user... are you confused now? + * NB: not implemented yet + */ + void setFilter(const QRegExp &expr); +#endif + + /** + * Lookups widget factories list (note that this function get called once in ctor) + */ + void lookupFactories(); + + /** + * Loads widget factories found in lookupFactories(). This is called once. + */ + void loadFactories(); + + WidgetLibraryPrivate *d; +}; + +} +#endif diff --git a/kexi/formeditor/widgetpropertyset.cpp b/kexi/formeditor/widgetpropertyset.cpp new file mode 100644 index 00000000..497fb5e6 --- /dev/null +++ b/kexi/formeditor/widgetpropertyset.cpp @@ -0,0 +1,1120 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr> + Copyright (C) 2004-2006 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 "widgetpropertyset.h" + +#include <qstringlist.h> +#include <qstrlist.h> +#include <qmetaobject.h> +#include <qvariant.h> +#include <qevent.h> +#include <qlayout.h> +#include <qapplication.h> +#include <qeventloop.h> + +#include <klocale.h> +#include <kdebug.h> +#include <kmessagebox.h> + +#include "objecttree.h" +#include "form.h" +#include "container.h" +#include "formmanager.h" +#include "widgetlibrary.h" +#include "commands.h" +#include "widgetwithsubpropertiesinterface.h" + +#include <kexiutils/utils.h> +#include <kexiutils/identifier.h> + +using namespace KFormDesigner; + +namespace KFormDesigner { + +//! @internal +typedef QValueList< QGuardedPtr<QWidget> > QGuardedWidgetList; + +//! @internal +class WidgetPropertySetPrivate +{ + public: + WidgetPropertySetPrivate() + : lastCommand(0), lastGeoCommand(0), + isUndoing(false), slotPropertyChangedEnabled(true), + slotPropertyChanged_addCommandEnabled(true), + origActiveColors(0) + {} + ~WidgetPropertySetPrivate() + { + delete origActiveColors; + } + + KoProperty::Set set; + // list of properties (not) to show in editor + QStringList properties; + // list of widgets + QGuardedWidgetList widgets; +// FormManager *manager; + + // used to update command's value when undoing + PropertyCommand *lastCommand; + GeometryPropertyCommand *lastGeoCommand; + bool isUndoing : 1; + bool slotPropertyChangedEnabled : 1; + bool slotPropertyChanged_addCommandEnabled : 1; + + // helper to change color palette when switching 'enabled' property + QColorGroup* origActiveColors; + + // i18n stuff + QMap<QCString, QString> propCaption; + QMap<QCString, QString> propValCaption; +}; +} + +WidgetPropertySet::WidgetPropertySet(QObject *parent) + : QObject(parent, "kfd_widgetPropertySet") +{ + d = new WidgetPropertySetPrivate(); +// d->manager = manager; + + connect(&d->set, SIGNAL(propertyChanged(KoProperty::Set&, KoProperty::Property&)), + this, SLOT(slotPropertyChanged(KoProperty::Set&, KoProperty::Property&))); + connect(&d->set, SIGNAL(propertyReset(KoProperty::Set&, KoProperty::Property&)), + this, SLOT(slotPropertyReset(KoProperty::Set&, KoProperty::Property&))); + + initPropertiesDescription(); +} + +WidgetPropertySet::~WidgetPropertySet() +{ + delete d; +} + +/*FormManager* +WidgetPropertySet::manager() +{ + return d->manager; +}*/ + +KoProperty::Property& +WidgetPropertySet::operator[](const QCString &name) +{ + return d->set[name]; +} + +KoProperty::Property& +WidgetPropertySet::property(const QCString &name) +{ + return d->set[name]; +} + +bool +WidgetPropertySet::contains(const QCString &property) +{ + return d->set.contains(property); +} + +KoProperty::Set* +WidgetPropertySet::set() +{ + return &(d->set); +} + +void +WidgetPropertySet::clearSet(bool dontSignalShowPropertySet) +{ + saveModifiedProperties(); + + if (!dontSignalShowPropertySet) + KFormDesigner::FormManager::self()->showPropertySet(0); + d->widgets.clear(); + d->lastCommand = 0; + d->lastGeoCommand = 0; + d->properties.clear(); + d->set.clear(); + + if(!d->widgets.isEmpty()) { + d->widgets.first()->removeEventFilter(this); + disconnect(d->widgets.first(), 0, this, 0); + } +} + +void +WidgetPropertySet::saveModifiedProperties() +{ + QWidget * w = d->widgets.first(); + if(!w || d->widgets.count() > 1 || !KFormDesigner::FormManager::self()->activeForm() || !KFormDesigner::FormManager::self()->activeForm()->objectTree()) + return; + ObjectTreeItem *tree = KFormDesigner::FormManager::self()->activeForm()->objectTree()->lookup(w->name()); + if(!tree) + return; + + for(KoProperty::Set::Iterator it(d->set); it.current(); ++it) { + if(it.current()->isModified()) + tree->addModifiedProperty(it.current()->name(), it.current()->oldValue()); + } +} + +void +WidgetPropertySet::setUndoing(bool isUndoing) +{ + d->isUndoing = isUndoing; +} + +bool +WidgetPropertySet::isUndoing() +{ + return d->isUndoing; +} + +/////////////// Functions related to adding widgets ///////////////////////////////////// + +void +WidgetPropertySet::setSelectedWidget(QWidget *w, bool add, bool forceReload, bool moreWillBeSelected) +{ + if(!w) { + clearSet(); + return; + } + + // don't add a widget twice + if(!forceReload && d->widgets.contains(QGuardedPtr<QWidget>(w))) { + kdWarning() << "WidgetPropertySet::setSelectedWidget() Widget is already selected" << endl; + return; + } + // if our list is empty,don't use add parameter value + if(d->widgets.count() == 0) + add = false; + + QCString prevProperty; + if(add) + addWidget(w); + else { + if (forceReload) { + KFormDesigner::FormManager::self()->showPropertySet(0, true/*force*/); + prevProperty = d->set.prevSelection(); + } + clearSet(true); //clear but do not reload to avoid blinking + d->widgets.append(QGuardedPtr<QWidget>(w)); + createPropertiesForWidget(w); + + w->installEventFilter(this); + connect(w, SIGNAL(destroyed()), this, SLOT(slotWidgetDestroyed())); + } + + if (!moreWillBeSelected) + KFormDesigner::FormManager::self()->showPropertySet(this, true/*force*/, prevProperty); +} + +void +WidgetPropertySet::addWidget(QWidget *w) +{ + d->widgets.append(QGuardedPtr<QWidget>(w)); + + // Reset some stuff + d->lastCommand = 0; + d->lastGeoCommand = 0; + d->properties.clear(); + + QCString classname; + if(d->widgets.first()->className() == w->className()) + classname = d->widgets.first()->className(); + + // show only properties shared by widget (properties chosen by factory) + bool isTopLevel = KFormDesigner::FormManager::self()->isTopLevel(w); + + //WidgetWithSubpropertiesInterface* subpropIface = dynamic_cast<WidgetWithSubpropertiesInterface*>(w); +// QWidget *subwidget = isSubproperty ? subpropIface->subwidget() : w; + + for(KoProperty::Set::Iterator it(d->set); it.current(); ++it) { + kdDebug() << it.currentKey() << endl; + if(!isPropertyVisible(it.currentKey(), isTopLevel, classname)) + d->set[it.currentKey()].setVisible(false); + } + + if (d->widgets.count()>=2) { + //second widget, update metainfo + d->set["this:className"].setValue("special:multiple"); + d->set["this:classString"].setValue( + i18n("Multiple Widgets") + QString(" (%1)").arg(d->widgets.count()) ); + d->set["this:iconName"].setValue("multiple_obj"); + //name doesn't make sense for now + d->set["name"].setValue(""); + } +} + +void +WidgetPropertySet::createPropertiesForWidget(QWidget *w) +{ + Form *form; + if (!KFormDesigner::FormManager::self() + || !(form = KFormDesigner::FormManager::self()->activeForm()) + || !KFormDesigner::FormManager::self()->activeForm()->objectTree()) + { + kdWarning() << "WidgetPropertySet::createPropertiesForWidget() no manager or active form!!!" << endl; + return; + } + ObjectTreeItem *tree = form->objectTree()->lookup(w->name()); + if(!tree) + return; + + const QVariantMap* modifiedProperties = tree->modifiedProperties(); + QVariantMapConstIterator modifiedPropertiesIt; + bool isTopLevel = KFormDesigner::FormManager::self()->isTopLevel(w); +// int count = 0; + KoProperty::Property *newProp = 0; + WidgetInfo *winfo = form->library()->widgetInfoForClassName(w->className()); + if (!winfo) { + kdWarning() << "WidgetPropertySet::createPropertiesForWidget() no widget info for class " + << w->className() << endl; + return; + } + + QStrList pList = w->metaObject()->propertyNames(true); + QStrListIterator it(pList); + + // add subproperties if available + WidgetWithSubpropertiesInterface* subpropIface = dynamic_cast<WidgetWithSubpropertiesInterface*>(w); + QStrList tmpList; //used to allocate copy of names + if (subpropIface) { + QValueList<QCString> subproperies( + subpropIface->subproperies() ); + foreach(QValueListConstIterator<QCString>, it, subproperies ) { + tmpList.append( *it ); + pList.append( tmpList.last() ); + kdDebug() << "Added subproperty: " << *it << endl; + } + } + + // iterate over the property list, and create Property objects + for(; it.current() != 0; ++it) { + //kdDebug() << ">> " << it.current() << endl; + const QMetaProperty *subMeta = // special case - subproperty + subpropIface ? subpropIface->findMetaSubproperty(it.current()) : 0; + const QMetaProperty *meta = subMeta ? subMeta + : w->metaObject()->property( w->metaObject()->findProperty(*it, true), true); + if (!meta) + continue; + const char* propertyName = meta->name(); + QWidget *subwidget = subMeta/*subpropIface*/ ? subpropIface->subwidget() : w; + WidgetInfo *subwinfo = form->library()->widgetInfoForClassName(subwidget->className()); +// kdDebug() << "$$$ " << subwidget->className() << endl; + + if(subwinfo && meta->designable(subwidget) && !d->set.contains(propertyName)) { + //! \todo add another list for property description + QString desc( d->propCaption[meta->name()] ); + //! \todo change i18n + if (desc.isEmpty()) //try to get property description from factory + desc = form->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 = QStringList::fromStrList( meta->enumKeys() ); + newProp = new KoProperty::Property(propertyName, createValueList(subwinfo, keys), + /* assign current or older value */ + meta->valueToKey( + oldValueExists ? modifiedPropertiesIt.data().toInt() : subwidget->property(propertyName).toInt() ), + desc, desc ); + //now set current value, so the old one is stored as old + if (oldValueExists) { + newProp->setValue( meta->valueToKey( subwidget->property(propertyName).toInt() ) ); + } + } + else { + newProp = new KoProperty::Property(propertyName, + /* assign current or older value */ + oldValueExists ? modifiedPropertiesIt.data() : subwidget->property(propertyName), + desc, desc, subwinfo->customTypeForProperty(propertyName)); + //now set current value, so the old one is stored as old + if (oldValueExists) { + newProp->setValue( subwidget->property(propertyName) ); + } + } + + d->set.addProperty(newProp); + if(!isPropertyVisible(propertyName, isTopLevel)) + newProp->setVisible(false); + //! TMP + if(newProp->type() == 0) // invalid type == null pixmap ? + newProp->setType(KoProperty::Pixmap); + } + +// if(0==qstrcmp(propertyName, "name")) +// (*this)["name"].setAutoSync(0); // name should be updated only when pressing Enter + + // \todo js what does this mean? why do you use WidgetInfo and not WidgetLibrary + /*if (winfo) { + tristate autoSync = winfo->autoSyncForProperty( propertyName ); + if (! ~autoSync) + d->set[propertyName].setAutoSync( autoSync ); + }*/ + + // update the Property.oldValue() and isModified() using the value stored in the ObjectTreeItem + updatePropertyValue(tree, propertyName, meta); + } + + (*this)["name"].setAutoSync(false); // name should be updated only when pressing Enter + (*this)["enabled"].setValue( QVariant(tree->isEnabled(), 3)); + + if (winfo) { + form->library()->setPropertyOptions(*this, *winfo, w); + d->set.addProperty( newProp = new KoProperty::Property("this:classString", winfo->name()) ); + newProp->setVisible(false); + d->set.addProperty( newProp = new KoProperty::Property("this:iconName", winfo->pixmap()) ); + newProp->setVisible(false); + } + d->set.addProperty( newProp = new KoProperty::Property("this:className", w->className()) ); + newProp->setVisible(false); + + /*! let's forget it for now, until we have new complete events editor + if (m_manager->lib()->advancedPropertiesVisible()) { + // add the signals property + QStrList strlist = w->metaObject()->signalNames(true); + QStrListIterator strIt(strlist); + QStringList list; + for(; strIt.current() != 0; ++strIt) + list.append(*strIt); + Property *prop = new Property("signals", i18n("Events")"", + new KexiProperty::ListData(list, descList(winfo, list)), + )); + }*/ + + if(KFormDesigner::FormManager::self()->activeForm() && tree->container()) // we are a container -> layout property + createLayoutProperty(tree); +} + +void +WidgetPropertySet::updatePropertyValue(ObjectTreeItem *tree, const char *property, const QMetaProperty *meta) +{ + const char *propertyName = meta ? meta->name() : property; + if (!d->set.contains(propertyName)) + return; + KoProperty::Property p( d->set[propertyName] ); + +//! \todo what about set properties, and lists properties + QMap<QString, QVariant>::ConstIterator it( tree->modifiedProperties()->find(propertyName) ); + if (it != tree->modifiedProperties()->constEnd()) { + blockSignals(true); + if(meta && meta->isEnumType()) { + p.setValue( meta->valueToKey( it.data().toInt() ), false ); + } + else { + p.setValue(it.data(), false ); + } + p.setValue(p.value(), true); + blockSignals(false); + } +} + +bool +WidgetPropertySet::isPropertyVisible(const QCString &property, bool isTopLevel, const QCString &classname) +{ + const bool multiple = d->widgets.count() >= 2; + if(multiple && classname.isEmpty()) + return false; +/* moved to WidgetLibrary::isPropertyVisible() + if(d->widgets.count() < 2) + { + if(d->properties.isEmpty() && !isTopLevel) + d->properties << "caption" << "icon" << "sizeIncrement" << "iconText"; + // don't show these properties for a non-toplevel widget + + if(! (d->properties.grep(property)).isEmpty() ) + return false; + } + else + { + if(classname.isEmpty()) + return false; + + if(d->properties.isEmpty()) { + d->properties << "font" << "paletteBackgroundColor" << "enabled" << "paletteForegroundColor" + << "cursor" << "paletteBackgroundPixmap"; + } // properties always shown in multiple mode + if(! (d->properties.grep(property)).isEmpty() ) + return true; + } +*/ + +// return KFormDesigner::FormManager::self()->lib()->isPropertyVisible(d->widgets.first()->className(), d->widgets.first(), + QWidget *w = d->widgets.first(); + WidgetWithSubpropertiesInterface* subpropIface = dynamic_cast<WidgetWithSubpropertiesInterface*>(w); + QWidget *subwidget; + if (subpropIface && subpropIface->findMetaSubproperty(property)) // special case - subproperty + subwidget = subpropIface->subwidget(); + else + subwidget = w; + + return KFormDesigner::FormManager::self()->activeForm()->library()->isPropertyVisible( + subwidget->className(), subwidget, property, multiple, isTopLevel); +} + +//////////////// Slots called when properties are modified /////////////// + +void +WidgetPropertySet::slotPropertyChanged(KoProperty::Set& set, KoProperty::Property& p) +{ + Q_UNUSED( set ); + + if(!d->slotPropertyChangedEnabled || !KFormDesigner::FormManager::self() || !KFormDesigner::FormManager::self()->activeForm() + || ! KFormDesigner::FormManager::self()->activeForm()->objectTree()) + return; + + QCString property = p.name(); + if (0==property.find("this:")) + return; //starts with magical prefix: it's a "meta" prop. + + QVariant value = p.value(); + + // check if the name is valid (ie is correct identifier) and there is no name conflict + if(property == "name") { + if(d->widgets.count()!=1) + return; + if(!isNameValid(value.toString())) + return; + } + // a widget with a background pixmap should have its own origin + else if(property == "paletteBackgroundPixmap") { + d->set["backgroundOrigin"] = "WidgetOrigin"; + //else if(property == "signals") + // return; + // special types of properties handled separately + } else if((property == "hAlign") || (property == "vAlign") || (property == "wordbreak")) { + saveAlignProperty(property); + return; + } + else if((property == "layout") || (property == "layoutMargin") || (property == "layoutSpacing")) { + saveLayoutProperty(property, value); + return; + } + // we cannot really disable the widget, we just change its color palette + else if(property == "enabled") { + saveEnabledProperty(value.toBool()); + return; + } + + // make sure we are not already undoing -> avoid recursion + if(d->isUndoing && !KFormDesigner::FormManager::self()->isRedoing()) + return; + + const bool alterLastCommand = d->lastCommand && d->lastCommand->property() == property; + + if(d->widgets.count() == 1) // one widget selected + { + // If the last command is the same, we just change its value + if(alterLastCommand && !KFormDesigner::FormManager::self()->isRedoing()) + d->lastCommand->setValue(value); + else { +// if(m_widgets.first() && ((m_widgets.first() != m_manager->activeForm()->widget()) || (property != "geometry"))) { + if (d->slotPropertyChanged_addCommandEnabled && !KFormDesigner::FormManager::self()->isRedoing()) { + d->lastCommand = new PropertyCommand(this, d->widgets.first()->name(), + d->widgets.first()->property(property), value, property); + KFormDesigner::FormManager::self()->activeForm()->addCommand(d->lastCommand, false); + } + + // If the property is changed, we add it in ObjectTreeItem modifProp + ObjectTreeItem *tree = KFormDesigner::FormManager::self()->activeForm()->objectTree()->lookup(d->widgets.first()->name()); + if (tree && p.isModified()) + tree->addModifiedProperty(property, d->widgets.first()->property(property)); + } + + if(property == "name") + emit widgetNameChanged(d->widgets.first()->name(), p.value().toCString()); + d->widgets.first()->setProperty(property, value); + emitWidgetPropertyChanged(d->widgets.first(), property, value); + } + else + { + if(alterLastCommand && !KFormDesigner::FormManager::self()->isRedoing()) + d->lastCommand->setValue(value); + else { + if (d->slotPropertyChanged_addCommandEnabled && !KFormDesigner::FormManager::self()->isRedoing()) { + // We store old values for each widget + QMap<QCString, QVariant> list; + // for(QWidget *w = d->widgets.first(); w; w = d->widgets.next()) + foreach(QGuardedWidgetList::ConstIterator, it, d->widgets) + list.insert((*it)->name(), (*it)->property(property)); + + d->lastCommand = new PropertyCommand(this, list, value, property); + KFormDesigner::FormManager::self()->activeForm()->addCommand(d->lastCommand, false); + } + } + +// for(QWidget *w = d->widgets.first(); w; w = d->widgets.next()) + foreach(QGuardedWidgetList::ConstIterator, it, d->widgets) { + if (!alterLastCommand) { + ObjectTreeItem *tree = KFormDesigner::FormManager::self()->activeForm()->objectTree() + ->lookup((*it)->name()); + if(tree && p.isModified()) + tree->addModifiedProperty(property, (*it)->property(property)); + } + (*it)->setProperty(property, value); + emitWidgetPropertyChanged((*it), property, value); + } + } +} + +void WidgetPropertySet::emitWidgetPropertyChanged(QWidget *w, const QCString& property, const QVariant& value) +{ + emit widgetPropertyChanged(w, property, value); + + Form *form = KFormDesigner::FormManager::self()->activeForm(); + if (form && form->library()->propertySetShouldBeReloadedAfterPropertyChange( w->className(), w, property)) { + //setSelectedWidget(0, false); + qApp->eventLoop()->processEvents(QEventLoop::AllEvents); //be sure events related to editors are consumed + setSelectedWidget(w, /*!add*/false, /*forceReload*/true); + qApp->eventLoop()->processEvents(QEventLoop::AllEvents); //be sure events related to editors are consumed + //KFormDesigner::FormManager::self()->showPropertySet(this, true/*forceReload*/); + } +} + +void +WidgetPropertySet::createPropertyCommandsInDesignMode(QWidget* widget, + const QMap<QCString, QVariant> &propValues, CommandGroup *group, bool addToActiveForm, + bool execFlagForSubCommands) +{ + if (!widget || propValues.isEmpty()) + return; + + //is this widget selected? (if so, use property system) + const bool widgetIsSelected = KFormDesigner::FormManager::self()->activeForm()->selectedWidget() == widget; + + d->slotPropertyChanged_addCommandEnabled = false; + QMap<QCString, QVariant>::ConstIterator endIt = propValues.constEnd(); +// CommandGroup *group = new CommandGroup(commandName); + for(QMap<QCString, QVariant>::ConstIterator it = propValues.constBegin(); it != endIt; ++it) + { + if (!d->set.contains(it.key())) { + kdWarning() << "WidgetPropertySet::createPropertyCommandsInDesignMode(): \"" <<it.key()<<"\" property not found"<<endl; + continue; + } + PropertyCommand *subCommand = new PropertyCommand(this, widget->name(), + widget->property(it.key()), it.data(), it.key()); + group->addCommand( subCommand, execFlagForSubCommands); + if (widgetIsSelected) { + d->set[it.key()].setValue(it.data()); + } + else { + WidgetWithSubpropertiesInterface* subpropIface = dynamic_cast<WidgetWithSubpropertiesInterface*>(widget); + QWidget *subwidget = (subpropIface && subpropIface->subwidget()) ? subpropIface->subwidget() : widget; + if (-1 != subwidget->metaObject()->findProperty(it.key(), true) && subwidget->property(it.key())!=it.data()) { + ObjectTreeItem *tree = KFormDesigner::FormManager::self()->activeForm()->objectTree()->lookup(widget->name()); + if (tree) + tree->addModifiedProperty(it.key(), subwidget->property(it.key())); + subwidget->setProperty(it.key(), it.data()); + emit widgetPropertyChanged(widget, it.key(), it.data()); + } + } + } + d->lastCommand = 0; + if (addToActiveForm) + KFormDesigner::FormManager::self()->activeForm()->addCommand(group, false/*no exec*/); + d->slotPropertyChanged_addCommandEnabled = true; +// } +} + +//! \todo make it support undo +void +WidgetPropertySet::saveEnabledProperty(bool value) +{ +// for(QWidget *w = d->widgets.first(); w; w = d->widgets.next()) { + foreach(QGuardedWidgetList::ConstIterator, it, d->widgets) { + ObjectTreeItem *tree = KFormDesigner::FormManager::self()->activeForm()->objectTree() + ->lookup((*it)->name()); + if(tree->isEnabled() == value) + continue; + + QPalette p( (*it)->palette() ); + if (!d->origActiveColors) + d->origActiveColors = new QColorGroup( p.active() ); + if (value) { + if (d->origActiveColors) + p.setActive( *d->origActiveColors ); //revert + } + else { + QColorGroup cg = p.disabled(); + //also make base color a bit disabled-like + cg.setColor(QColorGroup::Base, cg.color(QColorGroup::Background)); + p.setActive(cg); + } + (*it)->setPalette(p); + + tree->setEnabled(value); + emit widgetPropertyChanged((*it), "enabled", QVariant(value, 3)); + } +} + +bool +WidgetPropertySet::isNameValid(const QString &name) +{ + //! \todo add to undo buffer + QWidget *w = d->widgets.first(); + //also update widget's name in QObject member + if (!KexiUtils::isIdentifier(name)) { + KMessageBox::sorry(KFormDesigner::FormManager::self()->activeForm()->widget(), + i18n("Could not rename widget \"%1\" to \"%2\" because " + "\"%3\" is not a valid name (identifier) for a widget.\n") + .arg(w->name()).arg(name).arg(name)); + d->slotPropertyChangedEnabled = false; + d->set["name"].resetValue(); + d->slotPropertyChangedEnabled = true; + return false; + } + + if (KFormDesigner::FormManager::self()->activeForm()->objectTree()->lookup(name)) { + KMessageBox::sorry( KFormDesigner::FormManager::self()->activeForm()->widget(), + i18n("Could not rename widget \"%1\" to \"%2\" " + "because a widget with the name \"%3\" already exists.\n") + .arg(w->name()).arg(name).arg(name)); + d->slotPropertyChangedEnabled = false; + d->set["name"].resetValue(); + d->slotPropertyChangedEnabled = true; + return false; + } + + return true; //ie name is correct +} + +void +WidgetPropertySet::slotPropertyReset(KoProperty::Set& set, KoProperty::Property& property) +{ + Q_UNUSED( set ); + + if(d->widgets.count() < 2) + return; + + // We use the old value in modifProp for each widget +// for(QWidget *w = d->widgets.first(); w; w = d->widgets.next()) { + foreach(QGuardedWidgetList::ConstIterator, it, d->widgets) { + ObjectTreeItem *tree = KFormDesigner::FormManager::self()->activeForm()->objectTree()->lookup((*it)->name()); + if(tree->modifiedProperties()->contains(property.name())) + (*it)->setProperty(property.name(), tree->modifiedProperties()->find(property.name()).data()); + } +} + +void +WidgetPropertySet::slotWidgetDestroyed() +{ +// if(d->widgets.contains(QGuardedPtr<const QWidget>( dynamic_cast<const QWidget*>(sender()) ))) { + //only clear this set if it contains the destroyed widget + foreach(QGuardedWidgetList::ConstIterator, it, d->widgets) { + if (dynamic_cast<const QWidget*>(sender()) == *it) { + clearSet(); + break; + } + } +} + +bool +WidgetPropertySet::eventFilter(QObject *o, QEvent *ev) +{ + if(d->widgets.count() > 0 && o == d->widgets.first() && d->widgets.count() < 2) + { + if((ev->type() == QEvent::Resize) || (ev->type() == QEvent::Move)) { + if(!d->set.contains("geometry")) + return false; + if(d->set["geometry"].value() == o->property("geometry")) // to avoid infinite recursion + return false; + + d->set["geometry"] = static_cast<QWidget*>(o)->geometry(); + } + } + else if(d->widgets.count() > 1 && ev->type() == QEvent::Move) // the widget is being moved, we update the property + { + if(d->isUndoing) + return false; + + if(d->lastGeoCommand) + d->lastGeoCommand->setPos(static_cast<QMoveEvent*>(ev)->pos()); + else { + QStringList list; + foreach(QGuardedWidgetList::ConstIterator, it, d->widgets) + list.append((*it)->name()); + + d->lastGeoCommand = new GeometryPropertyCommand(this, list, static_cast<QMoveEvent*>(ev)->oldPos()); + if (KFormDesigner::FormManager::self()->activeForm()) + KFormDesigner::FormManager::self()->activeForm()->addCommand(d->lastGeoCommand, false); + } + } + + return false; +} + +// Alignment-related functions ///////////////////////////// + +void +WidgetPropertySet::createAlignProperty(const QMetaProperty *meta, QWidget *widget, QWidget *subwidget) +{ + if (!KFormDesigner::FormManager::self()->activeForm() +|| !KFormDesigner::FormManager::self()->activeForm()->objectTree()) + return; + + QStringList list; + QString value; + const int alignment = subwidget->property("alignment").toInt(); + const QStringList keys( QStringList::fromStrList( meta->valueToKeys(alignment) ) ); + + QStrList *enumKeys = new QStrList(meta->enumKeys()); + const QStringList possibleValues( QStringList::fromStrList(*enumKeys) ); + delete enumKeys; + + ObjectTreeItem *tree = KFormDesigner::FormManager::self()->activeForm()->objectTree()->lookup(widget->name()); + bool isTopLevel = KFormDesigner::FormManager::self()->isTopLevel(widget); + + if(possibleValues.find("AlignHCenter")!=possibleValues.constEnd()) { + // Create the horizontal alignment property + if(keys.find("AlignHCenter")!=keys.constEnd() || keys.find("AlignCenter")!=keys.constEnd()) + value = "AlignHCenter"; + else if(keys.find("AlignRight")!=keys.constEnd()) + value = "AlignRight"; + else if(keys.find("AlignLeft")!=keys.constEnd()) + value = "AlignLeft"; + else if(keys.find("AlignJustify")!=keys.constEnd()) + value = "AlignJustify"; + else + value = "AlignAuto"; + + list << "AlignAuto" << "AlignLeft" << "AlignRight" << "AlignHCenter" << "AlignJustify"; + KoProperty::Property *p = new KoProperty::Property("hAlign", createValueList(0, list), value, + i18n("Translators: please keep this string short (less than 20 chars)", "Hor. Alignment"), + i18n("Horizontal Alignment")); + d->set.addProperty(p); + if(!isPropertyVisible(p->name(), isTopLevel)) { + p->setVisible(false); + } + updatePropertyValue(tree, "hAlign"); + list.clear(); + } + + if(possibleValues.find("AlignTop")!=possibleValues.constEnd()) + { + // Create the ver alignment property + if(keys.find("AlignTop")!=keys.constEnd()) + value = "AlignTop"; + else if(keys.find("AlignBottom")!=keys.constEnd()) + value = "AlignBottom"; + else + value = "AlignVCenter"; + + list << "AlignTop" << "AlignVCenter" << "AlignBottom"; + KoProperty::Property *p = new KoProperty::Property("vAlign", createValueList(0, list), value, + i18n("Translators: please keep this string short (less than 20 chars)", "Ver. Alignment"), + i18n("Vertical Alignment")); + d->set.addProperty(p); + if(!isPropertyVisible(p->name(), isTopLevel)) { + p->setVisible(false); + } + updatePropertyValue(tree, "vAlign"); + } + + if(possibleValues.find("WordBreak")!=possibleValues.constEnd() +// && isPropertyVisible("wordbreak", false, subwidget->className()) +// && !subWidget->inherits("QLineEdit") /* QLineEdit doesn't support 'word break' is this generic enough?*/ + ) { + // Create the wordbreak property + KoProperty::Property *p = new KoProperty::Property("wordbreak", + QVariant(alignment & Qt::WordBreak, 3), i18n("Word Break"), i18n("Word Break") ); + d->set.addProperty(p); + updatePropertyValue(tree, "wordbreak"); + if (!KFormDesigner::FormManager::self()->activeForm()->library()->isPropertyVisible( + subwidget->className(), subwidget, p->name(), false/*multiple*/, isTopLevel)) + { + p->setVisible(false); + } + } +} + +void +WidgetPropertySet::saveAlignProperty(const QString &property) +{ + if (!KFormDesigner::FormManager::self()->activeForm()) + return; + + QStrList list; + if( d->set.contains("hAlign") ) + list.append( d->set["hAlign"].value().toCString() ); + if( d->set.contains("vAlign") ) + list.append( d->set["vAlign"].value().toCString() ); + if( d->set.contains("wordbreak") && d->set["wordbreak"].value().toBool() ) + list.append("WordBreak"); + + WidgetWithSubpropertiesInterface* subpropIface = dynamic_cast<WidgetWithSubpropertiesInterface*>( + (QWidget*)d->widgets.first() ); + QWidget *subwidget = (subpropIface && subpropIface->subwidget()) ? subpropIface->subwidget() : (QWidget*)d->widgets.first(); + int count = subwidget->metaObject()->findProperty("alignment", true); + const QMetaProperty *meta = subwidget->metaObject()->property(count, true); + subwidget->setProperty("alignment", meta->keysToValue(list)); + + ObjectTreeItem *tree = KFormDesigner::FormManager::self()->activeForm()->objectTree()->lookup( + d->widgets.first()->name() ); + if(tree && d->set[property.latin1()].isModified()) + tree->addModifiedProperty(property.latin1(), d->set[property.latin1()].oldValue()); + + if(d->isUndoing) + return; + + if(d->lastCommand && d->lastCommand->property() == "alignment") + d->lastCommand->setValue(meta->keysToValue(list)); + else { + d->lastCommand = new PropertyCommand(this, d->widgets.first()->name(), + subwidget->property("alignment"), meta->keysToValue(list), "alignment"); + KFormDesigner::FormManager::self()->activeForm()->addCommand(d->lastCommand, false); + } +} + +// Layout-related functions ////////////////////////// + +void +WidgetPropertySet::createLayoutProperty(ObjectTreeItem *item) +{ + Container *container = item->container(); + if (!container || !KFormDesigner::FormManager::self()->activeForm() || + !KFormDesigner::FormManager::self()->activeForm()->objectTree() || !container->widget()) + return; + // special containers have no 'layout' property, as it should not be changed + QCString className = container->widget()->className(); + if((className == "HBox") || (className == "VBox") || (className == "Grid")) + return; + + QStringList list; + QString value = Container::layoutTypeToString(container->layoutType()); + + list << "NoLayout" << "HBox" << "VBox" << "Grid" << "HFlow" << "VFlow"; + + KoProperty::Property *p = new KoProperty::Property("layout", createValueList(0, list), value, + i18n("Container's Layout"), i18n("Container's Layout")); + p->setVisible( container->form()->library()->advancedPropertiesVisible() ); + d->set.addProperty(p); + + updatePropertyValue(item, "layout"); + + p = new KoProperty::Property("layoutMargin", container->layoutMargin(), i18n("Layout Margin"), i18n("Layout Margin")); + d->set.addProperty(p); + updatePropertyValue(item, "layoutMargin"); + if(container->layoutType() == Container::NoLayout) + p->setVisible(false); + + p = new KoProperty::Property("layoutSpacing", container->layoutSpacing(), + i18n("Layout Spacing"), i18n("Layout Spacing")); + d->set.addProperty(p); + updatePropertyValue(item, "layoutSpacing"); + if(container->layoutType() == Container::NoLayout) + p->setVisible(false); + +} + +void +WidgetPropertySet::saveLayoutProperty(const QString &prop, const QVariant &value) +{ + Container *container=0; + if(!KFormDesigner::FormManager::self()->activeForm() || !KFormDesigner::FormManager::self()->activeForm()->objectTree()) + return; + ObjectTreeItem *item = KFormDesigner::FormManager::self()->activeForm()->objectTree()->lookup(d->widgets.first()->name()); + if(!item) + return; + container = item->container(); + + if(prop == "layout") { + Container::LayoutType type = Container::stringToLayoutType(value.toString()); + + if(d->lastCommand && d->lastCommand->property() == "layout" && !d->isUndoing) + d->lastCommand->setValue(value); + else if(!d->isUndoing) { + d->lastCommand = new LayoutPropertyCommand(this, d->widgets.first()->name(), + d->set["layout"].oldValue(), value); + KFormDesigner::FormManager::self()->activeForm()->addCommand(d->lastCommand, false); + } + + container->setLayout(type); + bool show = (type != Container::NoLayout); + if(show != d->set["layoutMargin"].isVisible()) { + d->set["layoutMargin"].setVisible(show); + d->set["layoutSpacing"].setVisible(show); + KFormDesigner::FormManager::self()->showPropertySet(this, true/*force*/); + } + return; + } + + if(prop == "layoutMargin" && container->layout()) { + container->setLayoutMargin(value.toInt()); + container->layout()->setMargin(value.toInt()); + } + else if(prop == "layoutSpacing" && container->layout()) { + container->setLayoutSpacing(value.toInt()); + container->layout()->setSpacing(value.toInt()); + } + + ObjectTreeItem *tree = KFormDesigner::FormManager::self()->activeForm()->objectTree()->lookup(d->widgets.first()->name()); + if(tree && d->set[ prop.latin1() ].isModified()) + tree->addModifiedProperty(prop.latin1(), d->set[prop.latin1()].oldValue()); + + if(d->isUndoing) + return; + + if(d->lastCommand && (QString(d->lastCommand->property()) == prop)) + d->lastCommand->setValue(value); + else { + d->lastCommand = new PropertyCommand(this, d->widgets.first()->name(), + d->set[ prop.latin1() ].oldValue(), value, prop.latin1()); + KFormDesigner::FormManager::self()->activeForm()->addCommand(d->lastCommand, false); + } +} + + + +////////////////////////////////////////// i18n related functions //////// + +void +WidgetPropertySet::initPropertiesDescription() +{ +//! \todo perhaps a few of them shouldn't be translated within KFD mode, +//! to be more Qt Designer friendly? + d->propCaption["name"] = i18n("Name"); + d->propCaption["caption"] = i18n("Caption"); + d->propCaption["text"] = i18n("Text"); + d->propCaption["paletteBackgroundPixmap"] = i18n("Background Pixmap"); + d->propCaption["enabled"] = i18n("Enabled"); + d->propCaption["geometry"] = i18n("Geometry"); + d->propCaption["sizePolicy"] = i18n("Size Policy"); + d->propCaption["minimumSize"] = i18n("Minimum Size"); + d->propCaption["maximumSize"] = i18n("Maximum Size"); + d->propCaption["font"] = i18n("Font"); + d->propCaption["cursor"] = i18n("Cursor"); + d->propCaption["paletteForegroundColor"] = i18n("Foreground Color"); + d->propCaption["paletteBackgroundColor"] = i18n("Background Color"); + d->propCaption["focusPolicy"] = i18n("Focus Policy"); + d->propCaption["margin"] = i18n("Margin"); + d->propCaption["readOnly"] = i18n("Read Only"); + //any QFrame + d->propCaption["frame"] = i18n("Frame"); + d->propCaption["lineWidth"] = i18n("Frame Width"); + d->propCaption["midLineWidth"] = i18n("Mid Frame Width"); + d->propCaption["frameShape"] = i18n("Frame Shape"); + d->propCaption["frameShadow"] = i18n("Frame Shadow"); + //any QScrollbar + d->propCaption["vScrollBarMode"] = i18n("Vertical ScrollBar"); + d->propCaption["hScrollBarMode"] = i18n("Horizontal ScrollBar"); + + d->propValCaption["NoBackground"] = i18n("No Background"); + d->propValCaption["PaletteForeground"] = i18n("Palette Foreground"); + d->propValCaption["AutoText"] = i18n("Auto (HINT: for AutoText)", "Auto"); + + d->propValCaption["AlignAuto"] = i18n("Auto (HINT: for Align)", "Auto"); + d->propValCaption["AlignLeft"] = i18n("Left (HINT: for Align)", "Left"); + d->propValCaption["AlignRight"] = i18n("Right (HINT: for Align)", "Right"); + d->propValCaption["AlignHCenter"] = i18n("Center (HINT: for Align)", "Center"); + d->propValCaption["AlignJustify"] = i18n("Justify (HINT: for Align)", "Justify"); + d->propValCaption["AlignVCenter"] = i18n("Center (HINT: for Align)", "Center"); + d->propValCaption["AlignTop"] = i18n("Top (HINT: for Align)", "Top"); + d->propValCaption["AlignBottom"] = i18n("Bottom (HINT: for Align)", "Bottom"); + + d->propValCaption["NoFrame"] = i18n("No Frame (HINT: for Frame Shape)", "No Frame"); + d->propValCaption["Box"] = i18n("Box (HINT: for Frame Shape)", "Box"); + d->propValCaption["Panel"] = i18n("Panel (HINT: for Frame Shape)", "Panel"); + d->propValCaption["WinPanel"] = i18n("Windows Panel (HINT: for Frame Shape)", "Windows Panel"); + d->propValCaption["HLine"] = i18n("Horiz. Line (HINT: for Frame Shape)", "Horiz. Line"); + d->propValCaption["VLine"] = i18n("Vertical Line (HINT: for Frame Shape)", "Vertical Line"); + d->propValCaption["StyledPanel"] = i18n("Styled (HINT: for Frame Shape)", "Styled"); + d->propValCaption["PopupPanel"] = i18n("Popup (HINT: for Frame Shape)", "Popup"); + d->propValCaption["MenuBarPanel"] = i18n("Menu Bar (HINT: for Frame Shape)", "Menu Bar"); + d->propValCaption["ToolBarPanel"] = i18n("Toolbar (HINT: for Frame Shape)", "Toolbar"); + d->propValCaption["LineEditPanel"] = i18n("Text Box (HINT: for Frame Shape)", "Text Box"); + d->propValCaption["TabWidgetPanel"] = i18n("Tab Widget (HINT: for Frame Shape)", "Tab Widget"); + d->propValCaption["GroupBoxPanel"] = i18n("Group Box (HINT: for Frame Shape)", "Group Box"); + + d->propValCaption["Plain"] = i18n("Plain (HINT: for Frame Shadow)", "Plain"); + d->propValCaption["Raised"] = i18n("Raised (HINT: for Frame Shadow)", "Raised"); + d->propValCaption["Sunken"] = i18n("Sunken (HINT: for Frame Shadow)", "Sunken"); + d->propValCaption["MShadow"] = i18n("for Frame Shadow", "Internal"); + + d->propValCaption["NoFocus"] = i18n("No Focus (HINT: for Focus)", "No Focus"); + d->propValCaption["TabFocus"] = i18n("Tab (HINT: for Focus)", "Tab"); + d->propValCaption["ClickFocus"] = i18n("Click (HINT: for Focus)", "Click"); + d->propValCaption["StrongFocus"] = i18n("Tab/Click (HINT: for Focus)", "Tab/Click"); + d->propValCaption["WheelFocus"] = i18n("Tab/Click/MouseWheel (HINT: for Focus)", "Tab/Click/MouseWheel"); + + d->propValCaption["Auto"] = i18n("Auto"); + d->propValCaption["AlwaysOff"] = i18n("Always Off"); + d->propValCaption["AlwaysOn"] = i18n("Always On"); + + //orientation + d->propValCaption["Horizontal"] = i18n("Horizontal"); + d->propValCaption["Vertical"] = i18n("Vertical"); +} + +QString +WidgetPropertySet::propertyCaption(const QCString &name) +{ + return d->propCaption[name]; +} + +QString +WidgetPropertySet::valueCaption(const QCString &name) +{ + return d->propValCaption[name]; +} + +KoProperty::Property::ListData* +WidgetPropertySet::createValueList(WidgetInfo *winfo, const QStringList &list) +{ +// QMap <QString, QVariant> map; + QStringList names; + QStringList::ConstIterator endIt = list.end(); + for(QStringList::ConstIterator it = list.begin(); it != endIt; ++it) { + QString n( d->propValCaption[ (*it).latin1() ] ); + if (n.isEmpty()) { //try within factory and (maybe) parent factory + if (winfo) + n = KFormDesigner::FormManager::self()->activeForm()->library()->propertyDescForValue( winfo, (*it).latin1() ); + if (n.isEmpty()) + names.append( *it ); //untranslated +// map.insert(*it, (*it).latin1()); //untranslated + else + names.append( n ); +// map.insert(*it, n); + } + else + names.append( n ); +// map.insert(*it, n); + } + return new KoProperty::Property::ListData(list, names); +} + +void +WidgetPropertySet::addPropertyCaption(const QCString &property, const QString &caption) +{ + if(!d->propCaption.contains(property)) + d->propCaption[property] = caption; +} + +void +WidgetPropertySet::addValueCaption(const QCString &value, const QString &caption) +{ + if(!d->propValCaption.contains(value)) + d->propValCaption[value] = caption; +} + +#include "widgetpropertyset.moc" diff --git a/kexi/formeditor/widgetpropertyset.h b/kexi/formeditor/widgetpropertyset.h new file mode 100644 index 00000000..71e7b225 --- /dev/null +++ b/kexi/formeditor/widgetpropertyset.h @@ -0,0 +1,206 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr> + Copyright (C) 2004-2006 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. +*/ + +#ifndef KFD_WIDGETPROPERTYSET_H +#define KFD_WIDGETPROPERTYSET_H + +#include <qobject.h> +#include <qstrlist.h> + +#include <koproperty/set.h> +#include <koproperty/property.h> + +class QMetaObject; +class QWidget; + +namespace KFormDesigner { + +class FormManager; +class ObjectTreeItem; +class WidgetPropertySetPrivate; +class WidgetInfo; +class CommandGroup; + +class KFORMEDITOR_EXPORT WidgetPropertySet : public QObject +{ + Q_OBJECT + + public: + WidgetPropertySet(QObject *parent); + ~WidgetPropertySet(); + +// FormManager* manager(); + + KoProperty::Property& operator[](const QCString &name); + + KoProperty::Property& property(const QCString &name); + + bool contains(const QCString &property); + + /*! i18n function used by factories to add new property caption. + Should be called on Factory creation. */ + void addPropertyCaption(const QCString &property, const QString &caption); + + void addValueCaption(const QCString &value, const QString &caption); + + public slots: + /*! Sets the widget which properties are shown in the property editor. + If \a add is true, the list switch to multiple widget mode + (only common properties are shown). Should be directly + connected to Form::widgetSelected() signal. + If \a forceReload is true, the the properties will be redisplayed in the property editor + even if these were already displayed. + If \a showPropertySet is true (the default), property editor will be updated for the current selection. + This flag is set to false when we're selecting multiple widgets. */ + void setSelectedWidget(QWidget *w, bool add = false, bool forceReload = false, + bool moreWillBeSelected = false); + + void setSelectedWidgetWithoutReload(QWidget *w, bool add = false, bool moreWillBeSelected = false) { + setSelectedWidget(w, add, false, moreWillBeSelected); + } + + /*! This function is called every time a property is modifed. It also takes + care of saving set and enum properties. */ + void slotPropertyChanged(KoProperty::Set& set, KoProperty::Property& property); + + /*! This slot is called when a property is reset using the "reload" button in PropertyEditor. */ + void slotPropertyReset(KoProperty::Set& set, KoProperty::Property& property); + + /*! This slot is called when the watched widget is destroyed. Resets the buffer.*/ + void slotWidgetDestroyed(); + +// void setPropertyValueInDesignMode(QWidget* widget, const QMap<QCString, QVariant> &propValues, + void createPropertyCommandsInDesignMode(QWidget* widget, const QMap<QCString, + QVariant> &propValues, CommandGroup *group, bool addToActiveForm = true, + bool execFlagForSubCommands = false); + + signals: + /*! This signal is emitted when a property was changed. + \a widg is the widget concerned, \a property + is the name of the modified property, and \a v is the new value of this property. */ + void widgetPropertyChanged(QWidget *w, const QCString &property, const QVariant &v); + + /*! This signal is emitted when the name of the widget is modified. + \a oldname is the name of the widget before the + change, \a newname is the name after renaming. */ + void widgetNameChanged(const QCString &oldname, const QCString &newname); + + protected: + /*! Adds the widget in d->widgets, and updates property visibilty. */ + void addWidget(QWidget *w); + + /*! Fills the list with properties related to the widget \a w. Also updates + properties old value and changed state. */ + void createPropertiesForWidget(QWidget *w); + + /*! Creates a map property description->prop. value from + the list of keys \a list. */ + KoProperty::Property::ListData* createValueList(WidgetInfo *winfo, const QStringList &list); + + /*! Changes \a property old value and changed state, using the value + stored in \a tree. Optional \a meta can be specified if you need to handle enum values. */ + void updatePropertyValue(ObjectTreeItem *tree, const char *property, const QMetaProperty *meta = 0); + + /*! \return the property list hold by this object. Do not modify the list, + just use this method to change Editor's list. */ + KoProperty::Set* set(); + + /*! Clears the set, and reset all members. */ + void clearSet(bool dontSignalShowPropertySet = false); + + /*! Saves old values of modified properties in ObjectTreeItem, so + that we can restore them later.*/ + void saveModifiedProperties(); + + /*! Checks if the name entered by user is valid, ie that it is + a valid identifier, and that there is no name conflict. */ + bool isNameValid(const QString &name); + + /*! Saves 'enabled' property, and takes care of updating widget's palette. */ + void saveEnabledProperty(bool value); + + /*! This function filters the event of the selected widget to + automatically updates the "geometry" property + when the widget is moved or resized in the Form. */ + bool eventFilter(QObject *o, QEvent *ev); + + /*! Changes undoing state of the list. Used by Undo command to + prevent recursion. */ + void setUndoing(bool isUndoing); + + bool isUndoing(); + + /*! This function is used to filter the properties to be shown + (ie not show "caption" if the widget isn't toplevel). + \return true if the property should be shown. False otherwise.*/ + bool isPropertyVisible(const QCString &property, bool isTopLevel, + const QCString &classname=QCString()); + + // Following functions are used to create special types of properties, different + // from Q_PROPERTY + + /*! Creates the properties related to alignment (ie hAlign, vAlign and WordBreak) for + the QWidget \a widget. \a subwidget is the same as \a widget if the widget itself handles + the property and it's a child widget if the child handles the property. + For example, the second case is true for KexiDBAutoField. + \a meta is the QMetaProperty for "alignment" property" of subwidget. */ + void createAlignProperty(const QMetaProperty *meta, QWidget *widget, QWidget *subwidget); + + /*! Saves the properties related to alignment (ie hAlign, vAlign and WordBreak) + and modifies the "alignment" property of the widget.*/ + void saveAlignProperty(const QString &property); + + /*! Creates the "layout" property, for the Container representing \a item. */ + void createLayoutProperty(ObjectTreeItem *item); + + /*! Saves the "layout" property and changes the Container 's layout ( + using Container::setLayout() ).*/ + void saveLayoutProperty(const QString &property, const QVariant &value); + + // Some i18n functions + //! Adds translations for general properties, by adding items in d->propDesc + void initPropertiesDescription(); + + /*! \return The i18n'ed name of the property whose name is \a name, that will be + displayed in PropertyEditor. */ + QString propertyCaption(const QCString &name); + + /*! \return The i18n'ed name of the property's value whose name is \a name. */ + QString valueCaption(const QCString &name); + + /*! \return The i18n'ed list of values, that will be shown by Property + Editor (using descFromValue()).*/ + //QStringList captionForList(const QStringList &list); + + //! Helper + void emitWidgetPropertyChanged(QWidget *w, const QCString& property, const QVariant& value); + + private: + WidgetPropertySetPrivate *d; + + friend class FormManager; + friend class PropertyCommand; + friend class LayoutPropertyCommand; + friend class GeometryPropertyCommand; +}; + +} + +#endif diff --git a/kexi/formeditor/widgetwithsubpropertiesinterface.cpp b/kexi/formeditor/widgetwithsubpropertiesinterface.cpp new file mode 100644 index 00000000..812379e1 --- /dev/null +++ b/kexi/formeditor/widgetwithsubpropertiesinterface.cpp @@ -0,0 +1,98 @@ +/* This file is part of the KDE project + Copyright (C) 2006 Jaroslaw Staniek <js@iidea.pl> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "widgetwithsubpropertiesinterface.h" + +#include <qmetaobject.h> +#include <qasciidict.h> + +#include <kdebug.h> + +using namespace KFormDesigner; + +WidgetWithSubpropertiesInterface::WidgetWithSubpropertiesInterface() +{ +} + +WidgetWithSubpropertiesInterface::~WidgetWithSubpropertiesInterface() +{ +} + +void WidgetWithSubpropertiesInterface::setSubwidget(QWidget *widget) +{ + m_subwidget = widget; + m_subproperies.clear(); + QAsciiDict<char> addedSubproperies(1024); + if (m_subwidget) { + //remember properties in the subwidget that are not present in the parent + for( QMetaObject *metaObject = m_subwidget->metaObject(); metaObject; metaObject = metaObject->superClass()) { + const int numProperties = metaObject->numProperties(); + for (int i = 0; i < numProperties; i++) { + const char *propertyName = metaObject->property( i )->name(); + if (dynamic_cast<QObject*>(this)->metaObject()->findProperty( propertyName, true )==-1 + && !addedSubproperies.find( propertyName ) ) + { + m_subproperies.append( propertyName ); + addedSubproperies.insert( propertyName, (char*)1 ); + kdDebug() << propertyName << endl; + } + } + } + qHeapSort( m_subproperies ); + } +} + +QWidget* WidgetWithSubpropertiesInterface::subwidget() const +{ + return m_subwidget; +} + +QValueList<QCString> WidgetWithSubpropertiesInterface::subproperies() const +{ + return m_subproperies; +} + +const QMetaProperty *WidgetWithSubpropertiesInterface::findMetaSubproperty(const char * name) const +{ + if (!m_subwidget || m_subproperies.find(name) == m_subproperies.constEnd()) { + return 0; + } + const int index = m_subwidget->metaObject()->findProperty( name, true ); + if (index==-1) + return 0; + return m_subwidget->metaObject()->property( index, true ); +} + +QVariant WidgetWithSubpropertiesInterface::subproperty( const char * name, bool &ok ) const +{ + if (!m_subwidget || m_subproperies.find(name) == m_subproperies.constEnd()) { + ok = false; + return QVariant(); + } + ok = true; + return m_subwidget->property( name ); +} + +bool WidgetWithSubpropertiesInterface::setSubproperty( const char * name, const QVariant & value ) +{ + if (!m_subwidget || m_subproperies.find(name) == m_subproperies.end()) { + return false; + } + return m_subwidget->setProperty( name, value ); +} diff --git a/kexi/formeditor/widgetwithsubpropertiesinterface.h b/kexi/formeditor/widgetwithsubpropertiesinterface.h new file mode 100644 index 00000000..878fefca --- /dev/null +++ b/kexi/formeditor/widgetwithsubpropertiesinterface.h @@ -0,0 +1,72 @@ +/* This file is part of the KDE project + Copyright (C) 2006 Jaroslaw Staniek <js@iidea.pl> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef WIDGETWITHSUBPROPERTIESINTERFACE_H +#define WIDGETWITHSUBPROPERTIESINTERFACE_H + +#include <qcstring.h> +#include <qvaluelist.h> +#include <qwidget.h> +#include <qguardedptr.h> +#include <qvariant.h> + +namespace KFormDesigner { + +//! An interface for declaring form widgets to have subproperties. +/*! Currently used in KexiDBAutoField to allow editing specific properties + of its internal editor. For example, if the autofield is of type Image Box, + the Image Box widget has some specific properties like "lineWidth". + Such properties are provided by the parent KexiDBAutoField object as subproperties. */ +class KFORMEDITOR_EXPORT WidgetWithSubpropertiesInterface +{ + public: + WidgetWithSubpropertiesInterface(); + virtual ~WidgetWithSubpropertiesInterface(); + + //! Sets \a widget subwidget handling subproperties. Setting 0 clears subwidget. +//! @todo maybe someone wants to add more than one widget here? + void setSubwidget(QWidget *widget); + + //! \return the assigned subwidget. + QWidget* subwidget() const; + + //! \return a list of subproperties available for this widget. + //! This is achieved by only listing those properties that are available in the + QValueList<QCString> subproperies() const; + + //! \return a meta property for a widget's subproperty or 0 if there + //! is no such subproperty. + const QMetaProperty *findMetaSubproperty(const char * name) const; + + //! \return a value of widget's subproperty. \a ok is set to true on success + //! and to false on failure. + QVariant subproperty( const char * name, bool &ok ) const; + + //! Sets a subproperty value \a value for a subproperty \a name + //! \return true on successful setting and false when there + //! is no such a subproperty in the subwidget or QObject::setProperty() failed. + bool setSubproperty( const char * name, const QVariant & value ); + + protected: + QGuardedPtr<QWidget> m_subwidget; + QValueList<QCString> m_subproperies; +}; +} + +#endif |