File indexing completed on 2024-05-12 16:39:58
0001 /* This file is part of the KDE project 0002 Copyright (C) 2003-2018 Jarosław Staniek <staniek@kde.org> 0003 0004 Contains code from kglobalsettings.cpp: 0005 Copyright (C) 2000, 2006 David Faure <faure@kde.org> 0006 Copyright (C) 2008 Friedrich W. H. Kossebau <kossebau@kde.org> 0007 0008 Contains code from kdialog.cpp: 0009 Copyright (C) 1998 Thomas Tanghus (tanghus@earthling.net) 0010 Additions 1999-2000 by Espen Sand (espen@kde.org) 0011 and Holger Freyther <freyther@kde.org> 0012 2005-2009 Olivier Goffart <ogoffart @ kde.org> 0013 2006 Tobias Koenig <tokoe@kde.org> 0014 0015 This program is free software; you can redistribute it and/or 0016 modify it under the terms of the GNU Library General Public 0017 License as published by the Free Software Foundation; either 0018 version 2 of the License, or (at your option) any later version. 0019 0020 This program is distributed in the hope that it will be useful, 0021 but WITHOUT ANY WARRANTY; without even the implied warranty of 0022 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0023 Library General Public License for more details. 0024 0025 You should have received a copy of the GNU Library General Public License 0026 along with this program; see the file COPYING. If not, write to 0027 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0028 * Boston, MA 02110-1301, USA. 0029 */ 0030 0031 #include "utils.h" 0032 #include "utils_p.h" 0033 #include "FontSettings_p.h" 0034 #include "kexiutils_global.h" 0035 #include <KexiIcon.h> 0036 0037 #include <QDomNode> 0038 #include <QPainter> 0039 #include <QImage> 0040 #include <QImageReader> 0041 #include <QImageWriter> 0042 #include <QIcon> 0043 #include <QMetaProperty> 0044 #include <QFocusEvent> 0045 #include <QFile> 0046 #include <QStyle> 0047 #include <QLayout> 0048 #include <KMessageBox> 0049 #include <QFileInfo> 0050 #include <QClipboard> 0051 #include <QMimeDatabase> 0052 #include <QMimeType> 0053 #include <QUrl> 0054 #include <QApplication> 0055 #include <QDesktopWidget> 0056 #include <QFontDatabase> 0057 #include <QTextCodec> 0058 #include <QDebug> 0059 #include <QFileDialog> 0060 #include <QDesktopServices> 0061 #include <QStyleHints> 0062 #include <QLineEdit> 0063 #include <QProcess> 0064 0065 #ifndef KEXI_MOBILE 0066 #include <KFileWidget> 0067 #include <KIO/JobUiDelegate> 0068 #include <KIO/OpenUrlJob> 0069 #include <KRecentDirs> 0070 #include <KRun> 0071 #include <kio_version.h> 0072 #endif 0073 #include <KAboutData> 0074 #include <KColorScheme> 0075 #include <KConfigGroup> 0076 #include <KIconEffect> 0077 0078 #if HAVE_LANGINFO_H 0079 #include <langinfo.h> 0080 #endif 0081 0082 #ifdef Q_OS_WIN 0083 #include <windows.h> 0084 0085 static QRgb qt_colorref2qrgb(COLORREF col) 0086 { 0087 return qRgb(GetRValue(col), GetGValue(col), GetBValue(col)); 0088 } 0089 #endif 0090 0091 using namespace KexiUtils; 0092 0093 DelayedCursorHandler::DelayedCursorHandler(QWidget *widget) 0094 : startedOrActive(false), m_widget(widget), m_handleWidget(widget) 0095 { 0096 m_timer.setSingleShot(true); 0097 connect(&m_timer, SIGNAL(timeout()), this, SLOT(show())); 0098 } 0099 void DelayedCursorHandler::start(bool noDelay) 0100 { 0101 startedOrActive = true; 0102 m_timer.start(noDelay ? 0 : 1000); 0103 } 0104 void DelayedCursorHandler::stop() 0105 { 0106 startedOrActive = false; 0107 m_timer.stop(); 0108 if (m_handleWidget && m_widget) { 0109 m_widget->unsetCursor(); 0110 } else { 0111 QApplication::restoreOverrideCursor(); 0112 } 0113 } 0114 0115 void DelayedCursorHandler::show() 0116 { 0117 const QCursor waitCursor(Qt::WaitCursor); 0118 if (m_handleWidget && m_widget) { 0119 m_widget->unsetCursor(); 0120 m_widget->setCursor(waitCursor); 0121 } else { 0122 QApplication::restoreOverrideCursor(); 0123 QApplication::setOverrideCursor(waitCursor); 0124 } 0125 } 0126 0127 Q_GLOBAL_STATIC(DelayedCursorHandler, _delayedCursorHandler) 0128 0129 void KexiUtils::setWaitCursor(bool noDelay) 0130 { 0131 if (qobject_cast<QApplication*>(qApp)) { 0132 _delayedCursorHandler->start(noDelay); 0133 } 0134 } 0135 0136 void KexiUtils::removeWaitCursor() 0137 { 0138 if (qobject_cast<QApplication*>(qApp)) { 0139 _delayedCursorHandler->stop(); 0140 } 0141 } 0142 0143 WaitCursor::WaitCursor(bool noDelay) 0144 : m_handler(nullptr) 0145 { 0146 setWaitCursor(noDelay); 0147 } 0148 0149 WaitCursor::WaitCursor(QWidget *widget, bool noDelay) 0150 { 0151 DelayedCursorHandler *handler = new DelayedCursorHandler(widget); 0152 handler->start(noDelay); 0153 m_handler = handler; 0154 } 0155 0156 WaitCursor::~WaitCursor() 0157 { 0158 if (m_handler) { 0159 qobject_cast<DelayedCursorHandler*>(m_handler)->stop(); 0160 delete m_handler; 0161 } else { 0162 removeWaitCursor(); 0163 } 0164 } 0165 0166 WaitCursorRemover::WaitCursorRemover() 0167 { 0168 m_reactivateCursor = _delayedCursorHandler->startedOrActive; 0169 _delayedCursorHandler->stop(); 0170 } 0171 0172 WaitCursorRemover::~WaitCursorRemover() 0173 { 0174 if (m_reactivateCursor) 0175 _delayedCursorHandler->start(true); 0176 } 0177 0178 //-------------------------------------------------------------------------------- 0179 0180 QObject* KexiUtils::findFirstQObjectChild(QObject *o, const char* className, const char* objName) 0181 { 0182 if (!o) 0183 return 0; 0184 const QObjectList list(o->children()); 0185 foreach(QObject *child, list) { 0186 if (child->inherits(className) && (!objName || child->objectName() == objName)) 0187 return child; 0188 } 0189 //try children 0190 foreach(QObject *child, list) { 0191 child = findFirstQObjectChild(child, className, objName); 0192 if (child) 0193 return child; 0194 } 0195 return 0; 0196 } 0197 0198 QMetaProperty KexiUtils::findPropertyWithSuperclasses(const QObject* object, 0199 const char* name) 0200 { 0201 const int index = object->metaObject()->indexOfProperty(name); 0202 if (index == -1) 0203 return QMetaProperty(); 0204 return object->metaObject()->property(index); 0205 } 0206 0207 bool KexiUtils::objectIsA(QObject* object, const QList<QByteArray>& classNames) 0208 { 0209 foreach(const QByteArray& ba, classNames) { 0210 if (objectIsA(object, ba.constData())) 0211 return true; 0212 } 0213 return false; 0214 } 0215 0216 QList<QMetaMethod> KexiUtils::methodsForMetaObject( 0217 const QMetaObject *metaObject, QFlags<QMetaMethod::MethodType> types, 0218 QFlags<QMetaMethod::Access> access) 0219 { 0220 const int count = metaObject ? metaObject->methodCount() : 0; 0221 QList<QMetaMethod> result; 0222 for (int i = 0; i < count; i++) { 0223 QMetaMethod method(metaObject->method(i)); 0224 if (types & method.methodType() && access & method.access()) 0225 result += method; 0226 } 0227 return result; 0228 } 0229 0230 QList<QMetaMethod> KexiUtils::methodsForMetaObjectWithParents( 0231 const QMetaObject *metaObject, QFlags<QMetaMethod::MethodType> types, 0232 QFlags<QMetaMethod::Access> access) 0233 { 0234 QList<QMetaMethod> result; 0235 while (metaObject) { 0236 const int count = metaObject->methodCount(); 0237 for (int i = 0; i < count; i++) { 0238 QMetaMethod method(metaObject->method(i)); 0239 if (types & method.methodType() && access & method.access()) 0240 result += method; 0241 } 0242 metaObject = metaObject->superClass(); 0243 } 0244 return result; 0245 } 0246 0247 QList<QMetaProperty> KexiUtils::propertiesForMetaObject( 0248 const QMetaObject *metaObject) 0249 { 0250 const int count = metaObject ? metaObject->propertyCount() : 0; 0251 QList<QMetaProperty> result; 0252 for (int i = 0; i < count; i++) 0253 result += metaObject->property(i); 0254 return result; 0255 } 0256 0257 QList<QMetaProperty> KexiUtils::propertiesForMetaObjectWithInherited( 0258 const QMetaObject *metaObject) 0259 { 0260 QList<QMetaProperty> result; 0261 while (metaObject) { 0262 const int count = metaObject->propertyCount(); 0263 for (int i = 0; i < count; i++) 0264 result += metaObject->property(i); 0265 metaObject = metaObject->superClass(); 0266 } 0267 return result; 0268 } 0269 0270 QStringList KexiUtils::enumKeysForProperty(const QMetaProperty& metaProperty, int filter) 0271 { 0272 QStringList result; 0273 const QMetaEnum enumerator(metaProperty.enumerator()); 0274 const int count = enumerator.keyCount(); 0275 int total = 0; 0276 for (int i = 0; i < count; i++) { 0277 if (filter == INT_MIN) { 0278 result.append(QString::fromLatin1(enumerator.key(i))); 0279 } else { 0280 const int v = enumerator.value(i); 0281 if ((v & filter) && !(total & v)) { // !(total & v) is a protection adding against masks 0282 result.append(QString::fromLatin1(enumerator.key(i))); 0283 total |= v; 0284 } 0285 } 0286 } 0287 return result; 0288 } 0289 0290 //! @internal 0291 static QFileDialog* getImageDialog(QWidget *parent, const QString &caption, const QUrl &directory, 0292 const QList<QByteArray> &supportedMimeTypes) 0293 { 0294 QFileDialog *dialog = new QFileDialog(parent, caption); 0295 dialog->setDirectoryUrl(directory); 0296 const QStringList mimeTypeFilters 0297 = KexiUtils::convertTypesUsingFunction<QByteArray, QString, &QString::fromLatin1>(supportedMimeTypes); 0298 dialog->setMimeTypeFilters(mimeTypeFilters); 0299 return dialog; 0300 } 0301 0302 QUrl KexiUtils::getOpenImageUrl(QWidget *parent, const QString &caption, const QUrl &directory) 0303 { 0304 QScopedPointer<QFileDialog> dialog( 0305 getImageDialog(parent, caption.isEmpty() ? i18n("Open") : caption, directory, 0306 QImageReader::supportedMimeTypes())); 0307 dialog->setFileMode(QFileDialog::ExistingFile); 0308 dialog->setAcceptMode(QFileDialog::AcceptOpen); 0309 if (QDialog::Accepted == dialog->exec()) { 0310 return dialog->selectedUrls().value(0); 0311 } else { 0312 return QUrl(); 0313 } 0314 } 0315 0316 QUrl KexiUtils::getSaveImageUrl(QWidget *parent, const QString &caption, const QUrl &directory) 0317 { 0318 QScopedPointer<QFileDialog> dialog( 0319 getImageDialog(parent, caption.isEmpty() ? i18n("Save") : caption, directory, 0320 QImageWriter::supportedMimeTypes())); 0321 dialog->setAcceptMode(QFileDialog::AcceptSave); 0322 if (QDialog::Accepted == dialog->exec()) { 0323 return dialog->selectedUrls().value(0); 0324 } else { 0325 return QUrl(); 0326 } 0327 } 0328 0329 #ifndef KEXI_MOBILE 0330 QUrl KexiUtils::getStartUrl(const QUrl &startDirOrVariable, QString *recentDirClass, 0331 const QString &fileName) 0332 { 0333 QUrl result; 0334 if (recentDirClass) { 0335 result = KFileWidget::getStartUrl(startDirOrVariable, *recentDirClass); 0336 // Fix bug introduced by Kexi 3.0.x in KexiFileWidget: remove file protocol from path 0337 // (the KRecentDirs::add(.., dir.url()) call was invalid because of prepended protocol) 0338 const QString protocol("file:/"); 0339 if (result.path().startsWith(protocol) && !result.path().startsWith(protocol + '/')) { 0340 result.setPath(result.path().mid(protocol.length() - 1)); 0341 } 0342 if (!fileName.isEmpty()) { 0343 result.setPath(result.path() + '/' + fileName); 0344 } 0345 } else { 0346 qWarning() << "Missing recentDirClass"; 0347 } 0348 return result; 0349 } 0350 0351 void KexiUtils::addRecentDir(const QString &fileClass, const QString &directory) 0352 { 0353 KRecentDirs::add(fileClass, directory); 0354 } 0355 #endif 0356 0357 bool KexiUtils::askForFileOverwriting(const QString& filePath, QWidget *parent) 0358 { 0359 QFileInfo fi(filePath); 0360 if (!fi.exists()) { 0361 return true; 0362 } 0363 const KMessageBox::ButtonCode res = KMessageBox::warningYesNo(parent, 0364 xi18nc("@info", "<para>The file <filename>%1</filename> already exists.</para>" 0365 "<para>Do you want to overwrite it?</para>", 0366 QDir::toNativeSeparators(filePath)), 0367 QString(), 0368 KStandardGuiItem::overwrite(), KStandardGuiItem::no()); 0369 return res == KMessageBox::Yes; 0370 } 0371 0372 QColor KexiUtils::blendedColors(const QColor& c1, const QColor& c2, int factor1, int factor2) 0373 { 0374 return QColor( 0375 int((c1.red()*factor1 + c2.red()*factor2) / (factor1 + factor2)), 0376 int((c1.green()*factor1 + c2.green()*factor2) / (factor1 + factor2)), 0377 int((c1.blue()*factor1 + c2.blue()*factor2) / (factor1 + factor2))); 0378 } 0379 0380 QColor KexiUtils::contrastColor(const QColor& c) 0381 { 0382 int g = qGray(c.rgb()); 0383 if (g > 110) 0384 return c.dark(200); 0385 else if (g > 80) 0386 return c.light(150); 0387 else if (g > 20) 0388 return c.light(300); 0389 return Qt::gray; 0390 } 0391 0392 QColor KexiUtils::bleachedColor(const QColor& c, int factor) 0393 { 0394 int h, s, v; 0395 c.getHsv(&h, &s, &v); 0396 QColor c2; 0397 if (factor < 100) 0398 factor = 100; 0399 if (s >= 250 && v >= 250) //for colors like cyan or red, make the result more white 0400 s = qMax(0, s - factor - 50); 0401 else if (s <= 5 && v <= 5) 0402 v += factor - 50; 0403 c2.setHsv(h, s, qMin(255, v + factor - 100)); 0404 return c2; 0405 } 0406 0407 QIcon KexiUtils::colorizeIconToTextColor(const QPixmap& icon, const QPalette& palette, 0408 QPalette::ColorRole role) 0409 { 0410 QPixmap pm( 0411 KIconEffect().apply(icon, KIconEffect::Colorize, 1.0f, 0412 palette.color(role), false)); 0413 KIconEffect::semiTransparent(pm); 0414 return QIcon(pm); 0415 } 0416 0417 QPixmap KexiUtils::emptyIcon(KIconLoader::Group iconGroup) 0418 { 0419 const int size = KIconLoader::global()->currentSize(iconGroup); 0420 QPixmap noIcon(size, size); 0421 noIcon.fill(Qt::transparent); 0422 return noIcon; 0423 } 0424 0425 static void drawOrScalePixmapInternal(QPainter* p, const QMargins& margins, const QRect& rect, 0426 QPixmap* pixmap, QPoint* pos, Qt::Alignment alignment, 0427 bool scaledContents, bool keepAspectRatio, 0428 Qt::TransformationMode transformMode = Qt::FastTransformation) 0429 { 0430 Q_ASSERT(pos); 0431 if (pixmap->isNull()) 0432 return; 0433 0434 const bool fast = false; 0435 const int w = rect.width() - margins.left() - margins.right(); 0436 const int h = rect.height() - margins.top() - margins.bottom(); 0437 //! @todo we can optimize painting by drawing rescaled pixmap here 0438 //! and performing detailed painting later (using QTimer) 0439 // QPixmap pixmapBuffer; 0440 // QPainter p2; 0441 // QPainter *target; 0442 // if (fast) { 0443 // target = p; 0444 // } else { 0445 // target = &p2; 0446 // } 0447 //! @todo only create buffered pixmap of the minimum size and then do not fillRect() 0448 // target->fillRect(0,0,rect.width(),rect.height(), backgroundColor); 0449 0450 *pos = rect.topLeft() + QPoint(margins.left(), margins.top()); 0451 if (scaledContents) { 0452 if (keepAspectRatio) { 0453 QImage img(pixmap->toImage()); 0454 img = img.scaled(w, h, Qt::KeepAspectRatio, transformMode); 0455 if (img.width() < w) { 0456 if (alignment & Qt::AlignRight) 0457 pos->setX(pos->x() + w - img.width()); 0458 else if (alignment & Qt::AlignHCenter) 0459 pos->setX(pos->x() + w / 2 - img.width() / 2); 0460 } 0461 else if (img.height() < h) { 0462 if (alignment & Qt::AlignBottom) 0463 pos->setY(pos->y() + h - img.height()); 0464 else if (alignment & Qt::AlignVCenter) 0465 pos->setY(pos->y() + h / 2 - img.height() / 2); 0466 } 0467 if (p) { 0468 p->drawImage(*pos, img); 0469 } 0470 else { 0471 *pixmap = QPixmap::fromImage(img); 0472 } 0473 } else { 0474 if (!fast) { 0475 *pixmap = pixmap->scaled(w, h, Qt::IgnoreAspectRatio, transformMode); 0476 if (p) { 0477 p->drawPixmap(*pos, *pixmap); 0478 } 0479 } 0480 } 0481 } 0482 else { 0483 if (alignment & Qt::AlignRight) 0484 pos->setX(pos->x() + w - pixmap->width()); 0485 else if (alignment & Qt::AlignHCenter) 0486 pos->setX(pos->x() + w / 2 - pixmap->width() / 2); 0487 else //left, etc. 0488 pos->setX(pos->x()); 0489 0490 if (alignment & Qt::AlignBottom) 0491 pos->setY(pos->y() + h - pixmap->height()); 0492 else if (alignment & Qt::AlignVCenter) 0493 pos->setY(pos->y() + h / 2 - pixmap->height() / 2); 0494 else //top, etc. 0495 pos->setY(pos->y()); 0496 *pos += QPoint(margins.left(), margins.top()); 0497 if (p) { 0498 p->drawPixmap(*pos, *pixmap); 0499 } 0500 } 0501 } 0502 0503 void KexiUtils::drawPixmap(QPainter* p, const QMargins& margins, const QRect& rect, 0504 const QPixmap& pixmap, Qt::Alignment alignment, bool scaledContents, 0505 bool keepAspectRatio, Qt::TransformationMode transformMode) 0506 { 0507 QPixmap px(pixmap); 0508 QPoint pos; 0509 drawOrScalePixmapInternal(p, margins, rect, &px, &pos, alignment, scaledContents, 0510 keepAspectRatio, transformMode); 0511 } 0512 0513 QPixmap KexiUtils::scaledPixmap(const QMargins& margins, const QRect& rect, 0514 const QPixmap& pixmap, QPoint* pos, Qt::Alignment alignment, 0515 bool scaledContents, bool keepAspectRatio, 0516 Qt::TransformationMode transformMode) 0517 { 0518 QPixmap px(pixmap); 0519 drawOrScalePixmapInternal(0, margins, rect, &px, pos, alignment, scaledContents, keepAspectRatio, transformMode); 0520 return px; 0521 } 0522 0523 bool KexiUtils::loadPixmapFromData(QPixmap *pixmap, const QByteArray &data, const char *format) 0524 { 0525 bool ok = pixmap->loadFromData(data, format); 0526 if (ok) { 0527 return true; 0528 } 0529 if (format) { 0530 return false; 0531 } 0532 const QList<QByteArray> commonFormats({"png", "jpg", "bmp", "tif"}); 0533 QList<QByteArray> formats(commonFormats); 0534 for(int i=0; ;) { 0535 ok = pixmap->loadFromData(data, formats[i]); 0536 if (ok) { 0537 return true; 0538 } 0539 ++i; 0540 if (i == formats.count()) {// try harder 0541 if (i == commonFormats.count()) { 0542 formats += QImageReader::supportedImageFormats(); 0543 if (formats.count() == commonFormats.count()) { 0544 break; // sanity check 0545 } 0546 } else { 0547 break; 0548 } 0549 } 0550 } 0551 return false; 0552 } 0553 0554 void KexiUtils::setFocusWithReason(QWidget* widget, Qt::FocusReason reason) 0555 { 0556 if (!widget) 0557 return; 0558 QFocusEvent fe(QEvent::FocusIn, reason); 0559 QCoreApplication::sendEvent(widget, &fe); 0560 } 0561 0562 void KexiUtils::unsetFocusWithReason(QWidget* widget, Qt::FocusReason reason) 0563 { 0564 if (!widget) 0565 return; 0566 QFocusEvent fe(QEvent::FocusOut, reason); 0567 QCoreApplication::sendEvent(widget, &fe); 0568 } 0569 0570 //-------- 0571 0572 void KexiUtils::adjustIfRtl(QMargins *margins) 0573 { 0574 if (margins && QGuiApplication::isRightToLeft()) { 0575 const int left = margins->left(); 0576 margins->setLeft(margins->right()); 0577 margins->setRight(left); 0578 } 0579 } 0580 0581 //--------- 0582 0583 Q_GLOBAL_STATIC(FontSettingsData, g_fontSettings) 0584 0585 QFont KexiUtils::smallestReadableFont() 0586 { 0587 return g_fontSettings->font(FontSettingsData::SmallestReadableFont); 0588 } 0589 0590 //--------------------- 0591 0592 KTextEditorFrame::KTextEditorFrame(QWidget * parent, Qt::WindowFlags f) 0593 : QFrame(parent, f) 0594 { 0595 QEvent dummy(QEvent::StyleChange); 0596 changeEvent(&dummy); 0597 } 0598 0599 void KTextEditorFrame::changeEvent(QEvent *event) 0600 { 0601 if (event->type() == QEvent::StyleChange) { 0602 if (style()->objectName() != "oxygen") // oxygen already nicely paints the frame 0603 setFrameStyle(QFrame::Sunken | QFrame::StyledPanel); 0604 else 0605 setFrameStyle(QFrame::NoFrame); 0606 } 0607 } 0608 0609 //--------------------- 0610 0611 int KexiUtils::marginHint() 0612 { 0613 return QApplication::style()->pixelMetric(QStyle::PM_DefaultChildMargin); 0614 } 0615 0616 int KexiUtils::spacingHint() 0617 { 0618 return QApplication::style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing); 0619 } 0620 0621 void KexiUtils::setStandardMarginsAndSpacing(QLayout *layout) 0622 { 0623 setMargins(layout, KexiUtils::marginHint()); 0624 layout->setSpacing(KexiUtils::spacingHint()); 0625 } 0626 0627 void KexiUtils::setMargins(QLayout *layout, int value) 0628 { 0629 layout->setContentsMargins(value, value, value, value); 0630 } 0631 0632 void KexiUtils::replaceColors(QPixmap* original, const QColor& color) 0633 { 0634 Q_ASSERT(original); 0635 QImage dest(original->toImage()); 0636 replaceColors(&dest, color); 0637 *original = QPixmap::fromImage(dest); 0638 } 0639 0640 void KexiUtils::replaceColors(QImage* original, const QColor& color) 0641 { 0642 Q_ASSERT(original); 0643 *original = original->convertToFormat(QImage::Format_ARGB32_Premultiplied); 0644 QPainter p(original); 0645 p.setCompositionMode(QPainter::CompositionMode_SourceIn); 0646 p.fillRect(original->rect(), color); 0647 } 0648 0649 bool KexiUtils::isLightColorScheme() 0650 { 0651 return KColorScheme(QPalette::Active, KColorScheme::Window).background().color().lightness() >= 128; 0652 } 0653 0654 int KexiUtils::dimmedAlpha() 0655 { 0656 return 150; 0657 } 0658 0659 QPalette KexiUtils::paletteWithDimmedColor(const QPalette &pal, QPalette::ColorGroup group, 0660 QPalette::ColorRole role) 0661 { 0662 QPalette result(pal); 0663 QColor color(result.color(group, role)); 0664 color.setAlpha(dimmedAlpha()); 0665 result.setColor(group, role, color); 0666 return result; 0667 } 0668 0669 QPalette KexiUtils::paletteWithDimmedColor(const QPalette &pal, QPalette::ColorRole role) 0670 { 0671 QPalette result(pal); 0672 QColor color(result.color(role)); 0673 color.setAlpha(dimmedAlpha()); 0674 result.setColor(role, color); 0675 return result; 0676 } 0677 0678 QPalette KexiUtils::paletteForReadOnly(const QPalette &palette) 0679 { 0680 QPalette p(palette); 0681 p.setBrush(QPalette::Base, palette.brush(QPalette::Disabled, QPalette::Base)); 0682 p.setBrush(QPalette::Text, palette.brush(QPalette::Disabled, QPalette::Text)); 0683 p.setBrush(QPalette::Highlight, palette.brush(QPalette::Disabled, QPalette::Highlight)); 0684 p.setBrush(QPalette::HighlightedText, palette.brush(QPalette::Disabled, QPalette::HighlightedText)); 0685 return p; 0686 } 0687 0688 void KexiUtils::setBackgroundColor(QWidget *widget, const QColor &color) 0689 { 0690 widget->setAutoFillBackground(true); 0691 QPalette pal(widget->palette()); 0692 pal.setColor(widget->backgroundRole(), color); 0693 widget->setPalette(pal); 0694 } 0695 0696 //--------------------- 0697 0698 void KexiUtils::installRecursiveEventFilter(QObject *object, QObject *filter) 0699 { 0700 if (!object || !filter || !object->isWidgetType()) 0701 return; 0702 0703 // qDebug() << "Installing event filter on widget:" << object 0704 // << "directed to" << filter->objectName(); 0705 object->installEventFilter(filter); 0706 0707 const QObjectList list(object->children()); 0708 foreach(QObject *obj, list) { 0709 installRecursiveEventFilter(obj, filter); 0710 } 0711 } 0712 0713 void KexiUtils::removeRecursiveEventFilter(QObject *object, QObject *filter) 0714 { 0715 object->removeEventFilter(filter); 0716 if (!object->isWidgetType()) 0717 return; 0718 0719 const QObjectList list(object->children()); 0720 foreach(QObject *obj, list) { 0721 removeRecursiveEventFilter(obj, filter); 0722 } 0723 } 0724 0725 PaintBlocker::PaintBlocker(QWidget* parent) 0726 : QObject(parent) 0727 , m_enabled(true) 0728 { 0729 parent->installEventFilter(this); 0730 } 0731 0732 void PaintBlocker::setEnabled(bool set) 0733 { 0734 m_enabled = set; 0735 } 0736 0737 bool PaintBlocker::enabled() const 0738 { 0739 return m_enabled; 0740 } 0741 0742 bool PaintBlocker::eventFilter(QObject* watched, QEvent* event) 0743 { 0744 if (m_enabled && watched == parent() && event->type() == QEvent::Paint) { 0745 return true; 0746 } 0747 return false; 0748 } 0749 0750 tristate KexiUtils::openHyperLink(const QUrl &url, QWidget *parent, const OpenHyperlinkOptions &options) 0751 { 0752 #ifdef KEXI_MOBILE 0753 //! @todo 0754 Q_UNUSED(url) 0755 Q_UNUSED(parent) 0756 Q_UNUSED(options) 0757 #else 0758 if (url.isLocalFile()) { 0759 QFileInfo fileInfo(url.toLocalFile()); 0760 if (!fileInfo.exists()) { 0761 KMessageBox::sorry(parent, xi18nc("@info", "The file or directory <filename>%1</filename> does not exist.", fileInfo.absoluteFilePath())); 0762 return false; 0763 } 0764 } 0765 0766 if (!url.isValid()) { 0767 KMessageBox::sorry(parent, xi18nc("@info", "Invalid hyperlink <link>%1</link>.", 0768 url.url(QUrl::PreferLocalFile))); 0769 return false; 0770 } 0771 0772 QMimeDatabase db; 0773 QString type = db.mimeTypeForUrl(url).name(); 0774 0775 if (!options.allowExecutable && KRun::isExecutableFile(url, type)) { 0776 KMessageBox::sorry(parent, xi18nc("@info", "Executable <link>%1</link> not allowed.", 0777 url.url(QUrl::PreferLocalFile))); 0778 return false; 0779 } 0780 0781 if (!options.allowRemote && !url.isLocalFile()) { 0782 KMessageBox::sorry(parent, xi18nc("@info", "Remote hyperlink <link>%1</link> not allowed.", 0783 url.url(QUrl::PreferLocalFile))); 0784 return false; 0785 } 0786 0787 if (KRun::isExecutableFile(url, type)) { 0788 int ret = KMessageBox::questionYesNo(parent 0789 , xi18nc("@info", "Do you want to run this file?" 0790 "<warning>Running executables can be dangerous.</warning>") 0791 , QString() 0792 , KGuiItem(xi18nc("@action:button Run script file", "Run"), koIconName("system-run")) 0793 , KStandardGuiItem::no() 0794 , "AllowRunExecutable", KMessageBox::Notify | KMessageBox::Dangerous); 0795 0796 if (ret != KMessageBox::Yes) { 0797 return cancelled; 0798 } 0799 } 0800 0801 switch(options.tool) { 0802 case OpenHyperlinkOptions::DefaultHyperlinkTool: 0803 #if KIO_VERSION >= QT_VERSION_CHECK(5, 71, 0) 0804 { 0805 auto *job = new KIO::OpenUrlJob(url, type); 0806 job->setUiDelegate(new KIO::JobUiDelegate(KJobUiDelegate::AutoHandlingEnabled, parent)); 0807 job->setRunExecutables(true); 0808 job->start(); 0809 return true; 0810 } 0811 #elif KIO_VERSION >= QT_VERSION_CHECK(5, 31, 0) 0812 return KRun::runUrl(url, type, parent, KRun::RunFlags(KRun::RunExecutables)); 0813 #else 0814 return KRun::runUrl(url, type, parent); 0815 #endif 0816 case OpenHyperlinkOptions::BrowserHyperlinkTool: 0817 return QDesktopServices::openUrl(url); 0818 case OpenHyperlinkOptions::MailerHyperlinkTool: 0819 return QDesktopServices::openUrl(url); 0820 default:; 0821 } 0822 #endif 0823 return false; 0824 } 0825 0826 // ---- 0827 0828 KexiDBDebugTreeWidget::KexiDBDebugTreeWidget(QWidget *parent) 0829 : QTreeWidget(parent) 0830 { 0831 } 0832 0833 void KexiDBDebugTreeWidget::copy() 0834 { 0835 if (currentItem()) { 0836 qApp->clipboard()->setText(currentItem()->text(0)); 0837 } 0838 } 0839 0840 // ---- 0841 0842 DebugWindow::DebugWindow(QWidget * parent) 0843 : QWidget(parent, Qt::Window) 0844 { 0845 } 0846 0847 // ---- 0848 0849 QSize KexiUtils::comboBoxArrowSize(QStyle *style) 0850 { 0851 if (!style) { 0852 style = QApplication::style(); 0853 } 0854 QStyleOptionComboBox cbOption; 0855 return style->subControlRect(QStyle::CC_ComboBox, &cbOption, QStyle::SC_ComboBoxArrow).size(); 0856 } 0857 0858 void KexiUtils::addDirtyFlag(QString *text) 0859 { 0860 Q_ASSERT(text); 0861 *text = xi18nc("'Dirty (modified) object' flag", "%1*", *text); 0862 } 0863 0864 //! From klocale_kde.cpp 0865 //! @todo KEXI3 support other OS-es (use from klocale_*.cpp) 0866 static QByteArray systemCodeset() 0867 { 0868 QByteArray codeset; 0869 #if HAVE_LANGINFO_H 0870 // Qt since 4.2 always returns 'System' as codecForLocale and KDE (for example 0871 // KEncodingFileDialog) expects real encoding name. So on systems that have langinfo.h use 0872 // nl_langinfo instead, just like Qt compiled without iconv does. Windows already has its own 0873 // workaround 0874 0875 codeset = nl_langinfo(CODESET); 0876 0877 if ((codeset == "ANSI_X3.4-1968") || (codeset == "US-ASCII")) { 0878 // means ascii, "C"; QTextCodec doesn't know, so avoid warning 0879 codeset = "ISO-8859-1"; 0880 } 0881 #endif 0882 return codeset; 0883 } 0884 0885 QTextCodec* g_codecForEncoding = 0; 0886 0887 bool setEncoding(int mibEnum) 0888 { 0889 QTextCodec *codec = QTextCodec::codecForMib(mibEnum); 0890 if (codec) { 0891 g_codecForEncoding = codec; 0892 } 0893 0894 return codec != 0; 0895 } 0896 0897 //! From klocale_kde.cpp 0898 static void initEncoding() 0899 { 0900 if (!g_codecForEncoding) { 0901 // This all made more sense when we still had the EncodingEnum config key. 0902 0903 QByteArray codeset = systemCodeset(); 0904 0905 if (!codeset.isEmpty()) { 0906 QTextCodec *codec = QTextCodec::codecForName(codeset); 0907 if (codec) { 0908 setEncoding(codec->mibEnum()); 0909 } 0910 } else { 0911 setEncoding(QTextCodec::codecForLocale()->mibEnum()); 0912 } 0913 0914 if (!g_codecForEncoding) { 0915 qWarning() << "Cannot resolve system encoding, defaulting to ISO 8859-1."; 0916 const int mibDefault = 4; // ISO 8859-1 0917 setEncoding(mibDefault); 0918 } 0919 Q_ASSERT(g_codecForEncoding); 0920 } 0921 } 0922 0923 QByteArray KexiUtils::encoding() 0924 { 0925 initEncoding(); 0926 return g_codecForEncoding->name(); 0927 } 0928 0929 namespace { 0930 0931 //! @internal for graphicEffectsLevel() 0932 class GraphicEffectsLevel 0933 { 0934 public: 0935 GraphicEffectsLevel() { 0936 KConfigGroup g(KSharedConfig::openConfig(), "KDE-Global GUI Settings"); 0937 0938 // Asking for hasKey we do not ask for graphicEffectsLevelDefault() that can 0939 // contain some very slow code. If we can save that time, do it. (ereslibre) 0940 if (g.hasKey("GraphicEffectsLevel")) { 0941 value = ((GraphicEffects) g.readEntry("GraphicEffectsLevel", QVariant((int) NoEffects)).toInt()); 0942 return; 0943 } 0944 0945 // For now, let always enable animations by default. The plan is to make 0946 // this code a bit smarter. (ereslibre) 0947 value = ComplexAnimationEffects; 0948 } 0949 GraphicEffects value; 0950 }; 0951 } 0952 0953 Q_GLOBAL_STATIC(GraphicEffectsLevel, g_graphicEffectsLevel) 0954 0955 GraphicEffects KexiUtils::graphicEffectsLevel() 0956 { 0957 return g_graphicEffectsLevel->value; 0958 } 0959 0960 #if defined Q_OS_UNIX && !defined Q_OS_MACOS 0961 //! For detectedDesktopSession() 0962 class DetectedDesktopSession 0963 { 0964 public: 0965 DetectedDesktopSession() : name(detect()), isKDE(name == QStringLiteral("KDE")) 0966 { 0967 } 0968 const QByteArray name; 0969 const bool isKDE; 0970 0971 private: 0972 static QByteArray detect() { 0973 // https://www.freedesktop.org/software/systemd/man/pam_systemd.html#%24XDG_SESSION_DESKTOP 0974 // KDE, GNOME, UNITY, LXDE, MATE, XFCE... 0975 const QString xdgSessionDesktop = qgetenv("XDG_SESSION_DESKTOP").trimmed(); 0976 if (!xdgSessionDesktop.isEmpty()) { 0977 return xdgSessionDesktop.toLatin1().toUpper(); 0978 } 0979 // Similar to detectDesktopEnvironment() from qgenericunixservices.cpp 0980 const QString xdgCurrentDesktop = qgetenv("XDG_CURRENT_DESKTOP").trimmed(); 0981 if (!xdgCurrentDesktop.isEmpty()) { 0982 return xdgCurrentDesktop.toLatin1().toUpper(); 0983 } 0984 // fallbacks 0985 if (!qEnvironmentVariableIsEmpty("KDE_FULL_SESSION")) { 0986 return QByteArrayLiteral("KDE"); 0987 } 0988 if (!qEnvironmentVariableIsEmpty("GNOME_DESKTOP_SESSION_ID")) { 0989 return QByteArrayLiteral("GNOME"); 0990 } 0991 const QString desktopSession = qgetenv("DESKTOP_SESSION").trimmed(); 0992 if (desktopSession.compare("gnome", Qt::CaseInsensitive) == 0) { 0993 return QByteArrayLiteral("GNOME"); 0994 } else if (desktopSession.compare("xfce", Qt::CaseInsensitive) == 0) { 0995 return QByteArrayLiteral("XFCE"); 0996 } 0997 return QByteArray(); 0998 } 0999 }; 1000 1001 Q_GLOBAL_STATIC(DetectedDesktopSession, s_detectedDesktopSession) 1002 1003 QByteArray KexiUtils::detectedDesktopSession() 1004 { 1005 return s_detectedDesktopSession->name; 1006 } 1007 1008 bool KexiUtils::isKDEDesktopSession() 1009 { 1010 return s_detectedDesktopSession->isKDE; 1011 } 1012 1013 bool KexiUtils::shouldUseNativeDialogs() 1014 { 1015 #if defined Q_OS_UNIX && !defined Q_OS_MACOS 1016 return isKDEDesktopSession() || detectedDesktopSession().isEmpty(); 1017 #else 1018 return true; 1019 #endif 1020 } 1021 1022 1023 //! @return value of XFCE property @a property for channel @a channel 1024 //! Sets the value pointed by @a ok to status. 1025 //! @todo Should be part of desktop integration or KF 1026 static QByteArray xfceSettingValue(const QByteArray &channel, const QByteArray &property, 1027 bool *ok = nullptr) 1028 { 1029 if (ok) { 1030 *ok = false; 1031 } 1032 QByteArray result; 1033 const QString program = QString::fromLatin1("xfconf-query"); 1034 const QString programPath = QStandardPaths::findExecutable(program); 1035 const QStringList arguments{ program, "-c", channel, "-p", property }; 1036 QProcess process; 1037 process.start(programPath, arguments);//, QIODevice::ReadOnly | QIODevice::Text); 1038 if (!process.waitForStarted()) { 1039 qWarning() << "Count not execute command" << programPath << arguments 1040 << "error:" << process.error(); 1041 return QByteArray(); 1042 } 1043 const int exitCode = process.exitCode(); // !=0 e.g. for "no such property or channel" 1044 if (exitCode != 0 || !process.waitForFinished() || process.exitStatus() != QProcess::NormalExit) { 1045 qWarning() << "Count not finish command" << programPath << arguments 1046 << "error:" << process.error() << "exit code:" << exitCode 1047 << "exit status:" << process.exitStatus(); 1048 return QByteArray(); 1049 } 1050 if (ok) { 1051 *ok = true; 1052 } 1053 result = process.readAll(); 1054 result.chop(1); 1055 return result; 1056 } 1057 1058 #else 1059 1060 QByteArray KexiUtils::detectedDesktopSession() 1061 { 1062 return QByteArray(); 1063 } 1064 #endif 1065 1066 bool KexiUtils::activateItemsOnSingleClick(QWidget *widget) 1067 { 1068 const KConfigGroup mainWindowGroup = KSharedConfig::openConfig()->group("MainWindow"); 1069 #ifdef Q_OS_WIN 1070 return mainWindowGroup.readEntry("SingleClickOpensItem", true); 1071 #else 1072 if (mainWindowGroup.hasKey("SingleClickOpensItem")) { 1073 return mainWindowGroup.readEntry("SingleClickOpensItem", true); 1074 } 1075 const QByteArray desktopSession = detectedDesktopSession(); 1076 if (desktopSession == "XFCE") { 1077 /* To test: 1078 Set to true: fconf-query -c xfce4-desktop -p /desktop-icons/single-click -n -t bool -s true 1079 Get value: xfconf-query -c xfce4-desktop -p /desktop-icons/single-click 1080 Reset: xfconf-query -c xfce4-desktop -p /desktop-icons/single-click -r 1081 */ 1082 return xfceSettingValue("xfce4-desktop", "/desktop-icons/single-click") == "true"; 1083 } 1084 # if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0) 1085 Q_UNUSED(widget) 1086 return QApplication::styleHints()->singleClickActivation(); 1087 # else 1088 QStyle *style = widget ? widget->style() : QApplication::style(); 1089 return style->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, 0, widget); 1090 # endif 1091 #endif 1092 } 1093 1094 // NOTE: keep this in sync with kdebase/workspace/kcontrol/colors/colorscm.cpp 1095 QColor KexiUtils::inactiveTitleColor() 1096 { 1097 #ifdef Q_OS_WIN 1098 return qt_colorref2qrgb(GetSysColor(COLOR_INACTIVECAPTION)); 1099 #else 1100 KConfigGroup g(KSharedConfig::openConfig(), "WM"); 1101 return g.readEntry("inactiveBackground", QColor(224, 223, 222)); 1102 #endif 1103 } 1104 1105 // NOTE: keep this in sync with kdebase/workspace/kcontrol/colors/colorscm.cpp 1106 QColor KexiUtils::inactiveTextColor() 1107 { 1108 #ifdef Q_OS_WIN 1109 return qt_colorref2qrgb(GetSysColor(COLOR_INACTIVECAPTIONTEXT)); 1110 #else 1111 KConfigGroup g(KSharedConfig::openConfig(), "WM"); 1112 return g.readEntry("inactiveForeground", QColor(75, 71, 67)); 1113 #endif 1114 } 1115 1116 // NOTE: keep this in sync with kdebase/workspace/kcontrol/colors/colorscm.cpp 1117 QColor KexiUtils::activeTitleColor() 1118 { 1119 #ifdef Q_OS_WIN 1120 return qt_colorref2qrgb(GetSysColor(COLOR_ACTIVECAPTION)); 1121 #else 1122 KConfigGroup g(KSharedConfig::openConfig(), "WM"); 1123 return g.readEntry("activeBackground", QColor(48, 174, 232)); 1124 #endif 1125 } 1126 1127 // NOTE: keep this in sync with kdebase/workspace/kcontrol/colors/colorscm.cpp 1128 QColor KexiUtils::activeTextColor() 1129 { 1130 #ifdef Q_OS_WIN 1131 return qt_colorref2qrgb(GetSysColor(COLOR_CAPTIONTEXT)); 1132 #else 1133 KConfigGroup g(KSharedConfig::openConfig(), "WM"); 1134 return g.readEntry("activeForeground", QColor(255, 255, 255)); 1135 #endif 1136 } 1137 1138 QString KexiUtils::makeStandardCaption(const QString &userCaption, CaptionFlags flags) 1139 { 1140 QString caption = KAboutData::applicationData().displayName(); 1141 if (caption.isEmpty()) { 1142 return QCoreApplication::instance()->applicationName(); 1143 } 1144 QString captionString = userCaption.isEmpty() ? caption : userCaption; 1145 1146 // If the document is modified, add '[modified]'. 1147 if (flags & ModifiedCaption) { 1148 captionString += QString::fromUtf8(" [") + xi18n("modified") + QString::fromUtf8("]"); 1149 } 1150 1151 if (!userCaption.isEmpty()) { 1152 // Add the application name if: 1153 // User asked for it, it's not a duplication and the app name (caption()) is not empty 1154 if (flags & AppNameCaption && 1155 !caption.isEmpty() && 1156 !userCaption.endsWith(caption)) { 1157 // TODO: check to see if this is a transient/secondary window before trying to add the app name 1158 // on platforms that need this 1159 captionString += xi18nc("Document/application separator in titlebar", " – ") + caption; 1160 } 1161 } 1162 return captionString; 1163 } 1164 1165 QString themedIconName(const QString &name) 1166 { 1167 static bool firstUse = true; 1168 if (firstUse) { 1169 // workaround for some kde-related crash 1170 const bool _unused = KIconLoader::global()->iconPath(name, KIconLoader::NoGroup, true).isEmpty(); 1171 Q_UNUSED(_unused); 1172 firstUse = false; 1173 } 1174 1175 // try load themed icon 1176 const QColor background = qApp->palette().background().color(); 1177 const bool useDarkIcons = background.value() > 100; 1178 return QLatin1String(useDarkIcons ? "dark_" : "light_") + name; 1179 } 1180 1181 QIcon themedIcon(const QString &name) 1182 { 1183 const QString realName(themedIconName(name)); 1184 const QIcon icon = QIcon::fromTheme(realName); 1185 1186 // fallback 1187 if (icon.isNull()) { 1188 return QIcon::fromTheme(name); 1189 } 1190 return icon; 1191 } 1192 1193 QString KexiUtils::localizedStringToHtmlSubstring(const KLocalizedString &string) 1194 { 1195 return string.isEmpty() ? QString() 1196 : string.toString(Kuit::RichText) 1197 .remove(QLatin1String("<html>")) 1198 .remove(QLatin1String("</html>")); 1199 } 1200 1201 QString KexiUtils::localizedSentencesToHtml(const KLocalizedString &part1, const KLocalizedString &part2, 1202 const KLocalizedString &part3, const KLocalizedString &part4, 1203 const KLocalizedString &part5, const KLocalizedString &part6) 1204 { 1205 return xi18nc("@info/plain Concatenated sentence1 sentence2 ...", "<html>%1%2%3%4%5%6</html>", 1206 KexiUtils::localizedStringToHtmlSubstring(part1), 1207 KexiUtils::localizedStringToHtmlSubstring(part2), 1208 KexiUtils::localizedStringToHtmlSubstring(part3), 1209 KexiUtils::localizedStringToHtmlSubstring(part4), 1210 KexiUtils::localizedStringToHtmlSubstring(part5), 1211 KexiUtils::localizedStringToHtmlSubstring(part6)); 1212 } 1213 1214 bool KexiUtils::cursorAtEnd(const QLineEdit *lineEdit) 1215 { 1216 if (!lineEdit) { 1217 return false; 1218 } 1219 if (lineEdit->inputMask().isEmpty()) { 1220 return lineEdit->cursorPosition() >= lineEdit->displayText().length(); 1221 } else { 1222 return lineEdit->cursorPosition() >= (lineEdit->displayText().length() - 1); 1223 } 1224 } 1225 1226 QDebug operator<<(QDebug dbg, const QDomNode &node) 1227 { 1228 QString s; 1229 QTextStream str(&s, QIODevice::WriteOnly); 1230 node.save(str, 2); 1231 dbg << qPrintable(s); 1232 return dbg; 1233 }