1 /* This file is part of the KDE project
2 Copyright (C) 2005-2017 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 "kexidatasourcepage.h"
21 #include <KexiIcon.h>
22 #include <config-kexi.h>
23 #include <widget/properties/KexiPropertyEditorView.h>
24 #include <widget/KexiObjectInfoLabel.h>
25 #include <widget/KexiDataSourceComboBox.h>
26 #include <widget/fields/KexiFieldListView.h>
27 #include <widget/fields/KexiFieldComboBox.h>
28 #include <kexiutils/SmallToolButton.h>
29 #include <kexiutils/KexiFadeWidgetEffect.h>
30 #include <kexiutils/utils.h>
31 #include <kexiproject.h>
32 #include <formeditor/commands.h>
33
34 #include <KDbConnection>
35
36 #include <KProperty>
37
38 #include <KLocalizedString>
39
40 #include <QLabel>
41 #include <QLineEdit>
42 #include <QHBoxLayout>
43
KexiDataSourcePage(QWidget * parent)44 KexiDataSourcePage::KexiDataSourcePage(QWidget *parent)
45 : KexiPropertyPaneViewBase(parent)
46 , m_noDataSourceAvailableSingleText(
47 xi18n("No data source could be assigned for this widget.") )
48 , m_noDataSourceAvailableMultiText(
49 xi18n("No data source could be assigned for multiple widgets.") )
50 , m_insideClearFormDataSourceSelection(false)
51 #ifndef KEXI_AUTOFIELD_FORM_WIDGET_SUPPORT
52 , m_tableOrQuerySchema(0)
53 #endif
54 {
55 infoLabel()->setContentsMargins(0, 0, 0, spacing());
56
57 m_noDataSourceAvailableLabel = new QLabel(m_noDataSourceAvailableSingleText, this);
58 m_noDataSourceAvailableLabel->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum);
59 m_noDataSourceAvailableLabel->setContentsMargins(0, 0, 0, spacing());
60 m_noDataSourceAvailableLabel->setAlignment(Qt::AlignBottom | Qt::AlignLeft);
61 m_noDataSourceAvailableLabel->setWordWrap(true);
62 mainLayout()->addWidget(m_noDataSourceAvailableLabel);
63
64 //-Widget's Data Source
65 QHBoxLayout *hlyr = new QHBoxLayout();
66 mainLayout()->addLayout(hlyr);
67 #if 0
68 //! @todo unhide this when expression work
69 // m_widgetDSLabel = new QLabel(futureI18nc("Table Field, Query Field or Expression", "Source field or expression"), this);
70 #else
71 m_widgetDSLabel = new QLabel(
72 xi18nc("Table Field or Query Field", "Widget's data source:"), this);
73 #endif
74 m_widgetDSLabel->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
75 m_widgetDSLabel->setAlignment(Qt::AlignLeft | Qt::AlignBottom);
76 hlyr->addWidget(m_widgetDSLabel);
77 mainLayout()->addSpacing(KexiUtils::spacingHint()); // needed because unlike m_dataSourceLabel we have no button in hlyr
78
79 #if 0
80 m_clearWidgetDSButton = new KexiSmallToolButton(
81 koIcon("edit-clear-locationbar-rtl"), QString(), this);
82 m_clearWidgetDSButton->setObjectName("clearWidgetDSButton");
83 m_clearWidgetDSButton->setMinimumHeight(m_widgetDSLabel->minimumHeight());
84 m_clearWidgetDSButton->setToolTip(futureI18n("Clear widget's data source"));
85 hlyr->addWidget(m_clearWidgetDSButton);
86 connect(m_clearWidgetDSButton, SIGNAL(clicked()),
87 this, SLOT(clearWidgetDataSourceSelection()));
88 #endif
89
90 m_widgetDataSourceCombo = new KexiFieldComboBox(this);
91 m_widgetDataSourceCombo->setObjectName("sourceFieldCombo");
92 m_widgetDataSourceCombo->setContentsMargins(0, 0, 0, 0);
93 m_widgetDSLabel->setBuddy(m_widgetDataSourceCombo);
94 connect(m_widgetDataSourceCombo, SIGNAL(editTextChanged(QString)),
95 this, SLOT(slotWidgetDataSourceTextChanged(QString)));
96 mainLayout()->addWidget(m_widgetDataSourceCombo);
97
98 m_widgetDataSourceComboSpacer = addWidgetSpacer();
99
100 //- Form's Data Source
101 hlyr = new QHBoxLayout();
102 hlyr->setContentsMargins(0, 0, 0, 0);
103 mainLayout()->addLayout(hlyr);
104 m_dataSourceLabel = new QLabel(xi18n("Form's data source:"), this);
105 m_dataSourceLabel->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
106 m_dataSourceLabel->setAlignment(Qt::AlignLeft | Qt::AlignBottom);
107 hlyr->addWidget(m_dataSourceLabel);
108
109 m_gotoButton = new KexiSmallToolButton(
110 koIcon("go-jump"), QString(), this);
111 m_gotoButton->setObjectName("gotoButton");
112 m_gotoButton->setToolTip(xi18n("Go to selected form's data source"));
113 m_gotoButton->setWhatsThis(xi18n("Goes to selected form's data source"));
114 hlyr->addWidget(m_gotoButton);
115 connect(m_gotoButton, SIGNAL(clicked()), this, SLOT(slotGotoSelected()));
116
117 #if 0
118 m_clearDSButton = new KexiSmallToolButton(
119 koIcon("edit-clear-locationbar-rtl"), QString(), this);
120 m_clearDSButton->setObjectName("clearDSButton");
121 m_clearDSButton->setMinimumHeight(m_dataSourceLabel->minimumHeight());
122 m_clearDSButton->setToolTip(futureI18n("Clear form's data source"));
123 hlyr->addWidget(m_clearDSButton);
124 connect(m_clearDSButton, SIGNAL(clicked()), this, SLOT(clearFormDataSourceSelection()));
125 #endif
126
127 m_formDataSourceCombo = new KexiDataSourceComboBox(this);
128 m_formDataSourceCombo->setObjectName("dataSourceCombo");
129 m_formDataSourceCombo->setContentsMargins(0, 0, 0, 0);
130 m_dataSourceLabel->setBuddy(m_formDataSourceCombo);
131 mainLayout()->addWidget(m_formDataSourceCombo);
132
133 m_formDataSourceComboSpacer = addWidgetSpacer();
134
135 #ifndef KEXI_AUTOFIELD_FORM_WIDGET_SUPPORT
136 mainLayout()->addStretch();
137 #else
138 //2. Inserting fields
139
140 //helper info
141 //! @todo allow to hide such helpers by adding global option
142 hlyr = new QHBoxLayout();
143 hlyr->setContentsMargins(0, 0, 0, 0);
144 mainLayout()->addLayout(hlyr);
145 m_mousePointerLabel = new QLabel(this);
146 hlyr->addWidget(m_mousePointerLabel);
147 m_mousePointerLabel->setPixmap(koIcon("tool-pointer"));
148 m_mousePointerLabel->setFixedWidth(m_mousePointerLabel->pixmap()
149 ? m_mousePointerLabel->pixmap()->width() : 0);
150 m_availableFieldsDescriptionLabel = new QLabel(
151 futureI18n("Select fields from the list below and drag them onto"
152 " a form or click the <interface>Insert</interface> button"), this);
153 m_availableFieldsDescriptionLabel->setAlignment(Qt::AlignLeft);
154 m_availableFieldsDescriptionLabel->setWordWrap(true);
155 hlyr->addWidget(m_availableFieldsDescriptionLabel);
156
157 //Available Fields
158 hlyr = new QHBoxLayout();
159 hlyr->setContentsMargins(0, 0, 0, 0);
160 mainLayout()->addLayout(hlyr);
161 m_availableFieldsLabel = new QLabel(futureI18n("Available fields"), this);
162 m_availableFieldsLabel->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
163 hlyr->addWidget(m_availableFieldsLabel);
164
165 m_addField = new KexiSmallToolButton(
166 KexiIcon("add-field"), futureI18nc("Insert selected field into form", "Insert"), this);
167 m_addField->setObjectName("addFieldButton");
168 m_addField->setFocusPolicy(Qt::StrongFocus);
169 m_addField->setToolTip(futureI18n("Insert selected fields into form"));
170 m_addField->setWhatsThis(futureI18n("Inserts selected fields into form"));
171 hlyr->addWidget(m_addField);
172 connect(m_addField, SIGNAL(clicked()), this, SLOT(slotInsertSelectedFields()));
173
174 m_fieldListView = new KexiFieldListView(this,
175 KexiFieldListView::ShowDataTypes | KexiFieldListView::AllowMultiSelection);
176 m_fieldListView->setObjectName("fieldListView");
177 m_fieldListView->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding));
178 m_availableFieldsLabel->setBuddy(m_fieldListView);
179 mainLayout()->addWidget(m_fieldListView, 1);
180 connect(m_fieldListView, SIGNAL(selectionChanged()),
181 this, SLOT(slotFieldListViewSelectionChanged()));
182 connect(m_fieldListView,
183 SIGNAL(fieldDoubleClicked(QString,QString,QString)),
184 this, SLOT(slotFieldDoubleClicked(QString,QString,QString)));
185 #endif
186
187 mainLayout()->addStretch(1);
188
189 connect(m_formDataSourceCombo, SIGNAL(editTextChanged(QString)),
190 this, SLOT(slotFormDataSourceTextChanged(QString)));
191 connect(m_formDataSourceCombo, SIGNAL(dataSourceChanged()),
192 this, SLOT(slotFormDataSourceChanged()));
193 connect(m_widgetDataSourceCombo, SIGNAL(selected()),
194 this, SLOT(slotFieldSelected()));
195
196 clearFormDataSourceSelection();
197 slotFieldListViewSelectionChanged();
198 }
199
~KexiDataSourcePage()200 KexiDataSourcePage::~KexiDataSourcePage()
201 {
202 #ifndef KEXI_AUTOFIELD_FORM_WIDGET_SUPPORT
203 delete m_tableOrQuerySchema;
204 #endif
205 }
206
setProject(KexiProject * prj)207 void KexiDataSourcePage::setProject(KexiProject *prj)
208 {
209 m_widgetDataSourceCombo->setProject(prj);
210 m_formDataSourceCombo->setProject(prj);
211 }
212
clearFormDataSourceSelection(bool alsoClearComboBox)213 void KexiDataSourcePage::clearFormDataSourceSelection(bool alsoClearComboBox)
214 {
215 if (m_insideClearFormDataSourceSelection)
216 return;
217 m_insideClearFormDataSourceSelection = true;
218 if (alsoClearComboBox && !m_formDataSourceCombo->selectedName().isEmpty())
219 m_formDataSourceCombo->setDataSource(QString(), QString());
220 m_gotoButton->setEnabled(false);
221 m_widgetDataSourceCombo->setFieldOrExpression(QString());
222 #ifdef KEXI_AUTOFIELD_FORM_WIDGET_SUPPORT
223 m_addField->setEnabled(false);
224 m_fieldListView->clear();
225 #endif
226 m_insideClearFormDataSourceSelection = false;
227 }
228
slotWidgetDataSourceTextChanged(const QString & text)229 void KexiDataSourcePage::slotWidgetDataSourceTextChanged(const QString &text)
230 {
231 if (text.isEmpty()) {
232 clearWidgetDataSourceSelection();
233 }
234 }
235
clearWidgetDataSourceSelection()236 void KexiDataSourcePage::clearWidgetDataSourceSelection()
237 {
238 m_widgetDataSourceCombo->setFieldOrExpression(QString());
239 slotFieldSelected();
240 }
241
slotGotoSelected()242 void KexiDataSourcePage::slotGotoSelected()
243 {
244 const QString pluginId(m_formDataSourceCombo->selectedPluginId());
245 bool ok;
246 (void)KexiProject::pluginIdToTableOrQueryType(pluginId, &ok);
247 if (ok) {
248 if (m_formDataSourceCombo->isSelectionValid())
249 emit jumpToObjectRequested(pluginId, m_formDataSourceCombo->selectedName());
250 }
251 }
252
slotInsertSelectedFields()253 void KexiDataSourcePage::slotInsertSelectedFields()
254 {
255 #ifdef KEXI_AUTOFIELD_FORM_WIDGET_SUPPORT
256 QStringList selectedFieldNames(m_fieldListView->selectedFieldNames());
257 if (selectedFieldNames.isEmpty())
258 return;
259
260 emit insertAutoFields(m_fieldListView->schema()->table()
261 ? "org.kexi-project.table" : "org.kexi-project.query",
262 m_fieldListView->schema()->name(), selectedFieldNames);
263 #endif
264 }
265
slotFieldDoubleClicked(const QString & sourcePluginId,const QString & sourceName,const QString & fieldName)266 void KexiDataSourcePage::slotFieldDoubleClicked(const QString& sourcePluginId, const QString& sourceName,
267 const QString& fieldName)
268 {
269 #ifdef KEXI_AUTOFIELD_FORM_WIDGET_SUPPORT
270 QStringList selectedFields;
271 selectedFields.append(fieldName);
272 emit insertAutoFields(sourcePluginId, sourceName, selectedFields);
273 #else
274 Q_UNUSED(sourcePluginId);
275 Q_UNUSED(sourceName);
276 Q_UNUSED(fieldName);
277 #endif
278 }
279
slotFormDataSourceTextChanged(const QString & text)280 void KexiDataSourcePage::slotFormDataSourceTextChanged(const QString &text)
281 {
282 const bool enable = m_formDataSourceCombo->isSelectionValid();
283 if (text.isEmpty()) {
284 clearFormDataSourceSelection();
285 } else if (!enable) {
286 clearFormDataSourceSelection(m_formDataSourceCombo->selectedName().isEmpty()/*alsoClearComboBox*/);
287 }
288 updateSourceFieldWidgetsAvailability();
289 }
290
slotFormDataSourceChanged()291 void KexiDataSourcePage::slotFormDataSourceChanged()
292 {
293 if (!m_formDataSourceCombo->project())
294 return;
295 const QString pluginId(m_formDataSourceCombo->selectedPluginId());
296 bool dataSourceFound = false;
297 QString name(m_formDataSourceCombo->selectedName());
298 bool isIdAcceptable;
299 const KDbTableOrQuerySchema::Type type = KexiProject::pluginIdToTableOrQueryType(
300 pluginId, &isIdAcceptable);
301 if (isIdAcceptable && m_formDataSourceCombo->isSelectionValid()) {
302 KDbTableOrQuerySchema *tableOrQuery = new KDbTableOrQuerySchema(
303 m_formDataSourceCombo->project()->dbConnection(), name.toLatin1(), type);
304 if (tableOrQuery->table() || tableOrQuery->query()) {
305 #ifdef KEXI_AUTOFIELD_FORM_WIDGET_SUPPORT
306 m_fieldListView->setSchema(tableOrQuery);
307 #else
308 m_tableOrQuerySchema = tableOrQuery;
309 #endif
310 dataSourceFound = true;
311 m_widgetDataSourceCombo->setTableOrQuery(name, type);
312 } else {
313 delete tableOrQuery;
314 }
315 }
316 if (!dataSourceFound) {
317 m_widgetDataSourceCombo->setTableOrQuery(QString(), KDbTableOrQuerySchema::Type::Table);
318 }
319 m_gotoButton->setEnabled(dataSourceFound);
320 if (dataSourceFound) {
321 slotFieldListViewSelectionChanged();
322 } else {
323 #ifdef KEXI_AUTOFIELD_FORM_WIDGET_SUPPORT
324 m_addField->setEnabled(false);
325 #endif
326 }
327 updateSourceFieldWidgetsAvailability();
328 emit formDataSourceChanged(pluginId, name);
329 }
330
slotFieldSelected()331 void KexiDataSourcePage::slotFieldSelected()
332 {
333 KDbField::Type dataType = KDbField::InvalidType;
334 #ifdef KEXI_AUTOFIELD_FORM_WIDGET_SUPPORT
335 //! @todo this should also work for expressions
336 KDbField *field = m_fieldListView->schema()->field(
337 m_widgetDataSourceCombo->fieldOrExpression());
338 #else
339 KDbField *field = m_tableOrQuerySchema->field(
340 m_widgetDataSourceCombo->fieldOrExpression()); //temp
341 #endif
342 if (field)
343 dataType = field->type();
344
345 emit dataSourceFieldOrExpressionChanged(
346 m_widgetDataSourceCombo->fieldOrExpression(),
347 m_widgetDataSourceCombo->fieldOrExpressionCaption(),
348 dataType
349 );
350 }
351
setFormDataSource(const QString & pluginId,const QString & name)352 void KexiDataSourcePage::setFormDataSource(const QString& pluginId, const QString& name)
353 {
354 m_formDataSourceCombo->setDataSource(pluginId, name);
355 }
356
357 #define KexiDataSourcePage_FADE 1
358
assignPropertySet(KPropertySet * propertySet)359 void KexiDataSourcePage::assignPropertySet(KPropertySet* propertySet)
360 {
361 QString objectName;
362 if (propertySet)
363 objectName = propertySet->propertyValue("objectName").toString();
364 if (!objectName.isEmpty() && objectName == m_currentObjectName)
365 return; //the same object
366 m_currentObjectName = objectName;
367
368 //! @todo
369 #if KexiDataSourcePage_FADE
370 KexiFadeWidgetEffect *animation = 0;
371 if (isVisible())
372 animation = new KexiFadeWidgetEffect(this);
373 #endif
374 QString objectClassName;
375 if (propertySet) {
376 objectClassName = propertySet->propertyValue("this:className").toString();
377 }
378 updateInfoLabelForPropertySet(propertySet);
379
380 const bool isForm = objectClassName == "KexiDBForm";
381 const bool multipleSelection = objectClassName == "special:multiple";
382 const bool hasDataSourceProperty = propertySet
383 && propertySet->contains("dataSource") && !multipleSelection;
384
385 if (!isForm) {
386 //this is a widget
387 QString dataSource;
388 if (hasDataSourceProperty) {
389 if (propertySet) {
390 dataSource = (*propertySet)["dataSource"].value().toString();
391 }
392 m_noDataSourceAvailableLabel->hide();
393 m_widgetDataSourceCombo->setFieldOrExpression(dataSource);
394 m_widgetDataSourceCombo->setEnabled(true);
395 m_widgetDSLabel->show();
396 m_widgetDataSourceCombo->show();
397 m_widgetDataSourceComboSpacer->show();
398 updateSourceFieldWidgetsAvailability();
399 }
400 }
401
402 if (isForm) {
403 m_noDataSourceAvailableLabel->hide();
404 }
405 else if (!hasDataSourceProperty) {
406 if (multipleSelection) {
407 m_noDataSourceAvailableLabel->setText(m_noDataSourceAvailableMultiText);
408 }
409 else {
410 m_noDataSourceAvailableLabel->setText(m_noDataSourceAvailableSingleText);
411 }
412 m_noDataSourceAvailableLabel->show();
413 m_widgetDataSourceCombo->setEditText(QString());
414 }
415
416 if (isForm || !hasDataSourceProperty) {
417 //no source field can be set
418 m_widgetDSLabel->hide();
419 m_widgetDataSourceCombo->hide();
420 m_widgetDataSourceComboSpacer->hide();
421 }
422 //! @todo
423 #if KexiDataSourcePage_FADE
424 if (animation)
425 animation->start(100);
426 #endif
427 }
428
slotFieldListViewSelectionChanged()429 void KexiDataSourcePage::slotFieldListViewSelectionChanged()
430 {
431 #ifdef KEXI_AUTOFIELD_FORM_WIDGET_SUPPORT
432 //update "add field" button's state
433 for (Q3ListViewItemIterator it(m_fieldListView); it.current(); ++it) {
434 if (it.current()->isSelected()) {
435 m_addField->setEnabled(true);
436 return;
437 }
438 }
439 m_addField->setEnabled(false);
440 #endif
441 }
442
updateSourceFieldWidgetsAvailability()443 void KexiDataSourcePage::updateSourceFieldWidgetsAvailability()
444 {
445 const bool hasDataSource = m_formDataSourceCombo->isSelectionValid();
446 m_widgetDataSourceCombo->setEnabled(hasDataSource);
447 m_widgetDSLabel->setEnabled(hasDataSource);
448 #ifdef KEXI_AUTOFIELD_FORM_WIDGET_SUPPORT
449 m_fieldListView->setEnabled(hasDataSource);
450 m_availableFieldsLabel->setEnabled(hasDataSource);
451 m_mousePointerLabel->setEnabled(hasDataSource);
452 m_availableFieldsDescriptionLabel->setEnabled(hasDataSource);
453 #endif
454 }
455
selectedPluginId() const456 QString KexiDataSourcePage::selectedPluginId() const
457 {
458 return m_formDataSourceCombo->selectedPluginId();
459 }
460
selectedName() const461 QString KexiDataSourcePage::selectedName() const
462 {
463 return m_formDataSourceCombo->selectedName();
464 }
465