1 /* This file is part of the KDE project
2 Copyright (C) 2005-2011 Jarosław Staniek <staniek@kde.org>
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License as published by the Free Software Foundation; either
7 version 2 of the License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 */
19
20 #include "kexiformmanager.h"
21 #include "kexidbform.h"
22 #ifdef KEXI_AUTOFIELD_FORM_WIDGET_SUPPORT
23 #include "kexidbautofield.h"
24 #endif
25 #include "kexiformscrollview.h"
26 #include "kexiformview.h"
27 #include "kexidatasourcepage.h"
28
29 #include <KexiIcon.h>
30
31 #include <QDomDocument>
32 #include <QAction>
33 #include <QDebug>
34
35 #include <KToggleAction>
36 #include <KActionCollection>
37 #include <KPageDialog>
38 #include <KTextEdit>
39 #include <KToolBar>
40 #include <KSharedConfig>
41 #include <KConfigGroup>
42
43 #include <formeditor/form.h>
44 #include <formeditor/widgetlibrary.h>
45 #include <formeditor/commands.h>
46 #include <formeditor/objecttree.h>
47 #include <formeditor/formIO.h>
48 #include <formeditor/kexiactionselectiondialog.h>
49 #include <formeditor/WidgetTreeWidget.h>
50
51 #include <KPropertySet>
52 #include <KProperty>
53 #include <widget/properties/KexiCustomPropertyFactory.h>
54 #include <core/KexiMainWindowIface.h>
55 #include <kexiutils/SmallToolButton.h>
56 #include <kexiutils/utils.h>
57 #include <config-kexi.h>
58
59 class KexiFormManagerPrivate {
60 public:
KexiFormManagerPrivate(KexiFormManager * qq)61 explicit KexiFormManagerPrivate(KexiFormManager *qq) : part(0)
62 , q(qq)
63 {
64 features = KFormDesigner::Form::NoFeatures;
65 widgetActionGroup = new KFormDesigner::ActionGroup(q);
66 #ifdef KFD_SIGSLOTS
67 dragConnectionAction = 0;
68 #endif
69 widgetTree = 0;
70 collection = 0;
71 }
~KexiFormManagerPrivate()72 ~KexiFormManagerPrivate() {
73 }
74 KexiFormPart* part;
75 KFormDesigner::WidgetLibrary* lib;
76 KFormDesigner::ActionGroup* widgetActionGroup;
77 KFormDesigner::WidgetTreeWidget *widgetTree;
78 KActionCollection *collection;
79 KFormDesigner::Form::Features features;
80 KToggleAction *pointerAction;
81 #ifdef KFD_SIGSLOTS
82 KToggleAction *dragConnectionAction;
83 #endif
84 KToggleAction *snapToGridAction;
85
86 KexiFormManager *q;
87 };
88
Q_GLOBAL_STATIC(KexiFormManager,g_manager)89 Q_GLOBAL_STATIC(KexiFormManager, g_manager)
90
91 KexiFormManager* KexiFormManager::self()
92 {
93 return g_manager;
94 }
95
KexiFormManager()96 KexiFormManager::KexiFormManager()
97 : QObject()
98 , d(new KexiFormManagerPrivate(this))
99 {
100 // needed for custom "pixmap" property editor widget
101 KexiCustomPropertyFactory::init();
102 }
103
~KexiFormManager()104 KexiFormManager::~KexiFormManager()
105 {
106 delete d;
107 }
108
init(KexiFormPart * part,KFormDesigner::WidgetTreeWidget * widgetTree)109 void KexiFormManager::init(KexiFormPart *part, KFormDesigner::WidgetTreeWidget *widgetTree)
110 {
111 /*! @todo add configuration for supported factory groups */
112 QStringList supportedFactoryGroups;
113 supportedFactoryGroups += "kexi";
114 d->lib = new KFormDesigner::WidgetLibrary(this, supportedFactoryGroups);
115 d->lib->setAdvancedPropertiesVisible(false);
116
117 connect(d->lib, SIGNAL(widgetCreated(QWidget*)),
118 this, SLOT(slotWidgetCreatedByFormsLibrary(QWidget*)));
119 connect(d->lib, SIGNAL(widgetActionToggled(QByteArray)),
120 this, SLOT(slotWidgetActionToggled(QByteArray)));
121
122 d->part = part;
123 KActionCollection *col = /*tmp*/ new KActionCollection(this);
124 if (col) {
125 createActions( col );
126 //connect actions provided by widget factories
127 connect(col->action("widget_assign_action"), SIGNAL(triggered()),
128 this, SLOT(slotAssignAction()));
129 }
130
131 d->widgetTree = widgetTree;
132 if (d->widgetTree) {
133 //! @todo KEXI3 Port this: connect()
134 //! @todo KEXI3 Port code related to KFormDesigner::FormManager::m_treeview here
135 //! @todo connect(m_propSet, SIGNAL(widgetNameChanged(QByteArray,QByteArray)),
136 //! @todo m_treeview, SLOT(renameItem(QByteArray,QByteArray)));
137 }
138 }
139
widgetActionGroup() const140 KFormDesigner::ActionGroup* KexiFormManager::widgetActionGroup() const
141 {
142 return d->widgetActionGroup;
143 }
144
createActions(KActionCollection * collection)145 void KexiFormManager::createActions(KActionCollection* collection)
146 {
147 d->collection = collection;
148 d->lib->createWidgetActions(d->widgetActionGroup);
149 //! @todo insertWidget() slot?
150
151 #ifdef KFD_SIGSLOTS
152 if (d->features & KFormDesigner::Form::EnableConnections) {
153 // nothing
154 }
155 else {
156 d->dragConnectionAction = new KToggleAction(
157 KexiIcon("signalslot"), futureI18n("Connect Signals/Slots"), d->collection);
158 d->dragConnectionAction->setObjectName("drag_connection");
159 connect(d->dragConnectionAction, SIGNAL(triggered()),
160 this, SLOT(startCreatingConnection()));
161 d->dragConnectionAction->setChecked(false);
162 }
163 #endif
164
165 d->pointerAction = new KToggleAction(
166 koIcon("tool-pointer"), xi18n("Pointer"), d->collection);
167 d->pointerAction->setObjectName("edit_pointer");
168 d->widgetActionGroup->addAction(d->pointerAction);
169 connect(d->pointerAction, SIGNAL(triggered()),
170 this, SLOT(slotPointerClicked()));
171 d->pointerAction->setChecked(true);
172
173 d->snapToGridAction = new KToggleAction(
174 xi18n("Snap to Grid"), d->collection);
175 d->snapToGridAction->setObjectName("snap_to_grid");
176
177 //! @todo
178 #if 0
179 // Create the Style selection action (with a combo box in toolbar and submenu items)
180 KSelectAction *styleAction = new KSelectAction(
181 xi18n("Style"), d->collection);
182 styleAction->setObjectName("change_style");
183 connect(styleAction, SIGNAL(triggered()),
184 this, SLOT(slotStyle()));
185 styleAction->setEditable(false);
186
187 QString currentStyle(kapp->style()->objectName().toLower());
188 const QStringList styles = QStyleFactory::keys();
189 styleAction->setItems(styles);
190 styleAction->setCurrentItem(0);
191
192 QStringList::ConstIterator endIt = styles.constEnd();
193 int idx = 0;
194 for (QStringList::ConstIterator it = styles.constBegin(); it != endIt; ++it, ++idx) {
195 if ((*it).toLower() == currentStyle) {
196 styleAction->setCurrentItem(idx);
197 break;
198 }
199 }
200 styleAction->setToolTip(xi18n("Set the current view style."));
201 styleAction->setMenuAccelsEnabled(true);
202 #endif
203
204 d->lib->addCustomWidgetActions(d->collection);
205
206 #ifdef KEXI_DEBUG_GUI
207 KConfigGroup generalGroup(KSharedConfig::openConfig()->group("General"));
208 if (generalGroup.readEntry("ShowInternalDebugger", false)) {
209 QAction *a = new QAction(koIcon("run-build-file"), xi18n("Show Form UI Code"), this);
210 d->collection->addAction("show_form_ui", a);
211 a->setShortcut(Qt::CTRL + Qt::Key_U);
212 connect(a, SIGNAL(triggered()), this, SLOT(showFormUICode()));
213 }
214 #endif
215
216 //! @todo move elsewhere
217 {
218 // (from obsolete kexiformpartinstui.rc)
219 QStringList formActions;
220 formActions
221 << "edit_pointer"
222 << QString() //sep
223 #ifndef KEXI_AUTOFIELD_FORM_WIDGET_SUPPORT
224 << "library_widget_KexiDBAutoField"
225 #endif
226 << "library_widget_KexiDBLabel"
227 << "library_widget_KexiDBLineEdit"
228 << "library_widget_KexiDBTextEdit"
229 << "library_widget_KexiDBComboBox"
230 << "library_widget_KexiDBCheckBox"
231 << "library_widget_KexiDBImageBox"
232 << QString() //sep
233 << "library_widget_KexiDBPushButton"
234 << QString() //sep
235 << "library_widget_KexiFrame"
236 << "library_widget_QGroupBox"
237 << "library_widget_KFDTabWidget"
238 << "library_widget_KexiLineWidget"
239 << QString() //sep
240 #ifdef HAVE_QTWEBKITWIDGETS
241 << "library_widget_WebBrowserWidget"
242 #endif
243 #ifdef HAVE_MARBLE
244 << "library_widget_MapBrowserWidget"
245 #endif
246 << "library_widget_KexiDBSlider"
247 << "library_widget_KexiDBProgressBar"
248 << "library_widget_KexiDBCommandLinkButton"
249 << "library_widget_KexiDBDatePicker"
250 << QString() //sep
251 ;
252 KexiMainWindowIface *win = KexiMainWindowIface::global();
253 foreach( const QString& actionName_, formActions ) {
254 QAction *a;
255 const QString actionName(actionName_.startsWith(':') ? actionName_.mid(1) : actionName_);
256 if (actionName.isEmpty()) {
257 a = new QAction(this);
258 a->setSeparator(true);
259 }
260 else {
261 a = d->widgetActionGroup->action(actionName);
262 }
263 if (actionName_.startsWith(':')) { // icon only
264 KexiSmallToolButton *btn = new KexiSmallToolButton(a, win->toolBar("form"));
265 btn->setToolButtonStyle(Qt::ToolButtonIconOnly);
266 win->appendWidgetToToolbar("form", btn);
267 }
268 else {
269 win->addToolBarAction("form", a);
270 }
271 }
272
273 QSet<QString> iconOnlyActions;
274 const QList<QAction*> actions( d->collection->actions() );
275 foreach( QAction *a, actions ) {
276 if (iconOnlyActions.contains(a->objectName())) { // icon only
277 KexiSmallToolButton *btn = new KexiSmallToolButton(a, win->toolBar("form"));
278 btn->setToolButtonStyle(Qt::ToolButtonIconOnly);
279 win->appendWidgetToToolbar("form", btn);
280 win->setWidgetVisibleInToolbar(btn, true);
281 }
282 else {
283 win->addToolBarAction("form", a);
284 }
285 }
286 }
287 }
288
slotWidgetCreatedByFormsLibrary(QWidget * widget)289 void KexiFormManager::slotWidgetCreatedByFormsLibrary(QWidget* widget)
290 {
291 QList<QMetaMethod> _signals(KexiUtils::methodsForMetaObject(
292 widget->metaObject(), QMetaMethod::Signal));
293
294 if (!_signals.isEmpty()) {
295 QByteArray handleDragMoveEventSignal = "handleDragMoveEvent(QDragMoveEvent*)";
296 QByteArray handleDropEventSignal = "handleDropEvent(QDropEvent*)";
297 KexiFormView *formView = KDbUtils::findParent<KexiFormView*>(widget);
298
299 foreach(const QMetaMethod& method, _signals) {
300 if (0 == qstrcmp(method.methodSignature(), handleDragMoveEventSignal)) {
301 //qDebug() << method.methodSignature();
302 if (formView) {
303 connect(widget, SIGNAL(handleDragMoveEvent(QDragMoveEvent*)),
304 formView, SLOT(slotHandleDragMoveEvent(QDragMoveEvent*)));
305 }
306 } else if (0 == qstrcmp(method.methodSignature(), handleDropEventSignal)) {
307 //qDebug() << method.methodSignature();
308 if (formView) {
309 connect(widget, SIGNAL(handleDropEvent(QDropEvent*)),
310 formView, SLOT(slotHandleDropEvent(QDropEvent*)));
311 }
312 }
313 }
314 }
315 }
316
slotWidgetActionToggled(const QByteArray & action)317 void KexiFormManager::slotWidgetActionToggled(const QByteArray& action)
318 {
319 KexiFormView* fv = activeFormViewWidget();
320 if (fv) {
321 fv->form()->enterWidgetInsertingState(action);
322 }
323 }
324
library() const325 KFormDesigner::WidgetLibrary* KexiFormManager::library() const
326 {
327 return d->lib;
328 }
329
action(const char * name)330 QAction* KexiFormManager::action(const char* name)
331 {
332 KActionCollection *col = d->part->actionCollectionForMode(Kexi::DesignViewMode);
333 if (!col)
334 return 0;
335 QString n(translateName(name));
336 QAction *a = col->action(n);
337 if (a)
338 return a;
339 if (activeFormViewWidget()) {
340 a = KexiMainWindowIface::global()->actionCollection()->action(n);
341 if (a)
342 return a;
343 }
344 return d->collection->action(name);
345 }
346
activeFormViewWidget() const347 KexiFormView* KexiFormManager::activeFormViewWidget() const
348 {
349 KexiWindow *currentWindow = KexiMainWindowIface::global()->currentWindow();
350 if (!currentWindow)
351 return 0;
352 KexiFormView *currentView = dynamic_cast<KexiFormView*>(currentWindow->selectedView());
353 if (!currentView || currentView->viewMode()!=Kexi::DesignViewMode) {
354 return 0;
355 }
356 KFormDesigner::Form *form = currentView->form();
357 if (!form) {
358 return 0;
359 }
360 KexiDBForm *dbform = dynamic_cast<KexiDBForm*>(form->formWidget());
361 if (!dbform) {
362 return 0;
363 }
364 KexiFormScrollView *scrollViewWidget = dynamic_cast<KexiFormScrollView*>(dbform->dataAwareObject());
365 if (!scrollViewWidget)
366 return 0;
367 return dynamic_cast<KexiFormView*>(scrollViewWidget->parent());
368 }
369
enableAction(const char * name,bool enable)370 void KexiFormManager::enableAction(const char* name, bool enable)
371 {
372 KexiFormView* formViewWidget = activeFormViewWidget();
373 if (!formViewWidget)
374 return;
375 formViewWidget->setAvailable(translateName(name).toLatin1(), enable);
376 }
377
setFormDataSource(const QString & pluginId,const QString & name)378 void KexiFormManager::setFormDataSource(const QString& pluginId, const QString& name)
379 {
380 KexiFormView* formViewWidget = activeFormViewWidget();
381 if (!formViewWidget)
382 return;
383 KexiDBForm* formWidget = dynamic_cast<KexiDBForm*>(formViewWidget->form()->widget());
384 if (!formWidget)
385 return;
386
387 QString oldDataSourcePartClass(formWidget->dataSourcePluginId());
388 QString oldDataSource(formWidget->dataSource());
389 if (pluginId != oldDataSourcePartClass || name != oldDataSource) {
390 QHash<QByteArray, QVariant> propValues;
391 propValues.insert("dataSource", name);
392 propValues.insert("dataSourcePartClass", pluginId);
393 KFormDesigner::PropertyCommandGroup *group = new KFormDesigner::PropertyCommandGroup(
394 xi18n("Set form's data source to <resource>%1</resource>", name));
395 formViewWidget->form()->createPropertyCommandsInDesignMode(
396 formWidget, propValues, group, true /*addToActiveForm*/);
397 }
398 }
399
setDataSourceFieldOrExpression(const QString & string,const QString & caption,KDbField::Type type)400 void KexiFormManager::setDataSourceFieldOrExpression(
401 const QString& string, const QString& caption, KDbField::Type type)
402 {
403 KexiFormView* formViewWidget = activeFormViewWidget();
404 if (!formViewWidget)
405 return;
406
407 KPropertySet* set = formViewWidget->form()->propertySet();
408 if (!set->contains("dataSource"))
409 return;
410
411 set->property("dataSource").setValue(string);
412
413 if (set->propertyValue("autoCaption", false).toBool()) {
414 set->changePropertyIfExists("fieldCaptionInternal", caption);
415 }
416 if (set->propertyValue("widgetType").toString() == "Auto") {
417 set->changePropertyIfExists("fieldTypeInternal", type);
418 }
419 }
420
insertAutoFields(const QString & sourcePartClass,const QString & sourceName,const QStringList & fields)421 void KexiFormManager::insertAutoFields(const QString& sourcePartClass, const QString& sourceName,
422 const QStringList& fields)
423 {
424 #ifdef KEXI_AUTOFIELD_FORM_WIDGET_SUPPORT
425 Q_UNUSED(sourcePartClass);
426 Q_UNUSED(sourceName);
427 Q_UNUSED(fields);
428 #else
429 KexiFormView* formViewWidget = activeFormViewWidget();
430 if (!formViewWidget || !formViewWidget->form() || !formViewWidget->form()->activeContainer())
431 return;
432 formViewWidget->insertAutoFields(sourcePartClass, sourceName, fields,
433 formViewWidget->form()->activeContainer());
434 #endif
435 }
436
slotHistoryCommandExecuted(KFormDesigner::Command * command)437 void KexiFormManager::slotHistoryCommandExecuted(KFormDesigner::Command *command)
438 {
439 if (command->childCount() == 2) {
440 KexiFormView* formViewWidget = activeFormViewWidget();
441 if (!formViewWidget)
442 return;
443 KexiDBForm* formWidget = dynamic_cast<KexiDBForm*>(formViewWidget->form()->widget());
444 if (!formWidget)
445 return;
446 const KFormDesigner::PropertyCommand* pc1
447 = dynamic_cast<const KFormDesigner::PropertyCommand*>(command->child(0));
448 const KFormDesigner::PropertyCommand* pc2
449 = dynamic_cast<const KFormDesigner::PropertyCommand*>(command->child(1));
450 if (pc1 && pc2 && pc1->propertyName() == "dataSource" && pc2->propertyName() == "dataSourcePartClass") {
451 const QHash<QByteArray, QVariant>::const_iterator it1(pc1->oldValues().constBegin());
452 const QHash<QByteArray, QVariant>::const_iterator it2(pc2->oldValues().constBegin());
453 if (it1.key() == formWidget->objectName() && it2.key() == formWidget->objectName())
454 d->part->dataSourcePage()->setFormDataSource(
455 formWidget->dataSourcePluginId(), formWidget->dataSource());
456 }
457 }
458 }
459
showFormUICode()460 void KexiFormManager::showFormUICode()
461 {
462 #ifdef KEXI_DEBUG_GUI
463 KexiFormView* formView = activeFormViewWidget();
464 if (!formView)
465 return;
466 formView->form()->resetInlineEditor();
467
468 QString uiCode;
469 const int indent = 2;
470 if (!KFormDesigner::FormIO::saveFormToString(formView->form(), uiCode, indent)) {
471 //! @todo show err?
472 return;
473 }
474
475 KPageDialog uiCodeDialog;
476 uiCodeDialog.setFaceType(KPageDialog::Tabbed);
477 uiCodeDialog.setModal(true);
478 uiCodeDialog.setWindowTitle(xi18nc("@title:window", "Form's UI Code"));
479 uiCodeDialog.resize(700, 600);
480 KTextEdit *currentUICodeDialogEditor = new KTextEdit(&uiCodeDialog);
481 uiCodeDialog.addPage(currentUICodeDialogEditor, xi18n("Current"));
482 currentUICodeDialogEditor->setReadOnly(true);
483 QFont f(currentUICodeDialogEditor->font());
484 f.setFamily("courier");
485 currentUICodeDialogEditor->setFont(f);
486
487 KTextEdit *originalUICodeDialogEditor = new KTextEdit(&uiCodeDialog);
488 uiCodeDialog.addPage(originalUICodeDialogEditor, xi18n("Original"));
489 originalUICodeDialogEditor->setReadOnly(true);
490 originalUICodeDialogEditor->setFont(f);
491 currentUICodeDialogEditor->setPlainText(uiCode);
492 //indent and set our original doc as well:
493 QDomDocument doc;
494 doc.setContent(formView->form()->m_recentlyLoadedUICode);
495 originalUICodeDialogEditor->setPlainText(doc.toString(indent));
496 uiCodeDialog.exec();
497 #endif
498 }
499
slotAssignAction()500 void KexiFormManager::slotAssignAction()
501 {
502 KexiFormView* formView = activeFormViewWidget();
503 if (!formView)
504 return;
505 KFormDesigner::Form *form = formView->form();
506 KexiDBForm *dbform = 0;
507 if (form->mode() != KFormDesigner::Form::DesignMode
508 || !(dbform = dynamic_cast<KexiDBForm*>(form->formWidget())))
509 {
510 return;
511 }
512
513 KPropertySet* set = form->propertySet();
514
515 KexiFormEventAction::ActionData data;
516 const KProperty &onClickActionProp = set->property("onClickAction");
517 if (!onClickActionProp.isNull())
518 data.string = onClickActionProp.value().toString();
519
520 const KProperty &onClickActionOptionProp = set->property("onClickActionOption");
521 if (!onClickActionOptionProp.isNull())
522 data.option = onClickActionOptionProp.value().toString();
523
524 KexiFormScrollView *scrollViewWidget
525 = dynamic_cast<KexiFormScrollView*>(dbform->dataAwareObject());
526 if (!scrollViewWidget)
527 return;
528 KexiFormView* formViewWidget = dynamic_cast<KexiFormView*>(scrollViewWidget->parent());
529 if (!formViewWidget)
530 return;
531
532 KexiActionSelectionDialog dlg(dbform, data,
533 set->property("objectName").value().toString());
534
535 if (dlg.exec() == QDialog::Accepted) {
536 data = dlg.currentAction();
537 //update property value
538 set->changeProperty("onClickAction", data.string);
539 set->changeProperty("onClickActionOption", data.option);
540 }
541 }
542
slotPointerClicked()543 void KexiFormManager::slotPointerClicked()
544 {
545 KexiFormView* formView = activeFormViewWidget();
546 if (!formView)
547 return;
548 formView->form()->enterWidgetSelectingState();
549 }
550
translateName(const char * name) const551 QString KexiFormManager::translateName(const char* name) const
552 {
553 QString n(QString::fromLatin1(name));
554 // translate to our name space:
555 if (n.startsWith("align_") || n.startsWith("adjust_")
556 || n == "format_raise" || n == "format_lower" || n == "taborder")
557 {
558 n.prepend("formpart_");
559 }
560 return n;
561 }
562
563