diff --git a/src/kexiutils/utils.cpp b/src/kexiutils/utils.cpp index b5411b33b..a9901840b 100644 --- a/src/kexiutils/utils.cpp +++ b/src/kexiutils/utils.cpp @@ -1,1022 +1,1037 @@ /* This file is part of the KDE project Copyright (C) 2003-2016 Jarosław Staniek Contains code from kglobalsettings.cpp: Copyright (C) 2000, 2006 David Faure Copyright (C) 2008 Friedrich W. H. Kossebau Contains code from kdialog.cpp: Copyright (C) 1998 Thomas Tanghus (tanghus@earthling.net) Additions 1999-2000 by Espen Sand (espen@kde.org) and Holger Freyther 2005-2009 Olivier Goffart 2006 Tobias Koenig 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 "utils.h" #include "utils_p.h" #include "FontSettings_p.h" #include "kexiutils_global.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if HAVE_LANGINFO_H #include #endif #ifdef Q_OS_WIN #include static QRgb qt_colorref2qrgb(COLORREF col) { return qRgb(GetRValue(col), GetGValue(col), GetBValue(col)); } #endif using namespace KexiUtils; DelayedCursorHandler::DelayedCursorHandler() : startedOrActive(false) { timer.setSingleShot(true); connect(&timer, SIGNAL(timeout()), this, SLOT(show())); } void DelayedCursorHandler::start(bool noDelay) { startedOrActive = true; timer.start(noDelay ? 0 : 1000); } void DelayedCursorHandler::stop() { startedOrActive = false; timer.stop(); QApplication::restoreOverrideCursor(); } void DelayedCursorHandler::show() { QApplication::restoreOverrideCursor(); QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); } Q_GLOBAL_STATIC(DelayedCursorHandler, _delayedCursorHandler) void KexiUtils::setWaitCursor(bool noDelay) { if (qobject_cast(qApp)) { _delayedCursorHandler->start(noDelay); } } void KexiUtils::removeWaitCursor() { if (qobject_cast(qApp)) { _delayedCursorHandler->stop(); } } WaitCursor::WaitCursor(bool noDelay) { setWaitCursor(noDelay); } WaitCursor::~WaitCursor() { removeWaitCursor(); } WaitCursorRemover::WaitCursorRemover() { m_reactivateCursor = _delayedCursorHandler->startedOrActive; _delayedCursorHandler->stop(); } WaitCursorRemover::~WaitCursorRemover() { if (m_reactivateCursor) _delayedCursorHandler->start(true); } //-------------------------------------------------------------------------------- QObject* KexiUtils::findFirstQObjectChild(QObject *o, const char* className, const char* objName) { if (!o) return 0; const QObjectList list(o->children()); foreach(QObject *child, list) { if (child->inherits(className) && (!objName || child->objectName() == objName)) return child; } //try children foreach(QObject *child, list) { child = findFirstQObjectChild(child, className, objName); if (child) return child; } return 0; } QMetaProperty KexiUtils::findPropertyWithSuperclasses(const QObject* object, const char* name) { const int index = object->metaObject()->indexOfProperty(name); if (index == -1) return QMetaProperty(); return object->metaObject()->property(index); } bool KexiUtils::objectIsA(QObject* object, const QList& classNames) { foreach(const QByteArray& ba, classNames) { if (objectIsA(object, ba.constData())) return true; } return false; } QList KexiUtils::methodsForMetaObject( const QMetaObject *metaObject, QFlags types, QFlags access) { const int count = metaObject ? metaObject->methodCount() : 0; QList result; for (int i = 0; i < count; i++) { QMetaMethod method(metaObject->method(i)); if (types & method.methodType() && access & method.access()) result += method; } return result; } QList KexiUtils::methodsForMetaObjectWithParents( const QMetaObject *metaObject, QFlags types, QFlags access) { QList result; while (metaObject) { const int count = metaObject->methodCount(); for (int i = 0; i < count; i++) { QMetaMethod method(metaObject->method(i)); if (types & method.methodType() && access & method.access()) result += method; } metaObject = metaObject->superClass(); } return result; } QList KexiUtils::propertiesForMetaObject( const QMetaObject *metaObject) { const int count = metaObject ? metaObject->propertyCount() : 0; QList result; for (int i = 0; i < count; i++) result += metaObject->property(i); return result; } QList KexiUtils::propertiesForMetaObjectWithInherited( const QMetaObject *metaObject) { QList result; while (metaObject) { const int count = metaObject->propertyCount(); for (int i = 0; i < count; i++) result += metaObject->property(i); metaObject = metaObject->superClass(); } return result; } QStringList KexiUtils::enumKeysForProperty(const QMetaProperty& metaProperty) { QStringList result; QMetaEnum enumerator(metaProperty.enumerator()); const int count = enumerator.keyCount(); for (int i = 0; i < count; i++) result.append(QString::fromLatin1(enumerator.key(i))); return result; } QString KexiUtils::fileDialogFilterString(const QMimeType &mime, bool kdeFormat) { if (!mime.isValid()) { return QString(); } QString str; if (kdeFormat) { if (mime.globPatterns().isEmpty()) { str = "*"; } else { str = mime.globPatterns().join(" "); } str += "|"; } str += mime.comment(); if (!mime.globPatterns().isEmpty() || !kdeFormat) { str += " ("; if (mime.globPatterns().isEmpty()) str += "*"; else str += mime.globPatterns().join("; "); str += ")"; } if (kdeFormat) str += "\n"; else str += ";;"; return str; } QString KexiUtils::fileDialogFilterString(const QString& mimeName, bool kdeFormat) { QMimeDatabase db; QMimeType mime = db.mimeTypeForName(mimeName); return fileDialogFilterString(mime, kdeFormat); } QString KexiUtils::fileDialogFilterStrings(const QStringList& mimeStrings, bool kdeFormat) { QString ret; QStringList::ConstIterator endIt = mimeStrings.constEnd(); for (QStringList::ConstIterator it = mimeStrings.constBegin(); it != endIt; ++it) ret += fileDialogFilterString(*it, kdeFormat); return ret; } //! @internal static QFileDialog* getImageDialog(QWidget *parent, const QString &caption, const QUrl &directory, const QList &supportedMimeTypes) { QFileDialog *dialog = new QFileDialog(parent, caption); dialog->setDirectoryUrl(directory); const QStringList mimeTypeFilters = KexiUtils::convertTypesUsingFunction(supportedMimeTypes); dialog->setMimeTypeFilters(mimeTypeFilters); return dialog; } QUrl KexiUtils::getOpenImageUrl(QWidget *parent, const QString &caption, const QUrl &directory) { QScopedPointer dialog( getImageDialog(parent, caption.isEmpty() ? i18n("Open") : caption, directory, QImageReader::supportedMimeTypes())); dialog->setFileMode(QFileDialog::ExistingFile); dialog->setAcceptMode(QFileDialog::AcceptOpen); dialog->exec(); return dialog->selectedUrls().value(0); } QUrl KexiUtils::getSaveImageUrl(QWidget *parent, const QString &caption, const QUrl &directory) { QScopedPointer dialog( getImageDialog(parent, caption.isEmpty() ? i18n("Save") : caption, directory, QImageWriter::supportedMimeTypes())); dialog->setAcceptMode(QFileDialog::AcceptSave); dialog->exec(); return dialog->selectedUrls().value(0); } +bool KexiUtils::askForFileOverwriting(const QString& filePath, QWidget *parent) +{ + QFileInfo fi(filePath); + if (!fi.exists()) { + return true; + } + const KMessageBox::ButtonCode res = KMessageBox::warningYesNo(parent, + xi18nc("@info", "The file %1 already exists." + "Do you want to overwrite it?", + QDir::toNativeSeparators(filePath)), + QString(), + KStandardGuiItem::overwrite(), KStandardGuiItem::no()); + return res == KMessageBox::Yes; +} + QColor KexiUtils::blendedColors(const QColor& c1, const QColor& c2, int factor1, int factor2) { return QColor( int((c1.red()*factor1 + c2.red()*factor2) / (factor1 + factor2)), int((c1.green()*factor1 + c2.green()*factor2) / (factor1 + factor2)), int((c1.blue()*factor1 + c2.blue()*factor2) / (factor1 + factor2))); } QColor KexiUtils::contrastColor(const QColor& c) { int g = qGray(c.rgb()); if (g > 110) return c.dark(200); else if (g > 80) return c.light(150); else if (g > 20) return c.light(300); return Qt::gray; } QColor KexiUtils::bleachedColor(const QColor& c, int factor) { int h, s, v; c.getHsv(&h, &s, &v); QColor c2; if (factor < 100) factor = 100; if (s >= 250 && v >= 250) //for colors like cyan or red, make the result more white s = qMax(0, s - factor - 50); else if (s <= 5 && v <= 5) v += factor - 50; c2.setHsv(h, s, qMin(255, v + factor - 100)); return c2; } QIcon KexiUtils::colorizeIconToTextColor(const QPixmap& icon, const QPalette& palette, QPalette::ColorRole role) { QPixmap pm( KIconEffect().apply(icon, KIconEffect::Colorize, 1.0f, palette.color(role), false)); KIconEffect::semiTransparent(pm); return QIcon(pm); } QPixmap KexiUtils::emptyIcon(KIconLoader::Group iconGroup) { QPixmap noIcon(IconSize(iconGroup), IconSize(iconGroup)); noIcon.fill(Qt::transparent); return noIcon; } static void drawOrScalePixmapInternal(QPainter* p, const QMargins& margins, const QRect& rect, QPixmap* pixmap, QPoint* pos, Qt::Alignment alignment, bool scaledContents, bool keepAspectRatio, Qt::TransformationMode transformMode = Qt::FastTransformation) { Q_ASSERT(pos); if (pixmap->isNull()) return; const bool fast = false; const int w = rect.width() - margins.left() - margins.right(); const int h = rect.height() - margins.top() - margins.bottom(); //! @todo we can optimize painting by drawing rescaled pixmap here //! and performing detailed painting later (using QTimer) // QPixmap pixmapBuffer; // QPainter p2; // QPainter *target; // if (fast) { // target = p; // } else { // target = &p2; // } //! @todo only create buffered pixmap of the minimum size and then do not fillRect() // target->fillRect(0,0,rect.width(),rect.height(), backgroundColor); *pos = rect.topLeft() + QPoint(margins.left(), margins.top()); if (scaledContents) { if (keepAspectRatio) { QImage img(pixmap->toImage()); img = img.scaled(w, h, Qt::KeepAspectRatio, transformMode); if (img.width() < w) { if (alignment & Qt::AlignRight) pos->setX(pos->x() + w - img.width()); else if (alignment & Qt::AlignHCenter) pos->setX(pos->x() + w / 2 - img.width() / 2); } else if (img.height() < h) { if (alignment & Qt::AlignBottom) pos->setY(pos->y() + h - img.height()); else if (alignment & Qt::AlignVCenter) pos->setY(pos->y() + h / 2 - img.height() / 2); } if (p) { p->drawImage(*pos, img); } else { *pixmap = QPixmap::fromImage(img); } } else { if (!fast) { *pixmap = pixmap->scaled(w, h, Qt::IgnoreAspectRatio, transformMode); if (p) { p->drawPixmap(*pos, *pixmap); } } } } else { if (alignment & Qt::AlignRight) pos->setX(pos->x() + w - pixmap->width()); else if (alignment & Qt::AlignHCenter) pos->setX(pos->x() + w / 2 - pixmap->width() / 2); else //left, etc. pos->setX(pos->x()); if (alignment & Qt::AlignBottom) pos->setY(pos->y() + h - pixmap->height()); else if (alignment & Qt::AlignVCenter) pos->setY(pos->y() + h / 2 - pixmap->height() / 2); else //top, etc. pos->setY(pos->y()); *pos += QPoint(margins.left(), margins.top()); if (p) { p->drawPixmap(*pos, *pixmap); } } } void KexiUtils::drawPixmap(QPainter* p, const QMargins& margins, const QRect& rect, const QPixmap& pixmap, Qt::Alignment alignment, bool scaledContents, bool keepAspectRatio, Qt::TransformationMode transformMode) { QPixmap px(pixmap); QPoint pos; drawOrScalePixmapInternal(p, margins, rect, &px, &pos, alignment, scaledContents, keepAspectRatio, transformMode); } QPixmap KexiUtils::scaledPixmap(const QMargins& margins, const QRect& rect, const QPixmap& pixmap, QPoint* pos, Qt::Alignment alignment, bool scaledContents, bool keepAspectRatio, Qt::TransformationMode transformMode) { QPixmap px(pixmap); drawOrScalePixmapInternal(0, margins, rect, &px, pos, alignment, scaledContents, keepAspectRatio, transformMode); return px; } bool KexiUtils::loadPixmapFromData(QPixmap *pixmap, const QByteArray &data, const char *format) { bool ok = pixmap->loadFromData(data, format); if (ok) { return true; } if (format) { return false; } const QList commonFormats({"png", "jpg", "bmp", "tif"}); QList formats(commonFormats); for(int i=0; ;) { ok = pixmap->loadFromData(data, formats[i]); if (ok) { return true; } ++i; if (i == formats.count()) {// try harder if (i == commonFormats.count()) { formats += QImageReader::supportedImageFormats(); if (formats.count() == commonFormats.count()) { break; // sanity check } } else { break; } } } return false; } void KexiUtils::setFocusWithReason(QWidget* widget, Qt::FocusReason reason) { if (!widget) return; QFocusEvent fe(QEvent::FocusIn, reason); QCoreApplication::sendEvent(widget, &fe); } void KexiUtils::unsetFocusWithReason(QWidget* widget, Qt::FocusReason reason) { if (!widget) return; QFocusEvent fe(QEvent::FocusOut, reason); QCoreApplication::sendEvent(widget, &fe); } //-------- void KexiUtils::adjustIfRtl(QMargins *margins) { if (margins && QGuiApplication::isRightToLeft()) { const int left = margins->left(); margins->setLeft(margins->right()); margins->setRight(left); } } //--------- Q_GLOBAL_STATIC(FontSettingsData, g_fontSettings) QFont KexiUtils::smallestReadableFont() { return g_fontSettings->font(FontSettingsData::SmallestReadableFont); } //--------------------- KTextEditorFrame::KTextEditorFrame(QWidget * parent, Qt::WindowFlags f) : QFrame(parent, f) { QEvent dummy(QEvent::StyleChange); changeEvent(&dummy); } void KTextEditorFrame::changeEvent(QEvent *event) { if (event->type() == QEvent::StyleChange) { if (style()->objectName() != "oxygen") // oxygen already nicely paints the frame setFrameStyle(QFrame::Sunken | QFrame::StyledPanel); else setFrameStyle(QFrame::NoFrame); } } //--------------------- int KexiUtils::marginHint() { return QApplication::style()->pixelMetric(QStyle::PM_DefaultChildMargin); } int KexiUtils::spacingHint() { return QApplication::style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing); } void KexiUtils::setStandardMarginsAndSpacing(QLayout *layout) { setMargins(layout, KexiUtils::marginHint()); layout->setSpacing(KexiUtils::spacingHint()); } void KexiUtils::setMargins(QLayout *layout, int value) { layout->setContentsMargins(value, value, value, value); } void KexiUtils::replaceColors(QPixmap* original, const QColor& color) { Q_ASSERT(original); QImage dest(original->toImage()); replaceColors(&dest, color); *original = QPixmap::fromImage(dest); } void KexiUtils::replaceColors(QImage* original, const QColor& color) { Q_ASSERT(original); QPainter p(original); p.setCompositionMode(QPainter::CompositionMode_SourceIn); p.fillRect(original->rect(), color); } bool KexiUtils::isLightColorScheme() { return KColorScheme(QPalette::Active, KColorScheme::Window).background().color().lightness() >= 128; } int KexiUtils::dimmedAlpha() { return 150; } QPalette KexiUtils::paletteWithDimmedColor(const QPalette &pal, QPalette::ColorGroup group, QPalette::ColorRole role) { QPalette result(pal); QColor color(result.color(group, role)); color.setAlpha(dimmedAlpha()); result.setColor(group, role, color); return result; } QPalette KexiUtils::paletteWithDimmedColor(const QPalette &pal, QPalette::ColorRole role) { QPalette result(pal); QColor color(result.color(role)); color.setAlpha(dimmedAlpha()); result.setColor(role, color); return result; } QPalette KexiUtils::paletteForReadOnly(const QPalette &palette) { QPalette p(palette); p.setBrush(QPalette::Base, palette.brush(QPalette::Disabled, QPalette::Base)); p.setBrush(QPalette::Text, palette.brush(QPalette::Disabled, QPalette::Text)); p.setBrush(QPalette::Highlight, palette.brush(QPalette::Disabled, QPalette::Highlight)); p.setBrush(QPalette::HighlightedText, palette.brush(QPalette::Disabled, QPalette::HighlightedText)); return p; } void KexiUtils::setBackgroundColor(QWidget *widget, const QColor &color) { widget->setAutoFillBackground(true); QPalette pal(widget->palette()); pal.setColor(widget->backgroundRole(), color); widget->setPalette(pal); } //--------------------- void KexiUtils::installRecursiveEventFilter(QObject *object, QObject *filter) { if (!object || !filter || !object->isWidgetType()) return; // qDebug() << "Installing event filter on widget:" << object // << "directed to" << filter->objectName(); object->installEventFilter(filter); const QObjectList list(object->children()); foreach(QObject *obj, list) { installRecursiveEventFilter(obj, filter); } } void KexiUtils::removeRecursiveEventFilter(QObject *object, QObject *filter) { object->removeEventFilter(filter); if (!object->isWidgetType()) return; const QObjectList list(object->children()); foreach(QObject *obj, list) { removeRecursiveEventFilter(obj, filter); } } PaintBlocker::PaintBlocker(QWidget* parent) : QObject(parent) , m_enabled(true) { parent->installEventFilter(this); } void PaintBlocker::setEnabled(bool set) { m_enabled = set; } bool PaintBlocker::enabled() const { return m_enabled; } bool PaintBlocker::eventFilter(QObject* watched, QEvent* event) { if (m_enabled && watched == parent() && event->type() == QEvent::Paint) { return true; } return false; } tristate KexiUtils::openHyperLink(const QUrl &url, QWidget *parent, const OpenHyperlinkOptions &options) { if (url.isLocalFile()) { QFileInfo fileInfo(url.toLocalFile()); if (!fileInfo.exists()) { KMessageBox::sorry(parent, xi18nc("@info", "The file or directory %1 does not exist.", fileInfo.absoluteFilePath())); return false; } } if (!url.isValid()) { KMessageBox::sorry(parent, xi18nc("@info", "Invalid hyperlink %1.", url.url(QUrl::PreferLocalFile))); return false; } QMimeDatabase db; QString type = db.mimeTypeForUrl(url).name(); if (!options.allowExecutable && KRun::isExecutableFile(url, type)) { KMessageBox::sorry(parent, xi18nc("@info", "Executable %1 not allowed.", url.url(QUrl::PreferLocalFile))); return false; } if (!options.allowRemote && !url.isLocalFile()) { KMessageBox::sorry(parent, xi18nc("@info", "Remote hyperlink %1 not allowed.", url.url(QUrl::PreferLocalFile))); return false; } if (KRun::isExecutableFile(url, type)) { int ret = KMessageBox::questionYesNo(parent , xi18nc("@info", "Do you want to run this file?" "Running executables can be dangerous.") , QString() , KGuiItem(xi18nc("@action:button Run script file", "Run"), koIconName("system-run")) , KStandardGuiItem::no() , "AllowRunExecutable", KMessageBox::Dangerous); if (ret != KMessageBox::Yes) { return cancelled; } } switch(options.tool) { case OpenHyperlinkOptions::DefaultHyperlinkTool: return KRun::runUrl(url, type, parent); case OpenHyperlinkOptions::BrowserHyperlinkTool: return QDesktopServices::openUrl(url); case OpenHyperlinkOptions::MailerHyperlinkTool: return QDesktopServices::openUrl(url); default:; } return false; } // ---- KexiDBDebugTreeWidget::KexiDBDebugTreeWidget(QWidget *parent) : QTreeWidget(parent) { } void KexiDBDebugTreeWidget::copy() { if (currentItem()) { qApp->clipboard()->setText(currentItem()->text(0)); } } // ---- DebugWindow::DebugWindow(QWidget * parent) : QWidget(parent, Qt::Window) { } // ---- QSize KexiUtils::comboBoxArrowSize(QStyle *style) { if (!style) { style = QApplication::style(); } QStyleOptionComboBox cbOption; return style->subControlRect(QStyle::CC_ComboBox, &cbOption, QStyle::SC_ComboBoxArrow).size(); } void KexiUtils::addDirtyFlag(QString *text) { Q_ASSERT(text); *text = xi18nc("'Dirty (modified) object' flag", "%1*", *text); } //! From klocale_kde.cpp //! @todo KEXI3 support other OS-es (use from klocale_*.cpp) static QByteArray systemCodeset() { QByteArray codeset; #if HAVE_LANGINFO_H // Qt since 4.2 always returns 'System' as codecForLocale and KDE (for example // KEncodingFileDialog) expects real encoding name. So on systems that have langinfo.h use // nl_langinfo instead, just like Qt compiled without iconv does. Windows already has its own // workaround codeset = nl_langinfo(CODESET); if ((codeset == "ANSI_X3.4-1968") || (codeset == "US-ASCII")) { // means ascii, "C"; QTextCodec doesn't know, so avoid warning codeset = "ISO-8859-1"; } #endif return codeset; } QTextCodec* g_codecForEncoding = 0; bool setEncoding(int mibEnum) { QTextCodec *codec = QTextCodec::codecForMib(mibEnum); if (codec) { g_codecForEncoding = codec; } return codec != 0; } //! From klocale_kde.cpp static void initEncoding() { if (!g_codecForEncoding) { // This all made more sense when we still had the EncodingEnum config key. QByteArray codeset = systemCodeset(); if (!codeset.isEmpty()) { QTextCodec *codec = QTextCodec::codecForName(codeset); if (codec) { setEncoding(codec->mibEnum()); } } else { setEncoding(QTextCodec::codecForLocale()->mibEnum()); } if (!g_codecForEncoding) { qWarning() << "Cannot resolve system encoding, defaulting to ISO 8859-1."; const int mibDefault = 4; // ISO 8859-1 setEncoding(mibDefault); } Q_ASSERT(g_codecForEncoding); } } QByteArray KexiUtils::encoding() { initEncoding(); return g_codecForEncoding->name(); } namespace { //! @internal for graphicEffectsLevel() class GraphicEffectsLevel { public: GraphicEffectsLevel() { KConfigGroup g(KSharedConfig::openConfig(), "KDE-Global GUI Settings"); // Asking for hasKey we do not ask for graphicEffectsLevelDefault() that can // contain some very slow code. If we can save that time, do it. (ereslibre) if (g.hasKey("GraphicEffectsLevel")) { value = ((GraphicEffects) g.readEntry("GraphicEffectsLevel", QVariant((int) NoEffects)).toInt()); return; } // For now, let always enable animations by default. The plan is to make // this code a bit smarter. (ereslibre) value = ComplexAnimationEffects; } GraphicEffects value; }; } Q_GLOBAL_STATIC(GraphicEffectsLevel, g_graphicEffectsLevel) GraphicEffects KexiUtils::graphicEffectsLevel() { return g_graphicEffectsLevel->value; } bool KexiUtils::activateItemsOnSingleClick(QWidget *widget) { #if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0) Q_UNUSED(widget) return QApplication::styleHints()->singleClickActivation(); #else QStyle *style = widget ? widget->style() : QApplication::style(); return style->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, 0, widget); #endif } // NOTE: keep this in sync with kdebase/workspace/kcontrol/colors/colorscm.cpp QColor KexiUtils::inactiveTitleColor() { #ifdef Q_OS_WIN return qt_colorref2qrgb(GetSysColor(COLOR_INACTIVECAPTION)); #else KConfigGroup g(KSharedConfig::openConfig(), "WM"); return g.readEntry("inactiveBackground", QColor(224, 223, 222)); #endif } // NOTE: keep this in sync with kdebase/workspace/kcontrol/colors/colorscm.cpp QColor KexiUtils::inactiveTextColor() { #ifdef Q_OS_WIN return qt_colorref2qrgb(GetSysColor(COLOR_INACTIVECAPTIONTEXT)); #else KConfigGroup g(KSharedConfig::openConfig(), "WM"); return g.readEntry("inactiveForeground", QColor(75, 71, 67)); #endif } // NOTE: keep this in sync with kdebase/workspace/kcontrol/colors/colorscm.cpp QColor KexiUtils::activeTitleColor() { #ifdef Q_OS_WIN return qt_colorref2qrgb(GetSysColor(COLOR_ACTIVECAPTION)); #else KConfigGroup g(KSharedConfig::openConfig(), "WM"); return g.readEntry("activeBackground", QColor(48, 174, 232)); #endif } // NOTE: keep this in sync with kdebase/workspace/kcontrol/colors/colorscm.cpp QColor KexiUtils::activeTextColor() { #ifdef Q_OS_WIN return qt_colorref2qrgb(GetSysColor(COLOR_CAPTIONTEXT)); #else KConfigGroup g(KSharedConfig::openConfig(), "WM"); return g.readEntry("activeForeground", QColor(255, 255, 255)); #endif } QString KexiUtils::makeStandardCaption(const QString &userCaption, CaptionFlags flags) { QString caption = KAboutData::applicationData().displayName(); if (caption.isEmpty()) { return QCoreApplication::instance()->applicationName(); } QString captionString = userCaption.isEmpty() ? caption : userCaption; // If the document is modified, add '[modified]'. if (flags & ModifiedCaption) { captionString += QString::fromUtf8(" [") + xi18n("modified") + QString::fromUtf8("]"); } if (!userCaption.isEmpty()) { // Add the application name if: // User asked for it, it's not a duplication and the app name (caption()) is not empty if (flags & AppNameCaption && !caption.isEmpty() && !userCaption.endsWith(caption)) { // TODO: check to see if this is a transient/secondary window before trying to add the app name // on platforms that need this captionString += xi18nc("Document/application separator in titlebar", " – ") + caption; } } return captionString; } QString themedIconName(const QString &name) { static bool firstUse = true; if (firstUse) { // workaround for some kde-related crash const bool _unused = KIconLoader::global()->iconPath(name, KIconLoader::NoGroup, true).isEmpty(); Q_UNUSED(_unused); firstUse = false; } // try load themed icon const QColor background = qApp->palette().background().color(); const bool useDarkIcons = background.value() > 100; return QLatin1String(useDarkIcons ? "dark_" : "light_") + name; } QIcon themedIcon(const QString &name) { const QString realName(themedIconName(name)); const QIcon icon = QIcon::fromTheme(realName); // fallback if (icon.isNull()) { return QIcon::fromTheme(name); } return icon; } QString KexiUtils::localizedStringToHtmlSubstring(const KLocalizedString& string) { return string.toString(Kuit::RichText) .remove(QLatin1String("")).remove(QLatin1String("")); } bool KexiUtils::cursorAtEnd(const QLineEdit *lineEdit) { if (!lineEdit) { return false; } if (lineEdit->inputMask().isEmpty()) { return lineEdit->cursorPosition() >= lineEdit->displayText().length(); } else { return lineEdit->cursorPosition() >= (lineEdit->displayText().length() - 1); } } diff --git a/src/kexiutils/utils.h b/src/kexiutils/utils.h index ec97a6b51..928cff167 100644 --- a/src/kexiutils/utils.h +++ b/src/kexiutils/utils.h @@ -1,623 +1,629 @@ /* This file is part of the KDE project Copyright (C) 2003-2016 Jarosław Staniek Contains code from kglobalsettings.h: Copyright (C) 2000, 2006 David Faure Copyright (C) 2008 Friedrich W. H. Kossebau Contains code from kdialog.h: Copyright (C) 1998 Thomas Tanghus (tanghus@earthling.net) Additions 1999-2000 by Espen Sand (espen@kde.org) and Holger Freyther 2005-2009 Olivier Goffart 2006 Tobias Koenig This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KEXIUTILS_UTILS_H #define KEXIUTILS_UTILS_H #include "kexiutils_export.h" #include "kexi_global.h" #include #include #include #include #include #include #include #include #include class QColor; class QMetaProperty; class QLayout; class QLineEdit; class KLocalizedString; //! @short General Utils namespace KexiUtils { //! \return true if parent of \a o that is of type \a className or false otherwise inline bool parentIs(QObject* o, const char* className = 0) { if (!o) return false; while ((o = o->parent())) { if (o->inherits(className)) return true; } return false; } //! \return parent object of \a o that is of type \a type or 0 if no such parent template inline type findParentByType(QObject* o) { if (!o) return 0; while ((o = o->parent())) { if (dynamic_cast< type >(o)) return dynamic_cast< type >(o); } return 0; } /*! \return first found child of \a o, inheriting \a className. If objName is 0 (the default), all object names match. Returned pointer type is casted. */ KEXIUTILS_EXPORT QObject* findFirstQObjectChild(QObject *o, const char* className, const char* objName); /*! \return first found child of \a o, that inherit \a className. If \a objName is 0 (the default), all object names match. Returned pointer type is casted. */ template inline type findFirstChild(QObject *o, const char* className, const char* objName = 0) { return ::qobject_cast< type >(findFirstQObjectChild(o, className, objName)); } //! Finds property for name \a name and object \a object returns it index; //! otherwise returns a null QMetaProperty. KEXIUTILS_EXPORT QMetaProperty findPropertyWithSuperclasses(const QObject* object, const char* name); //! \return true if \a object object is of class name \a className inline bool objectIsA(QObject* object, const char* className) { return 0 == qstrcmp(object->metaObject()->className(), className); } //! \return true if \a object object is of the class names inside \a classNames KEXIUTILS_EXPORT bool objectIsA(QObject* object, const QList& classNames); //! \return a list of methods for \a metaObject meta object. //! The methods are of type declared in \a types and have access declared //! in \a access. KEXIUTILS_EXPORT QList methodsForMetaObject( const QMetaObject *metaObject, QFlags types = QFlags(QMetaMethod::Method | QMetaMethod::Signal | QMetaMethod::Slot), QFlags access = QFlags(QMetaMethod::Private | QMetaMethod::Protected | QMetaMethod::Public)); //! Like \ref KexiUtils::methodsForMetaObject() but includes methods from all //! parent meta objects of the \a metaObject. KEXIUTILS_EXPORT QList methodsForMetaObjectWithParents( const QMetaObject *metaObject, QFlags types = QFlags(QMetaMethod::Method | QMetaMethod::Signal | QMetaMethod::Slot), QFlags access = QFlags(QMetaMethod::Private | QMetaMethod::Protected | QMetaMethod::Public)); //! \return a list with all this class's properties. KEXIUTILS_EXPORT QList propertiesForMetaObject( const QMetaObject *metaObject); //! \return a list with all this class's properties including thise inherited. KEXIUTILS_EXPORT QList propertiesForMetaObjectWithInherited( const QMetaObject *metaObject); //! \return a list of enum keys for meta property \a metaProperty. KEXIUTILS_EXPORT QStringList enumKeysForProperty(const QMetaProperty& metaProperty); //! Convert a list @a list of @a SourceType type to another list of @a DestinationType //! type using @a convertMethod function /*! Example: @code QList list = ....; QStringList result = KexiUtils::convertTypesUsingFunction(list); @endcode */ template QList convertTypesUsingFunction(const QList &list) { QList result; foreach(const SourceType &element, list) { result.append(ConvertFunction(element)); } return result; } //! Convert a list @a list of @a SourceType type to another list of @a DestinationType //! type using @a convertMethod /*! Example: @code QVariantList list = ....; QStringList result = KexiUtils::convertTypesUsingMethod(list); @endcode */ template QList convertTypesUsingMethod(const QList &list) { QList result; foreach(const SourceType &element, list) { result.append((element.*ConvertMethod)()); } return result; } /*! Sets "wait" cursor with 1 second delay (or 0 seconds if noDelay is true). Does nothing if the application has no GUI enabled. (see KApplication::guiEnabled()) */ KEXIUTILS_EXPORT void setWaitCursor(bool noDelay = false); /*! Remove "wait" cursor previously set with \a setWaitCursor(), even if it's not yet visible. Does nothing if the application has no GUI enabled. (see KApplication::guiEnabled()) */ KEXIUTILS_EXPORT void removeWaitCursor(); /*! Helper class. Allocate it in your code block as follows: KexiUtils::WaitCursor wait; .. and wait cursor will be visible (with one second delay) until you're in this block, without a need to call removeWaitCursor() before exiting the block. Does nothing if the application has no GUI enabled. (see KApplication::guiEnabled()) */ class KEXIUTILS_EXPORT WaitCursor { public: WaitCursor(bool noDelay = false); ~WaitCursor(); }; /*! Helper class. Allocate it in your code block as follows: KexiUtils::WaitCursorRemover remover; .. and the wait cursor will be hidden unless you leave this block, without a need to call setWaitCursor() before exiting the block. After leaving the codee block, the cursor will be visible again, if it was visible before creating the WaitCursorRemover object. Does nothing if the application has no GUI enabled. (see KApplication::guiEnabled()) */ class KEXIUTILS_EXPORT WaitCursorRemover { public: WaitCursorRemover(); ~WaitCursorRemover(); private: bool m_reactivateCursor; }; /*! \return filter string in QFileDialog format for a mime type pointed by \a mime If \a kdeFormat is true, QFileDialog-compatible filter string is generated, eg. "Image files (*.png *.xpm *.jpg)", otherwise KFileDialog -compatible filter string is generated, eg. "*.png *.xpm *.jpg|Image files (*.png *.xpm *.jpg)". "\\n" is appended if \a kdeFormat is true, otherwise ";;" is appended. */ KEXIUTILS_EXPORT QString fileDialogFilterString(const QMimeType &mime, bool kdeFormat = true); /*! @overload QString fileDialogFilterString(const QMimeType &mime, bool kdeFormat = true) */ KEXIUTILS_EXPORT QString fileDialogFilterString(const QString& mimeName, bool kdeFormat = true); /*! Like QString fileDialogFilterString(const QMimeType &mime, bool kdeFormat = true) but returns a list of filter strings. */ KEXIUTILS_EXPORT QString fileDialogFilterStrings(const QStringList& mimeStrings, bool kdeFormat); /*! Creates a modal file dialog which returns the selected url of image filr to open or an empty string if none was chosen. Like KFileDialog::getImageOpenUrl(). */ //! @todo KEXI3 add equivalent of kfiledialog:/// KEXIUTILS_EXPORT QUrl getOpenImageUrl(QWidget *parent = 0, const QString &caption = QString(), const QUrl &directory = QUrl()); /*! Creates a modal file dialog with returns the selected url of image file to save or an empty string if none was chosen. Like KFileDialog::getSaveUrl(). */ //! @todo KEXI3 add equivalent of kfiledialog:/// KEXIUTILS_EXPORT QUrl getSaveImageUrl(QWidget *parent = 0, const QString &caption = QString(), const QUrl &directory = QUrl()); +/*! Displays a "The file %1 already exists. Do you want to overwrite it?" Yes/No message box. + @a parent is used as a parent of the message box. + @return @c true if @a filePath file does not exist or user has agreed to overwrite it; + returns @c false if user does not agree to overwrite it. */ +KEXIUTILS_EXPORT bool askForFileOverwriting(const QString& filePath, QWidget *parent = nullptr); + /*! A global setting for minimal readable font. This can be used in dockers, rulers and other places where space is at a premium. @see QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont). @todo Add integration with KDE platform theme (how to detect it?); for now we don't assume it's installed */ KEXIUTILS_EXPORT QFont smallestReadableFont(); /*! \return a color being a result of blending \a c1 with \a c2 with \a factor1 and \a factor1 factors: (c1*factor1+c2*factor2)/(factor1+factor2). */ KEXIUTILS_EXPORT QColor blendedColors(const QColor& c1, const QColor& c2, int factor1 = 1, int factor2 = 1); /*! \return a contrast color for a color \a c: If \a c is light color, darker color created using c.dark(200) is returned; otherwise lighter color created using c.light(200) is returned. */ KEXIUTILS_EXPORT QColor contrastColor(const QColor& c); /*! \return a lighter color for a color \a c and a factor \a factor. For colors like Qt::red or Qt::green where hue and saturation are near to 255, hue is decreased so the result will be more bleached. For black color the result is dark gray rather than black. */ KEXIUTILS_EXPORT QColor bleachedColor(const QColor& c, int factor); /*! \return icon set computed as a result of colorizing \a icon pixmap with \a role color of \a palette palette. This function is useful for displaying monochromed icons on the list view or table view header, to avoid bloat, but still have the color compatible with accessibility settings. */ KEXIUTILS_EXPORT QIcon colorizeIconToTextColor(const QPixmap& icon, const QPalette& palette, QPalette::ColorRole role = QPalette::ButtonText); /*! Replaces colors in pixmap @a original using @a color color. Used for coloring bitmaps that have to reflect the foreground color. */ KEXIUTILS_EXPORT void replaceColors(QPixmap* original, const QColor& color); /*! Replaces colors in image @a original using @a color color. Used for coloring bitmaps that have to reflect the foreground color. */ KEXIUTILS_EXPORT void replaceColors(QImage* original, const QColor& color); /*! @return true if curent color scheme is light. Lightness of window background is checked to measure this. */ KEXIUTILS_EXPORT bool isLightColorScheme(); /*! @return alpha value for dimmed color (150). */ KEXIUTILS_EXPORT int dimmedAlpha(); /*! @return palette @a pal with dimmed color @a role. @see dimmedAlpha() */ KEXIUTILS_EXPORT QPalette paletteWithDimmedColor(const QPalette &pal, QPalette::ColorGroup group, QPalette::ColorRole role); /*! @overload paletteWithDimmedColor(const QPalette &, QPalette::ColorGroup, QPalette::ColorRole) */ KEXIUTILS_EXPORT QPalette paletteWithDimmedColor(const QPalette &pal, QPalette::ColorRole role); /*! @return palette altered for indicating "read only" flag. */ KEXIUTILS_EXPORT QPalette paletteForReadOnly(const QPalette &palette); /*! Convenience function that sets background color @a color for widget @a widget. "autoFillBackground" property is set to true for the widget. Background color role is obtained from QWidget::backgroundRole(). */ KEXIUTILS_EXPORT void setBackgroundColor(QWidget *widget, const QColor &color); /*! \return empty (fully transparent) pixmap that can be used as a place for icon of size \a iconGroup */ KEXIUTILS_EXPORT QPixmap emptyIcon(KIconLoader::Group iconGroup); #ifdef KEXI_DEBUG_GUI //! Creates debug window for convenient debugging output KEXIUTILS_EXPORT QWidget *createDebugWindow(QWidget *parent); //! Connects push button action to \a receiver and its \a slot. This allows to execute debug-related actions //! using buttons displayed in the debug window. KEXIUTILS_EXPORT void connectPushButtonActionForDebugWindow(const char* actionName, const QObject *receiver, const char* slot); #endif //! Sets focus for widget \a widget with reason \a reason. KEXIUTILS_EXPORT void setFocusWithReason(QWidget* widget, Qt::FocusReason reason); //! Unsets focus for widget \a widget with reason \a reason. KEXIUTILS_EXPORT void unsetFocusWithReason(QWidget* widget, Qt::FocusReason reason); //! If the application's layout direction, swaps left and right margins. //! @see QGuiApplication::isRightToLeft() void adjustIfRtl(QMargins *margins); //! Draws pixmap @a pixmap on painter @a p using predefined parameters. //! Used in KexiDBImageBox and KexiBlobTableEdit. KEXIUTILS_EXPORT void drawPixmap(QPainter* p, const QMargins& margins, const QRect& rect, const QPixmap& pixmap, Qt::Alignment alignment, bool scaledContents, bool keepAspectRatio, Qt::TransformationMode transformMode = Qt::FastTransformation); //! Scales pixmap @a pixmap on painter @a p using predefined parameters. //! Used in KexiDBImageBox and KexiBlobTableEdit. KEXIUTILS_EXPORT QPixmap scaledPixmap(const QMargins& margins, const QRect& rect, const QPixmap& pixmap, QPoint* pos, Qt::Alignment alignment, bool scaledContents, bool keepAspectRatio, Qt::TransformationMode transformMode = Qt::FastTransformation); //! This function should be used instead of QPixmap::loadFromData(). /** Loads a pixmap from @a data into @a pixmap. First tries to detect format, on failure * tries to load most common formats: png, jpg, bmp, tif. Then tries to load any of format * returned by QImageReader::supportedImageFormats(). * If @a format is provided, only this format is tried. * @return on success. * @note This function exists because QPixmap::loadFromData() not always works when there * are broken image format plugins installed (as it was the case with KRA plugin). * @todo Idea: Support while/black list of supported image formats. It would be useful * for security reasons because files can be loaded from remote locations. * @todo For the black/white list an enum describing local/remote data source is needed. */ KEXIUTILS_EXPORT bool loadPixmapFromData(QPixmap *pixmap, const QByteArray &data, const char *format = nullptr); //! A helper for automatic deleting of contents of containers. template class ContainerDeleter { public: explicit ContainerDeleter(Container& container) : m_container(container) {} ~ContainerDeleter() { clear(); } void clear() { qDeleteAll(m_container); m_container.clear(); } private: Container& m_container; }; /*! A modified QFrame which sets up sunken styled panel frame style depending on the current widget style. The widget also reacts on style changes. */ class KEXIUTILS_EXPORT KTextEditorFrame : public QFrame { Q_OBJECT public: explicit KTextEditorFrame(QWidget * parent = 0, Qt::WindowFlags f = 0); protected: virtual void changeEvent(QEvent *event); }; /** * Returns the number of pixels that should be used between a * dialog edge and the outermost widget(s) according to the KDE standard. * * Copied from QDialog. * * @deprecated Use the style's pixelMetric() function to query individual margins. * Different platforms may use different values for the four margins. */ KEXIUTILS_EXPORT int marginHint(); /** * Returns the number of pixels that should be used between * widgets inside a dialog according to the KDE standard. * * Copied from QDialog. * * @deprecated Use the style's layoutSpacing() function to query individual spacings. * Different platforms may use different values depending on widget types and pairs. */ KEXIUTILS_EXPORT int spacingHint(); /*! Sets KexiUtils::marginHint() margins and KexiUtils::spacingHint() spacing for the layout @a layout. */ KEXIUTILS_EXPORT void setStandardMarginsAndSpacing(QLayout *layout); /*! Sets the same @a value for layout @a layout margins. */ KEXIUTILS_EXPORT void setMargins(QLayout *layout, int value); //! sometimes we leave a space in the form of empty QFrame and want to insert here //! a widget that must be instantiated by hand. //! This macro inserts a widget \a what into a frame \a where. #define GLUE_WIDGET(what, where) \ { QVBoxLayout *lyr = new QVBoxLayout(where); \ lyr->addWidget(what); } //! A tool for setting temporary value for boolean variable. /*! After desctruction of the instance, the variable is set back to the original value. This class is useful in recursion guards. To use it, declare class atrribute of type bool and block it, e.g.: @code bool m_myNonRecursiveFunctionEnabled; // ... set m_myNonRecursiveFunctionEnabled initially to true void myNonRecursiveFunctionEnabled() { if (!m_myNonRecursiveFunctionEnabled) return; KexiUtils::BoolBlocker guard(&m_myNonRecursiveFunctionEnabled, false); // function's body guarded against recursion... } @endcode */ class KEXIUTILS_EXPORT BoolBlocker { public: inline BoolBlocker(bool *var, bool tempValue) : v(var), origValue(*var) { *var = tempValue; } inline ~BoolBlocker() { *v = origValue; } private: bool *v; bool origValue; }; /*! This helper function install an event filter on @a object and all of its children, directed to @a filter. */ KEXIUTILS_EXPORT void installRecursiveEventFilter(QObject *object, QObject *filter); /*! This helper function removes an event filter installed before on @a object and all of its children. */ KEXIUTILS_EXPORT void removeRecursiveEventFilter(QObject *object, QObject *filter); //! Blocks paint events on specified widget. /*! Works recursively. Useful when widget should be hidden without changing geometry it takes. */ class KEXIUTILS_EXPORT PaintBlocker : public QObject { Q_OBJECT public: explicit PaintBlocker(QWidget* parent); void setEnabled(bool set); bool enabled() const; protected: virtual bool eventFilter(QObject* watched, QEvent* event); private: bool m_enabled; }; /*! * \short Options for opening of hyperlinks * \sa openHyperLink() */ class KEXIUTILS_EXPORT OpenHyperlinkOptions : public QObject { Q_OBJECT public: /*! * A tool used for opening hyperlinks */ enum HyperlinkTool{ DefaultHyperlinkTool, /*!< Default tool for a given type of the hyperlink */ BrowserHyperlinkTool, /*!< Opens hyperlink in a browser */ MailerHyperlinkTool /*!< Opens hyperlink in a default mailer */ }; Q_ENUM(HyperlinkTool) OpenHyperlinkOptions() : tool(DefaultHyperlinkTool) , allowExecutable(false) , allowRemote(false) {} HyperlinkTool tool; bool allowExecutable; bool allowRemote; }; /*! * Opens the given \a url using \a options * It can fail if opening a given link is not possible or allowed. * @return true on success, cancelled if user has cancelled the opening and false on failure */ KEXIUTILS_EXPORT tristate openHyperLink(const QUrl &url, QWidget *parent, const OpenHyperlinkOptions &options); //! \return size of combo box arrow according to \a style /*! Application's style is the default. \see QStyle::SC_ComboBoxArrow */ KEXIUTILS_EXPORT QSize comboBoxArrowSize(QStyle *style = 0); //! Adds a dirty ("document modified") flag to @a text according to current locale. //! It is usually "*" character appended. KEXIUTILS_EXPORT void addDirtyFlag(QString *text); //! @return The name of the user's preferred encoding //! Based on KLocale::encoding() KEXIUTILS_EXPORT QByteArray encoding(); //! The desired level of effects on the GUI enum GraphicEffect { NoEffects = 0x0000, ///< GUI with no effects at all. GradientEffects = 0x0001, ///< GUI with only gradients enabled. SimpleAnimationEffects = 0x0002, ///< GUI with simple animations enabled. ComplexAnimationEffects = 0x0006 ///< GUI with complex animations enabled. ///< Note that ComplexAnimationsEffects implies SimpleAnimationEffects. }; Q_DECLARE_FLAGS(GraphicEffects, GraphicEffect) //! @return the desired level of effects on the GUI. //! @note A copy of KGlobalSettings::graphicEffectsLevel() needed for porting from kdelibs4. KEXIUTILS_EXPORT GraphicEffects graphicEffectsLevel(); //! @return the inactive titlebar background color //! @note A copy of KGlobalSettings::inactiveTitleColor() needed for porting from kdelibs4. KEXIUTILS_EXPORT QColor inactiveTitleColor(); //! @return the inactive titlebar text (foreground) color //! @note A copy of KGlobalSettings::inactiveTextColor() needed for porting from kdelibs4. KEXIUTILS_EXPORT QColor inactiveTextColor(); //! @return the active titlebar background color //! @note A copy of KGlobalSettings::activeTitleColor() needed for porting from kdelibs4. KEXIUTILS_EXPORT QColor activeTitleColor(); //! @return the active titlebar text (foreground) color //! @note A copy of KGlobalSettings::activeTextColor() needed for porting from kdelibs4. KEXIUTILS_EXPORT QColor activeTextColor(); //! @return Paper White color, see https://techbase.kde.org/Projects/Usability/HIG/Color inline QColor paperWhite() { return QColor(0xfcfcfc); } //! @return Charcoal Grey color, see https://techbase.kde.org/Projects/Usability/HIG/Color inline QColor charcoalGrey() { return QColor(0x31363b); } //! @return Shade Black color, see https://techbase.kde.org/Projects/Usability/HIG/Color inline QColor shadeBlack() { return QColor(0x232629); } /*! @return @c true if whether the app runs in a single click mode (the default). @c false if returned if the app runs in double click mode. For Qt < 5.5 this information is taken from @a widget widget's style. If there is no widget specified, QApplication::style() is used. For Qt >= 5.5 the result is equal to QApplication::styleHints()->singleClickActivation() and @a widget is ignored. @note This is a replacement for bool KGlobalSettings::singleClick(). */ KEXIUTILS_EXPORT bool activateItemsOnSingleClick(QWidget *widget = 0); /** * @enum CaptionFlag * Used to specify how to construct a window caption * * @value AppNameCaption Indicates that the method shall include * the application name when making the caption string. * @value ModifiedCaption Causes a 'modified' sign will be included in the * returned string. This is useful when indicating that a file is * modified, i.e., it contains data that has not been saved. */ enum CaptionFlag { NoCaptionFlags = 0, AppNameCaption = 1, ModifiedCaption = 2 }; Q_DECLARE_FLAGS(CaptionFlags, CaptionFlag) /** * Builds a caption that contains the application name along with the * userCaption using a standard layout. * * To make a compliant caption for your window, simply do: * @p setWindowTitle(makeStandardCaption(yourCaption)); * * To ensure that the caption is appropriate to the desktop in which the * application is running, pass in a pointer to the window the caption will * be applied to. * * @param userCaption The caption string you want to display in the * window caption area. Do not include the application name! * @param flags * @return the created caption * Based on KDialog::makeStandardCaption() */ KEXIUTILS_EXPORT QString makeStandardCaption(const QString &userCaption, CaptionFlags flags = AppNameCaption); /** * Return rich text for @a string. Equivalent of KLocalizedString::toString(Kuit::RichText) * but and is removed so the result can be used as %* argument in other string. */ KEXIUTILS_EXPORT QString localizedStringToHtmlSubstring(const KLocalizedString& string); /** * @return @c true if text cursor is at the end of the line edit @a lineEdit. * If the @a lineEdit edit has input mask, cursor is at the end if it's at position * lineEdit->displayText().length() - 1 or further. If the @a lineEdit has no input mask, cursor * is at the end if it's at position lineEdit->text().length() or further. */ KEXIUTILS_EXPORT bool cursorAtEnd(const QLineEdit *lineEdit); } //namespace KexiUtils #endif //KEXIUTILS_UTILS_H diff --git a/src/main/KexiMainWindow.cpp b/src/main/KexiMainWindow.cpp index b8b0f8b91..873c1ca0c 100644 --- a/src/main/KexiMainWindow.cpp +++ b/src/main/KexiMainWindow.cpp @@ -1,4391 +1,4391 @@ /* This file is part of the KDE project Copyright (C) 2003 Lucijan Busch Copyright (C) 2003-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 "KexiMainWindow.h" #include "KexiMainWindow_p.h" #include "kexiactionproxy.h" #include "kexipartmanager.h" #include "kexipart.h" #include "kexipartinfo.h" #include "kexipartguiclient.h" #include "kexiproject.h" #include "kexiprojectdata.h" #include "kexi.h" #include "kexiinternalpart.h" #include "kexiactioncategories.h" #include "kexifinddialog.h" #include "kexisearchandreplaceiface.h" #include "KexiBugReportDialog.h" #define KEXI_SKIP_REGISTERICONSRESOURCE #define KEXI_SKIP_SETUPPRIVATEICONSRESOURCE #include "KexiRegisterResource_p.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "startup/KexiStartup.h" #include "startup/KexiNewProjectAssistant.h" #include "startup/KexiOpenProjectAssistant.h" #include "startup/KexiWelcomeAssistant.h" #include "startup/KexiImportExportAssistant.h" #include "startup/KexiStartupDialog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if !defined(KexiVDebug) # define KexiVDebug if (0) qDebug() #endif #ifdef HAVE_KCRASH #include //! @todo else, add Breakpad? https://phabricator.kde.org/T1642 #endif KexiDockWidgetStyle::KexiDockWidgetStyle(const QString &baseStyleName) : QProxyStyle(baseStyleName) { } KexiDockWidgetStyle::~KexiDockWidgetStyle() { } void KexiDockWidgetStyle::polish(QWidget* widget) { baseStyle()->polish(widget); widget->setContentsMargins(0, 0, 0, 0); } class Q_DECL_HIDDEN KexiDockWidget::Private { public: Private() {} QSize hint; }; KexiDockWidget::KexiDockWidget(const QString &_tabText, QWidget *parent) : QDockWidget(parent), tabText(_tabText), d(new Private) { // No floatable dockers, Dolphin had problems, we don't want the same... // https://bugs.kde.org/show_bug.cgi?id=288629 // https://bugs.kde.org/show_bug.cgi?id=322299 setFeatures(QDockWidget::NoDockWidgetFeatures);//DockWidgetClosable); setAllowedAreas(Qt::LeftDockWidgetArea|Qt::RightDockWidgetArea); setFocusPolicy(Qt::NoFocus); if (style()->objectName().compare("windowsvista", Qt::CaseInsensitive) == 0) { // windowsvista style has broken accelerator visualization support KAcceleratorManager::setNoAccel(this); } KexiDockWidgetStyle *customStyle = new KexiDockWidgetStyle(style()->objectName()); customStyle->setParent(this); setStyle(customStyle); setTitleBarWidget(new QWidget(this)); // hide the title layout()->setContentsMargins(0, 0, 0, 0); layout()->setSpacing(0); } KexiDockWidget::~KexiDockWidget() { delete d; } void KexiDockWidget::paintEvent(QPaintEvent *pe) { Q_UNUSED(pe); QStylePainter p(this); if (isFloating()) { QStyleOptionFrame framOpt; framOpt.initFrom(this); p.drawPrimitive(QStyle::PE_FrameDockWidget, framOpt); } // Title must be painted after the frame, since the areas overlap, and // the title may wish to extend out to all sides (eg. XP style) QStyleOptionDockWidgetV2 titleOpt; initStyleOption(&titleOpt); p.drawControl(QStyle::CE_DockWidgetTitle, titleOpt); } void KexiDockWidget::setSizeHint(const QSize& hint) { d->hint = hint; } QSize KexiDockWidget::sizeHint() const { return d->hint.isValid() ? d->hint : QDockWidget::sizeHint(); } //------------------------------------------------- KexiMainWindowTabWidget::KexiMainWindowTabWidget(QWidget *parent, KexiMainWidget* mainWidget) : QTabWidget(parent) , m_mainWidget(mainWidget) , m_tabIndex(-1) { m_closeAction = new QAction(koIcon("tab-close"), xi18n("&Close Tab"), this); m_closeAction->setToolTip(xi18n("Close the current tab")); m_closeAction->setWhatsThis(xi18n("Closes the current tab.")); m_closeAllTabsAction = new QAction(xi18n("Cl&ose All Tabs"), this); m_closeAllTabsAction->setToolTip(xi18n("Close all tabs")); m_closeAllTabsAction->setWhatsThis(xi18n("Closes all tabs.")); connect(m_closeAction, SIGNAL(triggered()), this, SLOT(closeTab())); connect(m_closeAllTabsAction, SIGNAL(triggered()), this, SLOT(closeAllTabs())); //! @todo insert window list in the corner widget as in firefox #if 0 // close-tab button: QToolButton* rightWidget = new QToolButton(this); rightWidget->setDefaultAction(m_closeAction); rightWidget->setText(QString()); rightWidget->setAutoRaise(true); rightWidget->adjustSize(); setCornerWidget(rightWidget, Qt::TopRightCorner); #endif setMovable(true); setDocumentMode(true); tabBar()->setExpanding(true); } KexiMainWindowTabWidget::~KexiMainWindowTabWidget() { } void KexiMainWindowTabWidget::paintEvent(QPaintEvent * event) { if (count() > 0) QTabWidget::paintEvent(event); else QWidget::paintEvent(event); } void KexiMainWindowTabWidget::mousePressEvent(QMouseEvent *event) { //! @todo KEXI3 test KexiMainWindowTabWidget's contextMenu event port from KTabWidget if (event->button() == Qt::RightButton) { int tab = tabBar()->tabAt(event->pos()); const QPoint realPos(tabBar()->mapToGlobal(event->pos())); if (QRect(tabBar()->mapToGlobal(QPoint(0,0)), tabBar()->mapToGlobal(QPoint(tabBar()->width()-1, tabBar()->height()-1))).contains(realPos)) { showContextMenuForTab(tab, tabBar()->mapToGlobal(event->pos())); return; } } QTabWidget::mousePressEvent(event); } void KexiMainWindowTabWidget::closeTab() { KexiMainWindow *main = dynamic_cast(KexiMainWindowIface::global()); if (main) { main->closeWindowForTab(m_tabIndex); } } tristate KexiMainWindowTabWidget::closeAllTabs() { tristate alternateResult = true; QList windowList; KexiMainWindow *main = dynamic_cast(KexiMainWindowIface::global()); if (!main) { return alternateResult; } for (int i = 0; i < count(); i++) { KexiWindow *window = main->windowForTab(i); if (window) { windowList.append(window); } } foreach (KexiWindow *window, windowList) { tristate result = main->closeWindow(window); if (result != true && result != false) { return result; } if (result == false) { alternateResult = false; } } return alternateResult; } void KexiMainWindowTabWidget::showContextMenuForTab(int index, const QPoint& point) { QMenu menu; if (index >= 0) { menu.addAction(m_closeAction); } if (count() > 0) { menu.addAction(m_closeAllTabsAction); } //! @todo add "&Detach Tab" if (menu.actions().isEmpty()) { return; } setTabIndexFromContextMenu(index); menu.exec(point); } void KexiMainWindowTabWidget::setTabIndexFromContextMenu(int clickedIndex) { if (currentIndex() == -1) { m_tabIndex = -1; return; } m_tabIndex = clickedIndex; } //------------------------------------------------- static bool setupIconTheme(KLocalizedString *errorMessage, KLocalizedString *detailsErrorMessage) { // Register kexi resource first to have priority over the standard breeze theme. // For example "table" icon exists in both resources. if (!registerResource("icons/kexi_breeze.rcc", QStandardPaths::AppDataLocation, QString(), QString(), errorMessage, detailsErrorMessage) || !registerGlobalBreezeIconsResource(errorMessage, detailsErrorMessage)) { return false; } setupBreezeIconTheme(); // tell KIconLoader an co. about the theme KConfigGroup cg(KSharedConfig::openConfig(), "Icons"); cg.writeEntry("Theme", "breeze"); cg.sync(); return true; } //! @todo 3.1 replace with KexiStyle bool setupApplication() { #if defined Q_OS_WIN || defined Q_OS_MAC // Only this style matches current Kexi theme and can be supported/tested const char *name = "breeze"; QScopedPointer style(QStyleFactory::create(name)); if (!style || style->objectName() != name) { qWarning() << qPrintable(QString("Could not find application style %1. " "Kexi will not start. Please check if Kexi is properly installed. ") .arg(name)); return false; } qApp->setStyle(style.take()); #endif return true; } //static int KexiMainWindow::create(int &argc, char *argv[], const QString &componentName) { QApplication *app = qApp; QScopedPointer guard; if (!app) { //! @todo use non-GUI app when needed guard.reset(app = new QApplication(argc, argv)); } app->setQuitOnLastWindowClosed(false); KLocalizedString::setApplicationDomain("kexi"); //! @todo KEXI3 app->setAttribute(Qt::AA_UseHighDpiPixmaps, true); KexiAboutData aboutData; if (!componentName.isEmpty()) { aboutData.setComponentName(componentName); } KAboutData::setApplicationData(aboutData); if (!setupApplication()) { return 1; } #ifdef HAVE_KCRASH KCrash::initialize(); #endif KLocalizedString errorMessage; KLocalizedString detailsErrorMessage; if (!setupIconTheme(&errorMessage, &detailsErrorMessage)) { if (detailsErrorMessage.isEmpty()) { KMessageBox::error(nullptr, errorMessage.toString()); } else { KMessageBox::detailedError(nullptr, errorMessage.toString(), detailsErrorMessage.toString()); } qWarning() << qPrintable(errorMessage.toString(Kuit::PlainText)); return 1; } QApplication::setWindowIcon(koIcon("kexi")); tristate res = Kexi::startupHandler().init(); if (!res || ~res) { return (~res) ? 0 : 1; } //qDebug() << "startupActions OK"; /* Exit requested, e.g. after database removing. */ if (Kexi::startupHandler().action() == KexiStartupData::Exit) { return 0; } KexiMainWindow *win = new KexiMainWindow(); #ifdef KEXI_DEBUG_GUI QWidget* debugWindow = 0; KConfigGroup generalGroup = KSharedConfig::openConfig()->group("General"); if (generalGroup.readEntry("ShowInternalDebugger", false)) { debugWindow = KexiUtils::createDebugWindow(win); debugWindow->show(); } #endif if (true != win->startup()) { delete win; return 1; } win->restoreSettings(); win->show(); #ifdef KEXI_DEBUG_GUI win->raise(); static_cast(win)->activateWindow(); #endif /*foreach (QWidget *widget, QApplication::topLevelWidgets()) { qDebug() << widget; }*/ guard.take(); return 0; } //------------------------------------------------- KexiMainMenuActionShortcut::KexiMainMenuActionShortcut(const QKeySequence& key, QAction *action, QWidget *parent) : QShortcut(key, parent) , m_action(action) { connect(this, SIGNAL(activated()), this, SLOT(slotActivated())); } KexiMainMenuActionShortcut::~KexiMainMenuActionShortcut() { } void KexiMainMenuActionShortcut::slotActivated() { if (!m_action->isEnabled()) { return; } m_action->trigger(); } //------------------------------------------------- KexiMainWindow::KexiMainWindow(QWidget *parent) : KexiMainWindowSuper(parent) , KexiMainWindowIface() , KexiGUIMessageHandler(this) , d(new KexiMainWindow::Private(this)) { setObjectName("KexiMainWindow"); setAttribute(Qt::WA_DeleteOnClose); kexiTester() << KexiTestObject(this); if (d->userMode) qDebug() << "starting up in the User Mode"; setAsDefaultHost(); //this is default host now. //get informed connect(&Kexi::partManager(), SIGNAL(partLoaded(KexiPart::Part*)), this, SLOT(slotPartLoaded(KexiPart::Part*))); connect(&Kexi::partManager(), SIGNAL(newObjectRequested(KexiPart::Info*)), this, SLOT(newObject(KexiPart::Info*))); setAcceptDrops(true); setupActions(); setupMainWidget(); updateAppCaption(); if (!d->userMode) { setupContextHelp(); setupPropertyEditor(); } invalidateActions(); d->timer.singleShot(0, this, SLOT(slotLastActions())); if (Kexi::startupHandler().forcedFullScreen()) { toggleFullScreen(true); } // --- global config //! @todo move to specialized KexiConfig class KConfigGroup tablesGroup(d->config->group("Tables")); const int defaultMaxLengthForTextFields = tablesGroup.readEntry("DefaultMaxLengthForTextFields", int(-1)); if (defaultMaxLengthForTextFields >= 0) { KDbField::setDefaultMaxLength(defaultMaxLengthForTextFields); } // --- /global config } KexiMainWindow::~KexiMainWindow() { d->forceWindowClosing = true; closeProject(); delete d; Kexi::deleteGlobalObjects(); } KexiProject *KexiMainWindow::project() { return d->prj; } QList KexiMainWindow::allActions() const { return actionCollection()->actions(); } KActionCollection *KexiMainWindow::actionCollection() const { return d->actionCollection; } KexiWindow* KexiMainWindow::currentWindow() const { return windowForTab(d->mainWidget->tabWidget()->currentIndex()); } KexiWindow* KexiMainWindow::windowForTab(int tabIndex) const { if (!d->mainWidget->tabWidget()) return 0; KexiWindowContainer *windowContainer = dynamic_cast(d->mainWidget->tabWidget()->widget(tabIndex)); if (!windowContainer) return 0; return windowContainer->window; } void KexiMainWindow::setupMainMenuActionShortcut(QAction * action) { if (!action->shortcut().isEmpty()) { foreach(const QKeySequence &shortcut, action->shortcuts()) { (void)new KexiMainMenuActionShortcut(shortcut, action, this); } } } static void addThreeDotsToActionText(QAction* action) { action->setText(xi18nc("Action name with three dots...", "%1...", action->text())); } QAction * KexiMainWindow::addAction(const char *name, const QIcon &icon, const QString& text, const char *shortcut) { QAction *action = icon.isNull() ? new QAction(text, this) : new QAction(icon, text, this); actionCollection()->addAction(name, action); if (shortcut) { action->setShortcut(QKeySequence(shortcut)); QShortcut *s = new QShortcut(action->shortcut(), this); connect(s, SIGNAL(activated()), action, SLOT(trigger())); } return action; } QAction * KexiMainWindow::addAction(const char *name, const QString& text, const char *shortcut) { return addAction(name, QIcon(), text, shortcut); } void KexiMainWindow::setupActions() { KActionCollection *ac = actionCollection(); // PROJECT MENU QAction *action; ac->addAction("project_new", action = new KexiMenuWidgetAction(KStandardAction::New, this)); addThreeDotsToActionText(action); action->setShortcuts(KStandardShortcut::openNew()); action->setToolTip(xi18n("Create a new project")); action->setWhatsThis( xi18n("Creates a new project. Currently opened project is not affected.")); connect(action, SIGNAL(triggered()), this, SLOT(slotProjectNew())); setupMainMenuActionShortcut(action); ac->addAction("project_open", action = new KexiMenuWidgetAction(KStandardAction::Open, this)); action->setToolTip(xi18n("Open an existing project")); action->setWhatsThis( xi18n("Opens an existing project. Currently opened project is not affected.")); connect(action, SIGNAL(triggered()), this, SLOT(slotProjectOpen())); setupMainMenuActionShortcut(action); { ac->addAction("project_welcome", action = d->action_project_welcome = new KexiMenuWidgetAction( QIcon(), xi18n("Welcome"), this)); addThreeDotsToActionText(action); connect(action, SIGNAL(triggered()), this, SLOT(slotProjectWelcome())); setupMainMenuActionShortcut(action); action->setToolTip(xi18n("Show Welcome page")); action->setWhatsThis( xi18n("Shows Welcome page with list of recently opened projects and other information. ")); } ac->addAction("project_save", d->action_save = KStandardAction::save(this, SLOT(slotProjectSave()), this)); d->action_save->setToolTip(xi18n("Save object changes")); d->action_save->setWhatsThis(xi18n("Saves object changes from currently selected window.")); setupMainMenuActionShortcut(d->action_save); d->action_save_as = addAction("project_saveas", koIcon("document-save-as"), xi18n("Save &As...")); d->action_save_as->setToolTip(xi18n("Save object as")); d->action_save_as->setWhatsThis( xi18n("Saves object from currently selected window under a new name (within the same project).")); connect(d->action_save_as, SIGNAL(triggered()), this, SLOT(slotProjectSaveAs())); #ifdef KEXI_SHOW_UNIMPLEMENTED ac->addAction("project_properties", action = d->action_project_properties = new KexiMenuWidgetAction( koIcon("document-properties"), futureI18n("Project Properties"), this)); connect(action, SIGNAL(triggered()), this, SLOT(slotProjectProperties())); setupMainMenuActionShortcut(action); #else d->action_project_properties = d->dummy_action; #endif //! @todo replace document-import icon with something other ac->addAction("project_import_export_send", action = d->action_project_import_export_send = new KexiMenuWidgetAction( koIcon("document-import"), xi18n("&Import, Export or Send..."), this)); action->setToolTip(xi18n("Import, export or send project")); action->setWhatsThis( xi18n("Imports, exports or sends project.")); connect(action, SIGNAL(triggered()), this, SLOT(slotProjectImportExportOrSend())); setupMainMenuActionShortcut(action); ac->addAction("project_close", action = d->action_close = new KexiMenuWidgetAction( koIcon("window-close"), xi18nc("Close Project", "&Close"), this)); action->setToolTip(xi18n("Close the current project")); action->setWhatsThis(xi18n("Closes the current project.")); connect(action, SIGNAL(triggered()), this, SLOT(slotProjectClose())); setupMainMenuActionShortcut(action); ac->addAction("quit", action = new KexiMenuWidgetAction(KStandardAction::Quit, this)); connect(action, SIGNAL(triggered()), this, SLOT(slotProjectQuit())); action->setWhatsThis(xi18n("Quits Kexi application.")); setupMainMenuActionShortcut(action); #ifdef KEXI_SHOW_UNIMPLEMENTED d->action_project_relations = addAction("project_relations", KexiIcon("database-relations"), futureI18n("&Relationships..."), "Ctrl+R"); d->action_project_relations->setToolTip(futureI18n("Project relationships")); d->action_project_relations->setWhatsThis(futureI18n("Shows project relationships.")); connect(d->action_project_relations, SIGNAL(triggered()), this, SLOT(slotProjectRelations())); #else d->action_project_relations = d->dummy_action; #endif d->action_tools_import_project = addAction("tools_import_project", KexiIcon("database-import"), xi18n("&Import Database...")); d->action_tools_import_project->setToolTip(xi18n("Import entire database as a Kexi project")); d->action_tools_import_project->setWhatsThis( xi18n("Imports entire database as a Kexi project.")); connect(d->action_tools_import_project, SIGNAL(triggered()), this, SLOT(slotToolsImportProject())); d->action_tools_data_import = addAction("tools_import_tables", koIcon("document-import"), xi18n("Import Tables...")); d->action_tools_data_import->setToolTip(xi18n("Import data from an external source into this project")); d->action_tools_data_import->setWhatsThis(xi18n("Imports data from an external source into this project.")); connect(d->action_tools_data_import, SIGNAL(triggered()), this, SLOT(slotToolsImportTables())); d->action_tools_compact_database = addAction("tools_compact_database", //! @todo icon koIcon("application-x-compress"), xi18n("&Compact Database...")); d->action_tools_compact_database->setToolTip(xi18n("Compact the current database project")); d->action_tools_compact_database->setWhatsThis( xi18n("Compacts the current database project, so it will take less space and work faster.")); connect(d->action_tools_compact_database, SIGNAL(triggered()), this, SLOT(slotToolsCompactDatabase())); if (d->userMode) d->action_project_import_data_table = 0; else { d->action_project_import_data_table = addAction("project_import_data_table", KexiIcon("document-empty"), /*! @todo: change to "file_import" with a table or so */ xi18nc("Import->Table Data From File...", "Import Data From &File...")); d->action_project_import_data_table->setToolTip(xi18n("Import table data from a file")); d->action_project_import_data_table->setWhatsThis(xi18n("Imports table data from a file.")); connect(d->action_project_import_data_table, SIGNAL(triggered()), this, SLOT(slotProjectImportDataTable())); } d->action_project_export_data_table = addAction("project_export_data_table", KexiIcon("table"), /*! @todo: change to "file_export" with a table or so */ xi18nc("Export->Table or Query Data to File...", "Export Data to &File...")); d->action_project_export_data_table->setToolTip( xi18n("Export data from the active table or query to a file")); d->action_project_export_data_table->setWhatsThis( xi18n("Exports data from the active table or query to a file.")); connect(d->action_project_export_data_table, SIGNAL(triggered()), this, SLOT(slotProjectExportDataTable())); //! @todo new QAction(xi18n("From File..."), "document-open", 0, //! this, SLOT(slotImportFile()), actionCollection(), "project_import_file"); //! @todo new QAction(xi18n("From Server..."), "network-server-database", 0, //! this, SLOT(slotImportServer()), actionCollection(), "project_import_server"); #ifdef KEXI_QUICK_PRINTING_SUPPORT ac->addAction("project_print", d->action_project_print = KStandardAction::print(this, SLOT(slotProjectPrint()), this)); d->action_project_print->setToolTip(futureI18n("Print data from the active table or query")); d->action_project_print->setWhatsThis(futureI18n("Prints data from the active table or query.")); ac->addAction("project_print_preview", d->action_project_print_preview = KStandardAction::printPreview( this, SLOT(slotProjectPrintPreview()), this)); d->action_project_print_preview->setToolTip( futureI18n("Show print preview for the active table or query")); d->action_project_print_preview->setWhatsThis( futureI18n("Shows print preview for the active table or query.")); d->action_project_print_setup = addAction("project_print_setup", koIcon("configure"), futureI18n("Print Set&up...")); //!< @todo document-page-setup could be a better icon d->action_project_print_setup->setToolTip( futureI18n("Show print setup for the active table or query")); d->action_project_print_setup->setWhatsThis( futureI18n("Shows print setup for the active table or query.")); connect(d->action_project_print_setup, SIGNAL(triggered()), this, SLOT(slotProjectPageSetup())); #endif //EDIT MENU d->action_edit_cut = createSharedAction(KStandardAction::Cut); d->action_edit_copy = createSharedAction(KStandardAction::Copy); d->action_edit_paste = createSharedAction(KStandardAction::Paste); if (d->userMode) d->action_edit_paste_special_data_table = 0; else { d->action_edit_paste_special_data_table = addAction( "edit_paste_special_data_table", d->action_edit_paste->icon(), xi18nc("Paste Special->As Data &Table...", "Paste Special...")); d->action_edit_paste_special_data_table->setToolTip( xi18n("Paste clipboard data as a table")); d->action_edit_paste_special_data_table->setWhatsThis( xi18n("Pastes clipboard data as a table.")); connect(d->action_edit_paste_special_data_table, SIGNAL(triggered()), this, SLOT(slotEditPasteSpecialDataTable())); } d->action_edit_copy_special_data_table = addAction( "edit_copy_special_data_table", KexiIcon("table"), xi18nc("Copy Special->Table or Query Data...", "Copy Special...")); d->action_edit_copy_special_data_table->setToolTip( xi18n("Copy selected table or query data to clipboard")); d->action_edit_copy_special_data_table->setWhatsThis( xi18n("Copies selected table or query data to clipboard.")); connect(d->action_edit_copy_special_data_table, SIGNAL(triggered()), this, SLOT(slotEditCopySpecialDataTable())); d->action_edit_undo = createSharedAction(KStandardAction::Undo); d->action_edit_undo->setWhatsThis(xi18n("Reverts the most recent editing action.")); d->action_edit_redo = createSharedAction(KStandardAction::Redo); d->action_edit_redo->setWhatsThis(xi18n("Reverts the most recent undo action.")); ac->addAction("edit_find", d->action_edit_find = KStandardAction::find( this, SLOT(slotEditFind()), this)); d->action_edit_find->setToolTip(xi18n("Find text")); d->action_edit_find->setWhatsThis(xi18n("Looks up the first occurrence of a piece of text.")); ac->addAction("edit_findnext", d->action_edit_findnext = KStandardAction::findNext( this, SLOT(slotEditFindNext()), this)); ac->addAction("edit_findprevious", d->action_edit_findprev = KStandardAction::findPrev( this, SLOT(slotEditFindPrevious()), this)); d->action_edit_replace = 0; //! @todo d->action_edit_replace = KStandardAction::replace( //! this, SLOT(slotEditReplace()), actionCollection(), "project_print_preview" ); d->action_edit_replace_all = 0; //! @todo d->action_edit_replace_all = new QAction( xi18n("Replace All"), "", 0, //! this, SLOT(slotEditReplaceAll()), actionCollection(), "edit_replaceall"); d->action_edit_select_all = createSharedAction(KStandardAction::SelectAll); d->action_edit_delete = createSharedAction(xi18n("&Delete"), koIconName("edit-delete"), QKeySequence(), "edit_delete"); d->action_edit_delete->setToolTip(xi18n("Delete selected object")); d->action_edit_delete->setWhatsThis(xi18n("Deletes currently selected object.")); d->action_edit_delete_row = createSharedAction(xi18n("Delete Record"), KexiIconName("edit-table-delete-row"), QKeySequence(Qt::CTRL + Qt::Key_Delete), "edit_delete_row"); d->action_edit_delete_row->setToolTip(xi18n("Delete the current record")); d->action_edit_delete_row->setWhatsThis(xi18n("Deletes the current record.")); d->action_edit_clear_table = createSharedAction(xi18n("Clear Table Contents..."), KexiIconName("edit-table-clear"), QKeySequence(), "edit_clear_table"); d->action_edit_clear_table->setToolTip(xi18n("Clear table contents")); d->action_edit_clear_table->setWhatsThis(xi18n("Clears table contents.")); setActionVolatile(d->action_edit_clear_table, true); d->action_edit_edititem = createSharedAction(xi18n("Edit Item"), QString(), QKeySequence(), /* CONFLICT in TV: Qt::Key_F2, */ "edit_edititem"); d->action_edit_edititem->setToolTip(xi18n("Edit currently selected item")); d->action_edit_edititem->setWhatsThis(xi18n("Edits currently selected item.")); d->action_edit_insert_empty_row = createSharedAction(xi18n("&Insert Empty Row"), KexiIconName("edit-table-insert-row"), QKeySequence(Qt::SHIFT | Qt::CTRL | Qt::Key_Insert), "edit_insert_empty_row"); setActionVolatile(d->action_edit_insert_empty_row, true); d->action_edit_insert_empty_row->setToolTip(xi18n("Insert one empty row above")); d->action_edit_insert_empty_row->setWhatsThis( xi18n("Inserts one empty row above currently selected table row.")); //VIEW MENU /* UNUSED, see KexiToggleViewModeAction if (!d->userMode) { d->action_view_mode = new QActionGroup(this); ac->addAction( "view_data_mode", d->action_view_data_mode = new KToggleAction( KexiIcon("data-view"), xi18n("&Data View"), d->action_view_mode) ); // d->action_view_data_mode->setObjectName("view_data_mode"); d->action_view_data_mode->setShortcut(QKeySequence("F6")); //d->action_view_data_mode->setExclusiveGroup("view_mode"); d->action_view_data_mode->setToolTip(xi18n("Switch to data view")); d->action_view_data_mode->setWhatsThis(xi18n("Switches to data view.")); d->actions_for_view_modes.insert( Kexi::DataViewMode, d->action_view_data_mode ); connect(d->action_view_data_mode, SIGNAL(triggered()), this, SLOT(slotViewDataMode())); } else { d->action_view_mode = 0; d->action_view_data_mode = 0; } if (!d->userMode) { ac->addAction( "view_design_mode", d->action_view_design_mode = new KToggleAction( KexiIcon("design-view"), xi18n("D&esign View"), d->action_view_mode) ); // d->action_view_design_mode->setObjectName("view_design_mode"); d->action_view_design_mode->setShortcut(QKeySequence("F7")); //d->action_view_design_mode->setExclusiveGroup("view_mode"); d->action_view_design_mode->setToolTip(xi18n("Switch to design view")); d->action_view_design_mode->setWhatsThis(xi18n("Switches to design view.")); d->actions_for_view_modes.insert( Kexi::DesignViewMode, d->action_view_design_mode ); connect(d->action_view_design_mode, SIGNAL(triggered()), this, SLOT(slotViewDesignMode())); } else d->action_view_design_mode = 0; if (!d->userMode) { ac->addAction( "view_text_mode", d->action_view_text_mode = new KToggleAction( KexiIcon("sql-view"), xi18n("&Text View"), d->action_view_mode) ); d->action_view_text_mode->setObjectName("view_text_mode"); d->action_view_text_mode->setShortcut(QKeySequence("F8")); //d->action_view_text_mode->setExclusiveGroup("view_mode"); d->action_view_text_mode->setToolTip(xi18n("Switch to text view")); d->action_view_text_mode->setWhatsThis(xi18n("Switches to text view.")); d->actions_for_view_modes.insert( Kexi::TextViewMode, d->action_view_text_mode ); connect(d->action_view_text_mode, SIGNAL(triggered()), this, SLOT(slotViewTextMode())); } else d->action_view_text_mode = 0; */ if (d->isProjectNavigatorVisible) { d->action_show_nav = addAction("view_navigator", xi18n("Show Project Navigator"), "Alt+0"); d->action_show_nav->setToolTip(xi18n("Show the Project Navigator pane")); d->action_show_nav->setWhatsThis(xi18n("Shows the Project Navigator pane.")); connect(d->action_show_nav, SIGNAL(triggered()), this, SLOT(slotShowNavigator())); } else { d->action_show_nav = 0; } if (d->isProjectNavigatorVisible) { // Shortcut taken from "Activate Projects pane" http://doc.qt.io/qtcreator/creator-keyboard-shortcuts.html d->action_activate_nav = addAction("activate_navigator", xi18n("Activate Project Navigator"), "Alt+X"); d->action_activate_nav->setToolTip(xi18n("Activate the Project Navigator pane")); d->action_activate_nav->setWhatsThis(xi18n("Activates the Project Navigator pane. If it is hidden, shows it first.")); connect(d->action_activate_nav, SIGNAL(triggered()), this, SLOT(slotActivateNavigator())); } else { d->action_activate_nav = 0; } d->action_activate_mainarea = addAction("activate_mainarea", xi18n("Activate main area") // , "Alt+2"? //! @todo activate_mainarea: pressing Esc in project nav or propeditor should move back to the main area ); d->action_activate_mainarea->setToolTip(xi18n("Activate the main area")); d->action_activate_mainarea->setWhatsThis(xi18n("Activates the main area.")); connect(d->action_activate_mainarea, SIGNAL(triggered()), this, SLOT(slotActivateMainArea())); //! @todo windows with "_3" prefix have conflicting auto shortcut set to Alt+3 -> remove that! if (!d->userMode) { d->action_show_propeditor = addAction("view_propeditor", xi18n("Show Property Editor"), "Alt+3"); d->action_show_propeditor->setToolTip(xi18n("Show the Property Editor pane")); d->action_show_propeditor->setWhatsThis(xi18n("Shows the Property Editor pane.")); connect(d->action_show_propeditor, SIGNAL(triggered()), this, SLOT(slotShowPropertyEditor())); } else { d->action_show_propeditor = 0; } if (!d->userMode) { d->action_activate_propeditor = addAction("activate_propeditor", xi18n("Activate Property Editor"), "Alt+-"); d->action_activate_propeditor->setToolTip(xi18n("Activate the Property Editor pane")); d->action_activate_propeditor->setWhatsThis(xi18n("Activates the Property Editor pane. If it is hidden, shows it first.")); connect(d->action_activate_propeditor, SIGNAL(triggered()), this, SLOT(slotActivatePropertyEditor())); } else { d->action_activate_propeditor = 0; } d->action_view_global_search = addAction("view_global_search", xi18n("Switch to Global Search"), "Ctrl+K"); d->action_view_global_search->setToolTip(xi18n("Switch to Global Search box")); d->action_view_global_search->setWhatsThis(xi18n("Switches to Global Search box.")); // (connection is added elsewhere) //DATA MENU d->action_data_save_row = createSharedAction(xi18n("&Save Record"), koIconName("dialog-ok"), QKeySequence(Qt::SHIFT + Qt::Key_Return), "data_save_row"); d->action_data_save_row->setToolTip(xi18n("Save changes made to the current record")); d->action_data_save_row->setWhatsThis(xi18n("Saves changes made to the current record.")); //temp. disable because of problems with volatile actions setActionVolatile( d->action_data_save_row, true ); d->action_data_cancel_row_changes = createSharedAction(xi18n("&Cancel Record Changes"), koIconName("dialog-cancel"), QKeySequence(Qt::Key_Escape), "data_cancel_row_changes"); d->action_data_cancel_row_changes->setToolTip( xi18n("Cancel changes made to the current record")); d->action_data_cancel_row_changes->setWhatsThis( xi18n("Cancels changes made to the current record.")); //temp. disable because of problems with volatile actions setActionVolatile( d->action_data_cancel_row_changes, true ); d->action_data_execute = createSharedAction( xi18n("&Execute"), koIconName("media-playback-start"), QKeySequence(), "data_execute"); //! @todo d->action_data_execute->setToolTip(xi18n("")); //! @todo d->action_data_execute->setWhatsThis(xi18n("")); #ifdef KEXI_SHOW_UNIMPLEMENTED action = createSharedAction(futureI18n("&Filter"), koIconName("view-filter"), QKeySequence(), "data_filter"); setActionVolatile(action, true); #endif //! @todo action->setToolTip(xi18n("")); //! @todo action->setWhatsThis(xi18n("")); // - record-navigation related actions createSharedAction(KexiRecordNavigator::Actions::moveToFirstRecord(), QKeySequence(), "data_go_to_first_record"); createSharedAction(KexiRecordNavigator::Actions::moveToPreviousRecord(), QKeySequence(), "data_go_to_previous_record"); createSharedAction(KexiRecordNavigator::Actions::moveToNextRecord(), QKeySequence(), "data_go_to_next_record"); createSharedAction(KexiRecordNavigator::Actions::moveToLastRecord(), QKeySequence(), "data_go_to_last_record"); createSharedAction(KexiRecordNavigator::Actions::moveToNewRecord(), QKeySequence(), "data_go_to_new_record"); //FORMAT MENU d->action_format_font = createSharedAction(xi18n("&Font..."), koIconName("fonts-package"), QKeySequence(), "format_font"); d->action_format_font->setToolTip(xi18n("Change font for selected object")); d->action_format_font->setWhatsThis(xi18n("Changes font for selected object.")); //TOOLS MENU //WINDOW MENU //additional 'Window' menu items d->action_window_next = addAction("window_next", xi18n("&Next Window"), #ifdef Q_OS_WIN "Ctrl+Tab" #else "Alt+Right" #endif ); d->action_window_next->setToolTip(xi18n("Next window")); d->action_window_next->setWhatsThis(xi18n("Switches to the next window.")); connect(d->action_window_next, SIGNAL(triggered()), this, SLOT(activateNextWindow())); d->action_window_previous = addAction("window_previous", xi18n("&Previous Window"), #ifdef Q_OS_WIN "Ctrl+Shift+Tab" #else "Alt+Left" #endif ); d->action_window_previous->setToolTip(xi18n("Previous window")); d->action_window_previous->setWhatsThis(xi18n("Switches to the previous window.")); connect(d->action_window_previous, SIGNAL(triggered()), this, SLOT(activatePreviousWindow())); d->action_window_fullscreen = KStandardAction::fullScreen(this, SLOT(toggleFullScreen(bool)), this, ac); ac->addAction("full_screen", d->action_window_fullscreen); QList shortcuts; shortcuts << d->action_window_fullscreen->shortcut() << QKeySequence("F11"); d->action_window_fullscreen->setShortcuts(shortcuts); QShortcut *s = new QShortcut(d->action_window_fullscreen->shortcut(), this); connect(s, SIGNAL(activated()), d->action_window_fullscreen, SLOT(trigger())); if (d->action_window_fullscreen->shortcuts().count() > 1) { QShortcut *sa = new QShortcut(d->action_window_fullscreen->shortcuts().value(1), this); connect(sa, SIGNAL(activated()), d->action_window_fullscreen, SLOT(trigger())); } //SETTINGS MENU //! @todo put 'configure keys' into settings view #ifdef KEXI_SHOW_UNIMPLEMENTED //! @todo toolbars configuration will be handled in a special way #endif #ifdef KEXI_MACROS_SUPPORT Kexi::tempShowMacros() = true; #else Kexi::tempShowMacros() = false; #endif #ifdef KEXI_SCRIPTS_SUPPORT Kexi::tempShowScripts() = true; #else Kexi::tempShowScripts() = false; #endif #ifdef KEXI_SHOW_UNIMPLEMENTED //! @todo implement settings window in a specific way ac->addAction("settings", action = d->action_settings = new KexiMenuWidgetAction( KStandardAction::Preferences, this)); action->setObjectName("settings"); action->setText(futureI18n("Settings...")); action->setToolTip(futureI18n("Show Kexi settings")); action->setWhatsThis(futureI18n("Shows Kexi settings.")); connect(action, SIGNAL(triggered()), this, SLOT(slotSettings())); setupMainMenuActionShortcut(action); #else d->action_settings = d->dummy_action; #endif //! @todo reenable 'tip of the day' later #if 0 KStandardAction::tipOfDay(this, SLOT(slotTipOfTheDayAction()), actionCollection()) ->setWhatsThis(xi18n("This shows useful tips on the use of this application.")); #endif // GLOBAL d->action_show_help_menu = addAction("help_show_menu", xi18nc("Help Menu", "Help"), "Alt+H"); d->action_show_help_menu->setToolTip(xi18n("Show Help menu")); d->action_show_help_menu->setWhatsThis(xi18n("Shows Help menu.")); // (connection is added elsewhere) // ----- declare action categories, so form's "assign action to button" // (and macros in the future) will be able to recognize category // of actions and filter them ----------------------------------- //! @todo shouldn't we move this to core? Kexi::ActionCategories *acat = Kexi::actionCategories(); acat->addAction("data_execute", Kexi::PartItemActionCategory); //! @todo unused for now acat->addWindowAction("data_filter", KexiPart::TableObjectType, KexiPart::QueryObjectType, KexiPart::FormObjectType); acat->addWindowAction("data_save_row", KexiPart::TableObjectType, KexiPart::QueryObjectType, KexiPart::FormObjectType); acat->addWindowAction("data_cancel_row_changes", KexiPart::TableObjectType, KexiPart::QueryObjectType, KexiPart::FormObjectType); acat->addWindowAction("delete_table_row", KexiPart::TableObjectType, KexiPart::QueryObjectType, KexiPart::FormObjectType); //! @todo support this in KexiPart::FormObjectType as well acat->addWindowAction("data_sort_az", KexiPart::TableObjectType, KexiPart::QueryObjectType); //! @todo support this in KexiPart::FormObjectType as well acat->addWindowAction("data_sort_za", KexiPart::TableObjectType, KexiPart::QueryObjectType); //! @todo support this in KexiPart::FormObjectType as well acat->addWindowAction("edit_clear_table", KexiPart::TableObjectType, KexiPart::QueryObjectType); //! @todo support this in KexiPart::FormObjectType as well acat->addWindowAction("edit_copy_special_data_table", KexiPart::TableObjectType, KexiPart::QueryObjectType); //! @todo support this in FormObjectType as well acat->addWindowAction("project_export_data_table", KexiPart::TableObjectType, KexiPart::QueryObjectType); // GlobalActions, etc. acat->addAction("edit_copy", Kexi::GlobalActionCategory | Kexi::PartItemActionCategory); acat->addAction("edit_cut", Kexi::GlobalActionCategory | Kexi::PartItemActionCategory); acat->addAction("edit_paste", Kexi::GlobalActionCategory | Kexi::PartItemActionCategory); acat->addAction("edit_delete", Kexi::GlobalActionCategory | Kexi::PartItemActionCategory | Kexi::WindowActionCategory, KexiPart::TableObjectType, KexiPart::QueryObjectType, KexiPart::FormObjectType); acat->addAction("edit_delete_row", Kexi::GlobalActionCategory | Kexi::WindowActionCategory, KexiPart::TableObjectType, KexiPart::QueryObjectType, KexiPart::FormObjectType); acat->addAction("edit_edititem", Kexi::PartItemActionCategory | Kexi::WindowActionCategory, KexiPart::TableObjectType, KexiPart::QueryObjectType); acat->addAction("edit_find", Kexi::GlobalActionCategory | Kexi::WindowActionCategory, KexiPart::TableObjectType, KexiPart::QueryObjectType, KexiPart::FormObjectType); acat->addAction("edit_findnext", Kexi::GlobalActionCategory | Kexi::WindowActionCategory, KexiPart::TableObjectType, KexiPart::QueryObjectType, KexiPart::FormObjectType); acat->addAction("edit_findprevious", Kexi::GlobalActionCategory | Kexi::WindowActionCategory, KexiPart::TableObjectType, KexiPart::QueryObjectType, KexiPart::FormObjectType); acat->addAction("edit_replace", Kexi::GlobalActionCategory | Kexi::WindowActionCategory, KexiPart::TableObjectType, KexiPart::QueryObjectType, KexiPart::FormObjectType); acat->addAction("edit_paste_special_data_table", Kexi::GlobalActionCategory); acat->addAction("help_about_app", Kexi::GlobalActionCategory); acat->addAction("help_about_kde", Kexi::GlobalActionCategory); acat->addAction("help_contents", Kexi::GlobalActionCategory); acat->addAction("help_report_bug", Kexi::GlobalActionCategory); acat->addAction("help_whats_this", Kexi::GlobalActionCategory); acat->addAction("help_donate", Kexi::GlobalActionCategory); // disabled for now acat->addAction("switch_application_language", Kexi::GlobalActionCategory); acat->addAction("options_configure_keybinding", Kexi::GlobalActionCategory); acat->addAction("project_close", Kexi::GlobalActionCategory); acat->addAction("project_import_data_table", Kexi::GlobalActionCategory); acat->addAction("project_new", Kexi::GlobalActionCategory); acat->addAction("project_open", Kexi::GlobalActionCategory); #ifdef KEXI_QUICK_PRINTING_SUPPORT //! @todo support this in FormObjectType, ReportObjectType as well as others acat->addAction("project_print", Kexi::WindowActionCategory, KexiPart::TableObjectType, KexiPart::QueryObjectType); //! @todo support this in FormObjectType, ReportObjectType as well as others acat->addAction("project_print_preview", Kexi::WindowActionCategory, KexiPart::TableObjectType, KexiPart::QueryObjectType); //! @todo support this in FormObjectType, ReportObjectType as well as others acat->addAction("project_print_setup", Kexi::WindowActionCategory, KexiPart::TableObjectType, KexiPart::QueryObjectType); #endif acat->addAction("quit", Kexi::GlobalActionCategory); acat->addAction("tools_compact_database", Kexi::GlobalActionCategory); acat->addAction("tools_import_project", Kexi::GlobalActionCategory); acat->addAction("tools_import_tables", Kexi::GlobalActionCategory); acat->addAction("view_data_mode", Kexi::GlobalActionCategory); acat->addAction("view_design_mode", Kexi::GlobalActionCategory); acat->addAction("view_text_mode", Kexi::GlobalActionCategory); acat->addAction("view_mainarea", Kexi::GlobalActionCategory); acat->addAction("view_navigator", Kexi::GlobalActionCategory); acat->addAction("activate_navigator", Kexi::GlobalActionCategory); acat->addAction("view_propeditor", Kexi::GlobalActionCategory); acat->addAction("activate_mainarea", Kexi::GlobalActionCategory); acat->addAction("activate_propeditor", Kexi::GlobalActionCategory); acat->addAction("window_close", Kexi::GlobalActionCategory | Kexi::WindowActionCategory); acat->setAllObjectTypesSupported("window_close", true); acat->addAction("window_next", Kexi::GlobalActionCategory); acat->addAction("window_previous", Kexi::GlobalActionCategory); acat->addAction("full_screen", Kexi::GlobalActionCategory); //skipped - design view only acat->addAction("format_font", Kexi::NoActionCategory); acat->addAction("project_save", Kexi::NoActionCategory); acat->addAction("edit_insert_empty_row", Kexi::NoActionCategory); //! @todo support this in KexiPart::TableObjectType, KexiPart::QueryObjectType later acat->addAction("edit_select_all", Kexi::NoActionCategory); //! @todo support this in KexiPart::TableObjectType, KexiPart::QueryObjectType, KexiPart::FormObjectType later acat->addAction("edit_redo", Kexi::NoActionCategory); //! @todo support this in KexiPart::TableObjectType, KexiPart::QueryObjectType, KexiPart::FormObjectType later acat->addAction("edit_undo", Kexi::NoActionCategory); //record-navigation related actions acat->addAction("data_go_to_first_record", Kexi::WindowActionCategory, KexiPart::TableObjectType, KexiPart::QueryObjectType, KexiPart::FormObjectType); acat->addAction("data_go_to_previous_record", Kexi::WindowActionCategory, KexiPart::TableObjectType, KexiPart::QueryObjectType, KexiPart::FormObjectType); acat->addAction("data_go_to_next_record", Kexi::WindowActionCategory, KexiPart::TableObjectType, KexiPart::QueryObjectType, KexiPart::FormObjectType); acat->addAction("data_go_to_last_record", Kexi::WindowActionCategory, KexiPart::TableObjectType, KexiPart::QueryObjectType, KexiPart::FormObjectType); acat->addAction("data_go_to_new_record", Kexi::WindowActionCategory, KexiPart::TableObjectType, KexiPart::QueryObjectType, KexiPart::FormObjectType); //skipped - internal: acat->addAction("tablepart_create", Kexi::NoActionCategory); acat->addAction("querypart_create", Kexi::NoActionCategory); acat->addAction("formpart_create", Kexi::NoActionCategory); acat->addAction("reportpart_create", Kexi::NoActionCategory); acat->addAction("macropart_create", Kexi::NoActionCategory); acat->addAction("scriptpart_create", Kexi::NoActionCategory); } void KexiMainWindow::invalidateActions() { invalidateProjectWideActions(); invalidateSharedActions(); } void KexiMainWindow::invalidateSharedActions(QObject *o) { //! @todo enabling is more complex... /* d->action_edit_cut->setEnabled(true); d->action_edit_copy->setEnabled(true); d->action_edit_paste->setEnabled(true);*/ if (!o) o = focusWindow(); KexiSharedActionHost::invalidateSharedActions(o); } void KexiMainWindow::invalidateSharedActions() { invalidateSharedActions(0); } // unused, I think void KexiMainWindow::invalidateSharedActionsLater() { QTimer::singleShot(1, this, SLOT(invalidateSharedActions())); } void KexiMainWindow::invalidateProjectWideActions() { const bool has_window = currentWindow(); const bool window_dirty = currentWindow() && currentWindow()->isDirty(); const bool readOnly = d->prj && d->prj->dbConnection() && d->prj->dbConnection()->options()->isReadOnly(); //PROJECT MENU d->action_save->setEnabled(has_window && window_dirty && !readOnly); d->action_save_as->setEnabled(has_window && !readOnly); d->action_project_properties->setEnabled(d->prj); d->action_close->setEnabled(d->prj); d->action_project_relations->setEnabled(d->prj); //DATA MENU if (d->action_project_import_data_table) d->action_project_import_data_table->setEnabled(d->prj && !readOnly); if (d->action_tools_data_import) d->action_tools_data_import->setEnabled(d->prj && !readOnly); d->action_project_export_data_table->setEnabled( currentWindow() && currentWindow()->part()->info()->isDataExportSupported()); if (d->action_edit_paste_special_data_table) d->action_edit_paste_special_data_table->setEnabled(d->prj && !readOnly); #ifdef KEXI_QUICK_PRINTING_SUPPORT const bool printingActionsEnabled = currentWindow() && currentWindow()->part()->info()->isPrintingSupported() && !currentWindow()->neverSaved(); d->action_project_print->setEnabled(printingActionsEnabled); d->action_project_print_preview->setEnabled(printingActionsEnabled); d->action_project_print_setup->setEnabled(printingActionsEnabled); #endif //EDIT MENU //! @todo "copy special" is currently enabled only for data view mode; //! what about allowing it to enable in design view for "kexi/table" ? if (currentWindow() && currentWindow()->currentViewMode() == Kexi::DataViewMode) { KexiPart::Info *activePartInfo = currentWindow()->part()->info(); d->action_edit_copy_special_data_table->setEnabled( activePartInfo ? activePartInfo->isDataExportSupported() : false); } else { d->action_edit_copy_special_data_table->setEnabled(false); } d->action_edit_find->setEnabled(d->prj); //VIEW MENU if (d->action_show_nav) d->action_show_nav->setEnabled(d->prj); d->action_activate_mainarea->setEnabled(d->prj); if (d->action_show_propeditor) d->action_show_propeditor->setEnabled(d->prj); #ifdef KEXI_SHOW_CONTEXT_HELP d->action_show_helper->setEnabled(d->prj); #endif //CREATE MENU if (d->tabbedToolBar && d->tabbedToolBar->createWidgetToolBar()) d->tabbedToolBar->createWidgetToolBar()->setEnabled(d->prj); // DATA MENU //TOOLS MENU // "compact db" supported if there's no db or the current db supports compacting and is opened r/w: d->action_tools_compact_database->setEnabled( !d->prj || (!readOnly && d->prj && d->prj->dbConnection() && (d->prj->dbConnection()->driver()->features() & KDbDriver::CompactingDatabaseSupported)) ); //DOCKS if (d->navigator) { d->navigator->setEnabled(d->prj); } if (d->propEditor) d->propEditorTabWidget->setEnabled(d->prj); } tristate KexiMainWindow::startup() { tristate result = true; switch (Kexi::startupHandler().action()) { case KexiStartupHandler::CreateBlankProject: d->updatePropEditorVisibility(Kexi::NoViewMode); break; #ifdef KEXI_PROJECT_TEMPLATES case KexiStartupHandler::CreateFromTemplate: result = createProjectFromTemplate(*Kexi::startupHandler().projectData()); break; #endif case KexiStartupHandler::OpenProject: result = openProject(*Kexi::startupHandler().projectData()); break; case KexiStartupHandler::ImportProject: result = showProjectMigrationWizard( Kexi::startupHandler().importActionData().mimeType, Kexi::startupHandler().importActionData().fileName ); break; case KexiStartupHandler::ShowWelcomeScreen: //! @todo show welcome screen as soon as is available QTimer::singleShot(100, this, SLOT(slotProjectWelcome())); break; default: d->updatePropEditorVisibility(Kexi::NoViewMode); } return result; } static QString internalReason(const KDbResult &result) { const QString msg = result.message(); if (msg.isEmpty()) { return QString(); } return xi18n("
(reason: %1)", msg); } tristate KexiMainWindow::openProject(const KexiProjectData& projectData) { //qDebug() << projectData; QScopedPointer prj(createKexiProjectObject(projectData)); if (~KexiDBPasswordDialog::getPasswordIfNeeded(prj->data()->connectionData(), this)) { return cancelled; } bool incompatibleWithKexi; tristate res = prj->open(&incompatibleWithKexi); if (prj->data()->connectionData()->isPasswordNeeded()) { // password was supplied in this session, and shouldn't be stored or reused afterwards, // so let's remove it prj->data()->connectionData()->setPassword(QString()); } if (~res) { return cancelled; } else if (!res) { if (incompatibleWithKexi) { if (KMessageBox::Yes == KMessageBox::questionYesNo(this, xi18nc("@info (don't add tags around %1, it's done already)", "Database project %1 does not appear to have been created using Kexi." "Do you want to import it as a new Kexi project?", projectData.infoString()), QString(), KGuiItem(xi18nc("@action:button Import Database", "&Import..."), KexiIconName("database-import")), KStandardGuiItem::cancel())) { const bool anotherProjectAlreadyOpened = prj; tristate res = showProjectMigrationWizard("application/x-kexi-connectiondata", projectData.databaseName(), *projectData.connectionData()); if (!anotherProjectAlreadyOpened) //the project could have been opened within this instance return res; //always return cancelled because even if migration succeeded, new Kexi instance //will be started if user wanted to open the imported db return cancelled; } return cancelled; } return false; } // success d->prj = prj.take(); setupProjectNavigator(); d->prj->data()->setLastOpened(QDateTime::currentDateTime()); Kexi::recentProjects()->addProjectData(*d->prj->data()); updateReadOnlyState(); invalidateActions(); setMessagesEnabled(false); QTimer::singleShot(1, this, SLOT(slotAutoOpenObjectsLater())); if (d->tabbedToolBar) { d->tabbedToolBar->showTab("create");// not needed since create toolbar already shows toolbar! move when kexi starts d->tabbedToolBar->showTab("data"); d->tabbedToolBar->showTab("external"); d->tabbedToolBar->showTab("tools"); d->tabbedToolBar->hideTab("form");//temporalily until createToolbar is split d->tabbedToolBar->hideTab("report");//temporalily until createToolbar is split // make sure any tab is activated d->tabbedToolBar->setCurrentTab(0); } return true; } tristate KexiMainWindow::openProject(const KexiProjectData& data, const QString& shortcutPath, bool *opened) { if (!shortcutPath.isEmpty() && d->prj) { const tristate result = openProjectInExternalKexiInstance( shortcutPath, QString(), QString()); if (result == true) { *opened = true; } return result; } return openProject(data); } tristate KexiMainWindow::createProjectFromTemplate(const KexiProjectData& projectData) { Q_UNUSED(projectData); #ifdef KEXI_PROJECT_TEMPLATES QStringList mimetypes; mimetypes.append(KDb::defaultFileBasedDriverMimeType()); QString fname; //! @todo KEXI3 add equivalent of kfiledialog:/// const QString startDir("kfiledialog:///OpenExistingOrCreateNewProject"/*as in KexiNewProjectWizard*/); const QString caption(xi18nc("@window:title", "Select New Project's Location")); while (true) { if (fname.isEmpty() && !projectData.connectionData()->databaseName().isEmpty()) { //propose filename from db template name fname = projectData.connectionData()->databaseName(); } const bool specialDir = fname.isEmpty(); qDebug() << fname << "............."; QFileDialog dlg(specialDir ? QUrl(startDir) : QUrl(), QString(), this); dlg.setModal(true); dlg.setMimeFilter(mimetypes); if (!specialDir) dlg.selectUrl(QUrl::fromLocalFile(fname); // may also be a filename dlg.setFileMode(QFileDialog::ExistingFile); dlg.setFileMode(QFileDialog::AcceptOpen); dlg.setWindowTitle(caption); if (QDialog::Accepted != dlg.exec()) { return cancelled; } if (dlg.selectedFiles().isEmpty() { return cancelled; } fname = dlg.selectedFiles().first(); if (fname.isEmpty()) { return cancelled; } - if (KexiFileWidget::askForOverwriting(fname, this)) { + if (KexiUtils::askForFileOverwriting(fname, this)) { break; } } QFile sourceFile(projectData.connectionData()->fileName()); if (!sourceFile.copy(fname)) { //! @todo show error from with QFile::FileError return false; } return openProject(fname, 0, QString(), projectData.autoopenObjects/*copy*/); #else return false; #endif } void KexiMainWindow::updateReadOnlyState() { const bool readOnly = d->prj && d->prj->dbConnection() && d->prj->dbConnection()->options()->isReadOnly(); //! @todo KEXI3 show read-only flag in the GUI because we have no statusbar if (d->navigator) { d->navigator->setReadOnly(readOnly); } // update "insert ....." actions for every part KexiPart::PartInfoList *plist = Kexi::partManager().infoList(); if (plist) { foreach(KexiPart::Info *info, *plist) { QAction *a = info->newObjectAction(); if (a) a->setEnabled(!readOnly); } } } void KexiMainWindow::slotAutoOpenObjectsLater() { QString not_found_msg; bool openingCancelled; //ok, now open "autoopen: objects if (d->prj) { foreach(KexiProjectData::ObjectInfo* info, d->prj->data()->autoopenObjects) { KexiPart::Info *i = Kexi::partManager().infoForPluginId(info->value("type")); if (!i) { not_found_msg += "
  • "; if (!info->value("name").isEmpty()) not_found_msg += (QString("\"") + info->value("name") + "\" - "); if (info->value("action") == "new") not_found_msg += xi18n("cannot create object - unknown object type \"%1\"", info->value("type")); else not_found_msg += xi18n("unknown object type \"%1\"", info->value("type")); not_found_msg += internalReason(Kexi::partManager().result()) + "
  • "; continue; } // * NEW if (info->value("action") == "new") { if (!newObject(i, &openingCancelled) && !openingCancelled) { not_found_msg += "
  • "; not_found_msg += (xi18n("cannot create object of type \"%1\"", info->value("type")) + internalReason(d->prj->result()) + "
  • "); } else d->wasAutoOpen = true; continue; } KexiPart::Item *item = d->prj->item(i, info->value("name")); if (!item) { QString taskName; if (info->value("action") == "execute") taskName = xi18nc("\"executing object\" action", "executing"); #ifdef KEXI_QUICK_PRINTING_SUPPORT else if (info->value("action") == "print-preview") taskName = futureI18n("making print preview for"); else if (info->value("action") == "print") taskName = futureI18n("printing"); #endif else taskName = xi18n("opening"); not_found_msg += (QString("
  • ") + taskName + " \"" + info->value("name") + "\" - "); if ("table" == info->value("type").toLower()) not_found_msg += xi18n("table not found"); else if ("query" == info->value("type").toLower()) not_found_msg += xi18n("query not found"); else if ("macro" == info->value("type").toLower()) not_found_msg += xi18n("macro not found"); else if ("script" == info->value("type").toLower()) not_found_msg += xi18n("script not found"); else not_found_msg += xi18n("object not found"); not_found_msg += (internalReason(d->prj->result()) + "
  • "); continue; } // * EXECUTE, PRINT, PRINT PREVIEW if (info->value("action") == "execute") { tristate res = executeItem(item); if (false == res) { not_found_msg += (QString("
  • \"") + info->value("name") + "\" - " + xi18n("cannot execute object") + internalReason(d->prj->result()) + "
  • "); } continue; } #ifdef KEXI_QUICK_PRINTING_SUPPORT else if (info->value("action") == "print") { tristate res = printItem(item); if (false == res) { not_found_msg += (QString("
  • \"") + info->value("name") + "\" - " + futureI18n("cannot print object") + internalReason(d->prj->result()) + "
  • "); } continue; } else if (info->value("action") == "print-preview") { tristate res = printPreviewForItem(item); if (false == res) { not_found_msg += (QString("
  • \"") + info->value("name") + "\" - " + futureI18n("cannot make print preview of object") + internalReason(d->prj->result()) + "
  • "); } continue; } #endif Kexi::ViewMode viewMode; if (info->value("action") == "open") viewMode = Kexi::DataViewMode; else if (info->value("action") == "design") viewMode = Kexi::DesignViewMode; else if (info->value("action") == "edittext") viewMode = Kexi::TextViewMode; else continue; //sanity QString openObjectMessage; if (!openObject(item, viewMode, &openingCancelled, 0, &openObjectMessage) && (!openingCancelled || !openObjectMessage.isEmpty())) { not_found_msg += (QString("
  • \"") + info->value("name") + "\" - "); if (openObjectMessage.isEmpty()) not_found_msg += xi18n("cannot open object"); else not_found_msg += openObjectMessage; not_found_msg += internalReason(d->prj->result()) + "
  • "; continue; } else { d->wasAutoOpen = true; } } } setMessagesEnabled(true); if (!not_found_msg.isEmpty()) showErrorMessage(xi18n("You have requested selected objects to be automatically opened " "or processed on startup. Several objects cannot be opened or processed."), QString("
      %1
    ").arg(not_found_msg)); d->updatePropEditorVisibility(currentWindow() ? currentWindow()->currentViewMode() : Kexi::NoViewMode); #if defined(KDOCKWIDGET_P) if (d->propEditor) { KDockWidget *dw = (KDockWidget *)d->propEditorTabWidget->parentWidget(); KDockSplitter *ds = (KDockSplitter *)dw->parentWidget(); if (ds) ds->setSeparatorPosInPercent(d->config->readEntry("RightDockPosition", 80/* % */)); } #endif updateAppCaption(); if (d->tabbedToolBar) { d->tabbedToolBar->hideMainMenu(); } qApp->processEvents(); emit projectOpened(); } tristate KexiMainWindow::closeProject() { if (d->tabbedToolBar) d->tabbedToolBar->hideMainMenu(); #ifndef KEXI_NO_PENDING_DIALOGS if (d->pendingWindowsExist()) { qDebug() << "pendingWindowsExist..."; d->actionToExecuteWhenPendingJobsAreFinished = Private::CloseProjectAction; return cancelled; } #endif //only save nav. visibility setting if there is project opened d->saveSettingsForShowProjectNavigator = d->prj && d->isProjectNavigatorVisible; if (!d->prj) return true; { // make sure the project can be closed bool cancel = false; emit acceptProjectClosingRequested(&cancel); if (cancel) return cancelled; } d->windowExistedBeforeCloseProject = currentWindow(); #if defined(KDOCKWIDGET_P) //remember docks position - will be used on storeSettings() if (d->propEditor) { KDockWidget *dw = (KDockWidget *)d->propEditorTabWidget->parentWidget(); KDockSplitter *ds = (KDockSplitter *)dw->parentWidget(); if (ds) d->propEditorDockSeparatorPos = ds->separatorPosInPercent(); } if (d->nav) { if (d->propEditor) { //! @todo KEXI3 if (d->openedWindowsCount() == 0) //! @todo KEXI3 makeWidgetDockVisible(d->propEditorTabWidget); KDockWidget *dw = (KDockWidget *)d->propEditorTabWidget->parentWidget(); KDockSplitter *ds = (KDockSplitter *)dw->parentWidget(); if (ds) ds->setSeparatorPosInPercent(80); } KDockWidget *dw = (KDockWidget *)d->nav->parentWidget(); KDockSplitter *ds = (KDockSplitter *)dw->parentWidget(); int dwWidth = dw->width(); if (ds) { if (d->openedWindowsCount() != 0 && d->propEditorTabWidget && d->propEditorTabWidget->isVisible()) { d->navDockSeparatorPos = ds->separatorPosInPercent(); } else { d->navDockSeparatorPos = (100 * dwWidth) / width(); } } } #endif //close each window, optionally asking if user wants to close (if data changed) while (currentWindow()) { tristate res = closeWindow(currentWindow()); if (!res || ~res) return res; } // now we will close for sure emit beforeProjectClosing(); if (!d->prj->closeConnection()) return false; if (d->navigator) { d->navWasVisibleBeforeProjectClosing = d->navDockWidget->isVisible(); d->navDockWidget->hide(); d->navigator->setProject(0); slotProjectNavigatorVisibilityChanged(true); // hide side tab } if (d->propEditorDockWidget) d->propEditorDockWidget->hide(); d->clearWindows(); //sanity! delete d->prj; d->prj = 0; updateReadOnlyState(); invalidateActions(); updateAppCaption(); emit projectClosed(); return true; } void KexiMainWindow::setupContextHelp() { #ifdef KEXI_SHOW_CONTEXT_HELP d->ctxHelp = new KexiContextHelp(d->mainWidget, this); //! @todo /* d->ctxHelp->setContextHelp(xi18n("Welcome"),xi18n("The KEXI team wishes you a lot of productive work, " "with this product.


    If you have found a bug or have a feature request, please don't " "hesitate to report it at our issue " "tracking system .


    If you would like to join our effort, the development documentation " "at www.kexi-project.org is a good starting point."),0); */ addToolWindow(d->ctxHelp, KDockWidget::DockBottom | KDockWidget::DockLeft, getMainDockWidget(), 20); #endif } void KexiMainWindow::setupMainWidget() { QVBoxLayout *vlyr = new QVBoxLayout(this); vlyr->setContentsMargins(0, 0, 0, 0); vlyr->setSpacing(0); if (d->isMainMenuVisible) { QWidget *tabbedToolBarContainer = new QWidget(this); vlyr->addWidget(tabbedToolBarContainer); QVBoxLayout *tabbedToolBarContainerLyr = new QVBoxLayout(tabbedToolBarContainer); tabbedToolBarContainerLyr->setSpacing(0); tabbedToolBarContainerLyr->setContentsMargins( KexiUtils::marginHint() / 2, KexiUtils::marginHint() / 2, KexiUtils::marginHint() / 2, KexiUtils::marginHint() / 2); d->tabbedToolBar = new KexiTabbedToolBar(tabbedToolBarContainer); Q_ASSERT(d->action_view_global_search); connect(d->action_view_global_search, SIGNAL(triggered()), d->tabbedToolBar, SLOT(activateSearchLineEdit())); tabbedToolBarContainerLyr->addWidget(d->tabbedToolBar); d->tabbedToolBar->hideTab("form"); //temporarily until createToolbar is split d->tabbedToolBar->hideTab("report"); //temporarily until createToolbar is split } else { d->tabbedToolBar = 0; } QWidget *mainWidgetContainer = new QWidget(); vlyr->addWidget(mainWidgetContainer, 1); QHBoxLayout *mainWidgetContainerLyr = new QHBoxLayout(mainWidgetContainer); mainWidgetContainerLyr->setContentsMargins(0, 0, 0, 0); mainWidgetContainerLyr->setSpacing(0); KMultiTabBar *mtbar = new KMultiTabBar(KMultiTabBar::Left); mtbar->setStyle(KMultiTabBar::VSNET); mainWidgetContainerLyr->addWidget(mtbar); d->multiTabBars.insert(mtbar->position(), mtbar); d->mainWidget = new KexiMainWidget(); d->mainWidget->setParent(this); d->mainWidget->tabWidget()->setTabsClosable(true); connect(d->mainWidget->tabWidget(), SIGNAL(tabCloseRequested(int)), this, SLOT(closeWindowForTab(int))); mainWidgetContainerLyr->addWidget(d->mainWidget, 1); mtbar = new KMultiTabBar(KMultiTabBar::Right); mtbar->setStyle(KMultiTabBar::VSNET); mainWidgetContainerLyr->addWidget(mtbar); d->multiTabBars.insert(mtbar->position(), mtbar); } void KexiMainWindow::slotSetProjectNavigatorVisible(bool set) { if (d->navDockWidget) d->navDockWidget->setVisible(set); } void KexiMainWindow::slotSetPropertyEditorVisible(bool set) { if (d->propEditorDockWidget) d->propEditorDockWidget->setVisible(set); } void KexiMainWindow::slotProjectNavigatorVisibilityChanged(bool visible) { d->setTabBarVisible(KMultiTabBar::Left, PROJECT_NAVIGATOR_TABBAR_ID, d->navDockWidget, !visible); } void KexiMainWindow::slotPropertyEditorVisibilityChanged(bool visible) { if (!d->enable_slotPropertyEditorVisibilityChanged) return; d->setPropertyEditorTabBarVisible(!visible); if (!visible) d->propertyEditorCollapsed = true; } void KexiMainWindow::slotMultiTabBarTabClicked(int id) { if (id == PROJECT_NAVIGATOR_TABBAR_ID) { slotProjectNavigatorVisibilityChanged(true); d->navDockWidget->show(); } else if (id == PROPERTY_EDITOR_TABBAR_ID) { slotPropertyEditorVisibilityChanged(true); d->propEditorDockWidget->show(); d->propertyEditorCollapsed = false; } } static Qt::DockWidgetArea applyRightToLeftToDockArea(Qt::DockWidgetArea area) { if (QApplication::layoutDirection() == Qt::RightToLeft) { if (area == Qt::LeftDockWidgetArea) { return Qt::RightDockWidgetArea; } else if (area == Qt::RightDockWidgetArea) { return Qt::LeftDockWidgetArea; } } return area; } void KexiMainWindow::setupProjectNavigator() { if (!d->isProjectNavigatorVisible) return; if (d->navigator) { d->navDockWidget->show(); } else { KexiDockableWidget* navDockableWidget = new KexiDockableWidget; d->navigator = new KexiProjectNavigator(navDockableWidget); kexiTester() << KexiTestObject(d->navigator, "KexiProjectNavigator"); navDockableWidget->setWidget(d->navigator); d->navDockWidget = new KexiDockWidget(d->navigator->windowTitle(), d->mainWidget); d->navDockWidget->setObjectName("ProjectNavigatorDockWidget"); d->mainWidget->addDockWidget( applyRightToLeftToDockArea(Qt::LeftDockWidgetArea), d->navDockWidget, Qt::Vertical); navDockableWidget->setParent(d->navDockWidget); d->navDockWidget->setWidget(navDockableWidget); KConfigGroup mainWindowGroup(d->config->group("MainWindow")); const QSize projectNavigatorSize = mainWindowGroup.readEntry("ProjectNavigatorSize", QSize()); if (!projectNavigatorSize.isNull()) { navDockableWidget->setSizeHint(projectNavigatorSize); } connect(d->navDockWidget, SIGNAL(visibilityChanged(bool)), this, SLOT(slotProjectNavigatorVisibilityChanged(bool))); //Nav2 Signals connect(d->navigator, SIGNAL(openItem(KexiPart::Item*,Kexi::ViewMode)), this, SLOT(openObject(KexiPart::Item*,Kexi::ViewMode))); connect(d->navigator, SIGNAL(openOrActivateItem(KexiPart::Item*,Kexi::ViewMode)), this, SLOT(openObjectFromNavigator(KexiPart::Item*,Kexi::ViewMode))); connect(d->navigator, SIGNAL(newItem(KexiPart::Info*)), this, SLOT(newObject(KexiPart::Info*))); connect(d->navigator, SIGNAL(removeItem(KexiPart::Item*)), this, SLOT(removeObject(KexiPart::Item*))); connect(d->navigator->model(), SIGNAL(renameItem(KexiPart::Item*,QString,bool*)), this, SLOT(renameObject(KexiPart::Item*,QString,bool*))); connect(d->navigator->model(), SIGNAL(changeItemCaption(KexiPart::Item*,QString,bool*)), this, SLOT(setObjectCaption(KexiPart::Item*,QString,bool*))); connect(d->navigator, SIGNAL(executeItem(KexiPart::Item*)), this, SLOT(executeItem(KexiPart::Item*))); connect(d->navigator, SIGNAL(exportItemToClipboardAsDataTable(KexiPart::Item*)), this, SLOT(copyItemToClipboardAsDataTable(KexiPart::Item*))); connect(d->navigator, SIGNAL(exportItemToFileAsDataTable(KexiPart::Item*)), this, SLOT(exportItemAsDataTable(KexiPart::Item*))); #ifdef KEXI_QUICK_PRINTING_SUPPORT connect(d->navigator, SIGNAL(printItem(KexiPart::Item*)), this, SLOT(printItem(KexiPart::Item*))); connect(d->navigator, SIGNAL(pageSetupForItem(KexiPart::Item*)), this, SLOT(showPageSetupForItem(KexiPart::Item*))); #endif connect(d->navigator, SIGNAL(selectionChanged(KexiPart::Item*)), this, SLOT(slotPartItemSelectedInNavigator(KexiPart::Item*))); } if (d->prj->isConnected()) { QString partManagerErrorMessages; if (!partManagerErrorMessages.isEmpty()) { showWarningContinueMessage(partManagerErrorMessages, QString(), "ShowWarningsRelatedToPluginsLoading"); } d->navigator->setProject(d->prj, QString()/*all classes*/, &partManagerErrorMessages); } connect(d->prj, SIGNAL(newItemStored(KexiPart::Item*)), d->navigator->model(), SLOT(slotAddItem(KexiPart::Item*))); connect(d->prj, SIGNAL(itemRemoved(KexiPart::Item)), d->navigator->model(), SLOT(slotRemoveItem(KexiPart::Item))); d->navigator->setFocus(); if (d->forceShowProjectNavigatorOnCreation) { slotShowNavigator(); d->forceShowProjectNavigatorOnCreation = false; } else if (d->forceHideProjectNavigatorOnCreation) { d->forceHideProjectNavigatorOnCreation = false; } invalidateActions(); } void KexiMainWindow::slotLastActions() { } void KexiMainWindow::setupPropertyEditor() { if (!d->propEditor) { KConfigGroup mainWindowGroup(d->config->group("MainWindow")); //! @todo FIX LAYOUT PROBLEMS d->propEditorDockWidget = new KexiDockWidget(xi18n("Property Editor"), d->mainWidget); d->propEditorDockWidget->setObjectName("PropertyEditorDockWidget"); d->mainWidget->addDockWidget( applyRightToLeftToDockArea(Qt::RightDockWidgetArea), d->propEditorDockWidget, Qt::Vertical ); connect(d->propEditorDockWidget, SIGNAL(visibilityChanged(bool)), this, SLOT(slotPropertyEditorVisibilityChanged(bool))); d->propEditorDockableWidget = new KexiDockableWidget(d->propEditorDockWidget); d->propEditorDockWidget->setWidget(d->propEditorDockableWidget); const QSize propertyEditorSize = mainWindowGroup.readEntry("PropertyEditorSize", QSize()); if (!propertyEditorSize.isNull()) { d->propEditorDockableWidget->setSizeHint(propertyEditorSize); } QWidget *propEditorDockWidgetContents = new QWidget(d->propEditorDockableWidget); d->propEditorDockableWidget->setWidget(propEditorDockWidgetContents); QVBoxLayout *propEditorDockWidgetContentsLyr = new QVBoxLayout(propEditorDockWidgetContents); propEditorDockWidgetContentsLyr->setContentsMargins(0, 0, 0, 0); d->propEditorTabWidget = new QTabWidget(propEditorDockWidgetContents); d->propEditorTabWidget->setDocumentMode(true); propEditorDockWidgetContentsLyr->addWidget(d->propEditorTabWidget); d->propEditor = new KexiPropertyEditorView(d->propEditorTabWidget); d->propEditorTabWidget->setWindowTitle(d->propEditor->windowTitle()); d->propEditorTabWidget->addTab(d->propEditor, xi18n("Properties")); //! @todo REMOVE? d->propEditor->installEventFilter(this); KConfigGroup propertyEditorGroup(d->config->group("PropertyEditor")); QFont f(KexiUtils::smallestReadableFont()); const qreal pointSizeF = propertyEditorGroup.readEntry("FontPointSize", -1.0f); // points are more accurate if (pointSizeF > 0.0) { f.setPointSizeF(pointSizeF); } else { const int pixelSize = propertyEditorGroup.readEntry("FontSize", -1); // compatibility with Kexi 2.x if (pixelSize > 0) { f.setPixelSize(pixelSize); } } d->propEditorTabWidget->setFont(f); d->enable_slotPropertyEditorVisibilityChanged = false; d->propEditorDockWidget->setVisible(false); d->enable_slotPropertyEditorVisibilityChanged = true; } } void KexiMainWindow::slotPartLoaded(KexiPart::Part* p) { if (!p) return; p->createGUIClients(); } void KexiMainWindow::updateAppCaption() { //! @todo allow to set custom "static" app caption d->appCaptionPrefix.clear(); if (d->prj && d->prj->data()) {//add project name d->appCaptionPrefix = d->prj->data()->caption(); if (d->appCaptionPrefix.isEmpty()) { d->appCaptionPrefix = d->prj->data()->databaseName(); } if (d->prj->dbConnection()->options()->isReadOnly()) { d->appCaptionPrefix = xi18nc(" (read only)", "%1 (read only)", d->appCaptionPrefix); } } setWindowTitle(d->appCaptionPrefix); } bool KexiMainWindow::queryClose() { #ifndef KEXI_NO_PENDING_DIALOGS if (d->pendingWindowsExist()) { qDebug() << "pendingWindowsExist..."; d->actionToExecuteWhenPendingJobsAreFinished = Private::QuitAction; return false; } #endif const tristate res = closeProject(); if (~res) return false; if (res == true) storeSettings(); if (! ~res) { Kexi::deleteGlobalObjects(); qApp->quit(); } return ! ~res; } void KexiMainWindow::closeEvent(QCloseEvent *ev) { d->mainWidget->closeEvent(ev); } static const QSize KEXI_MIN_WINDOW_SIZE(1024, 768); void KexiMainWindow::restoreSettings() { KConfigGroup mainWindowGroup(d->config->group("MainWindow")); const bool maximize = mainWindowGroup.readEntry("Maximized", false); const QRect geometry(mainWindowGroup.readEntry("Geometry", QRect())); if (geometry.isValid()) setGeometry(geometry); else if (maximize) setWindowState(windowState() | Qt::WindowMaximized); else { QRect desk = QApplication::desktop()->screenGeometry( QApplication::desktop()->screenNumber(this)); if (desk.width() <= KEXI_MIN_WINDOW_SIZE.width() || desk.height() <= KEXI_MIN_WINDOW_SIZE.height()) { setWindowState(windowState() | Qt::WindowMaximized); } else { resize(KEXI_MIN_WINDOW_SIZE); } } // Saved settings } void KexiMainWindow::storeSettings() { //qDebug(); KConfigGroup mainWindowGroup(d->config->group("MainWindow")); if (isMaximized()) { mainWindowGroup.writeEntry("Maximized", true); mainWindowGroup.deleteEntry("Geometry"); } else { mainWindowGroup.deleteEntry("Maximized"); mainWindowGroup.writeEntry("Geometry", geometry()); } if (d->navigator) mainWindowGroup.writeEntry("ProjectNavigatorSize", d->navigator->parentWidget()->size()); if (d->propEditorDockableWidget) mainWindowGroup.writeEntry("PropertyEditorSize", d->propEditorDockableWidget->size()); d->config->sync(); } void KexiMainWindow::registerChild(KexiWindow *window) { //qDebug(); connect(window, SIGNAL(dirtyChanged(KexiWindow*)), this, SLOT(slotDirtyFlagChanged(KexiWindow*))); if (window->id() != -1) { d->insertWindow(window); } //qDebug() << "ID=" << window->id(); } void KexiMainWindow::updateCustomPropertyPanelTabs(KexiWindow *prevWindow, Kexi::ViewMode prevViewMode) { updateCustomPropertyPanelTabs( prevWindow ? prevWindow->part() : 0, prevWindow ? prevWindow->currentViewMode() : prevViewMode, currentWindow() ? currentWindow()->part() : 0, currentWindow() ? currentWindow()->currentViewMode() : Kexi::NoViewMode ); } void KexiMainWindow::updateCustomPropertyPanelTabs( KexiPart::Part *prevWindowPart, Kexi::ViewMode prevViewMode, KexiPart::Part *curWindowPart, Kexi::ViewMode curViewMode) { if (!d->propEditorTabWidget) return; if ( !curWindowPart || (/*prevWindowPart &&*/ curWindowPart && (prevWindowPart != curWindowPart || prevViewMode != curViewMode) ) ) { if (d->partForPreviouslySetupPropertyPanelTabs) { //remember current page number for this part if (( prevViewMode == Kexi::DesignViewMode && static_cast(d->partForPreviouslySetupPropertyPanelTabs) != curWindowPart) //part changed || curViewMode != Kexi::DesignViewMode) { //..or switching to other view mode d->recentlySelectedPropertyPanelPages.insert( d->partForPreviouslySetupPropertyPanelTabs, d->propEditorTabWidget->currentIndex()); } } //delete old custom tabs (other than 'property' tab) const int count = d->propEditorTabWidget->count(); for (int i = 1; i < count; i++) d->propEditorTabWidget->removeTab(1); } //don't change anything if part is not switched nor view mode changed if ((!prevWindowPart && !curWindowPart) || (prevWindowPart == curWindowPart && prevViewMode == curViewMode) || (curWindowPart && curViewMode != Kexi::DesignViewMode)) { //new part for 'previously setup tabs' d->partForPreviouslySetupPropertyPanelTabs = curWindowPart; return; } if (curWindowPart) { //recreate custom tabs curWindowPart->setupCustomPropertyPanelTabs(d->propEditorTabWidget); //restore current page number for this part if (d->recentlySelectedPropertyPanelPages.contains(curWindowPart)) { d->propEditorTabWidget->setCurrentIndex( d->recentlySelectedPropertyPanelPages[ curWindowPart ] ); } } //new part for 'previously setup tabs' d->partForPreviouslySetupPropertyPanelTabs = curWindowPart; } void KexiMainWindow::activeWindowChanged(KexiWindow *window, KexiWindow *prevWindow) { //qDebug() << "to=" << (window ? window->windowTitle() : ""); bool windowChanged = prevWindow != window; if (windowChanged) { if (prevWindow) { //inform previously activated dialog about deactivation prevWindow->deactivate(); } } updateCustomPropertyPanelTabs(prevWindow, prevWindow ? prevWindow->currentViewMode() : Kexi::NoViewMode); // inform the current view of the new dialog about property switching // (this will also call KexiMainWindow::propertySetSwitched() to update the current property editor's set if (windowChanged && currentWindow()) currentWindow()->selectedView()->propertySetSwitched(); if (windowChanged) { if (currentWindow() && currentWindow()->currentViewMode() != 0 && window) { //on opening new dialog it can be 0; we don't want this d->updatePropEditorVisibility(currentWindow()->currentViewMode()); restoreDesignTabIfNeeded(window->partItem()->pluginId(), window->currentViewMode(), prevWindow ? prevWindow->partItem()->identifier() : 0); activateDesignTabIfNeeded(window->partItem()->pluginId(), window->currentViewMode()); } } invalidateActions(); d->updateFindDialogContents(); if (window) window->setFocus(); } bool KexiMainWindow::activateWindow(int id) { qDebug(); #ifndef KEXI_NO_PENDING_DIALOGS Private::PendingJobType pendingType; return activateWindow(*d->openedWindowFor(id, pendingType)); #else return activateWindow(*d->openedWindowFor(id)); #endif } bool KexiMainWindow::activateWindow(KexiWindow& window) { //qDebug(); d->focus_before_popup = &window; d->mainWidget->tabWidget()->setCurrentWidget(window.parentWidget()/*container*/); window.activate(); return true; } void KexiMainWindow::activateNextWindow() { //! @todo activateNextWindow() } void KexiMainWindow::activatePreviousWindow() { //! @todo activatePreviousWindow() } void KexiMainWindow::slotSettings() { if (d->tabbedToolBar) { d->tabbedToolBar->showMainMenu("settings"); // dummy QLabel *dummy = KEXI_UNFINISHED_LABEL(actionCollection()->action("settings")->text()); d->tabbedToolBar->setMainMenuContent(dummy); } } void KexiMainWindow::slotConfigureKeys() { KShortcutsDialog::configure(actionCollection(), KShortcutsEditor::LetterShortcutsDisallowed, this); } void KexiMainWindow::slotConfigureToolbars() { KEditToolBar edit(actionCollection()); (void) edit.exec(); } void KexiMainWindow::slotProjectNew() { createNewProject(); } KexiProject* KexiMainWindow::createKexiProjectObject(const KexiProjectData &data) { KexiProject *prj = new KexiProject(data, this); connect(prj, SIGNAL(itemRenamed(KexiPart::Item,QString)), this, SLOT(slotObjectRenamed(KexiPart::Item,QString))); if (d->navigator){ connect(prj, SIGNAL(itemRemoved(KexiPart::Item)), d->navigator->model(), SLOT(slotRemoveItem(KexiPart::Item))); } return prj; } void KexiMainWindow::createNewProject() { if (!d->tabbedToolBar) return; d->tabbedToolBar->showMainMenu("project_new"); KexiNewProjectAssistant* assistant = new KexiNewProjectAssistant; connect(assistant, SIGNAL(createProject(KexiProjectData)), this, SLOT(createNewProject(KexiProjectData))); d->tabbedToolBar->setMainMenuContent(assistant); } tristate KexiMainWindow::createNewProject(const KexiProjectData &projectData) { QScopedPointer prj(createKexiProjectObject(projectData)); tristate res = prj->create(true /*overwrite*/); if (res != true) { return res; } //qDebug() << "new project created ---"; if (d->prj) { res = openProjectInExternalKexiInstance( prj->data()->connectionData()->databaseName(), prj->data()->connectionData(), prj->data()->databaseName()); Kexi::recentProjects()->addProjectData(*prj->data()); if (d->tabbedToolBar) { d->tabbedToolBar->hideMainMenu(); } return res; } if (d->tabbedToolBar) { d->tabbedToolBar->hideMainMenu(); } d->prj = prj.take(); setupProjectNavigator(); d->prj->data()->setLastOpened(QDateTime::currentDateTime()); Kexi::recentProjects()->addProjectData(*d->prj->data()); invalidateActions(); updateAppCaption(); return true; } void KexiMainWindow::slotProjectOpen() { if (!d->tabbedToolBar) return; d->tabbedToolBar->showMainMenu("project_open"); KexiOpenProjectAssistant* assistant = new KexiOpenProjectAssistant; connect(assistant, SIGNAL(openProject(KexiProjectData)), this, SLOT(openProject(KexiProjectData))); connect(assistant, SIGNAL(openProject(QString)), this, SLOT(openProject(QString))); d->tabbedToolBar->setMainMenuContent(assistant); } tristate KexiMainWindow::openProject(const QString& aFileName) { return openProject(aFileName, QString(), QString()); } tristate KexiMainWindow::openProject(const QString& aFileName, const QString& fileNameForConnectionData, const QString& dbName) { if (d->prj) return openProjectInExternalKexiInstance(aFileName, fileNameForConnectionData, dbName); KDbConnectionData *cdata = 0; if (!fileNameForConnectionData.isEmpty()) { cdata = Kexi::connset().connectionDataForFileName(fileNameForConnectionData); if (!cdata) { qWarning() << "cdata?"; return false; } } return openProject(aFileName, cdata, dbName); } tristate KexiMainWindow::openProject(const QString& aFileName, KDbConnectionData *cdata, const QString& dbName, const KexiProjectData::AutoOpenObjects& autoopenObjects) { if (d->prj) { return openProjectInExternalKexiInstance(aFileName, cdata, dbName); } KexiProjectData* projectData = 0; KexiStartupHandler &h = Kexi::startupHandler(); bool readOnly = h.isSet(h.options().readOnly); bool deleteAfterOpen = false; if (cdata) { //server-based project if (dbName.isEmpty()) {//no database name given, ask user bool cancel; projectData = Kexi::startupHandler().selectProject(cdata, &cancel, this); if (cancel) return cancelled; } else { //! @todo caption arg? projectData = new KexiProjectData(*cdata, dbName); deleteAfterOpen = true; } } else { if (aFileName.isEmpty()) { qWarning() << "aFileName.isEmpty()"; return false; } //file-based project qDebug() << "Project File: " << aFileName; KDbConnectionData fileConnData; fileConnData.setDatabaseName(aFileName); QString detectedDriverId; int detectOptions = 0; if (readOnly) { detectOptions |= KexiStartupHandler::OpenReadOnly; } KexiStartupData::Import importActionData; bool forceReadOnly; const tristate res = KexiStartupHandler::detectActionForFile( &importActionData, &detectedDriverId, fileConnData.driverId(), aFileName, this, detectOptions, &forceReadOnly); if (forceReadOnly) { readOnly = true; } if (true != res) return res; if (importActionData) { //importing requested return showProjectMigrationWizard(importActionData.mimeType, importActionData.fileName); } fileConnData.setDriverId(detectedDriverId); if (fileConnData.driverId().isEmpty()) return false; //opening requested projectData = new KexiProjectData(fileConnData); deleteAfterOpen = true; } if (!projectData) return false; projectData->setReadOnly(readOnly); projectData->autoopenObjects = autoopenObjects; const tristate res = openProject(*projectData); if (deleteAfterOpen) //projectData object has been copied delete projectData; return res; } tristate KexiMainWindow::openProjectInExternalKexiInstance(const QString& aFileName, KDbConnectionData *cdata, const QString& dbName) { QString fileNameForConnectionData; if (aFileName.isEmpty()) { //try .kexic file if (cdata) fileNameForConnectionData = Kexi::connset().fileNameForConnectionData(*cdata); } return openProjectInExternalKexiInstance(aFileName, fileNameForConnectionData, dbName); } tristate KexiMainWindow::openProjectInExternalKexiInstance(const QString& aFileName, const QString& fileNameForConnectionData, const QString& dbName) { QString fileName(aFileName); QStringList args; // open a file-based project or a server connection provided as a .kexic file // (we have no other simple way to provide the startup data to a new process) if (fileName.isEmpty()) { //try .kexic file if (!fileNameForConnectionData.isEmpty()) args << "--skip-conn-dialog"; //user does not expect conn. dialog to be shown here if (dbName.isEmpty()) { //use 'kexi --skip-conn-dialog file.kexic' fileName = fileNameForConnectionData; } else { //use 'kexi --skip-conn-dialog --connection file.kexic dbName' if (fileNameForConnectionData.isEmpty()) { qWarning() << "fileNameForConnectionData?"; return false; } args << "--connection" << fileNameForConnectionData; fileName = dbName; } } if (fileName.isEmpty()) { qWarning() << "fileName?"; return false; } //! @todo use KRun //! @todo untested //Can arguments be supplied to KRun like is used here? AP args << fileName; const bool ok = QProcess::startDetached( qApp->applicationFilePath(), args, QFileInfo(fileName).absoluteDir().absolutePath()); if (!ok) { d->showStartProcessMsg(args); } if (d->tabbedToolBar) { d->tabbedToolBar->hideMainMenu(); } return ok; } void KexiMainWindow::slotProjectWelcome() { if (!d->tabbedToolBar) return; d->tabbedToolBar->showMainMenu("project_welcome"); KexiWelcomeAssistant* assistant = new KexiWelcomeAssistant( Kexi::recentProjects(), this); connect(assistant, SIGNAL(openProject(KexiProjectData,QString,bool*)), this, SLOT(openProject(KexiProjectData,QString,bool*))); d->tabbedToolBar->setMainMenuContent(assistant); } void KexiMainWindow::slotProjectSave() { if (!currentWindow() || currentWindow()->currentViewMode() == Kexi::DataViewMode) { return; } saveObject(currentWindow()); updateAppCaption(); invalidateActions(); } void KexiMainWindow::slotProjectSaveAs() { if (!currentWindow() || currentWindow()->currentViewMode() == Kexi::DataViewMode) { return; } saveObject(currentWindow(), QString(), SaveObjectAs); updateAppCaption(); invalidateActions(); } void KexiMainWindow::slotProjectPrint() { #ifdef KEXI_QUICK_PRINTING_SUPPORT if (currentWindow() && currentWindow()->partItem()) printItem(currentWindow()->partItem()); #endif } void KexiMainWindow::slotProjectPrintPreview() { #ifdef KEXI_QUICK_PRINTING_SUPPORT if (currentWindow() && currentWindow()->partItem()) printPreviewForItem(currentWindow()->partItem()); #endif } void KexiMainWindow::slotProjectPageSetup() { #ifdef KEXI_QUICK_PRINTING_SUPPORT if (currentWindow() && currentWindow()->partItem()) showPageSetupForItem(currentWindow()->partItem()); #endif } void KexiMainWindow::slotProjectExportDataTable() { if (currentWindow() && currentWindow()->partItem()) exportItemAsDataTable(currentWindow()->partItem()); } void KexiMainWindow::slotProjectProperties() { if (!d->tabbedToolBar) return; d->tabbedToolBar->showMainMenu("project_properties"); // dummy QLabel *dummy = KEXI_UNFINISHED_LABEL(actionCollection()->action("project_properties")->text()); d->tabbedToolBar->setMainMenuContent(dummy); //! @todo load the implementation not the ui :) // ProjectSettingsUI u(this); // u.exec(); } void KexiMainWindow::slotProjectImportExportOrSend() { if (!d->tabbedToolBar) return; d->tabbedToolBar->showMainMenu("project_import_export_send"); KexiImportExportAssistant* assistant = new KexiImportExportAssistant( d->action_project_import_export_send, d->action_tools_import_project); connect(assistant, SIGNAL(importProject()), this, SLOT(slotToolsImportProject())); d->tabbedToolBar->setMainMenuContent(assistant); } void KexiMainWindow::slotProjectClose() { closeProject(); } void KexiMainWindow::slotProjectRelations() { if (!d->prj) return; KexiWindow *w = KexiInternalPart::createKexiWindowInstance("org.kexi-project.relations", this); activateWindow(*w); } void KexiMainWindow::slotImportFile() { KEXI_UNFINISHED("Import: " + xi18n("From File...")); } void KexiMainWindow::slotImportServer() { KEXI_UNFINISHED("Import: " + xi18n("From Server...")); } void KexiMainWindow::slotProjectQuit() { if (~ closeProject()) return; close(); } void KexiMainWindow::slotActivateNavigator() { if (!d->navigator) { return; } d->navigator->setFocus(); } void KexiMainWindow::slotActivateMainArea() { if (currentWindow()) currentWindow()->setFocus(); } void KexiMainWindow::slotActivatePropertyEditor() { if (!d->propEditor) { return; } if (d->propEditorTabWidget->currentWidget()) d->propEditorTabWidget->currentWidget()->setFocus(); } void KexiMainWindow::slotShowNavigator() { if (d->navDockWidget) d->navDockWidget->setVisible(!d->navDockWidget->isVisible()); } void KexiMainWindow::slotShowPropertyEditor() { if (d->propEditorDockWidget) d->propEditorDockWidget->setVisible(!d->propEditorDockWidget->isVisible()); } tristate KexiMainWindow::switchToViewMode(KexiWindow& window, Kexi::ViewMode viewMode) { const Kexi::ViewMode prevViewMode = currentWindow()->currentViewMode(); if (prevViewMode == viewMode) return true; if (!activateWindow(window)) return false; if (!currentWindow()) { return false; } if (&window != currentWindow()) return false; if (!currentWindow()->supportsViewMode(viewMode)) { showErrorMessage(xi18nc("@info", "Selected view is not supported for %1 object.", currentWindow()->partItem()->name()), xi18nc("@info", "Selected view (%1) is not supported by this object type (%2).", Kexi::nameForViewMode(viewMode), currentWindow()->part()->info()->name())); return false; } updateCustomPropertyPanelTabs(currentWindow()->part(), prevViewMode, currentWindow()->part(), viewMode); tristate res = currentWindow()->switchToViewMode(viewMode); if (!res) { updateCustomPropertyPanelTabs(0, Kexi::NoViewMode); //revert showErrorMessage(xi18n("Switching to other view failed (%1).", Kexi::nameForViewMode(viewMode)), currentWindow()); return false; } if (~res) { updateCustomPropertyPanelTabs(0, Kexi::NoViewMode); //revert return cancelled; } activateWindow(window); invalidateSharedActions(); invalidateProjectWideActions(); d->updateFindDialogContents(); d->updatePropEditorVisibility(viewMode); QString origTabToActivate; if (viewMode == Kexi::DesignViewMode) { // Save the orig tab: we want to back to design tab // when user moved to data view and then immediately to design view. origTabToActivate = d->tabsToActivateOnShow.value(currentWindow()->partItem()->identifier()); } restoreDesignTabIfNeeded(currentWindow()->partItem()->pluginId(), viewMode, currentWindow()->partItem()->identifier()); if (viewMode == Kexi::DesignViewMode) { activateDesignTab(currentWindow()->partItem()->pluginId()); // Restore the saved tab to the orig one. restoreDesignTabIfNeeded() saved tools tab probably. d->tabsToActivateOnShow.insert(currentWindow()->partItem()->identifier(), origTabToActivate); } return true; } void KexiMainWindow::slotViewDataMode() { if (currentWindow()) switchToViewMode(*currentWindow(), Kexi::DataViewMode); } void KexiMainWindow::slotViewDesignMode() { if (currentWindow()) switchToViewMode(*currentWindow(), Kexi::DesignViewMode); } void KexiMainWindow::slotViewTextMode() { if (currentWindow()) switchToViewMode(*currentWindow(), Kexi::TextViewMode); } //! Used to control if we're not Saving-As object under the original name class SaveAsObjectNameValidator : public KexiNameDialogValidator { public: SaveAsObjectNameValidator(const QString &originalObjectName) : m_originalObjectName(originalObjectName) { } virtual bool validate(KexiNameDialog *dialog) const { if (dialog->widget()->nameText() == m_originalObjectName) { KMessageBox::information(dialog, xi18nc("Could not save object under the original name.", "Could not save under the original name.")); return false; } return true; } private: QString m_originalObjectName; }; tristate KexiMainWindow::getNewObjectInfo( KexiPart::Item *partItem, const QString &originalName, KexiPart::Part *part, bool allowOverwriting, bool *overwriteNeeded, const QString& messageWhenAskingForName) { //data was never saved in the past -we need to create a new object at the backend KexiPart::Info *info = part->info(); if (!d->nameDialog) { d->nameDialog = new KexiNameDialog( messageWhenAskingForName, this); //check if that name is allowed d->nameDialog->widget()->addNameSubvalidator( new KDbObjectNameValidator(project()->dbConnection()->driver())); d->nameDialog->buttonBox()->button(QDialogButtonBox::Ok)->setText(xi18nc("@action:button Save object", "Save")); } else { d->nameDialog->widget()->setMessageText(messageWhenAskingForName); } d->nameDialog->widget()->setCaptionText(partItem->caption()); d->nameDialog->widget()->setNameText(partItem->name()); d->nameDialog->setWindowTitle(xi18nc("@title:window", "Save Object As")); d->nameDialog->setDialogIcon(info->iconName()); d->nameDialog->setAllowOverwriting(allowOverwriting); if (!originalName.isEmpty()) { d->nameDialog->setValidator(new SaveAsObjectNameValidator(originalName)); } if (d->nameDialog->execAndCheckIfObjectExists(*project(), *part, overwriteNeeded) != QDialog::Accepted) { return cancelled; } // close window of object that will be overwritten if (*overwriteNeeded) { KexiPart::Item* overwrittenItem = project()->item(info, d->nameDialog->widget()->nameText()); if (overwrittenItem) { KexiWindow * openedWindow = d->openedWindowFor(overwrittenItem->identifier()); if (openedWindow) { const tristate res = closeWindow(openedWindow); if (res != true) { return res; } } } } //update name and caption partItem->setName(d->nameDialog->widget()->nameText()); partItem->setCaption(d->nameDialog->widget()->captionText()); return true; } //! Used to delete part item on exit from block class PartItemDeleter : public QScopedPointer { public: explicit PartItemDeleter(KexiProject *prj) : m_prj(prj) {} ~PartItemDeleter() { if (!isNull()) { m_prj->deleteUnstoredItem(take()); } } private: KexiProject *m_prj; }; static void showSavingObjectFailedErrorMessage(KexiMainWindow *wnd, KexiPart::Item *item) { wnd->showErrorMessage( xi18nc("@info Saving object failed", "Saving %1 object failed.", item->name()), wnd->currentWindow()); } tristate KexiMainWindow::saveObject(KexiWindow *window, const QString& messageWhenAskingForName, SaveObjectOptions options) { tristate res; bool saveAs = options & SaveObjectAs; if (!saveAs && !window->neverSaved()) { //data was saved in the past -just save again res = window->storeData(options & DoNotAsk); if (!res) { showSavingObjectFailedErrorMessage(this, window->partItem()); } return res; } if (saveAs && window->neverSaved()) { //if never saved, saveAs == save saveAs = false; } const int oldItemID = window->partItem()->identifier(); KexiPart::Item *partItem; KexiView::StoreNewDataOptions storeNewDataOptions; PartItemDeleter itemDeleter(d->prj); if (saveAs) { partItem = d->prj->createPartItem(window->part()); if (!partItem) { //! @todo error return false; } itemDeleter.reset(partItem); } else { partItem = window->partItem(); } bool overwriteNeeded; res = getNewObjectInfo(partItem, saveAs ? window->partItem()->name() : QString(), window->part(), true /*allowOverwriting*/, &overwriteNeeded, messageWhenAskingForName); if (res != true) return res; if (overwriteNeeded) { storeNewDataOptions |= KexiView::OverwriteExistingData; } if (saveAs) { res = window->storeDataAs(partItem, storeNewDataOptions); } else { res = window->storeNewData(storeNewDataOptions); } if (~res) return cancelled; if (!res) { showSavingObjectFailedErrorMessage(this, partItem); return false; } d->updateWindowId(window, oldItemID); invalidateProjectWideActions(); itemDeleter.take(); return true; } tristate KexiMainWindow::closeWindow(KexiWindow *window) { return closeWindow(window ? window : currentWindow(), true); } tristate KexiMainWindow::closeCurrentWindow() { return closeWindow(0); } tristate KexiMainWindow::closeWindowForTab(int tabIndex) { KexiWindow* window = windowForTab(tabIndex); if (!window) return false; return closeWindow(window); } tristate KexiMainWindow::closeWindow(KexiWindow *window, bool layoutTaskBar, bool doNotSaveChanges) { //! @todo KEXI3 KexiMainWindow::closeWindow() ///@note Q_UNUSED layoutTaskBar Q_UNUSED(layoutTaskBar); if (!window) return true; if (d->insideCloseWindow) return true; const int previousItemId = window->partItem()->identifier(); #ifndef KEXI_NO_PENDING_DIALOGS d->addItemToPendingWindows(window->partItem(), Private::WindowClosingJob); #endif d->insideCloseWindow = true; if (window == currentWindow() && !window->isAttached()) { if (d->propEditor) { // ah, closing detached window - better switch off property buffer right now... d->propertySet = 0; d->propEditor->editor()->changeSet(0); } } bool remove_on_closing = window->partItem() ? window->partItem()->neverSaved() : false; if (window->isDirty() && !d->forceWindowClosing && !doNotSaveChanges) { //more accurate tool tips and what's this KGuiItem saveChanges(KStandardGuiItem::save()); saveChanges.setToolTip(xi18n("Save changes")); saveChanges.setWhatsThis( xi18nc("@info", "Saves all recent changes made in %1 object.", window->partItem()->name())); KGuiItem discardChanges(KStandardGuiItem::discard()); discardChanges.setWhatsThis( xi18nc("@info", "Discards all recent changes made in %1 object.", window->partItem()->name())); //dialog's data is dirty: //--adidional message, e.g. table designer will return // "Note: This table is already filled with data which will be removed." // if the window is in design view mode. const KLocalizedString additionalMessage( window->part()->i18nMessage(":additional message before saving design", window)); QString additionalMessageString; if (!additionalMessage.isEmpty()) additionalMessageString = additionalMessage.toString(); if (additionalMessageString.startsWith(':')) additionalMessageString.clear(); if (!additionalMessageString.isEmpty()) additionalMessageString = "

    " + additionalMessageString + "

    "; const KMessageBox::ButtonCode questionRes = KMessageBox::warningYesNoCancel(this, "

    " + window->part()->i18nMessage("Design of object %1 has been modified.", window) .subs(window->partItem()->name()).toString() + "

    " + xi18n("Do you want to save changes?") + "

    " + additionalMessageString /*may be empty*/, QString(), saveChanges, discardChanges); if (questionRes == KMessageBox::Cancel) { #ifndef KEXI_NO_PENDING_DIALOGS d->removePendingWindow(window->id()); #endif d->insideCloseWindow = false; d->windowsToClose.clear(); //give up with 'close all' return cancelled; } if (questionRes == KMessageBox::Yes) { //save it tristate res = saveObject(window, QString(), DoNotAsk); if (!res || ~res) { //! @todo show error info; (retry/ignore/cancel) #ifndef KEXI_NO_PENDING_DIALOGS d->removePendingWindow(window->id()); #endif d->insideCloseWindow = false; d->windowsToClose.clear(); //give up with 'close all' return res; } remove_on_closing = false; } } const int window_id = window->id(); //remember now, because removeObject() can destruct partitem object if (remove_on_closing) { //we won't save this object, and it was never saved -remove it if (!removeObject(window->partItem(), true)) { #ifndef KEXI_NO_PENDING_DIALOGS d->removePendingWindow(window->id()); #endif //msg? //! @todo ask if we'd continue and return true/false d->insideCloseWindow = false; d->windowsToClose.clear(); //give up with 'close all' return false; } } else { //not dirty now if (d->navigator) { d->navigator->updateItemName(*window->partItem(), false); } } hideDesignTab(previousItemId, QString()); d->removeWindow(window_id); d->setWindowContainerExistsFor(window->partItem()->identifier(), false); QWidget *windowContainer = window->parentWidget(); d->mainWidget->tabWidget()->removeTab( d->mainWidget->tabWidget()->indexOf(windowContainer)); #ifdef KEXI_QUICK_PRINTING_SUPPORT //also remove from 'print setup dialogs' cache, if needed int printedObjectID = 0; if (d->pageSetupWindowItemID2dataItemID_map.contains(window_id)) printedObjectID = d->pageSetupWindowItemID2dataItemID_map[ window_id ]; d->pageSetupWindows.remove(printedObjectID); #endif delete windowContainer; //focus navigator if nothing else available if (d->openedWindowsCount() == 0) { if (d->navigator) { d->navigator->setFocus(); } d->updatePropEditorVisibility(Kexi::NoViewMode); } invalidateActions(); d->insideCloseWindow = false; if (!d->windowsToClose.isEmpty()) {//continue 'close all' KexiWindow* w = d->windowsToClose.takeAt(0); closeWindow(w, true); } #ifndef KEXI_NO_PENDING_DIALOGS d->removePendingWindow(window_id); //perform pending global action that was suspended: if (!d->pendingWindowsExist()) { d->executeActionWhenPendingJobsAreFinished(); } #endif d->mainWidget->slotCurrentTabIndexChanged(d->mainWidget->tabWidget()->currentIndex()); showDesignTabIfNeeded(0); if (currentWindow()) { restoreDesignTabIfNeeded(currentWindow()->partItem()->pluginId(), currentWindow()->currentViewMode(), 0); } d->tabsToActivateOnShow.remove(previousItemId); return true; } QWidget* KexiMainWindow::findWindow(QWidget *w) { while (w && !acceptsSharedActions(w)) { if (w == d->propEditorDockWidget) return currentWindow(); w = w->parentWidget(); } return w; } KexiWindow* KexiMainWindow::openedWindowFor(int identifier) { return d->openedWindowFor(identifier); } KexiWindow* KexiMainWindow::openedWindowFor(const KexiPart::Item* item) { return item ? openedWindowFor(item->identifier()) : 0; } KDbQuerySchema* KexiMainWindow::unsavedQuery(int queryId) { KexiWindow * queryWindow = openedWindowFor(queryId); if (!queryWindow || !queryWindow->isDirty()) { return 0; } return queryWindow->part()->currentQuery(queryWindow->viewForMode(Kexi::DataViewMode)); } QList KexiMainWindow::currentParametersForQuery(int queryId) const { KexiWindow *queryWindow = d->openedWindowFor(queryId); if (!queryWindow) { return QList(); } KexiView *view = queryWindow->viewForMode(Kexi::DataViewMode); if (!view) { return QList(); } return view->currentParameters(); } bool KexiMainWindow::acceptsSharedActions(QObject *w) { return w->inherits("KexiWindow") || w->inherits("KexiView"); } bool KexiMainWindow::openingAllowed(KexiPart::Item* item, Kexi::ViewMode viewMode, QString* errorMessage) { //qDebug() << viewMode; //! @todo this can be more complex once we deliver ACLs... if (!d->userMode) return true; KexiPart::Part * part = Kexi::partManager().partForPluginId(item->pluginId()); if (!part) { if (errorMessage) { *errorMessage = Kexi::partManager().result().message(); } } //qDebug() << part << item->pluginId(); //if (part) // qDebug() << item->pluginId() << part->info()->supportedUserViewModes(); return part && (part->info()->supportedUserViewModes() & viewMode); } KexiWindow * KexiMainWindow::openObject(const QString& pluginId, const QString& name, Kexi::ViewMode viewMode, bool *openingCancelled, QMap* staticObjectArgs) { KexiPart::Item *item = d->prj->itemForPluginId(pluginId, name); if (!item) return 0; return openObject(item, viewMode, openingCancelled, staticObjectArgs); } KexiWindow * KexiMainWindow::openObject(KexiPart::Item* item, Kexi::ViewMode viewMode, bool *openingCancelled, QMap* staticObjectArgs, QString* errorMessage) { Q_ASSERT(openingCancelled); if (!d->prj || !item) { return 0; } if (!openingAllowed(item, viewMode, errorMessage)) { if (errorMessage) *errorMessage = xi18nc( "opening is not allowed in \"data view/design view/text view\" mode", "opening is not allowed in \"%1\" mode", Kexi::nameForViewMode(viewMode)); *openingCancelled = true; return 0; } //qDebug() << d->prj << item; KexiWindow *prevWindow = currentWindow(); KexiUtils::WaitCursor wait; #ifndef KEXI_NO_PENDING_DIALOGS Private::PendingJobType pendingType; KexiWindow *window = d->openedWindowFor(item, pendingType); if (pendingType != Private::NoJob) { *openingCancelled = true; return 0; } #else KexiWindow *window = openedWindowFor(item); #endif int previousItemId = currentWindow() ? currentWindow()->partItem()->identifier() : 0; *openingCancelled = false; bool alreadyOpened = false; KexiWindowContainer *windowContainer = 0; if (window) { if (viewMode != window->currentViewMode()) { if (true != switchToViewMode(*window, viewMode)) return 0; } else activateWindow(*window); alreadyOpened = true; } else { if (d->windowContainerExistsFor(item->identifier())) { // window not yet present but window container exists: return 0 and wait return 0; } KexiPart::Part *part = Kexi::partManager().partForPluginId(item->pluginId()); d->updatePropEditorVisibility(viewMode, part ? part->info() : 0); //update tabs before opening updateCustomPropertyPanelTabs(currentWindow() ? currentWindow()->part() : 0, currentWindow() ? currentWindow()->currentViewMode() : Kexi::NoViewMode, part, viewMode); // open new tab earlier windowContainer = new KexiWindowContainer(d->mainWidget->tabWidget()); d->setWindowContainerExistsFor(item->identifier(), true); const int tabIndex = d->mainWidget->tabWidget()->addTab( windowContainer, QIcon::fromTheme(part ? part->info()->iconName() : QString()), KexiWindow::windowTitleForItem(*item)); d->mainWidget->tabWidget()->setTabToolTip(tabIndex, KexiPart::fullCaptionForItem(item, part)); QString whatsThisText; if (part) { whatsThisText = xi18nc("@info", "Tab for %1 (%2).", item->captionOrName(), part->info()->name()); } else { whatsThisText = xi18nc("@info", "Tab for %1.", item->captionOrName()); } d->mainWidget->tabWidget()->setTabWhatsThis(tabIndex, whatsThisText); d->mainWidget->tabWidget()->setCurrentWidget(windowContainer); #ifndef KEXI_NO_PENDING_DIALOGS d->addItemToPendingWindows(item, Private::WindowOpeningJob); #endif window = d->prj->openObject(windowContainer, item, viewMode, staticObjectArgs); if (window) { windowContainer->setWindow(window); // update text and icon d->mainWidget->tabWidget()->setTabText( d->mainWidget->tabWidget()->indexOf(windowContainer), window->windowTitle()); d->mainWidget->tabWidget()->setTabIcon( d->mainWidget->tabWidget()->indexOf(windowContainer), window->windowIcon()); } } if (!window || !activateWindow(*window)) { #ifndef KEXI_NO_PENDING_DIALOGS d->removePendingWindow(item->identifier()); #endif d->setWindowContainerExistsFor(item->identifier(), false); d->mainWidget->tabWidget()->removeTab( d->mainWidget->tabWidget()->indexOf(windowContainer)); delete windowContainer; updateCustomPropertyPanelTabs(0, Kexi::NoViewMode); //revert //! @todo add error msg... return 0; } if (viewMode != window->currentViewMode()) invalidateSharedActions(); #ifndef KEXI_NO_PENDING_DIALOGS d->removePendingWindow(window->id()); //perform pending global action that was suspended: if (!d->pendingWindowsExist()) { d->executeActionWhenPendingJobsAreFinished(); } #endif if (window && !alreadyOpened) { // Call switchToViewMode() and propertySetSwitched() again here because // this is the time when then new window is the current one - previous call did nothing. switchToViewMode(*window, window->currentViewMode()); currentWindow()->selectedView()->propertySetSwitched(); } invalidateProjectWideActions(); restoreDesignTabIfNeeded(item->pluginId(), viewMode, previousItemId); activateDesignTabIfNeeded(item->pluginId(), viewMode); QString origTabToActivate; if (prevWindow) { // Save the orig tab for prevWindow that was stored in the restoreDesignTabIfNeeded() call above origTabToActivate = d->tabsToActivateOnShow.value(prevWindow->partItem()->identifier()); } activeWindowChanged(window, prevWindow); if (prevWindow) { // Restore the orig tab d->tabsToActivateOnShow.insert(prevWindow->partItem()->identifier(), origTabToActivate); } return window; } KexiWindow * KexiMainWindow::openObjectFromNavigator(KexiPart::Item* item, Kexi::ViewMode viewMode) { bool openingCancelled; return openObjectFromNavigator(item, viewMode, &openingCancelled); } KexiWindow * KexiMainWindow::openObjectFromNavigator(KexiPart::Item* item, Kexi::ViewMode viewMode, bool *openingCancelled) { Q_ASSERT(openingCancelled); if (!openingAllowed(item, viewMode)) { *openingCancelled = true; return 0; } if (!d->prj || !item) return 0; #ifndef KEXI_NO_PENDING_DIALOGS Private::PendingJobType pendingType; KexiWindow *window = d->openedWindowFor(item, pendingType); if (pendingType != Private::NoJob) { *openingCancelled = true; return 0; } #else KexiWindow *window = openedWindowFor(item); #endif *openingCancelled = false; if (window) { if (activateWindow(*window)) { return window; } } //if DataViewMode is not supported, try Design, then Text mode (currently useful for script part) KexiPart::Part *part = Kexi::partManager().partForPluginId(item->pluginId()); if (!part) return 0; if (viewMode == Kexi::DataViewMode && !(part->info()->supportedViewModes() & Kexi::DataViewMode)) { if (part->info()->supportedViewModes() & Kexi::DesignViewMode) return openObjectFromNavigator(item, Kexi::DesignViewMode, openingCancelled); else if (part->info()->supportedViewModes() & Kexi::TextViewMode) return openObjectFromNavigator(item, Kexi::TextViewMode, openingCancelled); } //do the same as in openObject() return openObject(item, viewMode, openingCancelled); } tristate KexiMainWindow::closeObject(KexiPart::Item* item) { #ifndef KEXI_NO_PENDING_DIALOGS Private::PendingJobType pendingType; KexiWindow *window = d->openedWindowFor(item, pendingType); if (pendingType == Private::WindowClosingJob) return true; else if (pendingType == Private::WindowOpeningJob) return cancelled; #else KexiWindow *window = openedWindowFor(item); #endif if (!window) return cancelled; return closeWindow(window); } bool KexiMainWindow::newObject(KexiPart::Info *info, bool* openingCancelled) { Q_ASSERT(openingCancelled); if (d->userMode) { *openingCancelled = true; return false; } *openingCancelled = false; if (!d->prj || !info) return false; KexiPart::Part *part = Kexi::partManager().part(info); if (!part) return false; KexiPart::Item *it = d->prj->createPartItem(info); if (!it) { //! @todo error return false; } if (!it->neverSaved()) { //only add stored objects to the browser d->navigator->model()->slotAddItem(it); } return openObject(it, Kexi::DesignViewMode, openingCancelled); } tristate KexiMainWindow::removeObject(KexiPart::Item *item, bool dontAsk) { if (d->userMode) return cancelled; if (!d->prj || !item) return false; KexiPart::Part *part = Kexi::partManager().partForPluginId(item->pluginId()); if (!part) return false; if (!dontAsk) { if (KMessageBox::No == KMessageBox::questionYesNo(this, xi18nc("@info Remove ?", "Do you want to permanently delete the following object?" "%1 %2" "If you click Delete, " "you will not be able to undo the deletion.", part->info()->name(), item->name()), xi18nc("@title:window Delete Object %1.", "Delete %1?", item->name()), KStandardGuiItem::del(), KStandardGuiItem::no(), QString(), KMessageBox::Notify | KMessageBox::Dangerous)) { return cancelled; } } tristate res = true; #ifdef KEXI_QUICK_PRINTING_SUPPORT //also close 'print setup' dialog for this item, if any KexiWindow * pageSetupWindow = d->pageSetupWindows[ item->identifier()]; const bool oldInsideCloseWindow = d->insideCloseWindow; { d->insideCloseWindow = false; if (pageSetupWindow) res = closeWindow(pageSetupWindow); } d->insideCloseWindow = oldInsideCloseWindow; if (!res || ~res) { return res; } #endif #ifndef KEXI_NO_PENDING_DIALOGS Private::PendingJobType pendingType; KexiWindow *window = d->openedWindowFor(item, pendingType); if (pendingType != Private::NoJob) { return cancelled; } #else KexiWindow *window = openedWindowFor(item); #endif if (window) {//close existing window const bool tmp = d->forceWindowClosing; d->forceWindowClosing = true; res = closeWindow(window); d->forceWindowClosing = tmp; //restore if (!res || ~res) { return res; } } #ifdef KEXI_QUICK_PRINTING_SUPPORT //in case the dialog is a 'print setup' dialog, also update d->pageSetupWindows int dataItemID = d->pageSetupWindowItemID2dataItemID_map[item->identifier()]; d->pageSetupWindowItemID2dataItemID_map.remove(item->identifier()); d->pageSetupWindows.remove(dataItemID); #endif if (!d->prj->removeObject(item)) { //! @todo better msg showSorryMessage(xi18n("Could not remove object.")); return false; } return true; } void KexiMainWindow::renameObject(KexiPart::Item *item, const QString& _newName, bool *success) { Q_ASSERT(success); if (d->userMode) { *success = false; return; } QString newName = _newName.trimmed(); if (newName.isEmpty()) { showSorryMessage(xi18n("Could not set empty name for this object.")); *success = false; return; } KexiWindow *window = openedWindowFor(item); if (window) { QString msg = xi18nc("@info", "Before renaming object %1 it should be closed." "Do you want to close it?", item->name()); int r = KMessageBox::questionYesNo(this, msg, QString(), KStandardGuiItem::closeWindow(), KStandardGuiItem::cancel()); if (r != KMessageBox::Yes) { *success = false; return; } } setMessagesEnabled(false); //to avoid double messages const bool res = d->prj->renameObject(item, newName); setMessagesEnabled(true); if (!res) { showErrorMessage(xi18nc("@info", "Renaming object %1 failed.", newName), d->prj); *success = false; return; } } void KexiMainWindow::setObjectCaption(KexiPart::Item *item, const QString& _newCaption, bool *success) { Q_ASSERT(success); if (d->userMode) { *success = false; return; } QString newCaption = _newCaption.trimmed(); setMessagesEnabled(false); //to avoid double messages const bool res = d->prj->setObjectCaption(item, newCaption); setMessagesEnabled(true); if (!res) { showErrorMessage(xi18nc("@info", "Setting caption for object %1 failed.", newCaption), d->prj); *success = false; return; } } void KexiMainWindow::slotObjectRenamed(const KexiPart::Item &item, const QString& oldName) { Q_UNUSED(oldName); #ifndef KEXI_NO_PENDING_DIALOGS Private::PendingJobType pendingType; KexiWindow *window = d->openedWindowFor(&item, pendingType); if (pendingType != Private::NoJob) return; #else KexiWindow *window = openedWindowFor(&item); #endif if (!window) return; //change item window->updateCaption(); if (static_cast(currentWindow()) == window)//optionally, update app. caption updateAppCaption(); } void KexiMainWindow::acceptPropertySetEditing() { if (d->propEditor) d->propEditor->editor()->acceptInput(); } void KexiMainWindow::propertySetSwitched(KexiWindow *window, bool force, bool preservePrevSelection, bool sortedProperties, const QByteArray& propertyToSelect) { KexiWindow* _currentWindow = currentWindow(); //qDebug() << "currentWindow(): " // << (_currentWindow ? _currentWindow->windowTitle() : QString("NULL")) // << " window: " << (window ? window->windowTitle() : QString("NULL")); if (_currentWindow && _currentWindow != window) { d->propertySet = 0; //we'll need to move to another prop. set return; } if (d->propEditor) { KPropertySet *newSet = _currentWindow ? _currentWindow->propertySet() : 0; if (!newSet || (force || static_cast(d->propertySet) != newSet)) { d->propertySet = newSet; if (preservePrevSelection || force) { KPropertyEditorView::SetOptions options = KPropertyEditorView::ExpandChildItems; if (preservePrevSelection) { options |= KPropertyEditorView::PreservePreviousSelection; } if (sortedProperties) { options |= KPropertyEditorView::AlphabeticalOrder; } if (propertyToSelect.isEmpty()) { d->propEditor->editor()->changeSet(d->propertySet, options); } else { d->propEditor->editor()->changeSet(d->propertySet, propertyToSelect, options); } } } } } void KexiMainWindow::slotDirtyFlagChanged(KexiWindow* window) { KexiPart::Item *item = window->partItem(); //update text in navigator and app. caption if (!d->userMode) { d->navigator->updateItemName(*item, window->isDirty()); } invalidateActions(); updateAppCaption(); d->mainWidget->tabWidget()->setTabText( d->mainWidget->tabWidget()->indexOf(window->parentWidget()), window->windowTitle()); } void KexiMainWindow::slotTipOfTheDay() { //! @todo } void KexiMainWindow::slotReportBug() { KexiBugReportDialog bugReport(this); bugReport.exec(); } bool KexiMainWindow::userMode() const { return d->userMode; } void KexiMainWindow::setupUserActions() { } void KexiMainWindow::slotToolsImportProject() { if (d->tabbedToolBar) d->tabbedToolBar->hideMainMenu(); showProjectMigrationWizard(QString(), QString()); } void KexiMainWindow::slotToolsImportTables() { if (project()) { QMap args; QDialog *dlg = KexiInternalPart::createModalDialogInstance("org.kexi-project.migration", "importtable", this, 0, &args); if (!dlg) return; //error msg has been shown by KexiInternalPart const int result = dlg->exec(); delete dlg; if (result != QDialog::Accepted) return; QString destinationTableName(args["destinationTableName"]); if (!destinationTableName.isEmpty()) { QString pluginId = "org.kexi-project.table"; bool openingCancelled; KexiMainWindow::openObject(pluginId, destinationTableName, Kexi::DataViewMode, &openingCancelled); } } } void KexiMainWindow::slotToolsCompactDatabase() { KexiProjectData *data = 0; KDbDriver *drv = 0; const bool projectWasOpened = d->prj; if (!d->prj) { KexiStartupDialog dlg( KexiStartupDialog::OpenExisting, 0, Kexi::connset(), this); if (dlg.exec() != QDialog::Accepted) return; if (dlg.selectedFileName().isEmpty()) { //! @todo add support for server based if needed? return; } KDbConnectionData cdata; cdata.setDatabaseName(dlg.selectedFileName()); //detect driver name for the selected file KexiStartupData::Import detectedImportAction; QString detectedDriverId; tristate res = KexiStartupHandler::detectActionForFile( &detectedImportAction, &detectedDriverId, QString() /*suggestedDriverId*/, cdata.databaseName(), 0, KexiStartupHandler::SkipMessages | KexiStartupHandler::ThisIsAProjectFile | KexiStartupHandler::DontConvert); if (true == res && !detectedImportAction) { cdata.setDriverId(detectedDriverId); drv = Kexi::driverManager().driver(cdata.driverId()); } if (!drv || !(drv->features() & KDbDriver::CompactingDatabaseSupported)) { KMessageBox::information(this, xi18n("Compacting database file %1 is not supported.", QDir::toNativeSeparators(cdata.databaseName()))); return; } data = new KexiProjectData(cdata); } else { //sanity if (!(d->prj && d->prj->dbConnection() && (d->prj->dbConnection()->driver()->features() & KDbDriver::CompactingDatabaseSupported))) return; KGuiItem yesItem(KStandardGuiItem::cont()); yesItem.setText(xi18nc("@action:button Compact database", "Compact")); if (KMessageBox::Yes != KMessageBox::questionYesNo(this, xi18n("The current project has to be closed before compacting the database. " "It will be open again after compacting.\n\nDo you want to continue?"), QString(), yesItem, KStandardGuiItem::cancel())) { return; } data = new KexiProjectData(*d->prj->data()); // a copy drv = d->prj->dbConnection()->driver(); const tristate res = closeProject(); if (~res || !res) { delete data; return; } } if (!drv->adminTools().vacuum(*data->connectionData(), data->databaseName())) { showErrorMessage(QString(), &drv->adminTools()); } if (projectWasOpened) openProject(*data); delete data; } tristate KexiMainWindow::showProjectMigrationWizard(const QString& mimeType, const QString& databaseName) { return d->showProjectMigrationWizard(mimeType, databaseName, 0); } tristate KexiMainWindow::showProjectMigrationWizard( const QString& mimeType, const QString& databaseName, const KDbConnectionData &cdata) { return d->showProjectMigrationWizard(mimeType, databaseName, &cdata); } tristate KexiMainWindow::executeItem(KexiPart::Item* item) { KexiPart::Info *info = item ? Kexi::partManager().infoForPluginId(item->pluginId()) : 0; if ((! info) || (! info->isExecuteSupported())) return false; KexiPart::Part *part = Kexi::partManager().part(info); if (!part) return false; return part->execute(item); } void KexiMainWindow::slotProjectImportDataTable() { //! @todo allow data appending (it is not possible now) if (d->userMode) return; QMap args; args.insert("sourceType", "file"); QDialog *dlg = KexiInternalPart::createModalDialogInstance( "org.kexi-project.importexport.csv", "KexiCSVImportDialog", this, 0, &args); if (!dlg) return; //error msg has been shown by KexiInternalPart dlg->exec(); delete dlg; } tristate KexiMainWindow::executeCustomActionForObject(KexiPart::Item* item, const QString& actionName) { if (actionName == "exportToCSV") return exportItemAsDataTable(item); else if (actionName == "copyToClipboardAsCSV") return copyItemToClipboardAsDataTable(item); qWarning() << "no such action:" << actionName; return false; } tristate KexiMainWindow::exportItemAsDataTable(KexiPart::Item* item) { if (!item) return false; QMap args; if (!checkForDirtyFlagOnExport(item, &args)) { return false; } //! @todo: accept record changes... args.insert("destinationType", "file"); args.insert("itemId", QString::number(item->identifier())); QDialog *dlg = KexiInternalPart::createModalDialogInstance( "org.kexi-project.importexport.csv", "KexiCSVExportWizard", this, 0, &args); if (!dlg) return false; //error msg has been shown by KexiInternalPart int result = dlg->exec(); delete dlg; return result == QDialog::Rejected ? tristate(cancelled) : tristate(true); } bool KexiMainWindow::checkForDirtyFlagOnExport(KexiPart::Item *item, QMap *args) { //! @todo: handle tables if (item->pluginId() != "org.kexi-project.query") { return true; } KexiWindow * itemWindow = openedWindowFor(item); if (itemWindow && itemWindow->isDirty()) { tristate result; if (item->neverSaved()) { result = true; } else { int prevWindowId = 0; if (!itemWindow->isVisible()) { prevWindowId = currentWindow()->id(); activateWindow(itemWindow->id()); } result = askOnExportingChangedQuery(item); if (prevWindowId != 0) { activateWindow(prevWindowId); } } if (~result) { return false; } else if (true == result) { args->insert("useTempQuery","1"); } } return true; } tristate KexiMainWindow::askOnExportingChangedQuery(KexiPart::Item *item) const { const KMessageBox::ButtonCode result = KMessageBox::warningYesNoCancel(const_cast(this), xi18nc("@info", "Design of query %1 that you want to export data" " from is changed and has not yet been saved. Do you want to use data" " from the changed query for exporting or from its original (saved)" " version?", item->captionOrName()), QString(), KGuiItem(xi18nc("@action:button Export query data", "Use the Changed Query")), KGuiItem(xi18nc("@action:button Export query data", "Use the Original Query")), KStandardGuiItem::cancel(), QString(), KMessageBox::Notify | KMessageBox::Dangerous); if (result == KMessageBox::Yes) { return true; } else if (result == KMessageBox::No) { return false; } return cancelled; } bool KexiMainWindow::printItem(KexiPart::Item* item, const QString& titleText) { //! @todo printItem(item, KexiSimplePrintingSettings::load(), titleText); Q_UNUSED(item) Q_UNUSED(titleText) return false; } tristate KexiMainWindow::printItem(KexiPart::Item* item) { return printItem(item, QString()); } bool KexiMainWindow::printPreviewForItem(KexiPart::Item* item, const QString& titleText, bool reload) { //! @todo printPreviewForItem(item, KexiSimplePrintingSettings::load(), titleText, reload); Q_UNUSED(item) Q_UNUSED(titleText) Q_UNUSED(reload) return false; } tristate KexiMainWindow::printPreviewForItem(KexiPart::Item* item) { return printPreviewForItem(item, QString(), //! @todo store cached record data? true/*reload*/); } tristate KexiMainWindow::showPageSetupForItem(KexiPart::Item* item) { Q_UNUSED(item) //! @todo check if changes to this object's design are saved, if not: ask for saving //! @todo accept record changes... //! @todo printActionForItem(item, PageSetupForItem); return false; } //! @todo reenable printItem() when ported #if 0 bool KexiMainWindow::printItem(KexiPart::Item* item, const KexiSimplePrintingSettings& settings, const QString& titleText) { //! @todo: check if changes to this object's design are saved, if not: ask for saving //! @todo: accept record changes... KexiSimplePrintingCommand cmd(this, item->identifier()); //modal return cmd.print(settings, titleText); } bool KexiMainWindow::printPreviewForItem(KexiPart::Item* item, const KexiSimplePrintingSettings& settings, const QString& titleText, bool reload) { //! @todo: check if changes to this object's design are saved, if not: ask for saving //! @todo: accept record changes... KexiSimplePrintingCommand* cmd = d->openedCustomObjectsForItem( item, "KexiSimplePrintingCommand"); if (!cmd) { d->addOpenedCustomObjectForItem( item, cmd = new KexiSimplePrintingCommand(this, item->identifier()), "KexiSimplePrintingCommand" ); } return cmd->showPrintPreview(settings, titleText, reload); } tristate KexiMainWindow::printActionForItem(KexiPart::Item* item, PrintActionType action) { if (!item) return false; KexiPart::Info *info = Kexi::partManager().infoForPluginId(item->pluginId()); if (!info->isPrintingSupported()) return false; KexiWindow *printingWindow = d->pageSetupWindows[ item->identifier()]; if (printingWindow) { if (!activateWindow(*printingWindow)) return false; if (action == PreviewItem || action == PrintItem) { QTimer::singleShot(0, printingWindow->selectedView(), (action == PreviewItem) ? SLOT(printPreview()) : SLOT(print())); } return true; } #ifndef KEXI_NO_PENDING_DIALOGS Private::PendingJobType pendingType; KexiWindow *window = d->openedWindowFor(item, pendingType); if (pendingType != Private::NoJob) return cancelled; #else KexiWindow *window = openedWindowFor(item); #endif if (window) { // accept record changes QWidget *prevFocusWidget = focusWidget(); window->setFocus(); d->action_data_save_row->activate(QAction::Trigger); if (prevFocusWidget) prevFocusWidget->setFocus(); // opened: check if changes made to this dialog are saved, if not: ask for saving if (window->neverSaved()) //sanity check return false; if (window->isDirty()) { KGuiItem saveChanges(KStandardGuiItem::save()); saveChanges.setToolTip(futureI18n("Save changes")); saveChanges.setWhatsThis( futureI18n("Pressing this button will save all recent changes made in \"%1\" object.", item->name())); KGuiItem doNotSave(KStandardGuiItem::no()); doNotSave.setWhatsThis( futureI18n("Pressing this button will ignore all unsaved changes made in \"%1\" object.", window->partItem()->name())); QString question; if (action == PrintItem) question = futureI18n("Do you want to save changes before printing?"); else if (action == PreviewItem) question = futureI18n("Do you want to save changes before making print preview?"); else if (action == PageSetupForItem) question = futureI18n("Do you want to save changes before showing page setup?"); else return false; const KMessageBox::ButtonCode questionRes = KMessageBox::warningYesNoCancel(this, "

    " + window->part()->i18nMessage("Design of object %1 has been modified.", window) .subs(item->name()) + "

    " + question + "

    ", QString(), saveChanges, doNotSave); if (KMessageBox::Cancel == questionRes) return cancelled; if (KMessageBox::Yes == questionRes) { tristate savingRes = saveObject(window, QString(), DoNotAsk); if (true != savingRes) return savingRes; } } } KexiPart::Part * printingPart = Kexi::partManager().partForClass("org.kexi-project.simpleprinting"); if (!printingPart) printingPart = new KexiSimplePrintingPart(); //hardcoded as there're no .desktop file KexiPart::Item* printingPartItem = d->prj->createPartItem( printingPart, item->name() //<-- this will look like "table1 : printing" on the window list ); QMap staticObjectArgs; staticObjectArgs["identifier"] = QString::number(item->identifier()); if (action == PrintItem) staticObjectArgs["action"] = "print"; else if (action == PreviewItem) staticObjectArgs["action"] = "printPreview"; else if (action == PageSetupForItem) staticObjectArgs["action"] = "pageSetup"; else return false; bool openingCancelled; printingWindow = openObject(printingPartItem, Kexi::DesignViewMode, &openingCancelled, &staticObjectArgs); if (openingCancelled) return cancelled; if (!printingWindow) //sanity return false; d->pageSetupWindows.insert(item->identifier(), printingWindow); d->pageSetupWindowItemID2dataItemID_map.insert( printingWindow->partItem()->identifier(), item->identifier()); return true; } #endif void KexiMainWindow::slotEditCopySpecialDataTable() { KexiPart::Item* item = d->navigator->selectedPartItem(); if (item) copyItemToClipboardAsDataTable(item); } tristate KexiMainWindow::copyItemToClipboardAsDataTable(KexiPart::Item* item) { if (!item) return false; QMap args; if (!checkForDirtyFlagOnExport(item, &args)) { return false; } args.insert("destinationType", "clipboard"); args.insert("itemId", QString::number(item->identifier())); QDialog *dlg = KexiInternalPart::createModalDialogInstance( "org.kexi-project.importexport.csv", "KexiCSVExportWizard", this, 0, &args); if (!dlg) return false; //error msg has been shown by KexiInternalPart const int result = dlg->exec(); delete dlg; return result == QDialog::Rejected ? tristate(cancelled) : tristate(true); } void KexiMainWindow::slotEditPasteSpecialDataTable() { //! @todo allow data appending (it is not possible now) if (d->userMode) return; QMap args; args.insert("sourceType", "clipboard"); QDialog *dlg = KexiInternalPart::createModalDialogInstance( "org.kexi-project.importexport.csv", "KexiCSVImportDialog", this, 0, &args); if (!dlg) return; //error msg has been shown by KexiInternalPart dlg->exec(); delete dlg; } void KexiMainWindow::slotEditFind() { KexiSearchAndReplaceViewInterface* iface = d->currentViewSupportingSearchAndReplaceInterface(); if (!iface) return; d->updateFindDialogContents(true/*create if does not exist*/); d->findDialog()->setReplaceMode(false); d->findDialog()->show(); d->findDialog()->activateWindow(); d->findDialog()->raise(); } void KexiMainWindow::slotEditFind(bool next) { KexiSearchAndReplaceViewInterface* iface = d->currentViewSupportingSearchAndReplaceInterface(); if (!iface) return; tristate res = iface->find( d->findDialog()->valueToFind(), d->findDialog()->options(), next); if (~res) return; d->findDialog()->updateMessage(true == res); //! @todo result } void KexiMainWindow::slotEditFindNext() { slotEditFind(true); } void KexiMainWindow::slotEditFindPrevious() { slotEditFind(false); } void KexiMainWindow::slotEditReplace() { KexiSearchAndReplaceViewInterface* iface = d->currentViewSupportingSearchAndReplaceInterface(); if (!iface) return; d->updateFindDialogContents(true/*create if does not exist*/); d->findDialog()->setReplaceMode(true); //! @todo slotEditReplace() d->findDialog()->show(); d->findDialog()->activateWindow(); } void KexiMainWindow::slotEditReplaceNext() { slotEditReplace(false); } void KexiMainWindow::slotEditReplace(bool all) { KexiSearchAndReplaceViewInterface* iface = d->currentViewSupportingSearchAndReplaceInterface(); if (!iface) return; //! @todo add question: "Do you want to replace every occurrence of \"%1\" with \"%2\"? //! You won't be able to undo this." + "Do not ask again". tristate res = iface->findNextAndReplace( d->findDialog()->valueToFind(), d->findDialog()->valueToReplaceWith(), d->findDialog()->options(), all); d->findDialog()->updateMessage(true == res); //! @todo result } void KexiMainWindow::slotEditReplaceAll() { slotEditReplace(true); } void KexiMainWindow::highlightObject(const QString& pluginId, const QString& name) { slotShowNavigator(); if (!d->prj) return; KexiPart::Item *item = d->prj->itemForPluginId(pluginId, name); if (!item) return; if (d->navigator) { d->navigator->selectItem(*item); } } void KexiMainWindow::slotPartItemSelectedInNavigator(KexiPart::Item* item) { Q_UNUSED(item); } KToolBar *KexiMainWindow::toolBar(const QString& name) const { return d->tabbedToolBar ? d->tabbedToolBar->toolBar(name) : 0; } void KexiMainWindow::appendWidgetToToolbar(const QString& name, QWidget* widget) { if (d->tabbedToolBar) d->tabbedToolBar->appendWidgetToToolbar(name, widget); } void KexiMainWindow::setWidgetVisibleInToolbar(QWidget* widget, bool visible) { if (d->tabbedToolBar) d->tabbedToolBar->setWidgetVisibleInToolbar(widget, visible); } void KexiMainWindow::addToolBarAction(const QString& toolBarName, QAction *action) { if (d->tabbedToolBar) d->tabbedToolBar->addAction(toolBarName, action); } void KexiMainWindow::updatePropertyEditorInfoLabel(const QString& textToDisplayForNullSet) { d->propEditor->updateInfoLabelForPropertySet(d->propertySet, textToDisplayForNullSet); } void KexiMainWindow::addSearchableModel(KexiSearchableModel *model) { if (d->tabbedToolBar) { d->tabbedToolBar->addSearchableModel(model); } } void KexiMainWindow::setReasonableDialogSize(QDialog *dialog) { dialog->setMinimumSize(600, 400); dialog->resize(size() * 0.8); } void KexiMainWindow::restoreDesignTabAndActivateIfNeeded(const QString &tabName) { if (!d->tabbedToolBar) { return; } d->tabbedToolBar->showTab(tabName); if (currentWindow() && currentWindow()->partItem() && currentWindow()->partItem()->identifier() != 0) // for unstored items id can be < 0 { const QString tabToActivate = d->tabsToActivateOnShow.value( currentWindow()->partItem()->identifier()); //qDebug() << "tabToActivate:" << tabToActivate << "tabName:" << tabName; if (tabToActivate == tabName) { d->tabbedToolBar->setCurrentTab(tabToActivate); } } } void KexiMainWindow::restoreDesignTabIfNeeded(const QString &pluginId, Kexi::ViewMode viewMode, int previousItemId) { //qDebug() << pluginId << viewMode << previousItemId; if (viewMode == Kexi::DesignViewMode) { switch (d->prj->typeIdForPluginId(pluginId)) { case KexiPart::FormObjectType: { hideDesignTab(previousItemId, "org.kexi-project.report"); restoreDesignTabAndActivateIfNeeded("form"); break; } case KexiPart::ReportObjectType: { hideDesignTab(previousItemId, "org.kexi-project.form"); restoreDesignTabAndActivateIfNeeded("report"); break; } default: hideDesignTab(previousItemId); } } else { hideDesignTab(previousItemId); } } void KexiMainWindow::activateDesignTab(const QString &pluginId) { if (!d->tabbedToolBar) { return; } switch (d->prj->typeIdForPluginId(pluginId)) { case KexiPart::FormObjectType: d->tabbedToolBar->setCurrentTab("form"); break; case KexiPart::ReportObjectType: d->tabbedToolBar->setCurrentTab("report"); break; default:; } } void KexiMainWindow::activateDesignTabIfNeeded(const QString &pluginId, Kexi::ViewMode viewMode) { if (!d->tabbedToolBar) { return; } const QString tabToActivate = d->tabsToActivateOnShow.value(currentWindow()->partItem()->identifier()); //qDebug() << pluginId << viewMode << tabToActivate; if (viewMode == Kexi::DesignViewMode && tabToActivate.isEmpty()) { activateDesignTab(pluginId); } else { d->tabbedToolBar->setCurrentTab(tabToActivate); } } void KexiMainWindow::hideDesignTab(int itemId, const QString &pluginId) { if (!d->tabbedToolBar) { return; } //qDebug() << itemId << pluginId; if ( itemId > 0 && d->tabbedToolBar->currentWidget()) { const QString currentTab = d->tabbedToolBar->currentWidget()->objectName(); //qDebug() << "d->tabsToActivateOnShow.insert" << itemId << currentTab; d->tabsToActivateOnShow.insert(itemId, currentTab); } switch (d->prj->typeIdForPluginId(pluginId)) { case KexiPart::FormObjectType: d->tabbedToolBar->hideTab("form"); break; case KexiPart::ReportObjectType: d->tabbedToolBar->hideTab("report"); break; default: d->tabbedToolBar->hideTab("form"); d->tabbedToolBar->hideTab("report"); } } void KexiMainWindow::showDesignTabIfNeeded(int previousItemId) { if (d->insideCloseWindow && d->tabbedToolBar) return; if (currentWindow()) { restoreDesignTabIfNeeded(currentWindow()->partItem()->pluginId(), currentWindow()->currentViewMode(), previousItemId); } else { hideDesignTab(previousItemId); } } KexiUserFeedbackAgent* KexiMainWindow::userFeedbackAgent() const { return &d->userFeedback; } KexiMigrateManagerInterface* KexiMainWindow::migrateManager() { if (!d->migrateManager) { d->migrateManager = dynamic_cast( KexiInternalPart::createObjectInstance( "org.kexi-project.migration", "manager", this, this, nullptr)); } return d->migrateManager; } void KexiMainWindow::toggleFullScreen(bool isFullScreen) { static bool isTabbarRolledDown; if (d->tabbedToolBar) { if (isFullScreen) { isTabbarRolledDown = !d->tabbedToolBar->isRolledUp(); if (isTabbarRolledDown) { d->tabbedToolBar->toggleRollDown(); } } else { if (isTabbarRolledDown && d->tabbedToolBar->isRolledUp()) { d->tabbedToolBar->toggleRollDown(); } } } const Qt::WindowStates s = windowState() & Qt::WindowMaximized; if (isFullScreen) { setWindowState(windowState() | Qt::WindowFullScreen | s); } else { setWindowState((windowState() & ~Qt::WindowFullScreen)); showMaximized(); } } diff --git a/src/migration/importwizard.cpp b/src/migration/importwizard.cpp index 797c8ba1e..8fe48c25b 100644 --- a/src/migration/importwizard.cpp +++ b/src/migration/importwizard.cpp @@ -1,1132 +1,1132 @@ /* This file is part of the KDE project Copyright (C) 2004-2009 Adam Pigg Copyright (C) 2004-2016 Jarosław Staniek Copyright (C) 2005 Martin Ellis 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 "importwizard.h" #include "keximigrate.h" #include "importoptionsdlg.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace KexiMigration; class Q_DECL_HIDDEN ImportWizard::Private { public: Private(QMap* args_) : srcProjectSelector(0) , fileBasedDstWasPresented(false) , setupFileBasedSrcNeeded(true) , importExecuted(false) , prjSet(0) , args(args_) { } ~Private() { delete prjSet; } QWidget *introPageWidget, *srcConnPageWidget, *srcDBPageWidget, *dstTypePageWidget, *dstPageWidget, *importTypePageWidget, *importingPageWidget, *finishPageWidget; KPageWidgetItem *introPageItem, *srcConnPageItem, *srcDBPageItem, *dstTypePageItem, *dstPageItem, *importTypePageItem, *importingPageItem, *finishPageItem; QGroupBox *importTypeGroupBox; QRadioButton *importTypeStructureAndDataCheckBox; QRadioButton *importTypeStructureOnlyCheckBox; KexiDBTitlePage* dstTitlePageWidget; KPageWidgetItem *dstTitlePageItem; KexiPrjTypeSelector *dstPrjTypeSelector; KexiConnectionSelectorWidget *srcConn, *dstConn; QString driverIdForSelectedSource; QLineEdit *dstNewDBTitleLineEdit; QLabel *dstNewDBNameLabel; QLineEdit *dstNewDBNameLineEdit; QLabel *dstNewDBNameUrlLabel; KUrlRequester *dstNewDBNameUrl; KexiStartupFileHandler *dstNewDBFileHandler; KexiProjectSelectorWidget *srcProjectSelector; QLabel *lblImportingTxt, *lblImportingErrTxt, *finishLbl; QCheckBox *openImportedProjectCheckBox; bool fileBasedDstWasPresented; bool setupFileBasedSrcNeeded; bool importExecuted; //!< used in import() KexiProjectSet* prjSet; QProgressBar *progressBar; QPushButton* importOptionsButton; QMap *args; QString predefinedDatabaseName, predefinedMimeType; KDbConnectionData *predefinedConnectionData; MigrateManager migrateManager; //!< object lives here, so status messages can be globally preserved //! Encoding for source db. Currently only used for MDB driver. //! @todo Hardcoded. Move to KexiMigrate driver's impl. QString sourceDBEncoding; }; //=========================================================== // ImportWizard::ImportWizard(QWidget *parent, QMap* args) : KAssistantDialog(parent) , d(new Private(args)) { setModal(true); setWindowTitle(xi18nc("@title:window", "Import Database")); setWindowIcon(KexiIcon("database-import")); KexiMainWindowIface::global()->setReasonableDialogSize(this); parseArguments(); setupIntro(); setupSrcConn(); setupSrcDB(); setupDstType(); setupDstTitle(); setupDst(); setupImportType(); setupImporting(); setupFinish(); connect(this, SIGNAL(currentPageChanged(KPageWidgetItem*,KPageWidgetItem*)), this, SLOT(slot_currentPageChanged(KPageWidgetItem*,KPageWidgetItem*))); connect(button(QDialogButtonBox::Help), &QPushButton::clicked, this, &ImportWizard::helpClicked); if (d->predefinedConnectionData) { // setup wizard for predefined server source d->srcConn->showAdvancedConn(); setAppropriate(d->srcConnPageItem, false); setAppropriate(d->srcDBPageItem, false); } else if (!d->predefinedDatabaseName.isEmpty()) { // setup wizard for predefined source // (used when external project type was opened in Kexi, e.g. mdb file) setAppropriate(d->srcConnPageItem, false); setAppropriate(d->srcDBPageItem, false); d->srcConn->showSimpleConn(); d->srcConn->setSelectedFileName(d->predefinedDatabaseName); #if 0 //disable all prev pages except "welcome" page for (int i = 0; i < indexOf(d->dstTypePage); i++) { if (page(i) != d->introPage) setAppropriate(page(i), false); } #endif } d->sourceDBEncoding = QString::fromLatin1(KexiUtils::encoding()); //default } //=========================================================== // ImportWizard::~ImportWizard() { delete d; } //=========================================================== // void ImportWizard::parseArguments() { d->predefinedConnectionData = 0; if (!d->args) return; if (!(*d->args)["databaseName"].isEmpty() && !(*d->args)["mimeType"].isEmpty()) { d->predefinedDatabaseName = (*d->args)["databaseName"]; d->predefinedMimeType = (*d->args)["mimeType"]; if (d->args->contains("connectionData")) { bool ok; d->predefinedConnectionData = new KDbConnectionData( KDbUtils::deserializeMap((*d->args)["connectionData"]), &ok); if (!ok) { delete d->predefinedConnectionData; d->predefinedConnectionData = 0; } } } d->args->clear(); } QString ImportWizard::selectedSourceFileName() const { if (d->predefinedDatabaseName.isEmpty()) return d->srcConn->selectedFileName(); return d->predefinedDatabaseName; } //=========================================================== // void ImportWizard::setupIntro() { d->introPageWidget = new QWidget(this); QVBoxLayout *vbox = new QVBoxLayout(); d->introPageWidget->setLayout(vbox); KexiUtils::setStandardMarginsAndSpacing(vbox); QLabel *lblIntro = new QLabel(d->introPageWidget); lblIntro->setAlignment(Qt::AlignTop | Qt::AlignLeft); lblIntro->setWordWrap(true); lblIntro->setTextFormat(Qt::RichText); QString msg; if (d->predefinedConnectionData) { //predefined import: server source msg = xi18nc("@info", "Database Importing Assistant is about to import %1 database " "(connection %2) into a Kexi project.", d->predefinedDatabaseName, d->predefinedConnectionData->toUserVisibleString()); } else if (!d->predefinedDatabaseName.isEmpty()) { //predefined import: file source //! @todo this message is currently ok for files only QMimeDatabase db; QMimeType mime = db.mimeTypeForName(d->predefinedMimeType); if (!mime.isValid()) { qWarning() << QString("'%1' mimetype not installed!").arg(d->predefinedMimeType); } d->driverIdForSelectedSource = driverIdForMimeType(mime); msg = xi18nc("@info", "Database Importing Assistant is about to import %1 file " "of type %2 into a Kexi project.", QDir::toNativeSeparators(d->predefinedDatabaseName), mime.isValid() ? mime.comment() : "???"); } else { msg = xi18nc("@info", "Database Importing Assistant allows you to import an existing database " "into a Kexi project."); } // note: we're using .arg() here because the msg argument is already in rich-text format QString finalMessage = xi18nc("@info", "%1" "Click Next button to continue or " "Cancel button to exit this assistant.").arg(msg); lblIntro->setText(finalMessage); vbox->addWidget(lblIntro); d->introPageItem = new KPageWidgetItem(d->introPageWidget, xi18n("Welcome to the Database Importing Assistant")); addPage(d->introPageItem); } //=========================================================== // void ImportWizard::setupSrcConn() { d->srcConnPageWidget = new QWidget(this); QVBoxLayout *vbox = new QVBoxLayout(d->srcConnPageWidget); KexiUtils::setStandardMarginsAndSpacing(vbox); d->srcConn = new KexiConnectionSelectorWidget(&Kexi::connset(), "kfiledialog:///ProjectMigrationSourceDir", KFileWidget::Opening, d->srcConnPageWidget); d->srcConn->hideConnectonIcon(); d->srcConn->showSimpleConn(); QSet excludedFilters; //! @todo remove when support for kexi files as source prj is added in migration excludedFilters += KDb::defaultFileBasedDriverMimeType(); excludedFilters += "application/x-kexiproject-shortcut"; excludedFilters += "application/x-kexi-connectiondata"; d->srcConn->fileWidget->setExcludedFilters(excludedFilters); vbox->addWidget(d->srcConn); d->srcConnPageItem = new KPageWidgetItem(d->srcConnPageWidget, xi18n("Select Location for Source Database")); addPage(d->srcConnPageItem); } //=========================================================== // void ImportWizard::setupSrcDB() { // arrivesrcdbPage creates widgets on that page d->srcDBPageWidget = new QWidget(this); d->srcDBPageItem = new KPageWidgetItem(d->srcDBPageWidget, xi18n("Select Source Database")); addPage(d->srcDBPageItem); } //=========================================================== // void ImportWizard::setupDstType() { d->dstTypePageWidget = new QWidget(this); QVBoxLayout *vbox = new QVBoxLayout(d->dstTypePageWidget); KexiUtils::setStandardMarginsAndSpacing(vbox); QHBoxLayout *hbox = new QHBoxLayout; vbox->addLayout(hbox); KexiUtils::setStandardMarginsAndSpacing(hbox); QLabel *lbl = new QLabel(xi18n("Destination database type:") /*+ ' '*/, d->dstTypePageWidget); lbl->setAlignment(Qt::AlignLeft | Qt::AlignTop); lbl->setTextFormat(Qt::RichText); hbox->addWidget(lbl); d->dstPrjTypeSelector = new KexiPrjTypeSelector(d->dstTypePageWidget); hbox->addWidget(d->dstPrjTypeSelector); d->dstPrjTypeSelector->option_file->setText(xi18n("Database project stored in a file")); d->dstPrjTypeSelector->option_server->setText(xi18n("Database project stored on a server")); hbox->addStretch(1); vbox->addStretch(1); d->dstTypePageItem = new KPageWidgetItem(d->dstTypePageWidget, xi18n("Select Destination Database Type")); addPage(d->dstTypePageItem); } //=========================================================== // void ImportWizard::setupDstTitle() { d->dstTitlePageWidget = new KexiDBTitlePage(xi18n("Destination project's caption:"), this); d->dstTitlePageWidget->layout()->setMargin(KexiUtils::marginHint()); d->dstTitlePageWidget->updateGeometry(); d->dstNewDBTitleLineEdit = d->dstTitlePageWidget->le_title; connect(d->dstNewDBTitleLineEdit, SIGNAL(textChanged(QString)), this, SLOT(destinationTitleTextChanged(QString))); d->dstNewDBNameUrlLabel = d->dstTitlePageWidget->label_requester; d->dstNewDBNameUrl = d->dstTitlePageWidget->file_requester; d->dstNewDBFileHandler = new KexiStartupFileHandler( QUrl("kfiledialog:///ProjectMigrationDestinationDir"), KexiStartupFileHandler::SavingFileBasedDB, d->dstTitlePageWidget->file_requester); d->dstNewDBNameLabel = new QLabel(xi18n("Destination project's name:"), d->dstTitlePageWidget); d->dstTitlePageWidget->formLayout->setWidget(2, QFormLayout::LabelRole, d->dstNewDBNameLabel); d->dstNewDBNameLineEdit = new QLineEdit(d->dstTitlePageWidget); d->dstNewDBNameLineEdit->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed)); KDbIdentifierValidator *idValidator = new KDbIdentifierValidator(this); idValidator->setLowerCaseForced(true); d->dstNewDBNameLineEdit->setValidator(idValidator); d->dstTitlePageWidget->formLayout->setWidget(2, QFormLayout::FieldRole, d->dstNewDBNameLineEdit); d->dstTitlePageItem = new KPageWidgetItem(d->dstTitlePageWidget, xi18n("Enter Destination Database Project's Caption")); addPage(d->dstTitlePageItem); } void ImportWizard::destinationTitleTextChanged(const QString & text) { Q_UNUSED(text); updateDestinationDBFileName(); } void ImportWizard::updateDestinationDBFileName() { d->dstNewDBFileHandler->updateUrl(d->dstNewDBTitleLineEdit->text()); d->dstNewDBNameLineEdit->setText(d->dstNewDBTitleLineEdit->text()); } //=========================================================== // void ImportWizard::setupDst() { d->dstPageWidget = new QWidget(this); QVBoxLayout *vbox = new QVBoxLayout(d->dstPageWidget); KexiUtils::setStandardMarginsAndSpacing(vbox); d->dstConn = new KexiConnectionSelectorWidget(&Kexi::connset(), "kfiledialog:///ProjectMigrationDestinationDir", KFileWidget::Saving, d->dstPageWidget); d->dstConn->hideHelpers(); vbox->addWidget(d->dstConn); connect(d->dstConn, SIGNAL(connectionItemExecuted(ConnectionDataLVItem*)), this, SLOT(next())); d->dstConn->showSimpleConn(); //anyway, db files will be _saved_ d->dstConn->fileWidget->setMode(KexiFileWidget::SavingFileBasedDB); d->dstPageItem = new KPageWidgetItem(d->dstPageWidget, xi18n("Select Location for Destination Database Project")); addPage(d->dstPageItem); } //=========================================================== // void ImportWizard::setupImportType() { d->importTypePageWidget = new QWidget(this); QVBoxLayout *vbox = new QVBoxLayout(d->importTypePageWidget); KexiUtils::setStandardMarginsAndSpacing(vbox); d->importTypeGroupBox = new QGroupBox(d->importTypePageWidget); vbox->addWidget(d->importTypeGroupBox); QVBoxLayout *importTypeGroupBoxLyr = new QVBoxLayout; importTypeGroupBoxLyr->addWidget( d->importTypeStructureAndDataCheckBox = new QRadioButton( xi18nc("Scope of import", "Structure and data"), d->importTypeGroupBox)); d->importTypeStructureAndDataCheckBox->setChecked(true); importTypeGroupBoxLyr->addWidget( d->importTypeStructureOnlyCheckBox = new QRadioButton( xi18nc("Scope of import", "Structure only"), d->importTypeGroupBox)); importTypeGroupBoxLyr->addStretch(1); d->importTypeGroupBox->setLayout(importTypeGroupBoxLyr); d->importTypePageItem = new KPageWidgetItem(d->importTypePageWidget, xi18n("Select Scope of Import")); addPage(d->importTypePageItem); } //=========================================================== // void ImportWizard::setupImporting() { d->importingPageWidget = new QWidget(this); d->importingPageWidget->hide(); QVBoxLayout *vbox = new QVBoxLayout(d->importingPageWidget); KexiUtils::setStandardMarginsAndSpacing(vbox); d->lblImportingTxt = new QLabel(d->importingPageWidget); d->lblImportingTxt->setAlignment(Qt::AlignTop | Qt::AlignLeft); d->lblImportingTxt->setWordWrap(true); d->lblImportingTxt->setTextFormat(Qt::RichText); d->lblImportingErrTxt = new QLabel(d->importingPageWidget); d->lblImportingErrTxt->setAlignment(Qt::AlignTop | Qt::AlignLeft); d->lblImportingErrTxt->setWordWrap(true); d->lblImportingErrTxt->setTextFormat(Qt::RichText); d->progressBar = new QProgressBar(d->importingPageWidget); d->progressBar->setRange(0, 100); d->progressBar->hide(); vbox->addWidget(d->lblImportingTxt); vbox->addWidget(d->lblImportingErrTxt); vbox->addStretch(1); QWidget *options_widget = new QWidget(d->importingPageWidget); vbox->addWidget(options_widget); QVBoxLayout *options_vbox = new QVBoxLayout(options_widget); options_vbox->setSpacing(KexiUtils::spacingHint()); QHBoxLayout *importOptionsButtonLyr = new QHBoxLayout; options_vbox->addLayout(importOptionsButtonLyr); d->importOptionsButton = new QPushButton(koIcon("configure"), xi18n("Advanced Options"), options_widget); connect(d->importOptionsButton, SIGNAL(clicked()), this, SLOT(slotOptionsButtonClicked())); importOptionsButtonLyr->addStretch(1); importOptionsButtonLyr->addWidget(d->importOptionsButton); importOptionsButtonLyr->addStretch(1); options_vbox->addStretch(1); vbox->addWidget(d->progressBar); vbox->addStretch(2); d->importingPageWidget->show(); d->importingPageItem = new KPageWidgetItem(d->importingPageWidget, xi18n("Importing")); addPage(d->importingPageItem); } //=========================================================== // void ImportWizard::setupFinish() { d->finishPageWidget = new QWidget(this); d->finishPageWidget->hide(); QVBoxLayout *vbox = new QVBoxLayout(d->finishPageWidget); KexiUtils::setStandardMarginsAndSpacing(vbox); d->finishLbl = new QLabel(d->finishPageWidget); d->finishLbl->setAlignment(Qt::AlignTop | Qt::AlignLeft); d->finishLbl->setWordWrap(true); d->finishLbl->setTextFormat(Qt::RichText); vbox->addWidget(d->finishLbl); d->openImportedProjectCheckBox = new QCheckBox(xi18n("Open imported project"), d->finishPageWidget); d->openImportedProjectCheckBox->setChecked(true); vbox->addSpacing(KexiUtils::spacingHint()); vbox->addWidget(d->openImportedProjectCheckBox); vbox->addStretch(1); d->finishPageItem = new KPageWidgetItem(d->finishPageWidget, xi18n("Success")); addPage(d->finishPageItem); } //=========================================================== // bool ImportWizard::checkUserInput() { QString issues; if (d->dstNewDBTitleLineEdit->text().isEmpty()) { issues = xi18nc("@info", "No new database name was entered."); } Kexi::ObjectStatus result; KexiMigrate* sourceDriver = prepareImport(result); if (sourceDriver && sourceDriver->isSourceAndDestinationDataSourceTheSame()) { // note: we're using .arg() here because the 'issues' argument is already in rich-text format issues = xi18nc("@info", "%1Source database is the same as destination.") .arg(issues); } if (!issues.isEmpty()) { // note: we're using .arg() here because the 'issues' argument is already in rich-text format d->lblImportingErrTxt->setText( xi18nc("@info", "Following issues were found with the data you entered:" "%1" "Please click Back button and correct these issues.") .arg(issues)); return false; } return true; } void ImportWizard::arriveSrcConnPage() { d->srcConnPageWidget->hide(); /*! @todo KexiFileWidget needs "open file" and "open server" modes in addition to just "open" */ if (d->setupFileBasedSrcNeeded) { d->setupFileBasedSrcNeeded = false; QSet additionalMimeTypes; d->srcConn->fileWidget->setMode(KexiFileWidget::Opening); d->srcConn->fileWidget->setAdditionalFilters(additionalMimeTypes); } /*! @todo Support different file extensions based on MigrationDriver */ d->srcConnPageWidget->show(); } void ImportWizard::arriveSrcDBPage() { if (fileBasedSrcSelected()) { //! @todo Back button doesn't work after selecting a file to import } else { if (!d->srcProjectSelector) { QVBoxLayout *vbox = new QVBoxLayout(d->srcDBPageWidget); d->srcProjectSelector = new KexiProjectSelectorWidget(d->srcDBPageWidget); vbox->addWidget(d->srcProjectSelector); KexiUtils::setStandardMarginsAndSpacing(vbox); d->srcProjectSelector->label()->setText(xi18n("Select source database you wish to import:")); } d->srcDBPageWidget->hide(); KDbConnectionData* condata = d->srcConn->selectedConnectionData(); Q_ASSERT(condata); Q_ASSERT(d->prjSet); d->srcProjectSelector->setProjectSet(d->prjSet); d->srcDBPageWidget->show(); } } void ImportWizard::arriveDstTitlePage() { d->dstNewDBNameUrlLabel->setVisible(fileBasedDstSelected()); d->dstNewDBNameUrl->setVisible(fileBasedDstSelected()); d->dstNewDBNameLabel->setVisible(!fileBasedDstSelected()); d->dstNewDBNameLineEdit->setVisible(!fileBasedDstSelected()); if (fileBasedSrcSelected()) { const QString fname(selectedSourceFileName()); QString suggestedDBName(QFileInfo(fname).fileName()); const QFileInfo fi(suggestedDBName); suggestedDBName = suggestedDBName.left(suggestedDBName.length() - (fi.completeSuffix().isEmpty() ? 0 : (fi.completeSuffix().length() + 1))); d->dstNewDBTitleLineEdit->setText(suggestedDBName); } else { if (d->predefinedConnectionData) { // server source db is predefined d->dstNewDBTitleLineEdit->setText(d->predefinedDatabaseName); } else { if (!d->srcProjectSelector || !d->srcProjectSelector->selectedProjectData()) { back(); //!< @todo return; } d->dstNewDBTitleLineEdit->setText(d->srcProjectSelector->selectedProjectData()->databaseName()); } } d->dstNewDBTitleLineEdit->selectAll(); d->dstNewDBTitleLineEdit->setFocus(); updateDestinationDBFileName(); } void ImportWizard::arriveDstPage() { if (fileBasedDstSelected()) { d->dstPageWidget->hide(); KAssistantDialog::next(); return; } else { d->dstConn->showAdvancedConn(); } d->dstPageWidget->show(); } void ImportWizard::arriveImportingPage() { d->importingPageWidget->hide(); nextButton()->setEnabled(checkUserInput()); d->lblImportingTxt->setText(xi18nc("@info", "All required information has now " "been gathered. Click Next button to start importing." "Depending on size of the database this may take some time." /*"Note: You may be asked for extra " "information such as field types if " "the wizard could not automatically " "determine this for you."*/)); //temp. hack for MS Access driver only //! @todo for other databases we will need KexiMigration::Conenction //! and KexiMigration::Driver classes bool showOptions = false; if (fileBasedSrcSelected()) { Kexi::ObjectStatus result; KexiMigrate* sourceDriver = prepareImport(result); if (sourceDriver) { showOptions = !result.error() && sourceDriver->propertyValue("source_database_has_nonunicode_encoding").toBool(); sourceDriver->setData(nullptr); } } if (showOptions) d->importOptionsButton->show(); else d->importOptionsButton->hide(); d->importingPageWidget->show(); } void ImportWizard::arriveFinishPage() { } bool ImportWizard::fileBasedSrcSelected() const { if (d->predefinedConnectionData) return false; // qDebug() << (d->srcConn->selectedConnectionType()==KexiConnectionSelectorWidget::FileBased); return d->srcConn->selectedConnectionType() == KexiConnectionSelectorWidget::FileBased; } bool ImportWizard::fileBasedDstSelected() const { return d->dstPrjTypeSelector->option_file->isChecked(); } void ImportWizard::progressUpdated(int percent) { d->progressBar->setValue(percent); qApp->processEvents(); } QString ImportWizard::driverIdForMimeType(const QMimeType &mime) const { if (!mime.isValid()) { return QString(); } const QStringList ids(d->migrateManager.driverIdsForMimeType(mime.name())); //! @todo do we want to return first migrate driver for the mime type or allow to select it? return ids.isEmpty() ? QString() : ids.first(); } QString ImportWizard::findDriverIdForSelectedSource() { if (fileBasedSrcSelected()) { QMimeDatabase db; QMimeType mime = db.mimeTypeForFile(selectedSourceFileName()); if (!mime.isValid() || mime.name() == "application/octet-stream" || mime.name() == "text/plain" || mime.name() == "application/zip") { //try by URL: mime = db.mimeTypeForFile(selectedSourceFileName()); } return driverIdForMimeType(mime); } //server-based QString sourceDriverId; if (d->predefinedConnectionData) { sourceDriverId = d->predefinedConnectionData->driverId(); } else if (d->srcConn->selectedConnectionData()) { sourceDriverId = d->srcConn->selectedConnectionData()->driverId(); } const QStringList migrationDriverIds(d->migrateManager.driverIdsForSourceDriver(sourceDriverId)); //! @todo First found driver ID is picked. It's OK as long as there is one migration //! driver per source database type. How about allowing users to pick migration driver? return migrationDriverIds.isEmpty() ? QString() : migrationDriverIds.first(); } //=========================================================== // void ImportWizard::accept() { if (d->args) { if ((!fileBasedDstSelected() && !d->args->contains("destinationConnectionShortcut")) || !d->openImportedProjectCheckBox->isChecked()) { //do not open dest db if used didn't want it //for server connections, destinationConnectionShortcut must be defined d->args->remove("destinationDatabaseName"); } } KAssistantDialog::accept(); } KexiMigrate* ImportWizard::prepareImport(Kexi::ObjectStatus& result) { KexiUtils::WaitCursor wait; // Start with a driver manager KDbDriverManager manager; //qDebug() << "Creating destination driver..."; // Get a driver to the destination database KDbDriver *destDriver = manager.driver( d->dstConn->selectedConnectionData() ? d->dstConn->selectedConnectionData()->driverId() : KDb::defaultFileBasedDriverId()); if (!destDriver || manager.result().isError()) { result.setStatus(manager.resultable()); qWarning() << "Manager error:" << manager.result(); } // Set up destination connection data KDbConnectionData *cdata = 0; QScopedPointer cdataDeleter; QString dbname; if (!result.error()) { if (d->dstConn->selectedConnectionData()) { //server-based project qDebug() << "Server destination..."; cdata = d->dstConn->selectedConnectionData(); dbname = d->dstNewDBNameLineEdit->text(); } else { //file-based project qDebug() << "File Destination..."; cdata = new KDbConnectionData(); cdataDeleter.reset(cdata); // ownership won't be transferred cdata->setCaption(d->dstNewDBTitleLineEdit->text()); cdata->setDriverId(KDb::defaultFileBasedDriverId()); dbname = d->dstTitlePageWidget->file_requester->url().toLocalFile(); cdata->setDatabaseName(dbname); qDebug() << "Current file name:" << dbname; } } // Find a source (migration) driver name if (!result.error()) { if (d->driverIdForSelectedSource.isEmpty()) result.setStatus(xi18n("No appropriate migration driver found."), d->migrateManager.possibleProblemsMessage()); } // Get a source (migration) driver KexiMigrate* sourceDriver = 0; if (!result.error()) { sourceDriver = d->migrateManager.driver(d->driverIdForSelectedSource); if (!sourceDriver || d->migrateManager.result().isError()) { qDebug() << "Import migrate driver error..."; result.setStatus(d->migrateManager.resultable()); } } KexiUtils::removeWaitCursor(); // Set up source (migration) data required for connection if (sourceDriver && !result.error() && cdata) { // Setup progress feedback for the GUI if (sourceDriver->progressSupported()) { d->progressBar->updateGeometry(); disconnect(sourceDriver, SIGNAL(progressPercent(int)), this, SLOT(progressUpdated(int))); connect(sourceDriver, SIGNAL(progressPercent(int)), this, SLOT(progressUpdated(int))); progressUpdated(0); } bool keepData; if (d->importTypeStructureAndDataCheckBox->isChecked()) { qDebug() << "Structure and data selected"; keepData = true; } else if (d->importTypeStructureOnlyCheckBox->isChecked()) { qDebug() << "structure only selected"; keepData = false; } else { qDebug() << "Neither radio button is selected (not possible?) presume keep data"; keepData = true; } KexiMigration::Data* md = new KexiMigration::Data(); md->setDestinationProjectData(new KexiProjectData(*cdata, dbname)); if (fileBasedSrcSelected()) { KDbConnectionData* conn_data = new KDbConnectionData(); conn_data->setDatabaseName(selectedSourceFileName()); md->source = conn_data; md->sourceName.clear(); } else { if (d->predefinedConnectionData) md->source = d->predefinedConnectionData; else md->source = d->srcConn->selectedConnectionData(); if (!d->predefinedDatabaseName.isEmpty()) md->sourceName = d->predefinedDatabaseName; else md->sourceName = d->srcProjectSelector->selectedProjectData()->databaseName(); //! @todo Aah, this is so C-like. Move to performImport(). } md->keepData = keepData; sourceDriver->setData(md); return sourceDriver; } return 0; } tristate ImportWizard::import() { d->importExecuted = true; Kexi::ObjectStatus result; KexiMigrate* sourceDriver = prepareImport(result); bool acceptingNeeded = false; // Perform import if (sourceDriver && !result.error()) { if (!d->sourceDBEncoding.isEmpty()) { sourceDriver->setPropertyValue("source_database_nonunicode_encoding", QVariant(d->sourceDBEncoding.toUpper().remove(' ')) // "CP1250", not "cp 1250" ); } if (!sourceDriver->checkIfDestinationDatabaseOverwritingNeedsAccepting(&result, &acceptingNeeded)) { qDebug() << "Abort import cause checkIfDestinationDatabaseOverwritingNeedsAccepting " "returned false."; return false; } qDebug() << sourceDriver->data()->destinationProjectData()->databaseName(); qDebug() << "Performing import..."; } if (sourceDriver && !result.error() && acceptingNeeded) { // ok, the destination-db already exists... if (KMessageBox::Yes != KMessageBox::warningYesNo(this, xi18nc("@info (don't add tags around %1, it's done already)", "Database %1 already exists." "Do you want to replace it with a new one?", KexiUtils::localizedStringToHtmlSubstring( sourceDriver->data()->destinationProjectData()->infoString())), 0, KGuiItem(xi18nc("@action:button Replace Database", "&Replace")), KStandardGuiItem::no())) { return cancelled; } } if (sourceDriver && !result.error() && sourceDriver->progressSupported()) { d->progressBar->show(); } if (sourceDriver && !result.error() && sourceDriver->performImport(&result)) { if (d->args) { d->args->insert("destinationDatabaseName", fileBasedDstSelected() ? sourceDriver->data()->destinationProjectData()->connectionData()->databaseName() : sourceDriver->data()->destinationProjectData()->databaseName()); QString destinationConnectionShortcut; if (d->dstConn->selectedConnectionData()) { destinationConnectionShortcut = Kexi::connset().fileNameForConnectionData(*d->dstConn->selectedConnectionData()); } if (!destinationConnectionShortcut.isEmpty()) { d->args->insert("destinationConnectionShortcut", destinationConnectionShortcut); } } d->finishPageItem->setHeader(xi18n("Success")); return true; } if (!sourceDriver || result.error()) { d->progressBar->setValue(0); d->progressBar->hide(); QString msg, details; KexiTextMessageHandler handler(&msg, &details); handler.showErrorMessage(&result); qDebug() << msg << "\n" << details; d->finishPageItem->setHeader(xi18n("Failure")); // note: we're using .arg() here because the msg and details arguments are already in rich-text format d->finishLbl->setText( xi18nc("@info", "Import failed." "%1" "%2" "You can click Back button and try again.") .arg(msg).arg(details)); return false; } return true; } void ImportWizard::reject() { KAssistantDialog::reject(); } //=========================================================== // void ImportWizard::next() { if (currentPage() == d->srcConnPageItem) { if (fileBasedSrcSelected() && /*! @todo use QUrl? */!QFileInfo(selectedSourceFileName()).isFile()) { KMessageBox::sorry(this, xi18n("Select source database filename.")); return; } KDbConnectionData* conndata = d->srcConn->selectedConnectionData(); if (!fileBasedSrcSelected() && !conndata) { KMessageBox::sorry(this, xi18n("Select source database.")); return; } d->driverIdForSelectedSource = findDriverIdForSelectedSource(); // cache KexiMigrate* import = d->migrateManager.driver(d->driverIdForSelectedSource); if (!import || d->migrateManager.result().isError()) { QString dbname; if (fileBasedSrcSelected()) dbname = selectedSourceFileName(); else dbname = conndata ? conndata->toUserVisibleString() : QString(); KMessageBox::error(this, dbname.isEmpty() ? xi18n("Could not import database. This type is not supported.") : xi18nc("@info", "Could not import database %1. " "This type is not supported.", dbname)); return; } if (!fileBasedSrcSelected()) { // make sure we have password if needed tristate passwordNeeded = false; if (conndata->password().isEmpty()) { passwordNeeded = KexiDBPasswordDialog::getPasswordIfNeeded(conndata, this); } bool ok = passwordNeeded != cancelled; if (ok) { KexiGUIMessageHandler handler; d->prjSet = new KexiProjectSet(&handler); if (!d->prjSet->setConnectionData(conndata)) { handler.showErrorMessage(d->prjSet->result()); ok = false; } } if (!ok) { if (passwordNeeded == true) { conndata->setPassword(QString::null); // not clear(), we have to remove password } delete d->prjSet; d->prjSet = 0; return; } } } else if (currentPage() == d->dstTitlePageItem) { if (fileBasedDstSelected()) { if (QFileInfo::exists(d->dstNewDBNameUrl->url().toLocalFile())) { - if (!KexiFileWidget::askForOverwriting(d->dstNewDBNameUrl->url().toLocalFile(), this)) { + if (!KexiUtils::askForFileOverwriting(d->dstNewDBNameUrl->url().toLocalFile(), this)) { return; } } } } else if (currentPage() == d->importTypePageItem) { if (!fileBasedDstSelected()) { // make sure we have password if needed tristate passwordNeeded = false; KDbConnectionData* condata = d->dstConn->selectedConnectionData(); if (condata->password().isEmpty()) { passwordNeeded = KexiDBPasswordDialog::getPasswordIfNeeded(condata, this); } bool ok = passwordNeeded != cancelled; if (!ok) { if (passwordNeeded == true) { condata->setPassword(QString::null); // not clear(), we have to remove password } return; } } } else if (currentPage() == d->importingPageItem) { if (!d->importExecuted) { d->importOptionsButton->hide(); backButton()->setEnabled(false); nextButton()->setEnabled(false); finishButton()->setEnabled(false); d->lblImportingTxt->setText(xi18n("Importing in progress...")); tristate res = import(); if (true == res) { d->finishLbl->setText( xi18nc("@info", "Database has been imported into Kexi project %1.", d->dstNewDBNameLineEdit->text())); button(QDialogButtonBox::Cancel)->setEnabled(false); backButton()->setEnabled(false); nextButton()->setEnabled(true); finishButton()->setEnabled(false); d->openImportedProjectCheckBox->show(); next(); return; } d->progressBar->hide(); button(QDialogButtonBox::Cancel)->setEnabled(true); backButton()->setEnabled(true); nextButton()->setEnabled(false); finishButton()->setEnabled(false); d->openImportedProjectCheckBox->hide(); if (!res) next(); else if (~res) { arriveImportingPage(); } d->importExecuted = false; return; } } setAppropriate(d->srcDBPageItem, !fileBasedSrcSelected() && !d->predefinedConnectionData); setAppropriate(d->dstPageItem, !fileBasedDstSelected()); KAssistantDialog::next(); } void ImportWizard::back() { setAppropriate(d->srcDBPageItem, !fileBasedSrcSelected() && !d->predefinedConnectionData); KAssistantDialog::back(); } void ImportWizard::slot_currentPageChanged(KPageWidgetItem* curPage,KPageWidgetItem* prevPage) { Q_UNUSED(prevPage); if (curPage == d->introPageItem) { } else if (curPage == d->srcConnPageItem) { arriveSrcConnPage(); } else if (curPage == d->srcDBPageItem) { arriveSrcDBPage(); } else if (curPage == d->dstTypePageItem) { } else if (curPage == d->dstTitlePageItem) { arriveDstTitlePage(); } else if (curPage == d->dstPageItem) { if (fileBasedDstSelected()) { if (prevPage == d->importTypePageItem) { KAssistantDialog::back(); } else { KAssistantDialog::next(); } } else { arriveDstPage(); } } else if (curPage == d->importingPageItem) { arriveImportingPage(); } else if (curPage == d->finishPageItem) { arriveFinishPage(); } } void ImportWizard::helpClicked() { if (currentPage() == d->introPageItem) { KMessageBox::information(this, xi18n("No help is available for this page."), xi18n("Help")); } else if (currentPage() == d->srcConnPageItem) { KMessageBox::information(this, xi18n("Here you can choose the location to import data from."), xi18n("Help")); } else if (currentPage() == d->srcDBPageItem) { KMessageBox::information(this, xi18n("Here you can choose the actual database to import data from."), xi18n("Help")); } else if (currentPage() == d->dstTypePageItem) { KMessageBox::information(this, xi18n("Here you can choose the location to save the data."), xi18n("Help")); } else if (currentPage() == d->dstPageItem) { KMessageBox::information(this, xi18n("Here you can choose the location to save the data in and the new database name."), xi18n("Help")); } else if (currentPage() == d->finishPageItem || currentPage() == d->importingPageItem) { KMessageBox::information(this, xi18n("No help is available for this page."), xi18n("Help")); } } void ImportWizard::slotOptionsButtonClicked() { QPointer dlg = new OptionsDialog(selectedSourceFileName(), d->sourceDBEncoding, this); if (QDialog::Accepted == dlg->exec()) { if (d->sourceDBEncoding != dlg->encodingComboBox()->selectedEncoding()) { d->sourceDBEncoding = dlg->encodingComboBox()->selectedEncoding(); } } delete dlg; } diff --git a/src/widget/KexiFileWidget.cpp b/src/widget/KexiFileWidget.cpp index 95132dd3d..878c4882f 100644 --- a/src/widget/KexiFileWidget.cpp +++ b/src/widget/KexiFileWidget.cpp @@ -1,475 +1,458 @@ /* This file is part of the KDE project Copyright (C) 2003-2007 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 "KexiFileWidget.h" #include #include #include #include #include //! @todo KEXI3 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //! @internal class Q_DECL_HIDDEN KexiFileWidget::Private { public: Private() : confirmOverwrites(true) , filtersUpdated(false) { } /*! Adds file dialog-compatible filter to @a filter and patterns to @allfilters based on @a mimeName mime type name. Does nothing is excludedMimeTypes contains this mime name. */ bool addFilterForType(QString *filter, QStringList *allfilters, const QString &mimeName) const { QMimeDatabase db; const QMimeType mime = db.mimeTypeForName(mimeName); if (mime.isValid() && !excludedMimeTypes.contains(mime.name().toLower())) { *filter += KexiUtils::fileDialogFilterString(mime); *allfilters += mime.globPatterns(); return true; } return false; } QString lastFileName; KexiFileWidget::Mode mode; QSet additionalMimeTypes, excludedMimeTypes; QString defaultExtension; bool confirmOverwrites; bool filtersUpdated; QUrl highlightedUrl; QString recentDirClass; }; //------------------ KexiFileWidget::KexiFileWidget( const QUrl &startDirOrVariable, Mode mode, QWidget *parent) : KFileWidget(startDirOrVariable, parent) , d(new Private()) { qDebug() << startDirOrVariable.scheme(); if (startDirOrVariable.scheme() == "kfiledialog") { //! @todo KEXI3 test it KFileWidget::getStartUrl(startDirOrVariable, d->recentDirClass); } setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); setMode(mode); QAction *previewAction = actionCollection()->action("preview"); if (previewAction) previewAction->setChecked(false); setFocusProxy(locationEdit()); connect(this, SIGNAL(fileHighlighted(QUrl)), this, SLOT(slotExistingFileHighlighted(QUrl))); } KexiFileWidget::~KexiFileWidget() { qDebug() << d->recentDirClass; if (!d->recentDirClass.isEmpty()) { QString hf = highlightedFile(); QUrl dir; if (hf.isEmpty()) { dir = baseUrl(); } else { QFileInfo fi(hf); QString dirStr = fi.isDir() ? fi.absoluteFilePath() : fi.dir().absolutePath(); dir = QUrl::fromLocalFile(dirStr); } qDebug() << dir; qDebug() << highlightedFile(); if (!dir.isEmpty()) KRecentDirs::add(d->recentDirClass, dir.url()); } delete d; } void KexiFileWidget::slotExistingFileHighlighted(const QUrl& url) { qDebug() << url; d->highlightedUrl = url; emit fileHighlighted(); } QString KexiFileWidget::highlightedFile() const { return d->highlightedUrl.toLocalFile(); } void KexiFileWidget::setMode(Mode mode) { //delayed d->mode = mode; d->filtersUpdated = false; updateFilters(); } QSet KexiFileWidget::additionalFilters() const { return d->additionalMimeTypes; } void KexiFileWidget::setAdditionalFilters(const QSet &mimeTypes) { //delayed d->additionalMimeTypes = mimeTypes; d->filtersUpdated = false; } QSet KexiFileWidget::excludedFilters() const { return d->excludedMimeTypes; } void KexiFileWidget::setExcludedFilters(const QSet &mimeTypes) { //delayed d->excludedMimeTypes.clear(); //convert to lowercase for(const QString& mimeType : mimeTypes) { d->excludedMimeTypes.insert(mimeType.toLower()); } d->filtersUpdated = false; } void KexiFileWidget::updateFilters() { if (d->filtersUpdated) return; d->filtersUpdated = true; d->lastFileName.clear(); clearFilter(); QString filter; QStringList allfilters; const bool normalOpeningMode = d->mode & Opening && !(d->mode & Custom); const bool normalSavingMode = d->mode & SavingFileBasedDB && !(d->mode & Custom); if (normalOpeningMode || normalSavingMode) { d->addFilterForType(&filter, &allfilters, KDb::defaultFileBasedDriverMimeType()); } if (normalOpeningMode || d->mode & SavingServerBasedDB) { d->addFilterForType(&filter, &allfilters, "application/x-kexiproject-shortcut"); } if (normalOpeningMode || d->mode & SavingServerBasedDB) { d->addFilterForType(&filter, &allfilters, "application/x-kexi-connectiondata"); } if (normalOpeningMode) { const QStringList supportedFileMimeTypes = KexiMainWindowIface::global()->migrateManager()->supportedFileMimeTypes(); qDebug() << supportedFileMimeTypes; foreach (const QString& supportedFileMimeType, supportedFileMimeTypes) { d->addFilterForType(&filter, &allfilters, supportedFileMimeType); } } foreach(const QString& mimeName, d->additionalMimeTypes) { if (mimeName == "all/allfiles") continue; d->addFilterForType(&filter, &allfilters, mimeName); } if (!d->excludedMimeTypes.contains("all/allfiles")) { filter += filterWidget()->defaultFilter(); } //remove duplicates made because upper- and lower-case extenstions are used: QStringList allfiltersUnique = allfilters.toSet().toList(); qSort(allfiltersUnique); if (allfiltersUnique.count() > 1) {//prepend "all supoported files" entry filter.prepend(allfilters.join(" ") + "|" + xi18n("All Supported Files (%1)", allfiltersUnique.join(", ")) + "\n"); } if (filter.right(1) == "\n") filter.truncate(filter.length() - 1); setFilter(filter); if (d->mode & Opening) { KFileWidget::setMode(KFile::ExistingOnly | KFile::LocalOnly | KFile::File); setOperationMode(KFileWidget::Opening); } else { KFileWidget::setMode(KFile::LocalOnly | KFile::File); setOperationMode(KFileWidget::Saving); } } void KexiFileWidget::showEvent(QShowEvent * event) { d->filtersUpdated = false; updateFilters(); KFileWidget::showEvent(event); } /*TODO QString KexiFileWidget::selectedFile() const { #ifdef Q_OS_WIN // QString path = selectedFile(); //js @todo // qDebug() << "selectedFile() == " << path << " '" << url().fileName() << "' " << m_lineEdit->text(); QString path = dir()->absolutePath(); if (!path.endsWith('/') && !path.endsWith("\\")) path.append("/"); path += m_lineEdit->text(); // QString path = QFileInfo(selectedFile()).dirPath(true) + "/" + m_lineEdit->text(); #else // QString path = locationEdit->currentText().trimmed(); //url.path().trimmed(); that does not work, if the full path is not in the location edit !!!!! QString path( KFileWidget::selectedFile() ); qDebug() << "prev selectedFile() == " << path; qDebug() << "locationEdit == " << locationEdit()->currentText().trimmed(); //make sure user-entered path is acceped: //! @todo KEXI3 setSelection( locationEdit()->currentText().trimmed() ); // path = KFileWidget::selectedFile(); path = locationEdit()->currentText().trimmed(); qDebug() << "selectedFile() == " << path; #endif if (!currentFilter().isEmpty()) { if (d->mode & SavingFileBasedDB) { const QStringList filters( currentFilter().split(' ') ); qDebug()<< " filter == " << filters; QString ext( QFileInfo(path).suffix() ); bool hasExtension = false; foreach (const QString& filter, filters) { const QString f( filter.trimmed() ); hasExtension = !f.mid(2).isEmpty() && ext==f.mid(2); if (hasExtension) break; } if (!hasExtension) { //no extension: add one QString defaultExtension( d->defaultExtension ); if (defaultExtension.isEmpty()) defaultExtension = filters.first().trimmed().mid(2); //first one path += (QString(".")+defaultExtension); qDebug() << "KexiFileWidget::checkURL(): append extension, " << path; } } } qDebug() << "KexiFileWidget::currentFileName() == " << path; return path; } */ bool KexiFileWidget::checkSelectedFile() { qDebug() << "d->highlightedUrl: " << d->highlightedUrl; if (!locationEdit()->lineEdit()->text().isEmpty()) { qDebug() << locationEdit()->lineEdit()->text(); qDebug() << locationEdit()->urls(); qDebug() << baseUrl(); d->highlightedUrl = baseUrl(); const QString firstUrl(locationEdit()->lineEdit()->text()); // FIXME: find first... if (QDir::isAbsolutePath(firstUrl)) { d->highlightedUrl = QUrl::fromLocalFile(firstUrl); } else { d->highlightedUrl = d->highlightedUrl.adjusted(QUrl::StripTrailingSlash); d->highlightedUrl.setPath(d->highlightedUrl.path() + '/' + firstUrl); } } qDebug() << "d->highlightedUrl: " << d->highlightedUrl; if (d->highlightedUrl.isEmpty()) { KMessageBox::error(this, xi18n("Enter a filename.")); return false; } if (!currentFilter().isEmpty()) { if (d->mode & SavingFileBasedDB) { const QStringList filters( currentFilter().split(' ') ); QString path = highlightedFile(); qDebug()<< "filter:" << filters << "path:" << path; QString ext( QFileInfo(path).suffix() ); bool hasExtension = false; foreach (const QString& filter, filters) { const QString f( filter.trimmed() ); hasExtension = !f.midRef(2).isEmpty() && ext==f.midRef(2); if (hasExtension) break; } if (!hasExtension) { //no extension: add one QString defaultExtension( d->defaultExtension ); if (defaultExtension.isEmpty()) { defaultExtension = filters.first().trimmed().mid(2); //first one } path += (QLatin1String(".")+defaultExtension); qDebug() << "appended extension" << path; setSelection( path ); d->highlightedUrl = QUrl(path); } } } qDebug() << "KexiFileWidget::checkURL() path: " << d->highlightedUrl; // qDebug() << "KexiFileWidget::checkURL() fname: " << url.fileName(); //! @todo if ( url.isLocalFile() ) { QFileInfo fi(d->highlightedUrl.toLocalFile()); if (mode() & KFile::ExistingOnly) { if (!fi.exists()) { KMessageBox::error(this, xi18nc("@info", "The file %1 does not exist.", QDir::toNativeSeparators(d->highlightedUrl.toLocalFile()))); return false; } else if (mode() & KFile::File) { if (!fi.isFile()) { KMessageBox::error(this, xi18nc("@info", "Enter a filename.")); return false; } else if (!fi.isReadable()) { KMessageBox::error(this, xi18nc("@info", "The file %1 is not readable.", QDir::toNativeSeparators(d->highlightedUrl.path()))); return false; } } - } else if (d->confirmOverwrites && !askForOverwriting(d->highlightedUrl.path(), this)) { + } else if (d->confirmOverwrites && !KexiUtils::askForFileOverwriting(d->highlightedUrl.path(), this)) { return false; } return true; } -//static -bool KexiFileWidget::askForOverwriting(const QString& filePath, QWidget *parent) -{ - QFileInfo fi(filePath); - if (!fi.exists()) - return true; - const KMessageBox::ButtonCode res = KMessageBox::warningYesNo(parent, - xi18nc("@info", "The file %1 already exists." - "Do you want to overwrite it?", - QDir::toNativeSeparators(filePath)), - QString(), - KStandardGuiItem::overwrite(), KStandardGuiItem::no()); - if (res == KMessageBox::Yes) - return true; - return false; -} - void KexiFileWidget::accept() { qDebug() << "KexiFileWidget::accept()..."; KFileWidget::accept(); // qDebug() << selectedFile(); // locationEdit->setFocus(); // QKeyEvent ev(QEvent::KeyPress, Qt::Key_Enter, '\n', 0); // QApplication::sendEvent(locationEdit, &ev); // QApplication::postEvent(locationEdit, &ev); // qDebug() << "KexiFileWidget::accept() m_lastUrl == " << m_lastUrl.path(); // if (m_lastUrl.path()==currentURL().path()) {//(js) to prevent more multiple kjob signals (I do not know why this is) /* if (d->lastFileName==selectedFile()) {//(js) to prevent more multiple kjob signals (I do not know why this is) // m_lastUrl=QUrl(); d->lastFileName.clear(); qDebug() << "d->lastFileName==selectedFile()"; #ifdef Q_OS_WIN return; #endif } qDebug() << "KexiFileWidget::accept(): path = " << selectedFile(); if ( checkSelectedFile() ) { emit accepted(); } d->lastFileName = selectedFile(); #ifdef Q_OS_WIN saveLastVisitedPath(d->lastFileName); #endif*/ } void KexiFileWidget::reject() { qDebug() << "KexiFileWidget: reject!"; emit rejected(); } void KexiFileWidget::setLocationText(const QString& fn) { locationEdit()->setUrl(QUrl(fn)); /* #ifdef Q_OS_WIN //js @todo setSelection(fn); #else setSelection(fn); // locationEdit->setCurrentText(fn); // locationEdit->lineEdit()->setEdited( true ); // setSelection(fn); #endif*/ } void KexiFileWidget::focusInEvent(QFocusEvent *) { locationEdit()->setFocus(); } /*bool KexiFileWidget::eventFilter ( QObject * watched, QEvent * e ) { //filter-out ESC key if (e->type()==QEvent::KeyPress && static_cast(e)->key()==Qt::Key_Escape && static_cast(e)->state()==Qt::NoButton) { static_cast(e)->accept(); emit rejected(); return true; } return KexiFileWidgetBase::eventFilter(watched,e); } */ void KexiFileWidget::setDefaultExtension(const QString& ext) { d->defaultExtension = ext; } void KexiFileWidget::setConfirmOverwrites(bool set) { d->confirmOverwrites = set; } diff --git a/src/widget/KexiFileWidget.h b/src/widget/KexiFileWidget.h index 7250ee57c..94de6e9ed 100644 --- a/src/widget/KexiFileWidget.h +++ b/src/widget/KexiFileWidget.h @@ -1,124 +1,118 @@ /* This file is part of the KDE project Copyright (C) 2003-2007 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. */ #ifndef KEXIFILEWIDGET_H #define KEXIFILEWIDGET_H #include "kexiextwidgets_export.h" #include #include //! @short Widget for opening/saving files supported by Kexi /*! For simplicity, initially the widget has hidden the preview pane. */ class KEXIEXTWIDGETS_EXPORT KexiFileWidget : public KFileWidget { Q_OBJECT public: /*! Dialog mode: - Opening opens existing database (or shortcut) - SavingFileBasedDB saves file-based database file - SavingServerBasedDB saves server-based (shortcut) file - CustomOpening can be used for opening other files, like CSV */ enum ModeFlag { Opening = 1, SavingFileBasedDB = 2, SavingServerBasedDB = 4, Custom = 256 }; Q_DECLARE_FLAGS(Mode, ModeFlag) //! @todo KEXI3 add equivalent of kfiledialog:/// for startDirOrVariable KexiFileWidget( const QUrl &startDirOrVariable, Mode mode, QWidget *parent); virtual ~KexiFileWidget(); - /*! Helper. Displays "The file %1 already exists. Do you want to overwrite it?" yes/no message box. - \a parent is used as a parent of the KMessageBox. - \return true if \a filePath file does not exists or user has agreed on overwriting, - false in user do not want to overwrite. */ - static bool askForOverwriting(const QString& filePath, QWidget *parent = 0); - using KFileWidget::setMode; void setMode(Mode mode); QSet additionalFilters() const; //! Sets additional filters list, e.g. "text/x-csv" void setAdditionalFilters(const QSet& mimeTypes); QSet excludedFilters() const; //! Excludes filters list void setExcludedFilters(const QSet& mimeTypes); //! @return selected file. //! @note Call checkSelectedFile() first virtual QString highlightedFile() const; //! just sets locationWidget()->setCurrentText(fn) //! (and something similar on win32) void setLocationText(const QString& fn); //! Sets default extension which will be added after accepting //! if user didn't provided one. This method is usable when there is //! more than one filter so there is no rule what extension should be selected //! (by default first one is selected). void setDefaultExtension(const QString& ext); /*! \return true if the current URL meets requies constraints (i.e. exists or doesn't exist); shows appropriate message box if needed. */ bool checkSelectedFile(); /*! If true, user will be asked to accept overwriting existing file. This is true by default. */ void setConfirmOverwrites(bool set); public Q_SLOTS: virtual void showEvent(QShowEvent * event); virtual void focusInEvent(QFocusEvent *); //! Typing a file that doesn't exist closes the file dialog, we have to //! handle this case better here. virtual void accept(); Q_SIGNALS: void fileHighlighted(); void rejected(); protected Q_SLOTS: virtual void reject(); void slotExistingFileHighlighted(const QUrl& url); private: void updateFilters(); class Private; Private * const d; }; Q_DECLARE_OPERATORS_FOR_FLAGS(KexiFileWidget::Mode) #endif