diff --git a/src/kexiutils/KexiTestHandler.cpp b/src/kexiutils/KexiTestHandler.cpp new file mode 100644 index 000000000..6be967d2f --- /dev/null +++ b/src/kexiutils/KexiTestHandler.cpp @@ -0,0 +1,57 @@ +/* This file is part of the KDE project + Copyright (C) 2012-2017 Jarosław Staniek + + 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 "KexiTestHandler.h" +#include + +class KexiTestHandler::Private +{ +public: + Private() {} + QList extraOptions; +}; + +KexiTestHandler::KexiTestHandler() + : d(new Private) +{ +} + +KexiTestHandler::~KexiTestHandler() +{ + delete d; +} + +QList KexiTestHandler::extraOptions() const +{ + return d->extraOptions; +} + +void KexiTestHandler::addExtraOption(const QCommandLineOption &option) +{ + d->extraOptions.append(option); +} + +void KexiTestHandler::removeOwnOptions(QStringList *args) +{ + for(const QCommandLineOption &extraOption : d->extraOptions) { + for(const QString &name : extraOption.names()) { + args->removeOne("--" + name); + } + } +} diff --git a/src/kexiutils/style/KexiStyle.cpp b/src/kexiutils/style/KexiStyle.cpp index 0451eeb24..7862c9b21 100644 --- a/src/kexiutils/style/KexiStyle.cpp +++ b/src/kexiutils/style/KexiStyle.cpp @@ -1,480 +1,480 @@ /* This file is part of the KDE project Copyright (C) 2016 Jarosław Staniek 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 "KexiStyle.h" #include "KexiPropertyPaneLineEditStyle.h" #include "utils.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace KexiStyle { KEXIUTILS_EXPORT bool setupApplication(KLocalizedString *errorMessage) { // Only this style matches current Kexi theme and can be supported. // @todo add support for more styles via theme plugins. const char *name = "breeze"; QScopedPointer style(QStyleFactory::create(name)); if (!style || style->objectName() != name) { *errorMessage = kxi18nc("@info", "Could not find application style %1." "Kexi will not start. " "Please check if Kexi is properly installed.") .subs(name); return false; } qApp->setStyle(style.take()); *errorMessage = KLocalizedString(); return true; } KEXIUTILS_EXPORT void setupFrame(QFrame *frame) { if (frame) { frame->setFrameStyle(QFrame::NoFrame); } } KEXIUTILS_EXPORT void setupGlobalViewModeSelector(QFrame *selector) { KexiStyle::setupFrame(selector); selector->setFont(KexiUtils::smallestReadableFont()); QPalette p(selector->palette()); p.setColor(QPalette::Window, KexiUtils::shadeBlack()); p.setColor(QPalette::Base, KexiUtils::shadeBlack()); p.setColor(QPalette::Button, KexiUtils::shadeBlack()); p.setColor(QPalette::AlternateBase, KexiUtils::shadeBlackLighter()); p.setColor(QPalette::WindowText, KexiUtils::cardboardGrey()); p.setColor(QPalette::ButtonText, KexiUtils::cardboardGrey()); p.setColor(QPalette::Text, KexiUtils::cardboardGrey()); p.setBrush(QPalette::Disabled, QPalette::Text, KexiUtils::iconGrey()); // yes, modes can be disabled //generic?? KColorScheme(QPalette::Disabled, KColorScheme::View).foreground()); p.setColor(QPalette::Highlight, KexiUtils::cardboardGrey()); p.setColor(QPalette::Active, QPalette::Highlight, KexiUtils::plasmaBlue()); // unused anyway because mode selector has no focus p.setColor(QPalette::HighlightedText, KexiUtils::charcoalGrey()); selector->setPalette(p); } KEXIUTILS_EXPORT void overpaintGlobalViewModeSelector(QWidget *widget, QPainter *painter, const QRect &selectedRect, const QColor &arrowColor) { // draw gradient painter->save(); int w = widget->fontMetrics().height() * 3 / 2; const bool rtl = QGuiApplication::isRightToLeft(); painter->translate(rtl ? 0 : (widget->width() - w), 0); QLinearGradient grad(0, 0, w, 0); QColor c(widget->palette().base().color()); c.setAlpha(0); grad.setColorAt(rtl ? 1.0 : 0.0, c); c.setAlpha(15); grad.setColorAt(0.5, c); grad.setColorAt(rtl ? 0.0 : 1.0, QColor(0, 0, 0, 50)); painter->fillRect(0, 0, w, widget->height(), QBrush(grad)); painter->restore(); // draw arrow: /| (or reversed for RTL) // \| if (!selectedRect.isNull()) { painter->save(); w = selectedRect.height() / 10; if (w % 2 == 0) { ++w; } painter->translate( selectedRect.x() + (rtl ? 0 : (selectedRect.width() - w)), selectedRect.y() + (selectedRect.height() - w * 2) / 2 - 0.5); QPolygon polygon; if (rtl) { polygon << QPoint(0, 0) << QPoint(w, w) << QPoint(0, w * 2); } else { polygon << QPoint(w, 0) << QPoint(w, w * 2) << QPoint(0, w); } painter->setPen(QPen(Qt::NoPen)); painter->setBrush(arrowColor); painter->drawPolygon(polygon); painter->restore(); } } KEXIUTILS_EXPORT void overpaintModeSelectorItem(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) { Q_UNUSED(painter) Q_UNUSED(option) Q_UNUSED(index) } KEXIUTILS_EXPORT QPalette alternativePalette(const QPalette &palette) { QPalette p(palette); p.setColor(QPalette::Window, KexiUtils::charcoalGrey()); p.setColor(QPalette::Base, KexiUtils::shadeBlack()); p.setColor(QPalette::Disabled, QPalette::Base, KexiUtils::charcoalGrey()); p.setColor(QPalette::Button, KexiUtils::shadeBlack()); p.setColor(QPalette::AlternateBase, KexiUtils::shadeBlackLighter()); p.setColor(QPalette::WindowText, KexiUtils::paperWhite()); p.setColor(QPalette::Disabled, QPalette::WindowText, KexiUtils::iconGrey().lighter(125)); p.setColor(QPalette::ButtonText, KexiUtils::paperWhite()); p.setColor(QPalette::Disabled, QPalette::ButtonText, KexiUtils::cardboardGreyAlternative()); p.setColor(QPalette::Text, KexiUtils::paperWhite()); p.setColor(QPalette::Disabled, QPalette::Text, p.color(QPalette::Disabled, QPalette::WindowText)); p.setColor(QPalette::Highlight, KexiUtils::cardboardGrey()); p.setColor(QPalette::Active, QPalette::Highlight, KexiUtils::plasmaBlue()); p.setColor(QPalette::HighlightedText, KexiUtils::charcoalGrey()); p.setColor(QPalette::Active, QPalette::HighlightedText, KexiUtils::cardboardGrey()); return p; } KEXIUTILS_EXPORT QPalette sidebarsPalette(const QPalette &palette) { return alternativePalette(palette); } KEXIUTILS_EXPORT void setSidebarsPalette(QWidget *widget) { widget->setPalette(sidebarsPalette(widget->palette())); widget->setAutoFillBackground(true); } KEXIUTILS_EXPORT QFont titleFont(const QFont &font) { QFont newFont(font); newFont.setCapitalization(QFont::AllUppercase); return newFont; } static const QString g_contexts[] = { QLatin1String("actions"), // Any QLatin1String("actions"), QLatin1String("apps"), QLatin1String("devices"), QLatin1String("filesystems"), QLatin1String("mimetypes"), QLatin1String("animations"), QLatin1String("categories"), QLatin1String("emblems"), QLatin1String("emotes"), QLatin1String("intl"), QLatin1String("places"), QLatin1String("status") }; KEXIUTILS_EXPORT QIcon darkIcon(const QString &iconName, KIconLoader::Context iconContext) { Q_ASSERT(iconContext < (sizeof(g_contexts) / sizeof(g_contexts[0]))); static const QIcon::Mode modes[] = { QIcon::Normal }; //can be supported too: , QIcon::Selected }; const QString prefix(QLatin1String(":/icons/breeze/") + g_contexts[iconContext] + QLatin1Char('/')); const QString suffixes[] = { iconName + QLatin1String("@dark.svg"), iconName + QLatin1String(".svg") }; static const QString sizesStr[] = { QString::fromLatin1("32/"), // important: opposite direction QString::fromLatin1("22/"), QString::fromLatin1("16/") }; static const QSize sizes[] = { QSize(32, 32), QSize(22, 22), QSize(16, 16) }; // important: opposite direction QIcon icon; for (int mode = 0; mode < int(sizeof(modes) / sizeof(modes[0])); ++mode) { for (int size = 0; size < int(sizeof(sizes) / sizeof(sizes[0])); ++size) { const QString fileName(prefix + sizesStr[size] + suffixes[mode]); //qDebug() << fileName << sizes[size] << modes[mode]; if (QFile::exists(fileName)) { icon.addFile(fileName, sizes[size], modes[mode], QIcon::Off); icon.addFile(fileName, sizes[size], modes[mode], QIcon::On); } } } return icon; } class IconEngine : public QIconEngine { public: IconEngine(const KexiStyledIconParameters ¶meters) : m_parameters(parameters) { } inline QIconEngine *clone() const Q_DECL_OVERRIDE { return new IconEngine(*this); } //! @todo add caching? QPixmap pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state) Q_DECL_OVERRIDE { Q_UNUSED(state) QFile f(QLatin1String(":/icons/breeze/") + g_contexts[m_parameters.context] + QChar('/') + QString::number(size.width()) + QChar('/') + m_parameters.name + ".svg"); if (!f.open(QIODevice::ReadOnly)) { return QPixmap(); } QByteArray svg(f.readAll()); QColor color; if (mode == QIcon::Selected && m_parameters.selectedColor.isValid()) { color = m_parameters.selectedColor; } else if (mode == QIcon::Disabled && m_parameters.disabledColor.isValid()) { color = m_parameters.disabledColor; //qDebug() << m_parameters.disabledColor; } else if (m_parameters.color.isValid()) { color = m_parameters.color; } if (color.isValid()) { svg.replace(KexiUtils::iconGrey().name().toLatin1(), color.name().toLatin1()); } QSvgRenderer renderer(svg); QPixmap pm(size); pm.fill(Qt::transparent); QPainter p(&pm); renderer.render(&p, pm.rect()); return pm; } //! Nothing to paint extra here void paint(QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state) Q_DECL_OVERRIDE { Q_UNUSED(painter) Q_UNUSED(rect) Q_UNUSED(mode) Q_UNUSED(state) } private: //! Needed for clone() - IconEngine(const IconEngine &other) : m_parameters(other.m_parameters) {} + IconEngine(const IconEngine &other) : QIconEngine(other), m_parameters(other.m_parameters) {} const KexiStyledIconParameters m_parameters; }; KEXIUTILS_EXPORT QIcon icon(const KexiStyledIconParameters ¶meters) { return QIcon(new IconEngine(parameters)); } const int propertyPaneMargin = 2; Q_GLOBAL_STATIC(PropertyPane, s_propertyPane) KEXIUTILS_EXPORT const PropertyPane& propertyPane() { return *s_propertyPane; } PropertyPane::PropertyPane() : margins(propertyPaneMargin, propertyPaneMargin * 2, propertyPaneMargin, propertyPaneMargin) //left top right bottom , verticalSpacing(5) , sectionTitleIndent(8) , horizontalSpacingAfterIcon(2) , horizontalSpacingAfterLabel(5) { } void PropertyPane::setupEditor(KPropertyEditorView *view) const { view->setGridLineColor(QColor()); view->setFrameShape(QFrame::NoFrame); } QPalette PropertyPane::sectionTitlePalette(const QPalette &palette) const { QPalette pal(palette); pal.setColor(QPalette::WindowText, KexiUtils::cardboardGreyAlternative()); return pal; } QPalette PropertyPane::labelPalette(const QPalette &palette) const { QPalette pal(palette); pal.setColor(QPalette::WindowText, QColor(0x7f8c8d)); return pal; } QPalette PropertyPane::warningLabelPalette(const QPalette &palette) const { QPalette pal(palette); QColor c(0xffa92d); // orange c.setAlpha(120); pal.setColor(QPalette::WindowText, c); return pal; } QFont PropertyPane::font() const { return KexiUtils::smallestReadableFont(); } void PropertyPane::alterLineEditStyle(QLineEdit *edit) const { alterPropertyPaneLineEditProxyStyle(edit); } void PropertyPane::alterComboBoxStyle(QComboBox *combo) const { if (combo) { combo->setContentsMargins(0, 0, 0, 0); alterPropertyPaneLineEditProxyStyle(combo); alterPropertyPaneLineEditProxyStyle(combo->lineEdit()); } } void PropertyPane::alterTitleFont(QWidget *widget) const { QFont f(widget->font()); f.setCapitalization(QFont::AllUppercase); widget->setFont(f); } QVBoxLayout* PropertyPane::createVLayout(QWidget *widget) const { QVBoxLayout *lyr = new QVBoxLayout(widget); lyr->setContentsMargins(0, 0, 0, 0); lyr->setSpacing(0); return lyr; } QGridLayout* PropertyPane::createFormLayout(QVBoxLayout *parentLayout) const { const PropertyPane &s = KexiStyle::propertyPane(); QGridLayout *formLyr = new QGridLayout; QMargins formMargins(0, 0, s.margins.right(), 0); KexiUtils::adjustIfRtl(&formMargins); formLyr->setContentsMargins(formMargins); formLyr->setVerticalSpacing(0); parentLayout->addLayout(formLyr); return formLyr; } static QLabel* createLabelInternal(const QString &labelText) { QLabel *label = new QLabel(labelText); label->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); label->setWordWrap(true); label->setContentsMargins(0, 0, 0, 0); if (QGuiApplication::isRightToLeft()) { label->setAlignment(Qt::AlignRight); } return label; } QLabel* PropertyPane::createLabel(const QString &labelText) const { const PropertyPane &s = KexiStyle::propertyPane(); QLabel* label = createLabelInternal(labelText); label->setPalette(s.labelPalette(label->palette())); return label; } QLabel* PropertyPane::createWarningLabel(const QString &labelText) const { const PropertyPane &s = KexiStyle::propertyPane(); QLabel* label = createLabelInternal(labelText); label->setPalette(s.warningLabelPalette(label->palette())); return label; } QLabel* PropertyPane::createTitleLabel(const QString &title, QVBoxLayout *layout) const { const PropertyPane &s = KexiStyle::propertyPane(); QLabel *lbl = new QLabel(title); lbl->setIndent(s.sectionTitleIndent); if (QGuiApplication::isRightToLeft()) { lbl->setAlignment(Qt::AlignRight); } s.alterTitleFont(lbl); lbl->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); lbl->setPalette(s.sectionTitlePalette(lbl->palette())); layout->addWidget(lbl); layout->addSpacing(verticalSpacing); return lbl; } void PropertyPane::addLabelAndWidget(const QString &labelText, QWidget *widget, QGridLayout *formLayout) const { const PropertyPane &s = KexiStyle::propertyPane(); QLabel * label = new QLabel(labelText); label->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum); QMargins labelMargins(s.sectionTitleIndent * 2, 0, s.horizontalSpacingAfterLabel, 0); KexiUtils::adjustIfRtl(&labelMargins); label->setContentsMargins(labelMargins); if (QGuiApplication::isRightToLeft()) { label->setAlignment(Qt::AlignRight); } label->setPalette(s.labelPalette(label->palette())); QComboBox* comboBox = qobject_cast(widget); if (comboBox) { s.alterComboBoxStyle(comboBox); } widget->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum); label->setBuddy(widget); formLayout->addWidget(label, formLayout->rowCount(), 0, Qt::AlignRight | Qt::AlignTop); formLayout->addWidget(widget, formLayout->rowCount() - 1, 1); formLayout->addItem(new QSpacerItem(1, verticalSpacing, QSizePolicy::Fixed, QSizePolicy::Fixed), formLayout->rowCount(), 1); } void PropertyPane::setFormLabelAndWidgetVisible(QWidget *widget, QGridLayout *formLayout, bool set) const { if (!widget) { return; } const int index = formLayout->indexOf(widget); if (index == -1) { qWarning() << "No widget" << widget << "in form layout" << formLayout; return; } int row, column, rowSpan, columnSpan; formLayout->getItemPosition(index, &row, &column, &rowSpan, &columnSpan); QLayoutItem *labelItem = formLayout->itemAtPosition(row, 0); if (labelItem->widget()) { labelItem->widget()->setVisible(set); } widget->setVisible(set); QLayoutItem *spacerItem = formLayout->itemAtPosition(row + 1, 1); if (spacerItem && spacerItem->spacerItem()) { spacerItem->spacerItem()->changeSize(set ? 1 : 0, set ? verticalSpacing : 0); } } QIcon PropertyPane::icon(const QString &iconName) const { return KexiStyle::darkIcon(iconName); } } // namespace KexiStyle diff --git a/src/plugins/forms/kexiformview.cpp b/src/plugins/forms/kexiformview.cpp index 49cbaba2a..9bdd80ac2 100644 --- a/src/plugins/forms/kexiformview.cpp +++ b/src/plugins/forms/kexiformview.cpp @@ -1,1292 +1,1301 @@ /* This file is part of the KDE project Copyright (C) 2004 Cedric Pasteur Copyright (C) 2004-2016 Jarosław Staniek 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 "kexiformview.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "widgets/kexidbform.h" #include "kexiformscrollview.h" #include "kexidatasourcepage.h" #include "kexiformmanager.h" #ifdef KEXI_AUTOFIELD_FORM_WIDGET_SUPPORT #include "kexidbautofield.h" #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //! @todo #define KEXI_SHOW_SPLITTER_WIDGET class Q_DECL_HIDDEN KexiFormView::Private { public: Private() : resizeMode(KexiFormView::ResizeDefault) , query(0) , queryIsOwned(false) , cursor(0) { } KexiDBForm *dbform; KexiFormScrollView *scrollView; /*! Database cursor used for data retrieving. It is shared between subsequent Data view sessions (just reopened on switch), but deleted and recreated from scratch when form's "dataSource" property changed since last form viewing (d->previousDataSourceString is used for that). */ QString previousDataSourceString; int resizeMode; KDbQuerySchema* query; /*! True, if d->query is created as temporary object within this form. If user selected an existing, predefined (stored) query, d->queryIsOwned will be false, so the query object will not be destroyed. */ bool queryIsOwned; KDbCursor *cursor; /*! For new (empty) forms only: Our form's area will be resized more than once. We will resize form widget itself later (in resizeEvent()). */ int delayedFormContentsResizeOnShow; //! Used in setFocusInternal() QPointer setFocusInternalOnce; #ifndef KEXI_AUTOFIELD_FORM_WIDGET_SUPPORT /*! Stores geometry of widget recently inserted using insertAutoFields() method. having this information, we'r eable to compute position for a newly inserted widget in insertAutoFields() is such position has not been specified. (the position is specified when a widget is inserted with mouse drag & dropping but not with clicking of 'Insert fields' button from Data Source pane) */ QRect widgetGeometryForRecentInsertAutoFields; #endif //! Cached form pointer QPointer form; }; KexiFormView::KexiFormView(QWidget *parent, bool dbAware) : KexiDataAwareView(parent) , d(new Private) { Q_UNUSED(dbAware); d->delayedFormContentsResizeOnShow = 0; //! @todo remove? setSortedProperties(true); d->scrollView = new KexiFormScrollView( // will be added to layout this, viewMode() == Kexi::DataViewMode); // in KexiDataAwareView::init() (void)initForm(); if (viewMode() == Kexi::DesignViewMode) { connect(form(), SIGNAL(propertySetSwitched()), this, SLOT(slotPropertySetSwitched())); connect(form(), SIGNAL(modified(bool)), this, SLOT(setDirty(bool))); connect(d->scrollView, SIGNAL(resized()), this, SLOT(setFormModified())); connect(d->dbform, SIGNAL(handleDragMoveEvent(QDragMoveEvent*)), this, SLOT(slotHandleDragMoveEvent(QDragMoveEvent*))); connect(d->dbform, SIGNAL(handleDropEvent(QDropEvent*)), this, SLOT(slotHandleDropEvent(QDropEvent*))); // action stuff plugSharedAction("formpart_taborder", form(), SLOT(editTabOrder())); plugSharedAction("formpart_adjust_size", form(), SLOT(adjustWidgetSize())); //! @todo add formpart_pixmap_collection action //! @todo add formpart_connections action plugSharedAction("edit_copy", form(), SLOT(copyWidget())); plugSharedAction("edit_cut", form(), SLOT(cutWidget())); plugSharedAction("edit_paste", form(), SLOT(pasteWidget())); plugSharedAction("edit_delete", form(), SLOT(deleteWidget())); plugSharedAction("edit_select_all", form(), SLOT(selectAll())); plugSharedAction("formpart_clear_contents", form(), SLOT(clearWidgetContent())); plugSharedAction("edit_undo", form(), SLOT(undo())); plugSharedAction("edit_redo", form(), SLOT(redo())); plugSharedAction("formpart_format_raise", form(), SLOT(bringWidgetToFront())); plugSharedAction("formpart_format_lower", form(), SLOT(sendWidgetToBack())); plugSharedAction("other_widgets_menu", form(), 0); setAvailable("other_widgets_menu", true); plugSharedAction("formpart_align_menu", form(), 0); plugSharedAction("formpart_align_to_left", form(), SLOT(alignWidgetsToLeft())); plugSharedAction("formpart_align_to_right", form(), SLOT(alignWidgetsToRight())); plugSharedAction("formpart_align_to_top", form(), SLOT(alignWidgetsToTop())); plugSharedAction("formpart_align_to_bottom", form(), SLOT(alignWidgetsToBottom())); plugSharedAction("formpart_align_to_grid", form(), SLOT(alignWidgetsToGrid())); plugSharedAction("formpart_adjust_size_menu", form(), 0); plugSharedAction("formpart_adjust_to_fit", form(), SLOT(adjustWidgetSize())); plugSharedAction("formpart_adjust_size_grid", form(), SLOT(adjustSizeToGrid())); plugSharedAction("formpart_adjust_height_small", form(), SLOT(adjustHeightToSmall())); plugSharedAction("formpart_adjust_height_big", form(), SLOT(adjustHeightToBig())); plugSharedAction("formpart_adjust_width_small", form(), SLOT(adjustWidthToSmall())); plugSharedAction("formpart_adjust_width_big", form(), SLOT(adjustWidthToBig())); plugSharedAction("format_font", form(), SLOT(changeFont())); // - setup local actions QList viewActions; QAction* a; a = form()->action("edit_undo"); a->setProperty("iconOnly", true); viewActions << a; a = form()->action("edit_redo"); a->setProperty("iconOnly", true); viewActions << a; setViewActions(viewActions); } KexiDataAwareView::init(d->scrollView, d->scrollView, d->scrollView, /* skip data-awarness if design mode */ viewMode() == Kexi::DesignViewMode); connect(this, SIGNAL(focus(bool)), this, SLOT(slotFocus(bool))); } KexiFormView::~KexiFormView() { deleteQuery(); KexiDataAwareView::propertySetSwitched(); delete d; } void KexiFormView::deleteQuery() { if (d->cursor) { KDbConnection *conn = KexiMainWindowIface::global()->project()->dbConnection(); conn->deleteCursor(d->cursor); d->cursor = 0; } if (d->queryIsOwned) { delete d->query; } else { //! @todo remove this shared query from listened queries list } d->query = 0; } void KexiFormView::setForm(KFormDesigner::Form *f) { if (viewMode() == Kexi::DataViewMode) tempData()->previewForm = f; else tempData()->form = f; d->form = f; } bool KexiFormView::initForm() { d->dbform = new KexiDBForm(d->scrollView->widget(), d->scrollView); if (viewMode() == Kexi::DataViewMode) { d->scrollView->setWidget(d->dbform); } else { d->scrollView->setMainAreaWidget(d->dbform); } d->dbform->setObjectName( xi18nc("A prefix for identifiers of forms. Based on that, identifiers such as " "form1, form2 are generated. " "This string can be used to refer the widget object as variables in programming " "languages or macros so it must _not_ contain white spaces and non latin1 characters, " "should start with lower case letter and if there are subsequent words, these should " "start with upper case letter. Example: smallCamelCase. " "Moreover, try to make this prefix as short as possible.", "form")); QPalette pal(d->dbform->palette()); pal.setBrush(QPalette::Window, palette().brush(QPalette::Window)); d->dbform->setPalette(pal); // avoid inheriting QPalette::Window role d->scrollView->setResizingEnabled(true); if (viewMode() == Kexi::DataViewMode) { d->scrollView->recordNavigator()->setRecordHandler(d->scrollView); QPalette pal(d->scrollView->viewport()->palette()); pal.setBrush(d->scrollView->viewport()->backgroundRole(), d->dbform->palette().brush(d->dbform->backgroundRole())); d->scrollView->viewport()->setPalette(pal); } setForm( new KFormDesigner::Form( KexiFormManager::self()->library(), viewMode() == Kexi::DataViewMode ? KFormDesigner::Form::DataMode : KFormDesigner::Form::DesignMode, *KexiMainWindowIface::global()->actionCollection(), *KexiFormManager::self()->widgetActionGroup()) ); form()->createToplevel(d->dbform, d->dbform); const bool newForm = window()->id() < 0; KDbFieldList *fields = 0; #ifndef KEXI_NO_FORM_DATASOURCE_WIZARD if (newForm) { // Show the form wizard if this is a new Form KexiDataSourceWizard *w = new KexiDataSourceWizard( KexiMainWindowIface::global()->thisWidget()); if (!w->exec()) fields = 0; else fields = w->fields(); delete w; } #endif if (fields) { #ifndef KEXI_NO_FORM_DATASOURCE_WIZARD QDomDocument dom; formPart()->generateForm(fields, dom); KFormDesigner::FormIO::loadFormFromDom(form(), d->dbform, &dom); //! @todo handle errors #endif } else { if (!loadForm()) { return false; } } if (form()->autoTabStops()) form()->autoAssignTabStops(); //collect tab order information d->dbform->updateTabStopsOrder(form()); if (viewMode() == Kexi::DesignViewMode) { connect(form(), SIGNAL(widgetNameChanged(QByteArray,QByteArray)), this, SLOT(slotWidgetNameChanged(QByteArray,QByteArray))); connect(form(), SIGNAL(selectionChanged(QWidget*,KFormDesigner::Form::WidgetSelectionFlags)), this, SLOT(slotWidgetSelectionChanged(QWidget*,KFormDesigner::Form::WidgetSelectionFlags))); form()->selectWidget(form()->widget()); } else { form()->setMode(KFormDesigner::Form::DataMode); d->dbform->setMinimumSize(d->dbform->size()); // make vscrollbar appear when viewport is too small } d->scrollView->setForm(form()); d->scrollView->refreshContentsSize(); if (newForm && !fields) { /* Our form's area will be resized more than once. Let's resize form widget itself later. */ d->delayedFormContentsResizeOnShow = 3; } slotPropertySetSwitched(); // this prepares the data source page updateDataSourcePage(); if (!newForm && viewMode() == Kexi::DesignViewMode) { form()->clearUndoStack(); } return true; } void KexiFormView::updateAutoFieldsDataSource() { #ifdef KEXI_AUTOFIELD_FORM_WIDGET_SUPPORT //! @todo call this when form's data source is changed //update autofields: //-inherit captions //-inherit data types //(this data has not been stored in the form) QString dataSourceString(d->dbform->dataSource()); QString dataSourcePartClassString(d->dbform->dataSourcePluginId()); KDbConnection *conn = KexiMainWindowIface::global()->project()->dbConnection(); KDbTableOrQuerySchema tableOrQuery( conn, dataSourceString.toLatin1(), dataSourcePartClassString == "org.kexi-project.table"); if (!tableOrQuery.table() && !tableOrQuery.query()) return; foreach (KFormDesigner::ObjectTreeItem *item, *form()->objectTree()->hash()) { KexiDBAutoField *afWidget = dynamic_cast(item->widget()); if (afWidget) { KDbQueryColumnInfo *colInfo = tableOrQuery.columnInfo(afWidget->dataSource()); if (colInfo) { afWidget->setColumnInfo(colInfo); } } } #endif } +void KexiFormView::propertySetSwitched() +{ + KexiDataAwareView::propertySetSwitched(); + updateDataSourcePage(); + if (viewMode() == Kexi::DesignViewMode) { + formPart()->dataSourcePage()->assignPropertySet(form()->propertySet(), KexiDataSourcePage::ForceAssign); + } +} + void KexiFormView::updateValuesForSubproperties() { //! @todo call this when form's data source is changed //update autofields: //-inherit captions //-inherit data types //(this data has not been stored in the form) QString dataSourceString(d->dbform->dataSource()); QString dataSourcePartClassString(d->dbform->dataSourcePluginId()); KDbConnection *conn = KexiMainWindowIface::global()->project()->dbConnection(); KDbTableOrQuerySchema tableOrQuery( conn, dataSourceString.toLatin1(), dataSourcePartClassString == "org.kexi-project.table"); if (!tableOrQuery.table() && !tableOrQuery.query()) return; foreach (KFormDesigner::ObjectTreeItem *item, *form()->objectTree()->hash()) { // (delayed) set values for subproperties //! @todo this could be at the KFD level, but KFD is going to be merged anyway with kexiforms, right? KFormDesigner::WidgetWithSubpropertiesInterface* subpropIface = dynamic_cast(item->widget()); if (subpropIface && subpropIface->subwidget() && item->subproperties()) { QWidget *subwidget = subpropIface->subwidget(); QHash* subprops = item->subproperties(); for (QHash::const_iterator subpropIt = subprops->constBegin(); subpropIt != subprops->constEnd(); ++subpropIt) { //qDebug() << "delayed setting of the subproperty: widget=" // << item->widget()->objectName() << "prop=" << subpropIt.key() << "val=" // << subpropIt.value(); QMetaProperty meta = KexiUtils::findPropertyWithSuperclasses( subwidget, qPrintable(subpropIt.key())); if (meta.isValid()) { // Special case: the property value of type enum (set) but is saved as a string list, // not as int, so we need to translate it to int. It's been created as such // by FormIO::readPropertyValue(). Example: "alignment" property. if (meta.isEnumType() && subpropIt.value().type() == QVariant::StringList) { const QByteArray keysCombined(subpropIt.value().toStringList().join("|").toLatin1()); subwidget->setProperty(subpropIt.key().toLatin1(), meta.enumerator().keysToValue(keysCombined.constData())); } else { subwidget->setProperty(subpropIt.key().toLatin1(), subpropIt.value()); } } }//for } } } //! Used in KexiFormView::loadForm() static void setUnsavedBLOBIdsForDataViewMode( QWidget* widget, const QHash& unsavedLocalBLOBsByName) { if (widget) { if (-1 != widget->metaObject()->indexOfProperty("pixmapId")) { const KexiBLOBBuffer::Id_t blobID = unsavedLocalBLOBsByName.value(widget->objectName().toLatin1()); if (blobID > 0) //! @todo KexiBLOBBuffer::Id_t is unsafe and unsupported by QVariant - fix it widget->setProperty( "pixmapId", int(blobID)); } const QList list(widget->findChildren()); if (list.isEmpty()) return; foreach(QWidget *w, list) { setUnsavedBLOBIdsForDataViewMode(w, unsavedLocalBLOBsByName); } } } bool KexiFormView::loadForm() { //! @todo also load d->resizeMode //qDebug() << "Loading the form with id" << window()->id(); // If we are previewing the Form, use the tempData instead of the form stored in the db if (viewMode() == Kexi::DataViewMode && !tempData()->tempForm.isNull()) { if (!KFormDesigner::FormIO::loadFormFromString(form(), d->dbform, tempData()->tempForm)) { return false; } setUnsavedBLOBIdsForDataViewMode(d->dbform, tempData()->unsavedLocalBLOBsByName); updateAutoFieldsDataSource(); updateValuesForSubproperties(); return true; } if (!window()->neverSaved()) { // normal load QString data; if (!loadDataBlock(&data)) { return false; } if (!KFormDesigner::FormIO::loadFormFromString(form(), d->dbform, data)) { return false; } } //"autoTabStops" property is loaded -set it within the form tree as well form()->setAutoTabStops(d->dbform->autoTabStops()); updateAutoFieldsDataSource(); updateValuesForSubproperties(); return true; } void KexiFormView::slotPropertySetSwitched() { propertySetReloaded(); if (viewMode() == Kexi::DesignViewMode) { formPart()->dataSourcePage()->assignPropertySet(form()->propertySet()); } } tristate KexiFormView::beforeSwitchTo(Kexi::ViewMode mode, bool *dontStore) { Q_ASSERT(dontStore); if (mode != viewMode()) { if (viewMode() == Kexi::DataViewMode) { if (!d->scrollView->acceptRecordEditing()) return cancelled; d->scrollView->beforeSwitchView(); } else { //remember our pos tempData()->scrollViewContentsPos = QPoint(d->scrollView->horizontalScrollBar()->value(), d->scrollView->verticalScrollBar()->value()); } } if (d->scrollView->data() && viewMode() == Kexi::DataViewMode) { //old data won't be needed nor valid d->scrollView->setData(0, false); } // we don't store on db, but in our TempData *dontStore = true; if (isDirty() && (mode == Kexi::DataViewMode) && form()->objectTree()) { KexiFormPartTempData* temp = tempData(); if (!KFormDesigner::FormIO::saveFormToString(form(), temp->tempForm)) return false; //collect blobs from design mode by name for use in data view mode temp->unsavedLocalBLOBsByName.clear(); for (QHash::const_iterator it = temp->unsavedLocalBLOBs.constBegin(); it != temp->unsavedLocalBLOBs.constEnd(); ++it) { if (!it.key()) continue; temp->unsavedLocalBLOBsByName.insert(it.key()->objectName().toLatin1(), it.value()); } } return true; } tristate KexiFormView::afterSwitchFrom(Kexi::ViewMode mode) { if (mode == 0 || mode == Kexi::DesignViewMode) { if (window()->neverSaved()) { d->scrollView->refreshContentsSizeLater(); } } if (mode == Kexi::DataViewMode) { //preserve contents pos after switching to other view d->scrollView->horizontalScrollBar()->setValue(tempData()->scrollViewContentsPos.x()); d->scrollView->verticalScrollBar()->setValue(tempData()->scrollViewContentsPos.y()); } if ((mode == Kexi::DesignViewMode) && viewMode() == Kexi::DataViewMode) { // The form may have been modified, so we must recreate the preview delete d->dbform; // also deletes form() if (!initForm()) { return false; } //reset position d->scrollView->horizontalScrollBar()->setValue(0); d->scrollView->verticalScrollBar()->setValue(0); d->dbform->move(0, 0); } //update tab stops if needed if (viewMode() == Kexi::DataViewMode) { } else { //set "autoTabStops" property d->dbform->setAutoTabStops(form()->autoTabStops()); } if (viewMode() == Kexi::DataViewMode) { //TMP!! initDataSource(); //handle events for this form d->scrollView->setMainWidgetForEventHandling(d->dbform); //set focus on 1st focusable widget which has valid dataSource property set QList *orderedFocusWidgets = d->dbform->orderedFocusWidgets(); if (!orderedFocusWidgets->isEmpty()) { KexiUtils::unsetFocusWithReason(QApplication::focusWidget(), Qt::TabFocusReason); QWidget *widget = 0; foreach(widget, *orderedFocusWidgets) { KexiFormDataItemInterface *iface = dynamic_cast(widget); if (iface) { //qDebug() << iface->dataSource(); } if (iface && iface->columnInfo() && !iface->isReadOnly() /*! @todo add option for skipping autoincremented fields */ /* also skip autoincremented fields:*/ && !iface->columnInfo()->field()->isAutoIncrement()) { break; } } if (!widget) //eventually, focus first available widget if nothing other is available widget = orderedFocusWidgets->first(); widget->setFocus(); KexiUtils::setFocusWithReason(widget, Qt::TabFocusReason); d->setFocusInternalOnce = widget; } if (d->query) d->scrollView->selectFirstRecord(); } //dirty only if it's a new object if (mode == Kexi::NoViewMode) setDirty(window()->partItem()->neverSaved()); updateActionsInternal(); return true; } KPropertySet* KexiFormView::propertySet() { return d->form->propertySet(); } KexiFormPartTempData* KexiFormView::tempData() const { return dynamic_cast(window()->data()); } KexiFormPart* KexiFormView::formPart() const { return dynamic_cast(part()); } void KexiFormView::initDataSource() { deleteQuery(); //! @todo also handle anonymous (not stored) queries provided as statements here KDbTableSchema *tableSchema = 0; KDbConnection *conn = 0; QStringList sources; bool forceReadOnlyDataSource = false; QString dataSourceString(d->dbform->dataSource()); bool ok = !dataSourceString.isEmpty(); if (ok) { //collect all data-aware widgets and create query schema d->scrollView->setMainDataSourceWidget(d->dbform); sources = d->scrollView->usedDataSources(); conn = KexiMainWindowIface::global()->project()->dbConnection(); QString dataSourcePartClassString(d->dbform->dataSourcePluginId()); if (dataSourcePartClassString.isEmpty() /*table type is the default*/ || dataSourcePartClassString == "org.kexi-project.table") { tableSchema = conn->tableSchema(dataSourceString); if (tableSchema) { /* We will build a _minimud-> query schema from selected table fields. */ d->query = new KDbQuerySchema(); d->queryIsOwned = true; if (dataSourcePartClassString.isEmpty()) d->dbform->setDataSourcePluginId("org.kexi-project.table"); //update for compatibility } } if (!tableSchema) { if (dataSourcePartClassString.isEmpty() /*also try to find a query (for compatibility with Kexi<=0.9)*/ || dataSourcePartClassString == "org.kexi-project.query") { //try to find predefined query schema. //Note: In general, we could not skip unused fields within this query because // it can have GROUP BY clause. //! @todo check if the query could have skipped unused fields (no GROUP BY, no joins, etc.) d->query = conn->querySchema(dataSourceString); d->queryIsOwned = false; ok = d->query != 0; if (ok && dataSourcePartClassString.isEmpty()) d->dbform->setDataSourcePluginId("org.kexi-project.query"); //update for compatibility // query results are read-only //! @todo There can be read-write queries, e.g. simple "SELECT * FROM...". Add a checking function to KDb. forceReadOnlyDataSource = true; } else { //no other classes are supported ok = false; } } } QSet invalidSources; if (ok) { KDbIndexSchema *pkey = tableSchema ? tableSchema->primaryKey() : 0; if (pkey) { //always add all fields from table's primary key // (don't worry about duplicates, unique list will be computed later) sources += pkey->names(); //qDebug() << "pkey added to data sources:" << pkey->names(); } //qDebug() << "sources=" << sources; int index = 0; for (QStringList::ConstIterator it = sources.constBegin(); it != sources.constEnd(); ++it, index++) { /*! @todo add expression support */ QString fieldName((*it).toLower()); //remove "tablename." if it was prepended if (tableSchema && fieldName.startsWith(tableSchema->name() + QLatin1Char('.'), Qt::CaseInsensitive)) fieldName.remove(0, tableSchema->name().length() + 1); //remove "queryname." if it was prepended if (!tableSchema && fieldName.startsWith(d->query->name() + QLatin1Char('.'), Qt::CaseInsensitive)) fieldName.remove(0, d->query->name().length() + 1); KDbField *f = tableSchema ? tableSchema->field(fieldName) : d->query->field(fieldName); if (!f) { /*! @todo show error */ //remove this widget from the set of data widgets in the provider /*! @todo fieldName is ok, but what about expressions? */ invalidSources.insert(fieldName); //qDebug() << "invalidSources+=" << index << "(" << (*it) << ")"; continue; } if (tableSchema) { if (!d->query->hasField(*f)) { //we're building a new query: add this field d->query->addField(f); } } } if (invalidSources.count() == sources.count()) { //all data sources are invalid! don't execute the query deleteQuery(); } else { //qDebug() << d->query->parameters(); // like in KexiQueryView::executeQuery() QList params; { KexiUtils::WaitCursorRemover remover; params = KexiQueryParameters::getParameters(this, *conn->driver(), d->query, &ok); } if (ok) //input cancelled d->cursor = conn->executeQuery(d->query, params); } d->scrollView->invalidateDataSources(invalidSources, d->query); ok = d->cursor != 0; } if (!invalidSources.isEmpty()) d->dbform->updateTabStopsOrder(); if (ok) { //! @todo PRIMITIVE!! data setting: //! @todo KDbTableViewData is not a great name for data class here... rename/move? KDbTableViewData* data = new KDbTableViewData(d->cursor); if (forceReadOnlyDataSource) data->setReadOnly(true); data->preloadAllRecords(); ///*! @todo few backends return result count for free! - no need to reopen() */ // int resultCount = -1; // if (ok) { // resultCount = d->conn->resultCount(d->conn->selectStatement(*d->query)); // ok = d->cursor->reopen(); // } // if (ok) // ok = ! (!d->cursor->moveFirst() && d->cursor->error()); d->scrollView->setData(data, true /*owner*/); } else { d->scrollView->setData(0, false); } } void KexiFormView::setFormModified() { form()->setModified(true); } KDbObject* KexiFormView::storeNewData(const KDbObject& object, KexiView::StoreNewDataOptions options, bool *cancel) { Q_ASSERT(cancel); KDbObject *s = KexiView::storeNewData(object, options, cancel); //qDebug() << "new id:" << s->id(); if (!s || *cancel) { delete s; return 0; } if (!storeData()) { //failure: remove object's object data to avoid garbage KDbConnection *conn = KexiMainWindowIface::global()->project()->dbConnection(); conn->removeObject(s->id()); delete s; return 0; } return s; } tristate KexiFormView::storeData(bool dontAsk) { Q_UNUSED(dontAsk); //qDebug() << window()->partItem()->name() << "[" << window()->id() << "]"; //-- first, store local BLOBs, so identifiers can be updated //! @todo remove unused data stored previously KDbConnection *conn = KexiMainWindowIface::global()->project()->dbConnection(); KDbTableSchema *blobsTable = conn->tableSchema("kexi__blobs"); if (!blobsTable) { //compatibility check for older Kexi project versions //! @todo show message about missing kexi__blobs? return false; } // Not all engines accept passing NULL to PKEY o_id, so we're omitting it. QStringList blobsFieldNamesWithoutID(blobsTable->names()); blobsFieldNamesWithoutID.pop_front(); KDbFieldList *blobsFieldsWithoutID = blobsTable->subList(blobsFieldNamesWithoutID); KDbPreparedStatement st = conn->prepareStatement( KDbPreparedStatement::InsertStatement, blobsFieldsWithoutID); if (!st.isValid()) { delete blobsFieldsWithoutID; //! @todo show message return false; } KexiBLOBBuffer *blobBuf = KexiBLOBBuffer::self(); KexiFormView *designFormView = dynamic_cast( window()->viewForMode(Kexi::DesignViewMode)); if (designFormView) { for (QHash::const_iterator it = tempData()->unsavedLocalBLOBs.constBegin(); it != tempData()->unsavedLocalBLOBs.constEnd(); ++it) { if (!it.key()) { qWarning() << "it.key()==0 !"; continue; } //qDebug() << "name=" << it.key()->objectName() << "dataID=" << it.value(); KexiBLOBBuffer::Handle h(blobBuf->objectForId(it.value(), /*!stored*/false)); if (!h) continue; //no BLOB assigned QString originalFileName(h.originalFileName()); QFileInfo fi(originalFileName); QString caption(fi.baseName().replace('_', ' ').simplified()); KDbPreparedStatementParameters parameters; parameters << h.data() << originalFileName << caption << h.mimeType() << int(/*! @todo unsafe */h.folderId()); if (!st.execute(parameters)) { delete blobsFieldsWithoutID; qWarning() << "execute error"; return false; } delete blobsFieldsWithoutID; blobsFieldsWithoutID = 0; const quint64 storedBLOBID = KDb::lastInsertedAutoIncValue( conn, st.lastInsertRecordId(), "o_id", "kexi__blobs"); if (std::numeric_limits::max() == storedBLOBID) { //! @todo show message? return false; } //qDebug() << "storedDataID=" << storedBLOBID; //! @todo unsafe - fix! h.setStoredWidthID((KexiBLOBBuffer::Id_t)storedBLOBID); //set widget's internal property so it can be saved... const QVariant oldStoredPixmapId(it.key()->property("storedPixmapId")); //! @todo KexiBLOBBuffer::Id_t is unsafe and unsupported by QVariant - fix! it.key()->setProperty("storedPixmapId", QVariant(int(storedBLOBID))); KFormDesigner::ObjectTreeItem *widgetItem = designFormView->form()->objectTree()->lookup(it.key()->objectName()); if (widgetItem) widgetItem->addModifiedProperty("storedPixmapId", oldStoredPixmapId); else qWarning() << "no" << it.key()->objectName() << "widget found within a form"; } } //-- now, save form's XML QString data; if (!KFormDesigner::FormIO::saveFormToString(tempData()->form, data)) return false; if (!storeDataBlock(data)) return false; //all blobs are now saved tempData()->unsavedLocalBLOBs.clear(); tempData()->tempForm.clear(); return true; } //! @todo reuse the action stuff code #if 0 /// Action stuff ///////////////// void KexiFormView::slotWidgetSelected(KFormDesigner::Form *f, bool multiple) { if (f != form()) return; enableFormActions(); // Enable edit actions setAvailable("edit_copy", true); setAvailable("edit_cut", true); setAvailable("edit_clear", true); // 'Align Widgets' menu setAvailable("formpart_align_menu", multiple); setAvailable("formpart_align_to_left", multiple); setAvailable("formpart_align_to_right", multiple); setAvailable("formpart_align_to_top", multiple); setAvailable("formpart_align_to_bottom", multiple); setAvailable("formpart_adjust_size_menu", true); setAvailable("formpart_adjust_width_small", multiple); setAvailable("formpart_adjust_width_big", multiple); setAvailable("formpart_adjust_height_small", multiple); setAvailable("formpart_adjust_height_big", multiple); setAvailable("formpart_format_raise", true); setAvailable("formpart_format_lower", true); // If the widgets selected is a container, we enable layout actions if (!multiple) { KFormDesigner::ObjectTreeItem *item = f->objectTree()->lookup(f->selectedWidgets()->first()->name()); if (item && item->container()) multiple = true; } } void KexiFormView::slotFormWidgetSelected(KFormDesigner::Form *f) { if (f != form()) return; disableWidgetActions(); enableFormActions(); } void KexiFormView::slotNoFormSelected() // == form in preview mode { disableWidgetActions(); // Disable paste action setAvailable("edit_paste", false); setAvailable("edit_undo", false); setAvailable("edit_redo", false); // Disable 'Tools' actions setAvailable("formpart_pixmap_collection", false); setAvailable("formpart_connections", false); setAvailable("formpart_taborder", false); setAvailable("formpart_change_style", false); } void KexiFormView::enableFormActions() { // Enable 'Tools' actions setAvailable("formpart_pixmap_collection", true); setAvailable("formpart_connections", true); setAvailable("formpart_taborder", true); //! @todo KEXI3 Port this.. //! @todo setAvailable("edit_paste", KFormDesigner::FormManager::self()->isPasteEnabled()); } void KexiFormView::disableWidgetActions() { // Disable edit actions setAvailable("edit_copy", false); setAvailable("edit_cut", false); setAvailable("edit_clear", false); // Disable format functions setAvailable("formpart_align_menu", false); setAvailable("formpart_align_to_left", false); setAvailable("formpart_align_to_right", false); setAvailable("formpart_align_to_top", false); setAvailable("formpart_align_to_bottom", false); setAvailable("formpart_adjust_size_menu", false); setAvailable("formpart_adjust_width_small", false); setAvailable("formpart_adjust_width_big", false); setAvailable("formpart_adjust_height_small", false); setAvailable("formpart_adjust_height_big", false); setAvailable("formpart_format_raise", false); setAvailable("formpart_format_lower", false); } void KexiFormView::setUndoEnabled(bool enabled) { setAvailable("edit_undo", enabled); } void KexiFormView::setRedoEnabled(bool enabled) { setAvailable("edit_redo", enabled); } #endif //0 int KexiFormView::resizeMode() const { return d->resizeMode; } KFormDesigner::Form* KexiFormView::form() const { return d->form; } QSize KexiFormView::preferredSizeHint(const QSize& otherSize) { return (d->dbform->size() + QSize(d->scrollView->verticalScrollBar()->isVisible() ? d->scrollView->verticalScrollBar()->width()*3 / 2 : 10, d->scrollView->horizontalScrollBar()->isVisible() ? d->scrollView->horizontalScrollBar()->height()*3 / 2 : 10)) .expandedTo(KexiView::preferredSizeHint(otherSize)); } void KexiFormView::resizeEvent(QResizeEvent *e) { if (viewMode() == Kexi::DataViewMode) { d->scrollView->refreshContentsSizeLater(); } KexiView::resizeEvent(e); if (d->delayedFormContentsResizeOnShow > 0) { d->delayedFormContentsResizeOnShow--; d->dbform->resize(e->size() - QSize(30, 30)); } } void KexiFormView::contextMenuEvent(QContextMenuEvent *e) { // qDebug() << form()->selectedWidget() << form()->widget() << e->reason(); if (form()->selectedWidget() && form()->selectedWidget() == form()->widget() && e->reason() == QContextMenuEvent::Keyboard) { // Outer form area received context key. // Redirect the event to top-level form widget. // It will be received in Container::eventFilter(). e->accept(); QContextMenuEvent me(QContextMenuEvent::Keyboard, QPoint(-1, -1)); QApplication::sendEvent(form()->widget(), &me); return; } KexiView::contextMenuEvent(e); } void KexiFormView::setFocusInternal() { if (viewMode() == Kexi::DataViewMode) { if (d->dbform->focusWidget()) { //better-looking focus if (d->setFocusInternalOnce) { KexiUtils::setFocusWithReason(d->setFocusInternalOnce, Qt::OtherFocusReason); d->setFocusInternalOnce = 0; } else { //ok? SET_FOCUS_USING_REASON(d->dbform->focusWidget(), QFocusEvent::Other); } return; } } QWidget::setFocus(); } void KexiFormView::slotFocus(bool in) { Q_UNUSED(in); } void KexiFormView::updateDataSourcePage() { if (viewMode() == Kexi::DesignViewMode) { const QString dataSourcePartClass = d->dbform->dataSourcePluginId(); const QString dataSource = d->dbform->dataSource(); formPart()->dataSourcePage()->setFormDataSource(dataSourcePartClass, dataSource); } } void KexiFormView::slotHandleDragMoveEvent(QDragMoveEvent* e) { #ifdef KEXI_AUTOFIELD_FORM_WIDGET_SUPPORT if (KexiFieldDrag::canDecode(e)) { e->setAccepted(true); } #else Q_UNUSED(e); #endif } void KexiFormView::slotHandleDropEvent(QDropEvent* e) { #ifdef KEXI_AUTOFIELD_FORM_WIDGET_SUPPORT const QWidget *targetContainerWidget = dynamic_cast(sender()); KFormDesigner::ObjectTreeItem *targetContainerWidgetItem = targetContainerWidget ? form()->objectTree()->lookup(targetContainerWidget->objectName()) : 0; if (targetContainerWidgetItem && targetContainerWidgetItem->container() && KexiFieldDrag::canDecode(e)) { QString sourcePartClass, sourceName; QStringList fields; if (!KexiFieldDrag::decode(e, &sourcePartClass, &sourceName, &fields)) return; insertAutoFields(sourcePartClass, sourceName, fields, targetContainerWidgetItem->container(), e->pos()); } #else Q_UNUSED(e); #endif } void KexiFormView::insertAutoFields(const QString& sourcePartClass, const QString& sourceName, const QStringList& fields, KFormDesigner::Container* targetContainer, const QPoint& _pos) { #ifdef KEXI_AUTOFIELD_FORM_WIDGET_SUPPORT if (fields.isEmpty()) return; KDbConnection *conn = KexiMainWindowIface::global()->project()->dbConnection(); KDbTableOrQuerySchema tableOrQuery(conn, sourceName.toLatin1(), sourcePartClass == "org.kexi-project.table"); if (!tableOrQuery.table() && !tableOrQuery.query()) { qWarning() << "no such table/query" << sourceName; return; } QPoint pos(_pos); //if pos is not specified, compute a new position: if (pos == QPoint(-1, -1)) { if (d->widgetGeometryForRecentInsertAutoFields.isValid()) { pos = d->widgetGeometryForRecentInsertAutoFields.bottomLeft() + QPoint(0, form()->gridSize()); } else { pos = QPoint(40, 40); //start here } } // there will be many actions performed, do not update property pane until all that's finished //! todo unnamed query columns are not supported QWidgetList widgetsToSelect; KFormDesigner::PropertyCommandGroup *group = new KFormDesigner::PropertyCommandGroup( fields.count() == 1 ? futureI18n("Insert AutoField widget") : futureI18n2("Insert %1 AutoField widgets", fields.count()) ); foreach(const QString& field, fields) { KDbQueryColumnInfo* column = tableOrQuery.columnInfo(field); if (!column) { qWarning() << "no such field" << field << "in table/query" << sourceName; continue; } //! todo add autolabel using field's caption or name KFormDesigner::InsertWidgetCommand *insertCmd = new KFormDesigner::InsertWidgetCommand( *targetContainer, //! @todo this is hardcoded! "KexiDBAutoField", //! @todo this name can be invalid for expressions: if so, fall back to a default class' prefix! pos, column->aliasOrName(), group ); insertCmd->redo(); KFormDesigner::ObjectTreeItem *newWidgetItem = form()->objectTree()->hash()->value(insertCmd->widgetName()); KexiDBAutoField* newWidget = newWidgetItem ? dynamic_cast(newWidgetItem->widget()) : 0; widgetsToSelect.append(newWidget); KFormDesigner::PropertyCommandGroup *subGroup = new KFormDesigner::PropertyCommandGroup( QString(), group); QHash propValues; propValues.insert("dataSource", column->aliasOrName()); propValues.insert("fieldTypeInternal", (int)column->field->type()); propValues.insert("fieldCaptionInternal", column->captionOrAliasOrName()); form()->createPropertyCommandsInDesignMode( newWidget, propValues, subGroup, false/*!addToActiveForm*/); subGroup->redo(); //set data source and caption //-we don't need to use PropertyCommand here beacause we don't need UNDO // for these single commands //resize again because autofield's type changed what can lead to changed sizeHint() QWidgetList list; list.append(newWidget); KFormDesigner::AdjustSizeCommand *adjustCommand = new KFormDesigner::AdjustSizeCommand( *form(), KFormDesigner::AdjustSizeCommand::SizeToFit, list, group); adjustCommand->redo(); if (newWidget) {//move position down for next widget pos.setY(pos.y() + newWidget->height() + form()->gridSize()); } } if (widgetsToSelect.last()) { //resize form if needed QRect oldFormRect(d->dbform->geometry()); QRect newFormRect(oldFormRect); newFormRect.setWidth(qMax(d->dbform->width(), widgetsToSelect.last()->geometry().right() + 1)); newFormRect.setHeight(qMax(d->dbform->height(), widgetsToSelect.last()->geometry().bottom() + 1)); if (newFormRect != oldFormRect) { //1. resize by hand d->dbform->setGeometry(newFormRect); //2. store information about resize (void)new KFormDesigner::PropertyCommand( *form(), d->dbform->objectName().toLatin1(), oldFormRect, newFormRect, "geometry", group); } //remember geometry of the last inserted widget d->widgetGeometryForRecentInsertAutoFields = widgetsToSelect.last()->geometry(); } //eventually, add entire command group to active form form()->addCommand(group); //qDebug() << *group; d->scrollView->widget()->update(); d->scrollView->refreshContentsSize(); //select all inserted widgets, if multiple if (widgetsToSelect.count() > 1) { form()->selectWidget(0); foreach (QWidget *w, widgetsToSelect) { form()->selectWidget(w, KFormDesigner::Form::AddToPreviousSelection | KFormDesigner::Form::DontRaise); } } //! @todo eventually, update property pane #else Q_UNUSED(sourcePartClass); Q_UNUSED(sourceName); Q_UNUSED(fields); Q_UNUSED(targetContainer); Q_UNUSED(_pos); #endif } void KexiFormView::setUnsavedLocalBLOB(QWidget *widget, KexiBLOBBuffer::Id_t id) { //! @todo if there already was data assigned, remember it should be dereferenced if (id == 0) tempData()->unsavedLocalBLOBs.remove(widget); else tempData()->unsavedLocalBLOBs.insert(widget, id); } void KexiFormView::updateActions(bool activated) { if (viewMode()==Kexi::DesignViewMode) { if (activated) { form()->emitActionSignals(); formPart()->widgetTreePage()->setForm(form()); } } KexiDataAwareView::updateActions(activated); updateActionsInternal(); } void KexiFormView::slotWidgetNameChanged(const QByteArray& oldname, const QByteArray& newname) { Q_UNUSED(oldname); Q_UNUSED(newname); //qDebug() << oldname << newname << form()->propertySet().propertyValue("objectName").toString(); KexiMainWindowIface::global()->updatePropertyEditorInfoLabel(); } void KexiFormView::slotWidgetSelectionChanged(QWidget *w, KFormDesigner::Form::WidgetSelectionFlags flags) { Q_UNUSED(w) Q_UNUSED(flags) updateActionsInternal(); } void KexiFormView::updateActionsInternal() { const QWidget* selectedWidget = form()->selectedWidget(); //qDebug() << selectedWidget << (viewMode()==Kexi::DesignViewMode) << widget_assign_action; QByteArray wClass; if (selectedWidget) { wClass = selectedWidget->metaObject()->className(); //qDebug() << wClass; } QAction *widget_assign_action = KexiFormManager::self()->action("widget_assign_action"); if (widget_assign_action) { widget_assign_action->setEnabled( viewMode()==Kexi::DesignViewMode && selectedWidget && (wClass == "QPushButton" || wClass == "KPushButton" || wClass == "KexiDBPushButton" || wClass == "KexiPushButton" || wClass == "KexiDBCommandLinkButton") ); } #ifdef KEXI_DEBUG_GUI QAction *show_form_ui_action = KexiFormManager::self()->action("show_form_ui"); if (show_form_ui_action) { show_form_ui_action->setEnabled(viewMode()==Kexi::DesignViewMode); } #endif }