diff --git a/CMakeLists.txt b/CMakeLists.txt index de3561183..ae70f144c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,161 +1,161 @@ cmake_minimum_required(VERSION 3.1) set(KDEPIM_VERSION_NUMBER "5.9.40") project(kmail VERSION ${KDEPIM_VERSION_NUMBER}) include(CheckIncludeFiles) if (POLICY CMP0063) cmake_policy(SET CMP0063 NEW) endif() set(KF5_VERSION "5.48.0") find_package(ECM ${KF5_VERSION} CONFIG REQUIRED) set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH}) include(ECMOptionalAddSubdirectory) include(ECMInstallIcons) include(ECMSetupVersion) include(ECMAddTests) include(ECMMarkNonGuiExecutable) include(GenerateExportHeader) include(ECMGenerateHeaders) include(CMakePackageConfigHelpers) include(FeatureSummary) include(CheckFunctionExists) include(ECMGeneratePriFile) include(KDEInstallDirs) include(KDECMakeSettings) include(KDECompilerSettings NO_POLICY_SCOPE) include(ECMAddAppIcon) include(ECMQtDeclareLoggingCategory) include(ECMCoverageOption) # Do NOT add quote set(KDEPIM_DEV_VERSION alpha) # add an extra space if(DEFINED KDEPIM_DEV_VERSION) set(KDEPIM_DEV_VERSION " ${KDEPIM_DEV_VERSION}") endif() set(KDEPIM_VERSION "${KDEPIM_VERSION_NUMBER}${KDEPIM_DEV_VERSION}") set(AKONADI_MIMELIB_VERSION "5.8.80") set(AKONADI_CONTACT_VERSION "5.8.80") set(KCONTACTS_LIB_VERSION "5.8.80") set(KCALENDARCORE_LIB_VERSION "5.8.80") set(CALENDARUTILS_LIB_VERSION "5.8.80") set(IDENTITYMANAGEMENT_LIB_VERSION "5.8.80") set(KLDAP_LIB_VERSION "5.8.80") set(KMAILTRANSPORT_LIB_VERSION "5.8.80") set(KONTACTINTERFACE_LIB_VERSION "5.8.80") set(KMIME_LIB_VERSION "5.8.80") set(KPIMTEXTEDIT_LIB_VERSION "5.8.80") set(AKONADI_VERSION "5.8.80") set(KTNEF_LIB_VERSION "5.8.80") set(KDEPIM_LIB_VERSION "${KDEPIM_VERSION_NUMBER}") set(KDEPIM_LIB_SOVERSION "5") set(QT_REQUIRED_VERSION "5.9.0") option(KDEPIM_ENTERPRISE_BUILD "Enable features specific to the enterprise branch, which are normally disabled. Also, it disables many components not needed for Kontact such as the Kolab client." FALSE) find_package(Qt5 ${QT_REQUIRED_VERSION} CONFIG REQUIRED DBus Network Test Widgets WebEngine WebEngineWidgets) set(LIBGRAVATAR_VERSION_LIB "5.8.80") set(MAILCOMMON_LIB_VERSION_LIB "5.8.80") set(KDEPIM_APPS_LIB_VERSION_LIB "5.8.80") set(MESSAGELIB_LIB_VERSION_LIB "5.8.81") set(LIBKLEO_LIB_VERSION_LIB "5.8.80") set(PIMCOMMON_LIB_VERSION_LIB "5.8.80") set(LIBKDEPIM_LIB_VERSION_LIB "5.8.80") set(LIBKSIEVE_LIB_VERSION_LIB "5.8.80") find_package(KF5WebEngineViewer ${MESSAGELIB_LIB_VERSION_LIB} CONFIG REQUIRED) find_package(KF5AkonadiSearch "5.8.80" CONFIG REQUIRED) set_package_properties(KF5AkonadiSearch PROPERTIES DESCRIPTION "The Akonadi Search libraries" URL "http://www.kde.org" TYPE REQUIRED PURPOSE "Provides search capabilities in KMail and Akonadi") set(GPGMEPP_LIB_VERSION "1.8.0") find_package(Gpgmepp ${GPGMEPP_LIB_VERSION} CONFIG REQUIRED) # Find KF5 package find_package(KF5Bookmarks ${KF5_VERSION} CONFIG REQUIRED) find_package(KF5Codecs ${KF5_VERSION} CONFIG REQUIRED) find_package(KF5Config ${KF5_VERSION} CONFIG REQUIRED) find_package(KF5ConfigWidgets ${KF5_VERSION} CONFIG REQUIRED) find_package(KF5Crash ${KF5_VERSION} CONFIG REQUIRED) find_package(KF5DBusAddons ${KF5_VERSION} CONFIG REQUIRED) find_package(KF5DocTools ${KF5_VERSION} REQUIRED) find_package(KF5GuiAddons ${KF5_VERSION} REQUIRED) find_package(KF5I18n ${KF5_VERSION} CONFIG REQUIRED) find_package(KF5ItemViews ${KF5_VERSION} CONFIG REQUIRED) find_package(KF5JobWidgets ${KF5_VERSION} REQUIRED) find_package(KF5KIO ${KF5_VERSION} CONFIG REQUIRED) find_package(KF5KCMUtils ${KF5_VERSION} CONFIG REQUIRED) find_package(KF5Notifications ${KF5_VERSION} CONFIG REQUIRED) find_package(KF5NotifyConfig ${KF5_VERSION} CONFIG REQUIRED) find_package(KF5Parts ${KF5_VERSION} CONFIG REQUIRED) find_package(KF5Service ${KF5_VERSION} CONFIG REQUIRED) find_package(KF5Sonnet ${KF5_VERSION} CONFIG REQUIRED) find_package(KF5TextWidgets ${KF5_VERSION} CONFIG REQUIRED) find_package(KF5WidgetsAddons ${KF5_VERSION} CONFIG REQUIRED) find_package(KF5WindowSystem ${KF5_VERSION} CONFIG REQUIRED) find_package(KF5XmlGui ${KF5_VERSION} CONFIG REQUIRED) find_package(KF5IconThemes ${KF5_VERSION} CONFIG REQUIRED) # Find KdepimLibs Package find_package(KF5Akonadi ${AKONADI_VERSION} CONFIG REQUIRED) find_package(KF5AkonadiContact ${AKONADI_CONTACT_VERSION} CONFIG REQUIRED) find_package(KF5AkonadiMime ${AKONADI_MIMELIB_VERSION} CONFIG REQUIRED) find_package(KF5Contacts ${KCONTACTS_LIB_VERSION} CONFIG REQUIRED) find_package(KF5CalendarCore ${KCALENDARCORE_LIB_VERSION} CONFIG REQUIRED) find_package(KF5CalendarUtils ${CALENDARUTILS_LIB_VERSION} CONFIG REQUIRED) find_package(KF5IdentityManagement ${IDENTITYMANAGEMENT_LIB_VERSION} CONFIG REQUIRED) find_package(KF5Ldap ${KLDAP_LIB_VERSION} CONFIG REQUIRED) find_package(KF5MailTransportAkonadi ${KMAILTRANSPORT_LIB_VERSION} CONFIG REQUIRED) find_package(KF5PimTextEdit ${KPIMTEXTEDIT_LIB_VERSION} CONFIG REQUIRED) find_package(KF5KontactInterface ${KONTACTINTERFACE_LIB_VERSION} CONFIG REQUIRED) find_package(KF5Mime ${KMIME_LIB_VERSION} CONFIG REQUIRED) find_package(KF5FollowupReminder ${KDEPIM_APPS_LIB_VERSION_LIB} CONFIG REQUIRED) find_package(KF5Gravatar ${LIBGRAVATAR_VERSION_LIB} CONFIG REQUIRED) find_package(KF5KdepimDBusInterfaces ${KDEPIM_APPS_LIB_VERSION_LIB} CONFIG REQUIRED) find_package(KF5LibkdepimAkonadi ${LIBKDEPIM_LIB_VERSION_LIB} CONFIG REQUIRED) find_package(KF5Libkleo ${LIBKLEO_LIB_VERSION_LIB} CONFIG REQUIRED) find_package(KF5LibKSieve ${LIBKSIEVE_LIB_VERSION_LIB} CONFIG REQUIRED) find_package(KF5MailCommon ${MAILCOMMON_LIB_VERSION_LIB} CONFIG REQUIRED) find_package(KF5MessageCore ${MESSAGELIB_LIB_VERSION_LIB} CONFIG REQUIRED) find_package(KF5MessageComposer ${MESSAGELIB_LIB_VERSION_LIB} CONFIG REQUIRED) find_package(KF5MessageList ${MESSAGELIB_LIB_VERSION_LIB} CONFIG REQUIRED) find_package(KF5MessageViewer ${MESSAGELIB_LIB_VERSION_LIB} CONFIG REQUIRED) find_package(KF5PimCommonAkonadi ${PIMCOMMON_LIB_VERSION_LIB} CONFIG REQUIRED) find_package(KF5SendLater ${KDEPIM_APPS_LIB_VERSION_LIB} CONFIG REQUIRED) find_package(KF5TemplateParser ${MESSAGELIB_LIB_VERSION_LIB} CONFIG REQUIRED) find_package(KF5Tnef ${KTNEF_LIB_VERSION} CONFIG REQUIRED) find_package(MailTransportDBusService CONFIG REQUIRED) configure_file(config-enterprise.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-enterprise.h ) include_directories(${kmail_SOURCE_DIR} ${kmail_BINARY_DIR}) configure_file(kmail-version.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/kmail-version.h @ONLY) add_definitions(-DQT_NO_CAST_FROM_ASCII) add_definitions(-DQT_NO_CAST_TO_ASCII) add_definitions(-DQT_NO_NARROWING_CONVERSIONS_IN_CONNECT) add_definitions(-DQT_NO_URL_CAST_FROM_STRING) add_definitions(-DQT_USE_QSTRINGBUILDER) -#add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x060000) +add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x060000) add_subdirectory(src) add_subdirectory(agents) add_subdirectory(ktnef) install( FILES kmail.renamecategories kmail.categories DESTINATION ${KDE_INSTALL_CONFDIR} ) add_subdirectory(doc) feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/ktnef/src/qwmf.cpp b/ktnef/src/qwmf.cpp index 53d98a42f..297de1560 100644 --- a/ktnef/src/qwmf.cpp +++ b/ktnef/src/qwmf.cpp @@ -1,1257 +1,1257 @@ /* Windows Meta File Loader/Painter Class Implementation * * Copyright ( C ) 1998 Stefan Taferner * Modified 2002 thierry lorthiois * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU 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 * MERCHANTABLILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. You should have received a copy * of the GNU General Public License along with this program; if not, write * to the Free Software Foundation, Inc, 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include // WORDS_BIGENDIAN #include #include #include #include #include #include #include #include #include #include "ktnef_debug.h" bool qwmfDebug = false; #include "qwmf.h" #include "wmfstruct.h" #include "metafuncs.h" #define QWMF_DEBUG 0 class WmfCmd { public: ~WmfCmd() { delete next; } WmfCmd *next; unsigned short funcIndex; long numParm; short *parm; }; class WinObjHandle { public: virtual ~WinObjHandle() { } virtual void apply(QPainter &p) = 0; }; class WinObjBrushHandle : public WinObjHandle { public: void apply(QPainter &p) override; QBrush brush; virtual ~WinObjBrushHandle() { } }; class WinObjPenHandle : public WinObjHandle { public: void apply(QPainter &p) override; QPen pen; virtual ~WinObjPenHandle() { } }; class WinObjPatternBrushHandle : public WinObjHandle { public: void apply(QPainter &p) override; QBrush brush; QImage image; virtual ~WinObjPatternBrushHandle() { } }; class WinObjFontHandle : public WinObjHandle { public: void apply(QPainter &p) override; QFont font; int rotation; virtual ~WinObjFontHandle() { } }; void WinObjBrushHandle::apply(QPainter &p) { p.setBrush(brush); } void WinObjPenHandle::apply(QPainter &p) { p.setPen(pen); } void WinObjPatternBrushHandle::apply(QPainter &p) { p.setBrush(brush); } void WinObjFontHandle::apply(QPainter &p) { p.setFont(font); } #define MAX_OBJHANDLE 64 //----------------------------------------------------------------------------- QWinMetaFile::QWinMetaFile() { mValid = false; mFirstCmd = NULL; mObjHandleTab = NULL; mDpi = 1000; } //----------------------------------------------------------------------------- QWinMetaFile::~QWinMetaFile() { delete mFirstCmd; if (mObjHandleTab) { delete[] mObjHandleTab; } } //----------------------------------------------------------------------------- bool QWinMetaFile::load(const QString &filename) { QFile file(filename); if (!file.exists()) { qCDebug(KTNEFAPPS_LOG) << "File" << QFile::encodeName(filename) << " does not exist"; return false; } if (!file.open(QIODevice::ReadOnly)) { qCDebug(KTNEFAPPS_LOG) << "Cannot open file" << QFile::encodeName(filename); return false; } QByteArray ba = file.readAll(); file.close(); QBuffer buffer(&ba); buffer.open(QIODevice::ReadOnly); return load(buffer); } //----------------------------------------------------------------------------- bool QWinMetaFile::load(QBuffer &buffer) { QDataStream st; WmfEnhMetaHeader eheader; WmfMetaHeader header; WmfPlaceableHeader pheader; WORD checksum; int filePos, idx, i; WmfCmd *cmd, *last; DWORD rdSize; WORD rdFunc; mTextAlign = 0; mRotation = 0; mTextColor = Qt::black; if (mFirstCmd) { delete mFirstCmd; } mFirstCmd = NULL; st.setDevice(&buffer); st.setByteOrder(QDataStream::LittleEndian); // Great, I love Qt ! //----- Read placeable metafile header st >> pheader.key; mIsPlaceable = (pheader.key == (DWORD)APMHEADER_KEY); if (mIsPlaceable) { st >> pheader.hmf; st >> pheader.bbox.left; st >> pheader.bbox.top; st >> pheader.bbox.right; st >> pheader.bbox.bottom; st >> pheader.inch; st >> pheader.reserved; st >> pheader.checksum; checksum = calcCheckSum(&pheader); if (pheader.checksum != checksum) { mIsPlaceable = false; } mDpi = pheader.inch; mBBox.setLeft(pheader.bbox.left); mBBox.setTop(pheader.bbox.top); mBBox.setRight(pheader.bbox.right); mBBox.setBottom(pheader.bbox.bottom); mHeaderBoundingBox = mBBox; if (QWMF_DEBUG) { qCDebug(KTNEFAPPS_LOG) << endl << "-------------------------------------------------"; qCDebug(KTNEFAPPS_LOG) << "WMF Placeable Header (" << static_cast(sizeof(pheader)) << "):"; qCDebug(KTNEFAPPS_LOG) << " bbox=(" << mBBox.left() << ";" << mBBox.top() << ";" << mBBox.width() << "; " << mBBox.height() << ")"; qCDebug(KTNEFAPPS_LOG) << " inch=" << pheader.inch; qCDebug(KTNEFAPPS_LOG) << " checksum=" << pheader.checksum << "(" << (pheader.checksum == checksum ? "ok" : "wrong") << " )"; } } else { buffer.reset(); } //----- Read as enhanced metafile header filePos = buffer.pos(); st >> eheader.iType; st >> eheader.nSize; st >> eheader.rclBounds.left; st >> eheader.rclBounds.top; st >> eheader.rclBounds.right; st >> eheader.rclBounds.bottom; st >> eheader.rclFrame.left; st >> eheader.rclFrame.top; st >> eheader.rclFrame.right; st >> eheader.rclFrame.bottom; st >> eheader.dSignature; mIsEnhanced = (eheader.dSignature == ENHMETA_SIGNATURE); if (mIsEnhanced) { // is it really enhanced ? st >> eheader.nVersion; st >> eheader.nBytes; st >> eheader.nRecords; st >> eheader.nHandles; st >> eheader.sReserved; st >> eheader.nDescription; st >> eheader.offDescription; st >> eheader.nPalEntries; st >> eheader.szlDevice.width; st >> eheader.szlDevice.height; st >> eheader.szlMillimeters.width; st >> eheader.szlMillimeters.height; if (QWMF_DEBUG) { qCDebug(KTNEFAPPS_LOG) << endl << "-------------------------------------------------"; qCDebug(KTNEFAPPS_LOG) << "WMF Extended Header:"; qCDebug(KTNEFAPPS_LOG) << " iType=" << eheader.iType; qCDebug(KTNEFAPPS_LOG) << " nSize=" << eheader.nSize; qCDebug(KTNEFAPPS_LOG) << " rclBounds=(" << eheader.rclBounds.left << ";" << eheader.rclBounds.top << ";" << eheader.rclBounds.right << "; " << eheader.rclBounds.bottom << ")"; qCDebug(KTNEFAPPS_LOG) << " rclFrame=(" << eheader.rclFrame.left << ";" << eheader.rclFrame.top << ";" << eheader.rclFrame.right << "; " << eheader.rclFrame.bottom << ")"; qCDebug(KTNEFAPPS_LOG) << " nBytes=" << eheader.nBytes; qCDebug(KTNEFAPPS_LOG) << "\nNOT YET IMPLEMENTED, SORRY."; } } else { // no, not enhanced //----- Read as standard metafile header buffer.seek(filePos); st >> header.mtType; st >> header.mtHeaderSize; st >> header.mtVersion; st >> header.mtSize; st >> header.mtNoObjects; st >> header.mtMaxRecord; st >> header.mtNoParameters; if (QWMF_DEBUG) { qCDebug(KTNEFAPPS_LOG) << "WMF Header:" << "mtSize=" << header.mtSize; } } //----- Test header validity mValid = ((header.mtHeaderSize == 9) && (header.mtNoParameters == 0)) || mIsEnhanced || mIsPlaceable; if (mValid) { //----- Read Metafile Records last = NULL; rdFunc = -1; while (!st.atEnd() && (rdFunc != 0)) { st >> rdSize; st >> rdFunc; idx = findFunc(rdFunc); rdSize -= 3; cmd = new WmfCmd; cmd->next = NULL; if (last) { last->next = cmd; } else { mFirstCmd = cmd; } cmd->funcIndex = idx; cmd->numParm = rdSize; cmd->parm = new WORD[ rdSize ]; last = cmd; for (i = 0; i < rdSize && !st.atEnd(); ++i) { st >> cmd->parm[ i ]; } if (rdFunc == 0x020B) { // SETWINDOWORG: dimensions mBBox.setLeft(cmd->parm[ 1 ]); mBBox.setTop(cmd->parm[ 0 ]); } if (rdFunc == 0x020C) { // SETWINDOWEXT: dimensions mBBox.setWidth(cmd->parm[ 1 ]); mBBox.setHeight(cmd->parm[ 0 ]); } if (i < rdSize) { qCDebug(KTNEFAPPS_LOG) << "WMF : file truncated !"; return false; } } //----- Test records validities mValid = (rdFunc == 0) && (mBBox.width() != 0) && (mBBox.height() != 0); if (!mValid) { qCDebug(KTNEFAPPS_LOG) << "WMF : incorrect file format !"; } } else { qCDebug(KTNEFAPPS_LOG) << "WMF Header : incorrect header !"; } buffer.close(); return mValid; } //----------------------------------------------------------------------------- bool QWinMetaFile::paint(QPaintDevice *aTarget, bool absolute) { int idx, i; WmfCmd *cmd; if (!mValid) { return false; } assert(aTarget != NULL); if (mPainter.isActive()) { return false; } if (mObjHandleTab) { delete[] mObjHandleTab; } mObjHandleTab = new WinObjHandle * [ MAX_OBJHANDLE ]; for (i = MAX_OBJHANDLE - 1; i >= 0; i--) { mObjHandleTab[ i ] = NULL; } mPainter.resetMatrix(); mWinding = false; mAbsoluteCoord = absolute; mPainter.begin(aTarget); if (QWMF_DEBUG) { qCDebug(KTNEFAPPS_LOG) << "Bounding box :" << mBBox.left() << " " << mBBox.top() << " " << mBBox.right() << " " << mBBox.bottom(); } if (mAbsoluteCoord) { mPainter.setWindow(mBBox.top(), mBBox.left(), mBBox.width(), mBBox.height()); } mInternalWorldMatrix.reset(); for (cmd = mFirstCmd; cmd; cmd = cmd->next) { idx = cmd->funcIndex; (this->*metaFuncTab[ idx ].method)(cmd->numParm, cmd->parm); if (QWMF_DEBUG) { QString str, param; if (metaFuncTab[ idx ].name == NULL) { str += QLatin1String("UNKNOWN "); } if (metaFuncTab[ idx ].method == &QWinMetaFile::noop) { str += QLatin1String("UNIMPLEMENTED "); } str += QLatin1String(metaFuncTab[ idx ].name); str += QLatin1String(" : "); for (i = 0; i < cmd->numParm; ++i) { param.setNum(cmd->parm[ i ]); str += param; str += QLatin1Char(' '); } qCDebug(KTNEFAPPS_LOG) << str; } } /* // TODO: cleanup this code when QPicture::setBoundingBox() is possible in KOClipart (QT31) // because actually QPicture::boundingBox() != mBBox() mWindowsCoord += 1; if ( mWindowsCoord == 2 ) { qCDebug(KTNEFAPPS_LOG) <<"DRAW ANGLES"; mPainter.setPen( Qt::white ); mPainter.drawPoint( mBBox.left(), mBBox.top() ); mPainter.drawPoint( mBBox.right(), mBBox.bottom() ); } */ mPainter.end(); return true; } //----------------s------------------------------------------------------------- // Metafile painter methods //----------------------------------------------------------------------------- void QWinMetaFile::setWindowOrg(long, short *parm) { if (mAbsoluteCoord) { QRect r = mPainter.window(); mPainter.setWindow(parm[ 1 ], parm[ 0 ], r.width(), r.height()); } else { double dx = mInternalWorldMatrix.dx(); double dy = mInternalWorldMatrix.dy(); mInternalWorldMatrix.translate(-dx, -dy); mInternalWorldMatrix.translate(-parm[ 1 ], -parm[ 0 ]); mPainter.translate(-dx, -dy); mPainter.translate(-parm[ 1 ], -parm[ 0 ]); } } //----------------------------------------------------------------------------- void QWinMetaFile::setWindowExt(long, short *parm) { // negative value allowed for width and height : QABS() forbidden if (mAbsoluteCoord) { QRect r = mPainter.window(); mPainter.setWindow(r.left(), r.top(), parm[ 1 ], parm[ 0 ]); } else { if ((parm[ 0 ] != 0) && (parm[ 1 ] != 0)) { QRect r = mPainter.window(); double dx = mInternalWorldMatrix.dx(); double dy = mInternalWorldMatrix.dy(); double sx = mInternalWorldMatrix.m11(); double sy = mInternalWorldMatrix.m22(); mInternalWorldMatrix.translate(-dx, -dy); mInternalWorldMatrix.scale(1 / sx, 1 / sy); mPainter.translate(-dx, -dy); mPainter.scale(1 / sx, 1 / sy); sx = (double)r.width() / (double)parm[ 1 ]; sy = (double)r.height() / (double)parm[ 0 ]; mInternalWorldMatrix.scale(sx, sy); mInternalWorldMatrix.translate(dx, dy); mPainter.scale(sx, sy); mPainter.translate(dx, dy); } } } //----------------------------------------------------------------------------- // Drawing //----------------------------------------------------------------------------- void QWinMetaFile::lineTo(long, short *parm) { mPainter.drawLine(mLastPos, QPoint(parm[1], parm[0])); } //----------------------------------------------------------------------------- void QWinMetaFile::moveTo(long, short *parm) { mLastPos = QPoint(parm[ 1 ], parm[ 0 ]); } //----------------------------------------------------------------------------- void QWinMetaFile::ellipse(long, short *parm) { mPainter.drawEllipse(parm[ 3 ], parm[ 2 ], parm[ 1 ] - parm[ 3 ], parm[ 0 ] - parm[ 2 ]); } //----------------------------------------------------------------------------- void QWinMetaFile::polygon(long, short *parm) { QPolygon *pa; // causing a memleck ??? pa = pointArray(parm[ 0 ], &parm[ 1 ]); if (mWinding) { mPainter.drawPolygon(*pa, Qt::WindingFill); } else { mPainter.drawPolygon(*pa, Qt::OddEvenFill); } delete pa; } //----------------------------------------------------------------------------- void QWinMetaFile::polyPolygon(long, short *parm) { QRegion region; int i, j, startPolygon; mPainter.save(); // define clipping region QRect win = bbox(); startPolygon = 1 + parm[ 0 ]; for (i = 0; i < parm[ 0 ]; ++i) { QPolygon pa1(parm[ 1 + i ]); for (j = 0; j < parm[ 1 + i ]; j++) { pa1.setPoint(j, parm[ startPolygon ], parm[ startPolygon + 1 ]); startPolygon += 2; } QRegion r(pa1); region = region.xored(r); } mPainter.setClipRegion(region); // fill polygons mPainter.fillRect(win.left(), win.top(), win.width(), win.height(), mPainter.brush()); // draw polygon's border if necessary if (mPainter.pen().style() != Qt::NoPen) { mPainter.setClipping(false); mPainter.setBrush(Qt::NoBrush); QPolygon *pa; int idxPolygon = 1 + parm[ 0 ]; for (i = 0; i < parm[ 0 ]; ++i) { pa = pointArray(parm[ 1 + i ], &parm[ idxPolygon ]); mPainter.drawPolygon(*pa); idxPolygon += parm[ 1 + i ] * 2; } } mPainter.restore(); } //----------------------------------------------------------------------------- void QWinMetaFile::polyline(long, short *parm) { QPolygon *pa; pa = pointArray(parm[ 0 ], &parm[ 1 ]); mPainter.drawPolyline(*pa); } //----------------------------------------------------------------------------- void QWinMetaFile::rectangle(long, short *parm) { mPainter.drawRect(parm[ 3 ], parm[ 2 ], parm[ 1 ] - parm[ 3 ], parm[ 0 ] - parm[ 2 ]); } //----------------------------------------------------------------------------- void QWinMetaFile::roundRect(long, short *parm) { int xRnd = 0, yRnd = 0; // convert (xRound, yRound) in percentage if ((parm[ 3 ] - parm[ 5 ]) != 0) { xRnd = (parm[ 1 ] * 100) / (parm[ 3 ] - parm[ 5 ]); } if ((parm[ 2 ] - parm[ 4 ]) != 0) { yRnd = (parm[ 0 ] * 100) / (parm[ 2 ] - parm[ 4 ]); } mPainter.drawRoundRect(parm[ 5 ], parm[ 4 ], parm[ 3 ] - parm[ 5 ], parm[ 2 ] - parm[ 4 ], xRnd, yRnd); } //----------------------------------------------------------------------------- void QWinMetaFile::arc(long, short *parm) { int xCenter, yCenter, angleStart, aLength; xCenter = parm[ 7 ] + ((parm[ 5 ] - parm[ 7 ]) / 2); yCenter = parm[ 6 ] + ((parm[ 4 ] - parm[ 6 ]) / 2); xyToAngle(parm[ 3 ] - xCenter, yCenter - parm[ 2 ], parm[ 1 ] - xCenter, yCenter - parm[ 0 ], angleStart, aLength); mPainter.drawArc(parm[ 7 ], parm[ 6 ], parm[ 5 ] - parm[ 7 ], parm[ 4 ] - parm[ 6 ], angleStart, aLength); } //----------------------------------------------------------------------------- void QWinMetaFile::chord(long, short *parm) { int xCenter, yCenter, angleStart, aLength; xCenter = parm[ 7 ] + ((parm[ 5 ] - parm[ 7 ]) / 2); yCenter = parm[ 6 ] + ((parm[ 4 ] - parm[ 6 ]) / 2); xyToAngle(parm[ 3 ] - xCenter, yCenter - parm[ 2 ], parm[ 1 ] - xCenter, yCenter - parm[ 0 ], angleStart, aLength); mPainter.drawChord(parm[ 7 ], parm[ 6 ], parm[ 5 ] - parm[ 7 ], parm[ 4 ] - parm[ 6 ], angleStart, aLength); } //----------------------------------------------------------------------------- void QWinMetaFile::pie(long, short *parm) { int xCenter, yCenter, angleStart, aLength; xCenter = parm[ 7 ] + ((parm[ 5 ] - parm[ 7 ]) / 2); yCenter = parm[ 6 ] + ((parm[ 4 ] - parm[ 6 ]) / 2); xyToAngle(parm[ 3 ] - xCenter, yCenter - parm[ 2 ], parm[ 1 ] - xCenter, yCenter - parm[ 0 ], angleStart, aLength); mPainter.drawPie(parm[ 7 ], parm[ 6 ], parm[ 5 ] - parm[ 7 ], parm[ 4 ] - parm[ 6 ], angleStart, aLength); } //----------------------------------------------------------------------------- void QWinMetaFile::setPolyFillMode(long, short *parm) { mWinding = parm[ 0 ]; } //----------------------------------------------------------------------------- void QWinMetaFile::setBkColor(long, short *parm) { mPainter.setBackground(QBrush(color(parm))); } //----------------------------------------------------------------------------- void QWinMetaFile::setBkMode(long, short *parm) { if (parm[ 0 ] == 1) { mPainter.setBackgroundMode(Qt::TransparentMode); } else { mPainter.setBackgroundMode(Qt::OpaqueMode); } } //----------------------------------------------------------------------------- void QWinMetaFile::setPixel(long, short *parm) { QPen pen = mPainter.pen(); mPainter.setPen(color(parm)); mPainter.drawPoint(parm[ 3 ], parm[ 2 ]); mPainter.setPen(pen); } //----------------------------------------------------------------------------- void QWinMetaFile::setRop(long, short *parm) { mPainter.setCompositionMode(winToQtComposition(parm[ 0 ])); } //----------------------------------------------------------------------------- void QWinMetaFile::saveDC(long, short *) { mPainter.save(); } //----------------------------------------------------------------------------- void QWinMetaFile::restoreDC(long, short *parm) { for (int i = 0; i > parm[ 0 ]; i--) { mPainter.restore(); } } //----------------------------------------------------------------------------- void QWinMetaFile::intersectClipRect(long, short *parm) { /* TODO: better implementation : need QT 3.0.2 QRegion region = mPainter.clipRegion(); if ( region.isEmpty() ) region = bbox(); */ QRegion region(bbox()); QRegion newRegion(parm[ 3 ], parm[ 2 ], parm[ 1 ] - parm[ 3 ], parm[ 0 ] - parm[ 2 ]); region = region.intersected(newRegion); mPainter.setClipRegion(region); } //----------------------------------------------------------------------------- void QWinMetaFile::excludeClipRect(long, short *parm) { /* TODO: better implementation : need QT 3.0.2 QRegion region = mPainter.clipRegion(); if ( region.isEmpty() ) region = bbox(); */ QRegion region(bbox()); QRegion newRegion(parm[ 3 ], parm[ 2 ], parm[ 1 ] - parm[ 3 ], parm[ 0 ] - parm[ 2 ]); region = region.subtracted(newRegion); mPainter.setClipRegion(region); } //----------------------------------------------------------------------------- // Text //----------------------------------------------------------------------------- void QWinMetaFile::setTextColor(long, short *parm) { mTextColor = color(parm); } //----------------------------------------------------------------------------- void QWinMetaFile::setTextAlign(long, short *parm) { mTextAlign = parm[ 0 ]; } //----------------------------------------------------------------------------- void QWinMetaFile::textOut(long num, short *parm) { short *copyParm = new short[ num + 1 ]; // re-order parameters int idxOffset = (parm[ 0 ] / 2) + 1 + (parm[ 0 ] & 1); copyParm[ 0 ] = parm[ idxOffset ]; copyParm[ 1 ] = parm[ idxOffset + 1 ]; copyParm[ 2 ] = parm[ 0 ]; copyParm[ 3 ] = 0; memcpy(©Parm[ 4 ], &parm[ 1 ], parm[ 0 ]); extTextOut(num + 1, copyParm); delete [] copyParm; } //----------------------------------------------------------------------------- void QWinMetaFile::extTextOut(long num, short *parm) { char *ptStr; int x, y, width, height; int idxOffset; if (parm[ 3 ] != 0) { // ETO_CLIPPED flag add 4 parameters ptStr = (char *)&parm[ 8 ]; } else { ptStr = (char *)&parm[ 4 ]; } QByteArray text(ptStr, parm[ 2 ] + 1); QFontMetrics fm(mPainter.font()); - width = fm.width(QLatin1String(text)) + fm.descent(); // because fm.width(text) isn't rigth with Italic text + width = fm.boundingRect(QLatin1String(text)).width() + fm.descent(); // because fm.width(text) isn't rigth with Italic text height = fm.height(); mPainter.save(); if (mTextAlign & 0x01) { // (left, top) position = current logical position x = mLastPos.x(); y = mLastPos.y(); } else { // (left, top) position = parameters x = parm[ 1 ]; y = parm[ 0 ]; } if (mRotation) { mPainter.translate(parm[ 1 ], parm[ 0 ]); mPainter.rotate(mRotation); mPainter.translate(-parm[ 1 ], -parm[ 0 ]); } // alignment if (mTextAlign & 0x06) { x -= (width / 2); } if (mTextAlign & 0x08) { y -= (height - fm.descent()); } mPainter.setPen(mTextColor); idxOffset = (parm[ 2 ] / 2) + 4 + (parm[ 2 ] & 1); if ((parm[ 2 ] > 1) && (num >= (idxOffset + parm[ 2 ])) && (parm[ 3 ] == 0)) { // offset for each char int left = x; mPainter.drawText(left, y, width, height, Qt::AlignLeft | Qt::AlignTop, QLatin1String(text.mid(0, 1))); for (int i = 1; i < parm[ 2 ]; ++i) { left += parm[ idxOffset + i - 1 ]; mPainter.drawText(left, y, width, height, Qt::AlignLeft | Qt::AlignTop, QLatin1String(text.mid(i, 1))); } } else { mPainter.drawText(x, y, width, height, Qt::AlignLeft | Qt::AlignTop, QLatin1String(text)); } mPainter.restore(); } //----------------------------------------------------------------------------- // Bitmap //----------------------------------------------------------------------------- void QWinMetaFile::dibBitBlt(long num, short *parm) { if (num > 9) { // DIB image QImage bmpSrc; if (dibToBmp(bmpSrc, (char *)&parm[ 8 ], (num - 8) * 2)) { long raster = toDWord(parm); mPainter.setCompositionMode(winToQtComposition(raster)); // wmf file allow negative width or height mPainter.save(); if (parm[ 5 ] < 0) { // width < 0 => horizontal flip QMatrix m(-1.0F, 0.0F, 0.0F, 1.0F, 0.0F, 0.0F); mPainter.setMatrix(m, true); } if (parm[ 4 ] < 0) { // height < 0 => vertical flip QMatrix m(1.0F, 0.0F, 0.0F, -1.0F, 0.0F, 0.0F); mPainter.setMatrix(m, true); } mPainter.drawImage(parm[ 7 ], parm[ 6 ], bmpSrc, parm[ 3 ], parm[ 2 ], parm[ 5 ], parm[ 4 ]); mPainter.restore(); } } else { qCDebug(KTNEFAPPS_LOG) << "QWinMetaFile::dibBitBlt without image: not implemented"; } } //----------------------------------------------------------------------------- void QWinMetaFile::dibStretchBlt(long num, short *parm) { QImage bmpSrc; if (dibToBmp(bmpSrc, (char *)&parm[ 10 ], (num - 10) * 2)) { long raster = toDWord(parm); mPainter.setCompositionMode(winToQtComposition(raster)); // wmf file allow negative width or height mPainter.save(); if (parm[ 7 ] < 0) { // width < 0 => horizontal flip QMatrix m(-1.0F, 0.0F, 0.0F, 1.0F, 0.0F, 0.0F); mPainter.setMatrix(m, true); } if (parm[ 6 ] < 0) { // height < 0 => vertical flip QMatrix m(1.0F, 0.0F, 0.0F, -1.0F, 0.0F, 0.0F); mPainter.setMatrix(m, true); } bmpSrc = bmpSrc.copy(parm[ 5 ], parm[ 4 ], parm[ 3 ], parm[ 2 ]); // TODO: scale the bitmap ( QImage::scale(parm[ 7 ], parm[ 6 ]) is actually too slow ) mPainter.drawImage(parm[ 9 ], parm[ 8 ], bmpSrc); mPainter.restore(); } } //----------------------------------------------------------------------------- void QWinMetaFile::stretchDib(long num, short *parm) { QImage bmpSrc; if (dibToBmp(bmpSrc, (char *)&parm[ 11 ], (num - 11) * 2)) { long raster = toDWord(parm); mPainter.setCompositionMode(winToQtComposition(raster)); // wmf file allow negative width or height mPainter.save(); if (parm[ 8 ] < 0) { // width < 0 => horizontal flip QMatrix m(-1.0F, 0.0F, 0.0F, 1.0F, 0.0F, 0.0F); mPainter.setMatrix(m, true); } if (parm[ 7 ] < 0) { // height < 0 => vertical flip QMatrix m(1.0F, 0.0F, 0.0F, -1.0F, 0.0F, 0.0F); mPainter.setMatrix(m, true); } bmpSrc = bmpSrc.copy(parm[ 6 ], parm[ 5 ], parm[ 4 ], parm[ 3 ]); // TODO: scale the bitmap ( QImage::scale(parm[ 8 ], parm[ 7 ]) is actually too slow ) mPainter.drawImage(parm[ 10 ], parm[ 9 ], bmpSrc); mPainter.restore(); } } //----------------------------------------------------------------------------- void QWinMetaFile::dibCreatePatternBrush(long num, short *parm) { WinObjPatternBrushHandle *handle = new WinObjPatternBrushHandle; addHandle(handle); QImage bmpSrc; if (dibToBmp(bmpSrc, (char *)&parm[ 2 ], (num - 2) * 2)) { handle->image = bmpSrc; handle->brush.setTextureImage(handle->image); } } //----------------------------------------------------------------------------- // Object handle //----------------------------------------------------------------------------- void QWinMetaFile::selectObject(long, short *parm) { int idx = parm[ 0 ]; if (idx >= 0 && idx < MAX_OBJHANDLE && mObjHandleTab[ idx ]) { mObjHandleTab[ idx ]->apply(mPainter); } } //----------------------------------------------------------------------------- void QWinMetaFile::deleteObject(long, short *parm) { deleteHandle(parm[ 0 ]); } //----------------------------------------------------------------------------- void QWinMetaFile::createEmptyObject(long, short *) { // allocation of an empty object (to keep object counting in sync) WinObjPenHandle *handle = new WinObjPenHandle; addHandle(handle); qCDebug(KTNEFAPPS_LOG) << "QWinMetaFile: unimplemented createObject"; } //----------------------------------------------------------------------------- void QWinMetaFile::createBrushIndirect(long, short *parm) { static Qt::BrushStyle hatchedStyleTab[] = { Qt::HorPattern, Qt::FDiagPattern, Qt::BDiagPattern, Qt::CrossPattern, Qt::DiagCrossPattern }; static Qt::BrushStyle styleTab[] = { Qt::SolidPattern, Qt::NoBrush, Qt::FDiagPattern, /* hatched */ Qt::Dense4Pattern, /* should be custom bitmap pattern */ Qt::HorPattern, /* should be BS_INDEXED (?) */ Qt::VerPattern, /* should be device-independent bitmap */ Qt::Dense6Pattern, /* should be device-independent packed-bitmap */ Qt::Dense2Pattern, /* should be BS_PATTERN8x8 */ Qt::Dense3Pattern /* should be device-independent BS_DIBPATTERN8x8 */ }; Qt::BrushStyle style; short arg; WinObjBrushHandle *handle = new WinObjBrushHandle; addHandle(handle); arg = parm[ 0 ]; if (arg == 2) { arg = parm[ 3 ]; if (arg >= 0 && arg < 5) { style = hatchedStyleTab[ arg ]; } else { qCDebug(KTNEFAPPS_LOG) << "QWinMetaFile::createBrushIndirect: invalid hatched brush" << arg; style = Qt::SolidPattern; } } else if (arg >= 0 && arg < 9) { style = styleTab[ arg ]; } else { qCDebug(KTNEFAPPS_LOG) << "QWinMetaFile::createBrushIndirect: invalid brush" << arg; style = Qt::SolidPattern; } handle->brush.setStyle(style); handle->brush.setColor(color(parm + 1)); } //----------------------------------------------------------------------------- void QWinMetaFile::createPenIndirect(long, short *parm) { static Qt::PenStyle styleTab[] = { Qt::SolidLine, Qt::DashLine, Qt::DotLine, Qt::DashDotLine, Qt::DashDotDotLine, Qt::NoPen, Qt::SolidLine }; Qt::PenStyle style; WinObjPenHandle *handle = new WinObjPenHandle; addHandle(handle); if (parm[ 0 ] >= 0 && parm[ 0 ] < 6) { style = styleTab[ parm[ 0 ] ]; } else { qCDebug(KTNEFAPPS_LOG) << "QWinMetaFile::createPenIndirect: invalid pen" << parm[ 0 ]; style = Qt::SolidLine; } handle->pen.setStyle(style); handle->pen.setColor(color(parm + 3)); handle->pen.setCapStyle(Qt::RoundCap); //int width = 0; // TODO : width of pen proportional to device context width // DOESN'T WORK /* QRect devRec; devRec = mPainter.transformed( mBBox ); width = ( parm[ 0 ] * devRec.width() ) / mBBox.width() ; qCDebug(KTNEFAPPS_LOG) <<"CreatePenIndirect:"; qCDebug(KTNEFAPPS_LOG) <<" log coord. :" << mBBox.width() <<"" << mBBox.height(); qCDebug(KTNEFAPPS_LOG) <<" log. pen :" << parm[ 1 ] <<"" << parm[ 2 ]; qCDebug(KTNEFAPPS_LOG) <<" dev. pen :" << width; handle->pen.setWidth( width ); */ } //----------------------------------------------------------------------------- void QWinMetaFile::createFontIndirect(long, short *parm) { WinObjFontHandle *handle = new WinObjFontHandle; addHandle(handle); QString family(QLatin1String((const char *)&parm[ 9 ])); mRotation = -parm[ 2 ] / 10; // text rotation (in 1/10 degree) // TODO: memorisation of rotation in object Font handle->font.setFamily(family); handle->font.setFixedPitch(((parm[ 8 ] & 0x01) == 0)); // TODO: investigation why some test case need -2. (size of font in logical point) handle->font.setPointSize(qAbs(parm[ 0 ]) - 2); handle->font.setWeight((parm[ 4 ] >> 3)); handle->font.setItalic((parm[ 5 ] & 0x01)); handle->font.setUnderline((parm[ 5 ] & 0x100)); } //----------------------------------------------------------------------------- // Misc //----------------------------------------------------------------------------- void QWinMetaFile::noop(long, short *) { } void QWinMetaFile::end(long, short *) { // end of file : // qCDebug(KTNEFAPPS_LOG) <<"END bbox=(" << mBBox.left() <<";" << mBBox.top() <<";" << mBBox.width() <<";" << mBBox.height() <<")"; } //----------------------------------------------------------------------------- unsigned short QWinMetaFile::calcCheckSum(WmfPlaceableHeader *apmfh) { WORD *lpWord; WORD wResult, i; // Start with the first word wResult = *(lpWord = (WORD *)(apmfh)); // XOR in each of the other 9 words for (i = 1; i <= 9; ++i) { wResult ^= lpWord[ i ]; } return wResult; } //----------------------------------------------------------------------------- int QWinMetaFile::findFunc(unsigned short aFunc) const { int i; for (i = 0; metaFuncTab[ i ].name; ++i) { if (metaFuncTab[ i ].func == aFunc) { return i; } } // here : unknown function return i; } //----------------------------------------------------------------------------- QPolygon *QWinMetaFile::pointArray(short num, short *parm) { int i; mPoints.resize(num); for (i = 0; i < num; ++i, parm += 2) { mPoints.setPoint(i, parm[ 0 ], parm[ 1 ]); } return &mPoints; } //----------------------------------------------------------------------------- unsigned int QWinMetaFile::toDWord(short *parm) { unsigned int l; #if !defined(WORDS_BIGENDIAN) l = *(unsigned int *)(parm); #else char *bytes; char swap[ 4 ]; bytes = (char *)parm; swap[ 0 ] = bytes[ 2 ]; swap[ 1 ] = bytes[ 3 ]; swap[ 2 ] = bytes[ 0 ]; swap[ 3 ] = bytes[ 1 ]; l = *(unsigned int *)(swap); #endif return l; } //----------------------------------------------------------------------------- QColor QWinMetaFile::color(short *parm) { unsigned int colorRef; int red, green, blue; colorRef = toDWord(parm) & 0xffffff; red = colorRef & 255; green = (colorRef >> 8) & 255; blue = (colorRef >> 16) & 255; return QColor(red, green, blue); } //----------------------------------------------------------------------------- void QWinMetaFile::xyToAngle(int xStart, int yStart, int xEnd, int yEnd, int &angleStart, int &angleLength) { float aStart, aLength; aStart = atan2((double)yStart, (double)xStart); aLength = atan2((double)yEnd, (double)xEnd) - aStart; angleStart = (int)(aStart * 2880 / 3.14166); angleLength = (int)(aLength * 2880 / 3.14166); if (angleLength < 0) { angleLength = 5760 + angleLength; } } //----------------------------------------------------------------------------- void QWinMetaFile::addHandle(WinObjHandle *handle) { int idx; for (idx = 0; idx < MAX_OBJHANDLE; idx++) { if (mObjHandleTab[ idx ] == NULL) { break; } } if (idx < MAX_OBJHANDLE) { mObjHandleTab[ idx ] = handle; } else { qCDebug(KTNEFAPPS_LOG) << "QWinMetaFile error: handle table full !"; } } //----------------------------------------------------------------------------- void QWinMetaFile::deleteHandle(int idx) { if (idx >= 0 && idx < MAX_OBJHANDLE && mObjHandleTab[ idx ]) { delete mObjHandleTab[ idx ]; mObjHandleTab[ idx ] = NULL; } } //----------------------------------------------------------------------------- QPainter::CompositionMode QWinMetaFile::winToQtComposition(short parm) const { static const QPainter::CompositionMode opTab[] = { // ### untested (conversion from Qt::RasterOp) QPainter::CompositionMode_Source, // Qt::CopyROP QPainter::CompositionMode_Clear, // Qt::ClearROP QPainter::CompositionMode_SourceOut, // Qt::NandROP QPainter::CompositionMode_SourceOut, // Qt::NotAndROP QPainter::CompositionMode_DestinationOut, // Qt::NotCopyROP QPainter::CompositionMode_DestinationOut, // Qt::AndNotROP QPainter::CompositionMode_DestinationOut, // Qt::NotROP QPainter::CompositionMode_Xor, // Qt::XorROP QPainter::CompositionMode_Source, // Qt::NorROP QPainter::CompositionMode_SourceIn, // Qt::AndROP QPainter::CompositionMode_SourceIn, // Qt::NotXorROP QPainter::CompositionMode_Destination, // Qt::NopROP QPainter::CompositionMode_Destination, // Qt::NotOrROP QPainter::CompositionMode_Source, // Qt::CopyROP QPainter::CompositionMode_Source, // Qt::OrNotROP QPainter::CompositionMode_SourceOver, // Qt::OrROP QPainter::CompositionMode_Source // Qt::SetROP }; if (parm > 0 && parm <= 16) { return opTab[ parm ]; } else { return QPainter::CompositionMode_Source; } } //----------------------------------------------------------------------------- QPainter::CompositionMode QWinMetaFile::winToQtComposition(long parm) const { /* TODO: Ternary raster operations 0x00C000CA dest = (source AND pattern) 0x00F00021 dest = pattern 0x00FB0A09 dest = DPSnoo 0x005A0049 dest = pattern XOR dest */ static const struct OpTab { long winRasterOp; QPainter::CompositionMode qtRasterOp; } opTab[] = { // ### untested (conversion from Qt::RasterOp) { 0x00CC0020, QPainter::CompositionMode_Source }, // CopyROP { 0x00EE0086, QPainter::CompositionMode_SourceOver }, // OrROP { 0x008800C6, QPainter::CompositionMode_SourceIn }, // AndROP { 0x00660046, QPainter::CompositionMode_Xor }, // XorROP { 0x00440328, QPainter::CompositionMode_DestinationOut }, // AndNotROP { 0x00330008, QPainter::CompositionMode_DestinationOut }, // NotCopyROP { 0x001100A6, QPainter::CompositionMode_SourceOut }, // NandROP { 0x00C000CA, QPainter::CompositionMode_Source }, // CopyROP { 0x00BB0226, QPainter::CompositionMode_Destination }, // NotOrROP { 0x00F00021, QPainter::CompositionMode_Source }, // CopyROP { 0x00FB0A09, QPainter::CompositionMode_Source }, // CopyROP { 0x005A0049, QPainter::CompositionMode_Source }, // CopyROP { 0x00550009, QPainter::CompositionMode_DestinationOut }, // NotROP { 0x00000042, QPainter::CompositionMode_Clear }, // ClearROP { 0x00FF0062, QPainter::CompositionMode_Source } // SetROP }; int i; for (i = 0; i < 15; ++i) { if (opTab[ i ].winRasterOp == parm) { break; } } if (i < 15) { return opTab[ i ].qtRasterOp; } else { return QPainter::CompositionMode_Source; } } //----------------------------------------------------------------------------- bool QWinMetaFile::dibToBmp(QImage &bmp, const char *dib, long size) { typedef struct _BMPFILEHEADER { WORD bmType; DWORD bmSize; WORD bmReserved1; WORD bmReserved2; DWORD bmOffBits; } BMPFILEHEADER; int sizeBmp = size + 14; QByteArray pattern; // BMP header and DIB data pattern.fill(0, sizeBmp); //resize and fill pattern.insert(14, QByteArray::fromRawData(dib, size)); // add BMP header BMPFILEHEADER *bmpHeader; bmpHeader = (BMPFILEHEADER *)((const char *)pattern); bmpHeader->bmType = 0x4D42; bmpHeader->bmSize = sizeBmp; if (!bmp.loadFromData((const uchar *)bmpHeader, pattern.size(), "BMP")) { qCDebug(KTNEFAPPS_LOG) << "QWinMetaFile::dibToBmp: invalid bitmap"; return false; } else { // if ( bmp.save("/home/software/kde-cvs/qt/examples/wmf/test.bmp", "BMP") ) // if ( bmp.load( "/home/software/kde-cvs/qt/examples/wmf/test.bmp", "BMP" ) ) // fprintf(stderr, "Bitmap ok \n"); return true; } } diff --git a/src/configuredialog/configagentdelegate.cpp b/src/configuredialog/configagentdelegate.cpp index 2b649084d..dd662e6e7 100644 --- a/src/configuredialog/configagentdelegate.cpp +++ b/src/configuredialog/configagentdelegate.cpp @@ -1,247 +1,247 @@ /* Copyright (c) 2010 Casey Link Copyright (c) 2009-2010 Klaralvdalens Datakonsult AB, a KDAB Group company Copyright (c) 2006-2008 Tobias Koenig 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 "configagentdelegate.h" #include #include #include #include #include #include #include #include #include #include using Akonadi::AgentInstanceModel; using Akonadi::AgentInstance; static const int s_delegatePaddingSize = 7; struct Icons { Icons() : readyPixmap(QIcon::fromTheme(QStringLiteral("user-online")).pixmap(QSize(16, 16))) , syncPixmap(QIcon::fromTheme(QStringLiteral("network-connect")).pixmap(QSize(16, 16))) , errorPixmap(QIcon::fromTheme(QStringLiteral("dialog-error")).pixmap(QSize(16, 16))) , offlinePixmap(QIcon::fromTheme(QStringLiteral("network-disconnect")).pixmap(QSize(16, 16))) , checkMailIcon(QIcon::fromTheme(QStringLiteral("mail-receive"))) { } QPixmap readyPixmap, syncPixmap, errorPixmap, offlinePixmap; QIcon checkMailIcon; }; Q_GLOBAL_STATIC(Icons, s_icons) ConfigAgentDelegate::ConfigAgentDelegate(QObject *parent) : QStyledItemDelegate(parent) { } QTextDocument *ConfigAgentDelegate::document(const QStyleOptionViewItem &option, const QModelIndex &index) const { if (!index.isValid()) { return nullptr; } const QString name = index.model()->data(index, Qt::DisplayRole).toString(); int status = index.model()->data(index, AgentInstanceModel::StatusRole).toInt(); uint progress = index.model()->data(index, AgentInstanceModel::ProgressRole).toUInt(); const QString statusMessage = index.model()->data(index, AgentInstanceModel::StatusMessageRole).toString(); QTextDocument *document = new QTextDocument(nullptr); const QSize decorationSize(KIconLoader::global()->currentSize(KIconLoader::Desktop), KIconLoader::global()->currentSize(KIconLoader::Desktop)); const QVariant data = index.model()->data(index, Qt::DecorationRole); if (data.isValid() && data.type() == QVariant::Icon) { document->addResource(QTextDocument::ImageResource, QUrl(QStringLiteral("agent_icon")), qvariant_cast(data).pixmap(decorationSize)); } if (!index.data(AgentInstanceModel::OnlineRole).toBool()) { document->addResource(QTextDocument::ImageResource, QUrl(QStringLiteral("status_icon")), s_icons->offlinePixmap); } else if (status == AgentInstance::Idle) { document->addResource(QTextDocument::ImageResource, QUrl(QStringLiteral("status_icon")), s_icons->readyPixmap); } else if (status == AgentInstance::Running) { document->addResource(QTextDocument::ImageResource, QUrl(QStringLiteral("status_icon")), s_icons->syncPixmap); } else { document->addResource(QTextDocument::ImageResource, QUrl(QStringLiteral("status_icon")), s_icons->errorPixmap); } QPalette::ColorGroup cg = (option.state & QStyle::State_Enabled) ? QPalette::Normal : QPalette::Disabled; if (cg == QPalette::Normal && !(option.state & QStyle::State_Active)) { cg = QPalette::Inactive; } QColor textColor; if (option.state & QStyle::State_Selected) { textColor = option.palette.color(cg, QPalette::HighlightedText); } else { textColor = option.palette.color(cg, QPalette::Text); } const QString content = QStringLiteral( "" "" "" "" "" "" "").arg(textColor.name().toUpper()).arg(name) + QStringLiteral( "" "" "").arg(statusMessage).arg(status == 1 ? QStringLiteral("(%1%)").arg(progress) : QLatin1String("")) + QLatin1String("
  %2
%1 %2
"); document->setHtml(content); return document; } void ConfigAgentDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { if (!index.isValid()) { return; } QTextDocument *doc = document(option, index); if (!doc) { return; } QStyleOptionButton buttonOpt = buttonOption(option); doc->setTextWidth(option.rect.width() - buttonOpt.rect.width()); painter->setRenderHint(QPainter::Antialiasing); QPen pen = painter->pen(); QPalette::ColorGroup cg = (option.state & QStyle::State_Enabled) ? QPalette::Normal : QPalette::Disabled; if (cg == QPalette::Normal && !(option.state & QStyle::State_Active)) { cg = QPalette::Inactive; } QStyleOptionViewItem opt(option); opt.showDecorationSelected = true; QApplication::style()->drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, painter); painter->save(); painter->translate(option.rect.topLeft()); doc->drawContents(painter); QApplication::style()->drawControl(QStyle::CE_PushButton, &buttonOpt, painter); painter->restore(); painter->setPen(pen); drawFocus(painter, option, option.rect); delete doc; } QSize ConfigAgentDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &) const { const int iconHeight = KIconLoader::global()->currentSize(KIconLoader::Desktop) + (s_delegatePaddingSize * 2); //icon height + padding either side const int textHeight = option.fontMetrics.height() + qMax(option.fontMetrics.height(), 16) + (s_delegatePaddingSize * 2); //height of text + icon/text + padding either side return QSize(1, qMax(iconHeight, textHeight)); //any width,the view will give us the whole thing in list mode } QWidget *ConfigAgentDelegate::createEditor(QWidget *, const QStyleOptionViewItem &, const QModelIndex &) const { return nullptr; } bool ConfigAgentDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index) { Q_UNUSED(model); if (!index.isValid()) { return false; } if (!((event->type() == QEvent::MouseButtonRelease) || (event->type() == QEvent::MouseButtonPress) || (event->type() == QEvent::MouseMove))) { return false; } QMouseEvent *me = static_cast(event); const QPoint mousePos = me->pos() - option.rect.topLeft(); QStyleOptionButton buttonOpt = buttonOption(option); if (buttonOpt.rect.contains(mousePos)) { switch (event->type()) { case QEvent::MouseButtonPress: return false; case QEvent::MouseButtonRelease: { QPoint pos = buttonOpt.rect.bottomLeft() + option.rect.topLeft(); const QString ident = index.data(Akonadi::AgentInstanceModel::InstanceIdentifierRole).toString(); Q_EMIT optionsClicked(ident, pos); return true; } default: return false; } } return false; } void ConfigAgentDelegate::drawFocus(QPainter *painter, const QStyleOptionViewItem &option, const QRect &rect) const { if (option.state & QStyle::State_HasFocus) { QStyleOptionFocusRect o; o.QStyleOption::operator=(option); o.rect = rect; o.state |= QStyle::State_KeyboardFocusChange; QPalette::ColorGroup cg = (option.state & QStyle::State_Enabled) ? QPalette::Normal : QPalette::Disabled; o.backgroundColor = option.palette.color(cg, (option.state & QStyle::State_Selected) ? QPalette::Highlight : QPalette::Background); QApplication::style()->drawPrimitive(QStyle::PE_FrameFocusRect, &o, painter); } } QStyleOptionButton ConfigAgentDelegate::buttonOption(const QStyleOptionViewItem &option) const { const QString label = i18n("Retrieval Options"); QStyleOptionButton buttonOpt; QRect buttonRect = option.rect; int height = option.rect.height() / 2; - int width = 22 + option.fontMetrics.width(label) + 40; // icon size + label size + arrow and padding + int width = 22 + option.fontMetrics.boundingRect(label).width() + 40; // icon size + label size + arrow and padding buttonRect.setTop(0); buttonRect.setHeight(height); buttonRect.setLeft(option.rect.right() - width); buttonRect.setWidth(width); buttonOpt.rect = buttonRect; buttonOpt.state = option.state; buttonOpt.text = label; buttonOpt.palette = option.palette; buttonOpt.features = QStyleOptionButton::HasMenu; buttonOpt.icon = s_icons->checkMailIcon; buttonOpt.iconSize = QSize(22, 22); // FIXME don't hardcode this icon size return buttonOpt; } diff --git a/src/kmkernel.cpp b/src/kmkernel.cpp index aafb9ee5d..67f2145f2 100644 --- a/src/kmkernel.cpp +++ b/src/kmkernel.cpp @@ -1,1917 +1,1917 @@ /* */ #include "kmkernel.h" #include "settings/kmailsettings.h" #include "libkdepim/broadcaststatus.h" #include "job/opencomposerjob.h" #include "job/newmessagejob.h" #include "job/opencomposerhiddenjob.h" #include "job/fillcomposerjob.h" #include #include using KPIM::BroadcastStatus; #include "kmstartup.h" #include "kmmainwin.h" #include "editor/composer.h" #include "kmreadermainwin.h" #include "undostack.h" #include "kmmainwidget.h" #include "search/checkindexingmanager.h" #include "libkdepim/recentaddresses.h" using KPIM::RecentAddresses; #include "configuredialog/configuredialog.h" #include "kmcommands.h" #include "unityservicemanager.h" #include #include "mailcommon/mailutil.h" #include "pop3settings.h" #include "MailCommon/FolderTreeView" #include "MailCommon/KMFilterDialog" #include "mailcommonsettings_base.h" #include "mailfilteragentinterface.h" #include "PimCommon/PimUtil" #include "folderarchive/folderarchivemanager.h" #include "sieveimapinterface/kmailsieveimapinstanceinterface.h" // kdepim includes #include "kmail-version.h" // kdepimlibs includes #include #include #include #include #include #include #include #include "mailserviceimpl.h" using KMail::MailServiceImpl; #include "mailcommon/jobscheduler.h" #include #include "messagelistsettings.h" #include "gravatarsettings.h" #include "messagelist/messagelistutil.h" #include "messageviewer/messageviewersettings.h" #include "MessageComposer/AkonadiSender" #include "messagecomposer/messagecomposersettings.h" #include "MessageComposer/MessageHelper" #include "MessageComposer/MessageComposerSettings" #include "PimCommon/PimCommonSettings" #include "PimCommon/AutoCorrection" #include #include "globalsettings_templateparser.h" #include "TemplateParser/TemplatesUtil" #include "mailcommon/foldersettings.h" #include "editor/codec/codecmanager.h" #include #include #include #include #include #include #include "kmail_debug.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 "kmailinterface.h" #include "mailcommon/foldercollectionmonitor.h" #include "imapresourcesettings.h" #include "util.h" #include "MailCommon/MailKernel" #include "searchdialog/searchdescriptionattribute.h" #include "kmail_options.h" using namespace MailCommon; static KMKernel *mySelf = nullptr; static bool s_askingToGoOnline = false; /********************************************************************/ /* Constructor and destructor */ /********************************************************************/ KMKernel::KMKernel(QObject *parent) : QObject(parent) { //Initialize kmail sieveimap interface KSieveUi::SieveImapInstanceInterfaceManager::self()->setSieveImapInstanceInterface(new KMailSieveImapInstanceInterface); mDebug = !qEnvironmentVariableIsEmpty("KDEPIM_DEBUGGING"); mSystemNetworkStatus = PimCommon::NetworkManager::self()->networkConfigureManager()->isOnline(); Akonadi::AttributeFactory::registerAttribute(); QDBusConnection::sessionBus().registerService(QStringLiteral("org.kde.kmail")); qCDebug(KMAIL_LOG) << "Starting up..."; mySelf = this; the_firstInstance = true; the_undoStack = nullptr; the_msgSender = nullptr; mFilterEditDialog = nullptr; // make sure that we check for config updates before doing anything else KMKernel::config(); // this shares the kmailrc parsing too (via KSharedConfig), and reads values from it // so better do it here, than in some code where changing the group of config() // would be unexpected KMailSettings::self(); mJobScheduler = new JobScheduler(this); mAutoCorrection = new PimCommon::AutoCorrection(); KMime::setUseOutlookAttachmentEncoding(MessageComposer::MessageComposerSettings::self()->outlookCompatibleAttachments()); // cberzan: this crap moved to CodecManager ====================== mNetCodec = QTextCodec::codecForLocale(); // In the case of Japan. Japanese locale name is "eucjp" but // The Japanese mail systems normally used "iso-2022-jp" of locale name. // We want to change locale name from eucjp to iso-2022-jp at KMail only. // (Introduction to i18n, 6.6 Limit of Locale technology): // EUC-JP is the de-facto standard for UNIX systems, ISO 2022-JP // is the standard for Internet, and Shift-JIS is the encoding // for Windows and Macintosh. const QByteArray netCodecLower = mNetCodec->name().toLower(); if (netCodecLower == "eucjp" #if defined Q_OS_WIN || defined Q_OS_MACX || netCodecLower == "shift-jis" // OK? #endif ) { mNetCodec = QTextCodec::codecForName("jis7"); } // until here ================================================ Akonadi::Session *session = new Akonadi::Session("KMail Kernel ETM", this); mFolderCollectionMonitor = new FolderCollectionMonitor(session, this); connect(mFolderCollectionMonitor->monitor(), &Akonadi::Monitor::collectionRemoved, this, &KMKernel::slotCollectionRemoved); mEntityTreeModel = new Akonadi::EntityTreeModel(folderCollectionMonitor(), this); mEntityTreeModel->setListFilter(Akonadi::CollectionFetchScope::Enabled); mEntityTreeModel->setItemPopulationStrategy(Akonadi::EntityTreeModel::LazyPopulation); mCollectionModel = new Akonadi::EntityMimeTypeFilterModel(this); mCollectionModel->setSourceModel(mEntityTreeModel); mCollectionModel->addMimeTypeInclusionFilter(Akonadi::Collection::mimeType()); mCollectionModel->setHeaderGroup(Akonadi::EntityTreeModel::CollectionTreeHeaders); mCollectionModel->setDynamicSortFilter(true); mCollectionModel->setSortCaseSensitivity(Qt::CaseInsensitive); connect(folderCollectionMonitor(), QOverload &>::of(&Akonadi::ChangeRecorder::collectionChanged), this, &KMKernel::slotCollectionChanged); connect(MailTransport::TransportManager::self(), &MailTransport::TransportManager::transportRemoved, this, &KMKernel::transportRemoved); connect(MailTransport::TransportManager::self(), &MailTransport::TransportManager::transportRenamed, this, &KMKernel::transportRenamed); QDBusConnection::sessionBus().connect(QString(), QStringLiteral("/MailDispatcherAgent"), QStringLiteral("org.freedesktop.Akonadi.MailDispatcherAgent"), QStringLiteral( "itemDispatchStarted"), this, SLOT(itemDispatchStarted())); connect(Akonadi::AgentManager::self(), &Akonadi::AgentManager::instanceStatusChanged, this, &KMKernel::instanceStatusChanged); connect(Akonadi::AgentManager::self(), &Akonadi::AgentManager::instanceError, this, &KMKernel::slotInstanceError); connect(Akonadi::AgentManager::self(), &Akonadi::AgentManager::instanceWarning, this, &KMKernel::slotInstanceWarning); connect(Akonadi::AgentManager::self(), &Akonadi::AgentManager::instanceRemoved, this, &KMKernel::slotInstanceRemoved); connect(PimCommon::NetworkManager::self()->networkConfigureManager(), &QNetworkConfigurationManager::onlineStateChanged, this, &KMKernel::slotSystemNetworkStatusChanged); connect(KPIM::ProgressManager::instance(), &KPIM::ProgressManager::progressItemCompleted, this, &KMKernel::slotProgressItemCompletedOrCanceled); connect(KPIM::ProgressManager::instance(), &KPIM::ProgressManager::progressItemCanceled, this, &KMKernel::slotProgressItemCompletedOrCanceled); connect(identityManager(), &KIdentityManagement::IdentityManager::deleted, this, &KMKernel::slotDeleteIdentity); CommonKernel->registerKernelIf(this); CommonKernel->registerSettingsIf(this); CommonKernel->registerFilterIf(this); mFolderArchiveManager = new FolderArchiveManager(this); mIndexedItems = new Akonadi::Search::PIM::IndexedItems(this); mCheckIndexingManager = new CheckIndexingManager(mIndexedItems, this); mUnityServiceManager = new KMail::UnityServiceManager(this); } KMKernel::~KMKernel() { delete mMailService; mMailService = nullptr; stopAgentInstance(); slotSyncConfig(); delete mAutoCorrection; delete mMailCommonSettings; mySelf = nullptr; } Akonadi::ChangeRecorder *KMKernel::folderCollectionMonitor() const { return mFolderCollectionMonitor->monitor(); } Akonadi::EntityTreeModel *KMKernel::entityTreeModel() const { return mEntityTreeModel; } Akonadi::EntityMimeTypeFilterModel *KMKernel::collectionModel() const { return mCollectionModel; } void KMKernel::setupDBus() { (void)new KmailAdaptor(this); QDBusConnection::sessionBus().registerObject(QStringLiteral("/KMail"), this); mMailService = new MailServiceImpl(); } static QUrl makeAbsoluteUrl(const QString &str, const QString &cwd) { return QUrl::fromUserInput(str, cwd, QUrl::AssumeLocalFile); } bool KMKernel::handleCommandLine(bool noArgsOpensReader, const QStringList &args, const QString &workingDir) { QString to, cc, bcc, subj, body, inReplyTo, replyTo; QStringList customHeaders; QUrl messageFile; QList attachURLs; QString identity; bool mailto = false; bool checkMail = false; bool viewOnly = false; bool calledWithSession = false; // for ignoring '-session foo' // process args: QCommandLineParser parser; kmail_options(&parser); QStringList newargs; bool addAttachmentAttribute = false; for (const QString &argument : qAsConst(args)) { if (argument == QLatin1String("--attach")) { addAttachmentAttribute = true; } else { if (argument.startsWith(QLatin1String("--"))) { addAttachmentAttribute = false; } if (argument.contains(QLatin1Char('@')) || argument.startsWith(QLatin1String("mailto:"))) { //address mustn't be trade as a attachment addAttachmentAttribute = false; } if (addAttachmentAttribute) { newargs.append(QStringLiteral("--attach")); newargs.append(argument); } else { newargs.append(argument); } } } parser.process(newargs); if (parser.isSet(QStringLiteral("subject"))) { subj = parser.value(QStringLiteral("subject")); // if kmail is called with 'kmail -session abc' then this doesn't mean // that the user wants to send a message with subject "ession" but // (most likely) that the user clicked on KMail's system tray applet // which results in KMKernel::raise() calling "kmail kmail newInstance" // via D-Bus which apparently executes the application with the original // command line arguments and those include "-session ..." if // kmail/kontact was restored by session management if (subj == QLatin1String("ession")) { subj.clear(); calledWithSession = true; } else { mailto = true; } } if (parser.isSet(QStringLiteral("cc"))) { mailto = true; cc = parser.value(QStringLiteral("cc")); } if (parser.isSet(QStringLiteral("bcc"))) { mailto = true; bcc = parser.value(QStringLiteral("bcc")); } if (parser.isSet(QStringLiteral("replyTo"))) { mailto = true; replyTo = parser.value(QStringLiteral("replyTo")); } if (parser.isSet(QStringLiteral("msg"))) { mailto = true; const QString file = parser.value(QStringLiteral("msg")); messageFile = makeAbsoluteUrl(file, workingDir); } if (parser.isSet(QStringLiteral("body"))) { mailto = true; body = parser.value(QStringLiteral("body")); } const QStringList attachList = parser.values(QStringLiteral("attach")); if (!attachList.isEmpty()) { mailto = true; QStringList::ConstIterator end = attachList.constEnd(); for (QStringList::ConstIterator it = attachList.constBegin(); it != end; ++it) { if (!(*it).isEmpty()) { if ((*it) != QLatin1String("--")) { attachURLs.append(makeAbsoluteUrl(*it, workingDir)); } } } } customHeaders = parser.values(QStringLiteral("header")); if (parser.isSet(QStringLiteral("composer"))) { mailto = true; } if (parser.isSet(QStringLiteral("check"))) { checkMail = true; } if (parser.isSet(QStringLiteral("identity"))) { identity = parser.value(QStringLiteral("identity")); } if (parser.isSet(QStringLiteral("view"))) { viewOnly = true; const QString filename = parser.value(QStringLiteral("view")); messageFile = QUrl::fromUserInput(filename, workingDir); } if (!calledWithSession) { // only read additional command line arguments if kmail/kontact is // not called with "-session foo" for (const QString &arg : parser.positionalArguments()) { if (arg.startsWith(QLatin1String("mailto:"), Qt::CaseInsensitive)) { const QUrl urlDecoded(QUrl::fromPercentEncoding(arg.toUtf8())); QMap values = MessageCore::StringUtil::parseMailtoUrl(urlDecoded); QString previousKey; for (auto it = values.cbegin(), end = values.cend(); it != end; ++it) { const QString key = it.key().toLower(); if (key == QLatin1Literal("to")) { if (!it->isEmpty()) { to += *it + QStringLiteral(", "); } previousKey.clear(); } else if (key == QLatin1Literal("cc")) { if (!it->isEmpty()) { cc += *it + QStringLiteral(", "); } previousKey.clear(); } else if (key == QLatin1Literal("bcc")) { if (!it->isEmpty()) { bcc += *it + QStringLiteral(", "); } previousKey.clear(); } else if (key == QLatin1Literal("subject")) { subj = it.value(); previousKey.clear(); } else if (key == QLatin1Literal("body")) { body = it.value(); previousKey = key; } else if (key == QLatin1Literal("in-reply-to")) { inReplyTo = it.value(); previousKey.clear(); } else if (key == QLatin1Literal("attachment") || key == QLatin1Literal("attach")) { if (!it->isEmpty()) { attachURLs << makeAbsoluteUrl(*it, workingDir); } previousKey.clear(); } else { qCWarning(KMAIL_LOG) << "unknown key" << key; //Workaround: https://bugs.kde.org/show_bug.cgi?id=390939 //QMap parseMailtoUrl(const QUrl &url) parses correctly url //But if we have a "&" unknown key we lost it. if (previousKey == QLatin1Literal("body")) { body += QLatin1Char('&') + key + QLatin1Char('=') + it.value(); } previousKey.clear(); } } } else { QUrl url(arg); if (url.isValid() && !url.scheme().isEmpty()) { attachURLs += url; } else { to += arg + QStringLiteral(", "); } } mailto = true; } if (!to.isEmpty()) { // cut off the superfluous trailing ", " to.truncate(to.length() - 2); } } if (!noArgsOpensReader && !mailto && !checkMail && !viewOnly) { return false; } if (viewOnly) { viewMessage(messageFile); } else { action(mailto, checkMail, to, cc, bcc, subj, body, messageFile, attachURLs, customHeaders, replyTo, inReplyTo, identity); } return true; } /********************************************************************/ /* D-Bus-callable, and command line actions */ /********************************************************************/ void KMKernel::checkMail() //might create a new reader but won't show!! { if (!kmkernel->askToGoOnline()) { return; } const QString resourceGroupPattern(QStringLiteral("Resource %1")); const Akonadi::AgentInstance::List lst = MailCommon::Util::agentInstances(); for (Akonadi::AgentInstance type : lst) { const QString id = type.identifier(); KConfigGroup group(KMKernel::config(), resourceGroupPattern.arg(id)); if (group.readEntry("IncludeInManualChecks", true)) { if (!type.isOnline()) { type.setIsOnline(true); } if (mResourcesBeingChecked.isEmpty()) { qCDebug(KMAIL_LOG) << "Starting manual mail check"; Q_EMIT startCheckMail(); } if (!mResourcesBeingChecked.contains(id)) { mResourcesBeingChecked.append(id); } type.synchronize(); } } } void KMKernel::openReader() { openReader(false); } QStringList KMKernel::accounts() const { QStringList accountLst; const Akonadi::AgentInstance::List lst = MailCommon::Util::agentInstances(); accountLst.reserve(lst.count()); for (const Akonadi::AgentInstance &type : lst) { // Explicitly make a copy, as we're not changing values of the list but only // the local copy which is passed to action. accountLst << type.identifier(); } return accountLst; } void KMKernel::checkAccount(const QString &account) //might create a new reader but won't show!! { if (account.isEmpty()) { checkMail(); } else { Akonadi::AgentInstance agent = Akonadi::AgentManager::self()->instance(account); if (agent.isValid()) { agent.synchronize(); } else { qCDebug(KMAIL_LOG) << "- account with name '" << account << "' not found"; } } } void KMKernel::openReader(bool onlyCheck) { KMainWindow *ktmw = nullptr; foreach (KMainWindow *window, KMainWindow::memberList()) { if (::qobject_cast(window)) { ktmw = window; break; } } bool activate; if (ktmw) { KMMainWin *win = static_cast(ktmw); activate = !onlyCheck; // existing window: only activate if not --check if (activate) { win->show(); } } else { KMMainWin *win = new KMMainWin; win->show(); activate = false; // new window: no explicit activation (#73591) } } void KMKernel::openComposer(const QString &to, const QString &cc, const QString &bcc, const QString &subject, const QString &body, bool hidden, const QString &messageFile, const QStringList &attachmentPaths, const QStringList &customHeaders, const QString &replyTo, const QString &inReplyTo, const QString &identity) { const OpenComposerSettings settings(to, cc, bcc, subject, body, hidden, messageFile, attachmentPaths, customHeaders, replyTo, inReplyTo, identity); OpenComposerJob *job = new OpenComposerJob(this); job->setOpenComposerSettings(settings); job->start(); } void KMKernel::openComposer(const QString &to, const QString &cc, const QString &bcc, const QString &subject, const QString &body, bool hidden, const QString &attachName, const QByteArray &attachCte, const QByteArray &attachData, const QByteArray &attachType, const QByteArray &attachSubType, const QByteArray &attachParamAttr, const QString &attachParamValue, const QByteArray &attachContDisp, const QByteArray &attachCharset, unsigned int identity) { fillComposer(hidden, to, cc, bcc, subject, body, attachName, attachCte, attachData, attachType, attachSubType, attachParamAttr, attachParamValue, attachContDisp, attachCharset, identity, false); } void KMKernel::openComposer(const QString &to, const QString &cc, const QString &bcc, const QString &subject, const QString &body, const QString &attachName, const QByteArray &attachCte, const QByteArray &attachData, const QByteArray &attachType, const QByteArray &attachSubType, const QByteArray &attachParamAttr, const QString &attachParamValue, const QByteArray &attachContDisp, const QByteArray &attachCharset, unsigned int identity) { fillComposer(false, to, cc, bcc, subject, body, attachName, attachCte, attachData, attachType, attachSubType, attachParamAttr, attachParamValue, attachContDisp, attachCharset, identity, true); } void KMKernel::fillComposer(bool hidden, const QString &to, const QString &cc, const QString &bcc, const QString &subject, const QString &body, const QString &attachName, const QByteArray &attachCte, const QByteArray &attachData, const QByteArray &attachType, const QByteArray &attachSubType, const QByteArray &attachParamAttr, const QString &attachParamValue, const QByteArray &attachContDisp, const QByteArray &attachCharset, unsigned int identity, bool forceShowWindow) { const FillComposerJobSettings settings(hidden, to, cc, bcc, subject, body, attachName, attachCte, attachData, attachType, attachSubType, attachParamAttr, attachParamValue, attachContDisp, attachCharset, identity, forceShowWindow); FillComposerJob *job = new FillComposerJob; job->setSettings(settings); job->start(); } void KMKernel::openComposer(const QString &to, const QString &cc, const QString &bcc, const QString &subject, const QString &body, bool hidden) { const OpenComposerHiddenJobSettings settings(to, cc, bcc, subject, body, hidden); OpenComposerHiddenJob *job = new OpenComposerHiddenJob(this); job->setSettings(settings); job->start(); } void KMKernel::newMessage(const QString &to, const QString &cc, const QString &bcc, bool hidden, bool useFolderId, const QString & /*messageFile*/, const QString &_attachURL) { QSharedPointer folder; Akonadi::Collection col; uint id = 0; if (useFolderId) { //create message with required folder identity folder = currentFolderCollection(); id = folder ? folder->identity() : 0; col = currentCollection(); } const NewMessageJobSettings settings(to, cc, bcc, hidden, _attachURL, folder, id, col); NewMessageJob *job = new NewMessageJob(this); job->setNewMessageJobSettings(settings); job->start(); } void KMKernel::viewMessage(const QUrl &url) { KMOpenMsgCommand *openCommand = new KMOpenMsgCommand(nullptr, url); openCommand->start(); } int KMKernel::viewMessage(const QString &messageFile) { KMOpenMsgCommand *openCommand = new KMOpenMsgCommand(nullptr, QUrl::fromLocalFile(messageFile)); openCommand->start(); return 1; } void KMKernel::raise() { QDBusInterface iface(QStringLiteral("org.kde.kmail"), QStringLiteral("/MainApplication"), QStringLiteral("org.kde.PIMUniqueApplication"), QDBusConnection::sessionBus()); QDBusReply reply; if (!iface.isValid() || !(reply = iface.call(QStringLiteral("newInstance"))).isValid()) { QDBusError err = iface.lastError(); qCritical() << "Communication problem with KMail. " << "Error message was:" << err.name() << ": \"" << err.message() << "\""; } } bool KMKernel::showMail(qint64 serialNumber) { KMMainWidget *mainWidget = nullptr; // First look for a KMainWindow. foreach (KMainWindow *window, KMainWindow::memberList()) { // Then look for a KMMainWidget. QList l = window->findChildren(); if (!l.isEmpty() && l.first()) { mainWidget = l.first(); if (window->isActiveWindow()) { break; } } } if (mainWidget) { Akonadi::ItemFetchJob *job = new Akonadi::ItemFetchJob(Akonadi::Item(serialNumber), this); job->fetchScope().fetchFullPayload(); job->fetchScope().setAncestorRetrieval(Akonadi::ItemFetchScope::Parent); if (job->exec()) { if (job->items().count() >= 1) { KMReaderMainWin *win = new KMReaderMainWin(MessageViewer::Viewer::UseGlobalSetting, false); const auto item = job->items().at(0); win->showMessage(MessageCore::MessageCoreSettings::self()->overrideCharacterEncoding(), item, item.parentCollection()); win->show(); return true; } } } return false; } void KMKernel::pauseBackgroundJobs() { mBackgroundTasksTimer->stop(); mJobScheduler->pause(); } void KMKernel::resumeBackgroundJobs() { mJobScheduler->resume(); mBackgroundTasksTimer->start(4 * 60 * 60 * 1000); } void KMKernel::stopNetworkJobs() { if (KMailSettings::self()->networkState() == KMailSettings::EnumNetworkState::Offline) { return; } setAccountStatus(false); KMailSettings::setNetworkState(KMailSettings::EnumNetworkState::Offline); BroadcastStatus::instance()->setStatusMsg(i18n("KMail is set to be offline; all network jobs are suspended")); Q_EMIT onlineStatusChanged((KMailSettings::EnumNetworkState::type)KMailSettings::networkState()); } void KMKernel::setAccountStatus(bool goOnline) { const Akonadi::AgentInstance::List lst = MailCommon::Util::agentInstances(false); for (Akonadi::AgentInstance type : lst) { const QString identifier(type.identifier()); if (PimCommon::Util::isImapResource(identifier) || identifier.contains(POP3_RESOURCE_IDENTIFIER) || identifier.contains(QLatin1String("akonadi_maildispatcher_agent")) || type.type().capabilities().contains(QLatin1String("NeedsNetwork"))) { type.setIsOnline(goOnline); } } if (goOnline && MessageComposer::MessageComposerSettings::self()->sendImmediate()) { const auto col = CommonKernel->collectionFromId(CommonKernel->outboxCollectionFolder().id()); const qint64 nbMsgOutboxCollection = col.statistics().count(); if (nbMsgOutboxCollection > 0) { if (!kmkernel->msgSender()->sendQueued()) { KMessageBox::error(KMKernel::self()->mainWin(), i18n("Impossible to send email"), i18n("Send Email")); } } } } const QString KMKernel::xmlGuiInstanceName() const { return mXmlGuiInstance; } void KMKernel::setXmlGuiInstanceName(const QString &instance) { mXmlGuiInstance = instance; } KMail::UndoStack *KMKernel::undoStack() const { return the_undoStack; } void KMKernel::resumeNetworkJobs() { if (KMailSettings::self()->networkState() == KMailSettings::EnumNetworkState::Online) { return; } if (mSystemNetworkStatus) { setAccountStatus(true); BroadcastStatus::instance()->setStatusMsg(i18n("KMail is set to be online; all network jobs resumed")); } else { BroadcastStatus::instance()->setStatusMsg(i18n("KMail is set to be online; all network jobs will resume when a network connection is detected")); } KMailSettings::setNetworkState(KMailSettings::EnumNetworkState::Online); Q_EMIT onlineStatusChanged((KMailSettings::EnumNetworkState::type)KMailSettings::networkState()); KMMainWidget *widget = getKMMainWidget(); if (widget) { widget->refreshMessageListSelection(); } } bool KMKernel::isOffline() { if ((KMailSettings::self()->networkState() == KMailSettings::EnumNetworkState::Offline) || !PimCommon::NetworkManager::self()->networkConfigureManager()->isOnline()) { return true; } else { return false; } } void KMKernel::verifyAccount() { const QString resourceGroupPattern(QStringLiteral("Resource %1")); const Akonadi::AgentInstance::List lst = MailCommon::Util::agentInstances(); for (Akonadi::AgentInstance type : lst) { KConfigGroup group(KMKernel::config(), resourceGroupPattern.arg(type.identifier())); if (group.readEntry("CheckOnStartup", false)) { if (!type.isOnline()) { type.setIsOnline(true); } type.synchronize(); } // "false" is also hardcoded in ConfigureDialog, don't forget to change there. if (group.readEntry("OfflineOnShutdown", false)) { if (!type.isOnline()) { type.setIsOnline(true); } } } } void KMKernel::slotCheckAccount(Akonadi::ServerManager::State state) { if (state == Akonadi::ServerManager::Running) { disconnect(Akonadi::ServerManager::self(), SIGNAL(stateChanged(Akonadi::ServerManager::State))); verifyAccount(); } } void KMKernel::checkMailOnStartup() { if (!kmkernel->askToGoOnline()) { return; } if (Akonadi::ServerManager::state() != Akonadi::ServerManager::Running) { connect(Akonadi::ServerManager::self(), &Akonadi::ServerManager::stateChanged, this, &KMKernel::slotCheckAccount); } else { verifyAccount(); } } bool KMKernel::askToGoOnline() { // already asking means we are offline and need to wait anyhow if (s_askingToGoOnline) { return false; } if (KMailSettings::self()->networkState() == KMailSettings::EnumNetworkState::Offline) { s_askingToGoOnline = true; int rc = KMessageBox::questionYesNo(KMKernel::self()->mainWin(), i18n("KMail is currently in offline mode. " "How do you want to proceed?"), i18n("Online/Offline"), KGuiItem(i18n("Work Online")), KGuiItem(i18n("Work Offline"))); s_askingToGoOnline = false; if (rc == KMessageBox::No) { return false; } else { kmkernel->resumeNetworkJobs(); } } if (kmkernel->isOffline()) { return false; } return true; } void KMKernel::slotSystemNetworkStatusChanged(bool isOnline) { mSystemNetworkStatus = isOnline; if (KMailSettings::self()->networkState() == KMailSettings::EnumNetworkState::Offline) { return; } if (isOnline) { BroadcastStatus::instance()->setStatusMsg(i18n( "Network connection detected, all network jobs resumed")); kmkernel->setAccountStatus(true); } else { BroadcastStatus::instance()->setStatusMsg(i18n( "No network connection detected, all network jobs are suspended")); kmkernel->setAccountStatus(false); } } /********************************************************************/ /* Kernel methods */ /********************************************************************/ void KMKernel::quit() { // Called when all windows are closed. Will take care of compacting, // sending... should handle session management too!! } /* TODO later: Asuming that: - msgsender is nonblocking (our own, QSocketNotifier based. Pops up errors and sends signal senderFinished when done) o If we are getting mail, stop it (but don't lose something!) [Done already, see mailCheckAborted] o If we are sending mail, go on UNLESS this was called by SM, in which case stop ASAP that too (can we warn? should we continue on next start?) o If we are compacting, or expunging, go on UNLESS this was SM call. In that case stop compacting ASAP and continue on next start, before touching any folders. [Not needed anymore with CompactionJob] KMKernel::quit () { SM call? if compacting, stop; if sending, stop; if receiving, stop; Windows will take care of themselves (composer should dump its messages, if any but not in deadMail) declare us ready for the End of the Session No, normal quit call All windows are off. Anything to do, should compact or sender sends? Yes, maybe put an icon in panel as a sign of life if sender sending, connect us to his finished slot, declare us ready for quit and wait for senderFinished if not, Folder manager, go compact sent-mail and outbox } (= call slotFinished()) void KMKernel::slotSenderFinished() { good, Folder manager go compact sent-mail and outbox clean up stage1 (release folders and config, unregister from dcop) -- another kmail may start now --- qApp->quit(); } */ /********************************************************************/ /* Init, Exit, and handler methods */ /********************************************************************/ //----------------------------------------------------------------------------- // Open a composer for each message found in the dead.letter folder void KMKernel::recoverDeadLetters() { const QString pathName = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/kmail2/"); QDir dir(pathName); if (!dir.exists(QStringLiteral("autosave"))) { return; } dir.cd(pathName + QLatin1String("autosave")); const QFileInfoList autoSaveFiles = dir.entryInfoList(); for (const QFileInfo &file : autoSaveFiles) { // Disregard the '.' and '..' folders const QString filename = file.fileName(); if (filename == QLatin1String(".") || filename == QLatin1String("..") || file.isDir()) { continue; } qCDebug(KMAIL_LOG) << "Opening autosave file:" << file.absoluteFilePath(); QFile autoSaveFile(file.absoluteFilePath()); if (autoSaveFile.open(QIODevice::ReadOnly)) { const KMime::Message::Ptr autoSaveMessage(new KMime::Message()); const QByteArray msgData = autoSaveFile.readAll(); autoSaveMessage->setContent(msgData); autoSaveMessage->parse(); // Show the a new composer dialog for the message KMail::Composer *autoSaveWin = KMail::makeComposer(); autoSaveWin->setMessage(autoSaveMessage, false, false, false); autoSaveWin->setAutoSaveFileName(filename); autoSaveWin->show(); autoSaveFile.close(); } else { KMessageBox::sorry(nullptr, i18n("Failed to open autosave file at %1.\nReason: %2", file.absoluteFilePath(), autoSaveFile.errorString()), i18n("Opening Autosave File Failed")); } } } void KMKernel::akonadiStateChanged(Akonadi::ServerManager::State state) { qCDebug(KMAIL_LOG) << "KMKernel has akonadi state changed to:" << int(state); if (state == Akonadi::ServerManager::Running) { CommonKernel->initFolders(); } } static void kmCrashHandler(int sigId) { fprintf(stderr, "*** KMail got signal %d (Exiting)\n", sigId); // try to cleanup all windows if (kmkernel) { kmkernel->dumpDeadLetters(); fprintf(stderr, "*** Dead letters dumped.\n"); kmkernel->stopAgentInstance(); kmkernel->cleanupTemporaryFiles(); } } void KMKernel::init() { the_shuttingDown = false; the_firstStart = KMailSettings::self()->firstStart(); KMailSettings::self()->setFirstStart(false); the_undoStack = new KMail::UndoStack(20); the_msgSender = new MessageComposer::AkonadiSender; // filterMgr->dump(); mBackgroundTasksTimer = new QTimer(this); mBackgroundTasksTimer->setSingleShot(true); connect(mBackgroundTasksTimer, &QTimer::timeout, this, &KMKernel::slotRunBackgroundTasks); #ifdef DEBUG_SCHEDULER // for debugging, see jobscheduler.h mBackgroundTasksTimer->start(10000); // 10s, singleshot #else mBackgroundTasksTimer->start(5 * 60000); // 5 minutes, singleshot #endif KCrash::setEmergencySaveFunction(kmCrashHandler); qCDebug(KMAIL_LOG) << "KMail init with akonadi server state:" << int(Akonadi::ServerManager::state()); if (Akonadi::ServerManager::state() == Akonadi::ServerManager::Running) { CommonKernel->initFolders(); } connect(Akonadi::ServerManager::self(), &Akonadi::ServerManager::stateChanged, this, &KMKernel::akonadiStateChanged); } bool KMKernel::doSessionManagement() { // Do session management if (qApp->isSessionRestored()) { int n = 1; while (KMMainWin::canBeRestored(n)) { //only restore main windows! (Matthias); if (KMMainWin::classNameOfToplevel(n) == QLatin1String("KMMainWin")) { (new KMMainWin)->restoreDockedState(n); } ++n; } return true; // we were restored by SM } return false; // no, we were not restored } bool KMKernel::firstInstance() const { return the_firstInstance; } void KMKernel::setFirstInstance(bool value) { the_firstInstance = value; } void KMKernel::closeAllKMailWindows() { foreach (KMainWindow *window, KMainWindow::memberList()) { if (::qobject_cast(window) || ::qobject_cast(window)) { // close and delete the window window->setAttribute(Qt::WA_DeleteOnClose); window->close(); } } } void KMKernel::cleanup() { disconnect(Akonadi::AgentManager::self(), SIGNAL(instanceStatusChanged(Akonadi::AgentInstance))); disconnect(Akonadi::AgentManager::self(), SIGNAL(instanceError(Akonadi::AgentInstance,QString))); disconnect(Akonadi::AgentManager::self(), SIGNAL(instanceWarning(Akonadi::AgentInstance,QString))); disconnect(Akonadi::AgentManager::self(), SIGNAL(instanceRemoved(Akonadi::AgentInstance))); disconnect(KPIM::ProgressManager::instance(), SIGNAL(progressItemCompleted(KPIM::ProgressItem*))); disconnect(KPIM::ProgressManager::instance(), SIGNAL(progressItemCanceled(KPIM::ProgressItem*))); dumpDeadLetters(); the_shuttingDown = true; closeAllKMailWindows(); // Flush the cache of foldercollection objects. This results // in configuration writes, so we need to do it early enough. MailCommon::FolderSettings::clearCache(); // Write the config while all other managers are alive delete the_msgSender; the_msgSender = nullptr; delete the_undoStack; the_undoStack = nullptr; delete mConfigureDialog; mConfigureDialog = nullptr; KSharedConfig::Ptr config = KMKernel::config(); if (RecentAddresses::exists()) { RecentAddresses::self(config.data())->save(config.data()); } Akonadi::Collection trashCollection = CommonKernel->trashCollectionFolder(); if (trashCollection.isValid()) { if (KMailSettings::self()->emptyTrashOnExit()) { OrgFreedesktopAkonadiMailFilterAgentInterface mailFilterInterface(QStringLiteral("org.freedesktop.Akonadi.MailFilterAgent"), QStringLiteral( "/MailFilterAgent"), QDBusConnection::sessionBus(), this); if (mailFilterInterface.isValid()) { mailFilterInterface.expunge(static_cast(trashCollection.id())); } else { qCWarning(KMAIL_LOG) << "Mailfilter is not active"; } } } } void KMKernel::dumpDeadLetters() { if (shuttingDown()) { return; //All documents should be saved before shutting down is set! } // make all composer windows autosave their contents foreach (KMainWindow *window, KMainWindow::memberList()) { if (KMail::Composer *win = ::qobject_cast(window)) { win->autoSaveMessage(true); while (win->isComposing()) { qCWarning(KMAIL_LOG) << "Danger, using an event loop, this should no longer be happening!"; qApp->processEvents(); } } } } void KMKernel::action(bool mailto, bool check, const QString &to, const QString &cc, const QString &bcc, const QString &subj, const QString &body, const QUrl &messageFile, const QList &attachURLs, const QStringList &customHeaders, const QString &replyTo, const QString &inReplyTo, const QString &identity) { if (mailto) { openComposer(to, cc, bcc, subj, body, 0, messageFile.toLocalFile(), QUrl::toStringList(attachURLs), customHeaders, replyTo, inReplyTo, identity); } else { openReader(check); } if (check) { checkMail(); } //Anything else? } void KMKernel::slotRequestConfigSync() { // ### FIXME: delay as promised in the kdoc of this function ;-) slotSyncConfig(); } void KMKernel::slotSyncConfig() { PimCommon::PimCommonSettings::self()->save(); MessageCore::MessageCoreSettings::self()->save(); MessageViewer::MessageViewerSettings::self()->save(); MessageComposer::MessageComposerSettings::self()->save(); TemplateParser::TemplateParserSettings::self()->save(); MessageList::MessageListSettings::self()->save(); mMailCommonSettings->save(); Gravatar::GravatarSettings::self()->save(); KMailSettings::self()->save(); KMKernel::config()->sync(); //Laurent investigate why we need to reload them. PimCommon::PimCommonSettings::self()->load(); MessageCore::MessageCoreSettings::self()->load(); MessageViewer::MessageViewerSettings::self()->load(); MessageComposer::MessageComposerSettings::self()->load(); TemplateParser::TemplateParserSettings::self()->load(); MessageList::MessageListSettings::self()->load(); mMailCommonSettings->load(); Gravatar::GravatarSettings::self()->load(); KMailSettings::self()->load(); KMKernel::config()->reparseConfiguration(); } void KMKernel::updateConfig() { slotConfigChanged(); } void KMKernel::slotShowConfigurationDialog() { if (KMKernel::getKMMainWidget() == nullptr) { // ensure that there is a main widget available // as parts of the configure dialog (identity) rely on this // and this slot can be called when there is only a KMComposeWin showing KMMainWin *win = new KMMainWin; win->show(); } if (!mConfigureDialog) { mConfigureDialog = new ConfigureDialog(nullptr, false); mConfigureDialog->setObjectName(QStringLiteral("configure")); connect(mConfigureDialog, &ConfigureDialog::configChanged, this, &KMKernel::slotConfigChanged); } // Save all current settings. if (getKMMainWidget()) { getKMMainWidget()->writeReaderConfig(); } if (mConfigureDialog->isHidden()) { mConfigureDialog->show(); } else { mConfigureDialog->raise(); } } void KMKernel::slotConfigChanged() { CodecManager::self()->updatePreferredCharsets(); Q_EMIT configChanged(); } //------------------------------------------------------------------------------- bool KMKernel::haveSystemTrayApplet() const { return mUnityServiceManager->haveSystemTrayApplet(); } QTextCodec *KMKernel::networkCodec() const { return mNetCodec; } void KMKernel::updateSystemTray() { if (!the_shuttingDown) { mUnityServiceManager->initListOfCollection(); } } KIdentityManagement::IdentityManager *KMKernel::identityManager() { return KIdentityManagement::IdentityManager::self(); } JobScheduler *KMKernel::jobScheduler() const { return mJobScheduler; } KMainWindow *KMKernel::mainWin() { // First look for a KMMainWin. foreach (KMainWindow *window, KMainWindow::memberList()) { if (::qobject_cast(window)) { return window; } } // There is no KMMainWin. Use any other KMainWindow instead (e.g. in // case we are running inside Kontact) because we anyway only need // it for modal message boxes and for KNotify events. if (!KMainWindow::memberList().isEmpty()) { KMainWindow *kmWin = KMainWindow::memberList().constFirst(); if (kmWin) { return kmWin; } } // There's not a single KMainWindow. Create a KMMainWin. // This could happen if we want to pop up an error message // while we are still doing the startup wizard and no other // KMainWindow is running. return new KMMainWin; } KMKernel *KMKernel::self() { return mySelf; } KSharedConfig::Ptr KMKernel::config() { assert(mySelf); if (!mySelf->mConfig) { mySelf->mConfig = KSharedConfig::openConfig(QStringLiteral("kmail2rc")); // Check that all updates have been run on the config file: MessageList::MessageListSettings::self()->setSharedConfig(mySelf->mConfig); MessageList::MessageListSettings::self()->load(); TemplateParser::TemplateParserSettings::self()->setSharedConfig(mySelf->mConfig); TemplateParser::TemplateParserSettings::self()->load(); MessageComposer::MessageComposerSettings::self()->setSharedConfig(mySelf->mConfig); MessageComposer::MessageComposerSettings::self()->load(); MessageCore::MessageCoreSettings::self()->setSharedConfig(mySelf->mConfig); MessageCore::MessageCoreSettings::self()->load(); MessageViewer::MessageViewerSettings::self()->setSharedConfig(mySelf->mConfig); MessageViewer::MessageViewerSettings::self()->load(); mMailCommonSettings = new MailCommon::MailCommonSettings; mMailCommonSettings->setSharedConfig(mySelf->mConfig); mMailCommonSettings->load(); PimCommon::PimCommonSettings::self()->setSharedConfig(mySelf->mConfig); PimCommon::PimCommonSettings::self()->load(); Gravatar::GravatarSettings::self()->setSharedConfig(mySelf->mConfig); Gravatar::GravatarSettings::self()->load(); } return mySelf->mConfig; } void KMKernel::syncConfig() { slotRequestConfigSync(); } void KMKernel::selectCollectionFromId(Akonadi::Collection::Id id) { KMMainWidget *widget = getKMMainWidget(); Q_ASSERT(widget); if (!widget) { return; } Akonadi::Collection colFolder = CommonKernel->collectionFromId(id); if (colFolder.isValid()) { widget->slotSelectCollectionFolder(colFolder); } } bool KMKernel::selectFolder(const QString &folder) { KMMainWidget *widget = getKMMainWidget(); Q_ASSERT(widget); if (!widget) { return false; } const Akonadi::Collection colFolder = CommonKernel->collectionFromId(folder.toLongLong()); if (colFolder.isValid()) { widget->slotSelectCollectionFolder(colFolder); return true; } return false; } KMMainWidget *KMKernel::getKMMainWidget() { //This could definitely use a speadup const QWidgetList l = QApplication::topLevelWidgets(); for (QWidget *wid : l) { QList l2 = wid->window()->findChildren(); if (!l2.isEmpty() && l2.first()) { return l2.first(); } } return nullptr; } void KMKernel::slotRunBackgroundTasks() // called regularly by timer { // Hidden KConfig keys. Not meant to be used, but a nice fallback in case // a stable kmail release goes out with a nasty bug in CompactionJob... if (KMailSettings::self()->autoExpiring()) { mFolderCollectionMonitor->expireAllFolders(false /*scheduled, not immediate*/, entityTreeModel()); } if (KMailSettings::self()->checkCollectionsIndexing()) { mCheckIndexingManager->start(entityTreeModel()); } #ifdef DEBUG_SCHEDULER // for debugging, see jobscheduler.h mBackgroundTasksTimer->start(60 * 1000); // check again in 1 minute #else mBackgroundTasksTimer->start(4 * 60 * 60 * 1000); // check again in 4 hours #endif } static Akonadi::Collection::List collect_collections(const QAbstractItemModel *model, const QModelIndex &parent) { Akonadi::Collection::List collections; QStack stack; stack.push(parent); while (!stack.isEmpty()) { - const auto idx = stack.pop(); + const QModelIndex idx = stack.pop(); if (idx.isValid()) { collections << model->data(idx, Akonadi::EntityTreeModel::CollectionRole).value(); for (int i = model->rowCount(idx) - 1; i >= 0; --i) { - stack.push(idx.child(i, 0)); + stack.push(model->index(i, 0, idx)); } } } return collections; } Akonadi::Collection::List KMKernel::allFolders() const { return collect_collections(collectionModel(), QModelIndex()); } Akonadi::Collection::List KMKernel::subfolders(const Akonadi::Collection &col) const { const auto idx = collectionModel()->match({}, Akonadi::EntityTreeModel::CollectionRole, QVariant::fromValue(col), 1, Qt::MatchExactly); if (!idx.isEmpty()) { return collect_collections(collectionModel(), idx[0]); } return {}; } void KMKernel::expireAllFoldersNow() // called by the GUI { mFolderCollectionMonitor->expireAllFolders(true /*immediate*/, entityTreeModel()); } bool KMKernel::canQueryClose() { if (KMMainWidget::mainWidgetList() && KMMainWidget::mainWidgetList()->count() > 1) { return true; } return mUnityServiceManager->canQueryClose(); } Akonadi::Collection KMKernel::currentCollection() { KMMainWidget *widget = getKMMainWidget(); Akonadi::Collection col; if (widget) { col = widget->currentCollection(); } return col; } QSharedPointer KMKernel::currentFolderCollection() { KMMainWidget *widget = getKMMainWidget(); QSharedPointer folder; if (widget) { folder = widget->currentFolder(); } return folder; } MailCommon::MailCommonSettings *KMKernel::mailCommonSettings() const { return mMailCommonSettings; } Akonadi::Search::PIM::IndexedItems *KMKernel::indexedItems() const { return mIndexedItems; } // can't be inline, since KMSender isn't known to implement // KMail::MessageSender outside this .cpp file MessageComposer::MessageSender *KMKernel::msgSender() { return the_msgSender; } void KMKernel::transportRemoved(int id, const QString &name) { Q_UNUSED(id); // reset all identities using the deleted transport QStringList changedIdents; KIdentityManagement::IdentityManager *im = identityManager(); KIdentityManagement::IdentityManager::Iterator end = im->modifyEnd(); for (KIdentityManagement::IdentityManager::Iterator it = im->modifyBegin(); it != end; ++it) { if (name == (*it).transport()) { (*it).setTransport(QString()); changedIdents += (*it).identityName(); } } // if the deleted transport is the currently used transport reset it to default const QString ¤tTransport = KMailSettings::self()->currentTransport(); if (name == currentTransport) { KMailSettings::self()->setCurrentTransport(QString()); } if (!changedIdents.isEmpty()) { QString information = i18np("This identity has been changed to use the default transport:", "These %1 identities have been changed to use the default transport:", changedIdents.count()); //Don't set parent otherwise we will swith to current KMail and we configure it. So not good KMessageBox::informationList(nullptr, information, changedIdents); im->commit(); } } void KMKernel::transportRenamed(int id, const QString &oldName, const QString &newName) { Q_UNUSED(id); QStringList changedIdents; KIdentityManagement::IdentityManager *im = identityManager(); KIdentityManagement::IdentityManager::Iterator end = im->modifyEnd(); for (KIdentityManagement::IdentityManager::Iterator it = im->modifyBegin(); it != end; ++it) { if (oldName == (*it).transport()) { (*it).setTransport(newName); changedIdents << (*it).identityName(); } } if (!changedIdents.isEmpty()) { const QString information = i18np("This identity has been changed to use the modified transport:", "These %1 identities have been changed to use the modified transport:", changedIdents.count()); //Don't set parent otherwise we will swith to current KMail and we configure it. So not good KMessageBox::informationList(nullptr, information, changedIdents); im->commit(); } } void KMKernel::itemDispatchStarted() { // Watch progress of the MDA. KPIM::ProgressManagerAkonadi::createProgressItem(nullptr, MailTransport::DispatcherInterface().dispatcherInstance(), QStringLiteral("Sender"), i18n("Sending messages"), i18n("Initiating sending process..."), true, KPIM::ProgressItem::Unknown); } void KMKernel::instanceStatusChanged(const Akonadi::AgentInstance &instance) { if (instance.identifier() == QLatin1String("akonadi_mailfilter_agent")) { // Creating a progress item twice is ok, it will simply return the already existing // item KPIM::ProgressItem *progress = KPIM::ProgressManagerAkonadi::createProgressItem(nullptr, instance, instance.identifier(), instance.name(), instance.statusMessage(), false, KPIM::ProgressItem::Encrypted); progress->setProperty("AgentIdentifier", instance.identifier()); return; } if (MailCommon::Util::agentInstances(true).contains(instance)) { if (instance.status() == Akonadi::AgentInstance::Running) { if (mResourcesBeingChecked.isEmpty()) { qCDebug(KMAIL_LOG) << "A Resource started to synchronize, starting a mail check."; Q_EMIT startCheckMail(); } const QString identifier(instance.identifier()); if (!mResourcesBeingChecked.contains(identifier)) { mResourcesBeingChecked.append(identifier); } KPIM::ProgressItem::CryptoStatus cryptoStatus = KPIM::ProgressItem::Unencrypted; if (mResourceCryptoSettingCache.contains(identifier)) { cryptoStatus = mResourceCryptoSettingCache.value(identifier); } else { if (PimCommon::Util::isImapResource(identifier)) { MailCommon::ResourceReadConfigFile resourceFile(identifier); const KConfigGroup grp = resourceFile.group(QStringLiteral("network")); if (grp.isValid()) { const QString imapSafety = grp.readEntry(QStringLiteral("Safety")); if (imapSafety == QLatin1String("None")) { cryptoStatus = KPIM::ProgressItem::Unencrypted; } else { cryptoStatus = KPIM::ProgressItem::Encrypted; } mResourceCryptoSettingCache.insert(identifier, cryptoStatus); } } else if (identifier.contains(POP3_RESOURCE_IDENTIFIER)) { MailCommon::ResourceReadConfigFile resourceFile(identifier); const KConfigGroup grp = resourceFile.group(QStringLiteral("General")); if (grp.isValid()) { if (grp.readEntry(QStringLiteral("useSSL"), false) || grp.readEntry(QStringLiteral("useTLS"), false)) { cryptoStatus = KPIM::ProgressItem::Encrypted; } mResourceCryptoSettingCache.insert(identifier, cryptoStatus); } } } // Creating a progress item twice is ok, it will simply return the already existing // item KPIM::ProgressItem *progress = KPIM::ProgressManagerAkonadi::createProgressItem(nullptr, instance, instance.identifier(), instance.name(), instance.statusMessage(), true, cryptoStatus); progress->setProperty("AgentIdentifier", instance.identifier()); } else if (instance.status() == Akonadi::AgentInstance::Broken) { agentInstanceBroken(instance); } } } void KMKernel::agentInstanceBroken(const Akonadi::AgentInstance &instance) { const QString summary = i18n("Resource %1 is broken.", instance.name()); KNotification::event(QStringLiteral("akonadi-resource-broken"), summary, QPixmap(), nullptr, KNotification::CloseOnTimeout); } void KMKernel::slotProgressItemCompletedOrCanceled(KPIM::ProgressItem *item) { const QString identifier = item->property("AgentIdentifier").toString(); const Akonadi::AgentInstance agent = Akonadi::AgentManager::self()->instance(identifier); if (agent.isValid()) { mResourcesBeingChecked.removeAll(identifier); if (mResourcesBeingChecked.isEmpty()) { qCDebug(KMAIL_LOG) << "Last resource finished syncing, mail check done"; Q_EMIT endCheckMail(); } } } void KMKernel::updatedTemplates() { Q_EMIT customTemplatesChanged(); } void KMKernel::cleanupTemporaryFiles() { QDir dir(QDir::tempPath()); const QStringList lst = dir.entryList(QStringList{QStringLiteral("messageviewer_*")}); qCDebug(KMAIL_LOG) << " list file to delete " << lst; for (const QString &file : lst) { QDir tempDir(QDir::tempPath() + QLatin1Char('/') + file); if (!tempDir.removeRecursively()) { fprintf(stderr, "%s was not removed .\n", qPrintable(tempDir.absolutePath())); } else { fprintf(stderr, "%s was removed .\n", qPrintable(tempDir.absolutePath())); } } } void KMKernel::stopAgentInstance() { const QString resourceGroupPattern(QStringLiteral("Resource %1")); const Akonadi::AgentInstance::List lst = MailCommon::Util::agentInstances(); for (Akonadi::AgentInstance type : lst) { const QString identifier = type.identifier(); KConfigGroup group(KMKernel::config(), resourceGroupPattern.arg(identifier)); // Keep sync in ConfigureDialog, don't forget to change there. if (group.readEntry("OfflineOnShutdown", identifier.startsWith(QLatin1String("akonadi_pop3_resource")) ? true : false)) { type.setIsOnline(false); } } } void KMKernel::slotCollectionRemoved(const Akonadi::Collection &col) { KConfigGroup group(KMKernel::config(), MailCommon::FolderSettings::configGroupName(col)); group.deleteGroup(); group.sync(); const QString colStr = QString::number(col.id()); TemplateParser::Util::deleteTemplate(colStr); MessageList::Util::deleteConfig(colStr); } void KMKernel::slotDeleteIdentity(uint identity) { TemplateParser::Util::deleteTemplate(QStringLiteral("IDENTITY_%1").arg(identity)); } bool KMKernel::showPopupAfterDnD() { return KMailSettings::self()->showPopupAfterDnD(); } bool KMKernel::excludeImportantMailFromExpiry() { return KMailSettings::self()->excludeImportantMailFromExpiry(); } qreal KMKernel::closeToQuotaThreshold() { return KMailSettings::self()->closeToQuotaThreshold(); } Akonadi::Collection::Id KMKernel::lastSelectedFolder() { return KMailSettings::self()->lastSelectedFolder(); } void KMKernel::setLastSelectedFolder(Akonadi::Collection::Id col) { KMailSettings::self()->setLastSelectedFolder(col); } QStringList KMKernel::customTemplates() { return GlobalSettingsBase::self()->customTemplates(); } void KMKernel::openFilterDialog(bool createDummyFilter) { if (!mFilterEditDialog) { mFilterEditDialog = new MailCommon::KMFilterDialog(getKMMainWidget()->actionCollections(), nullptr, createDummyFilter); mFilterEditDialog->setObjectName(QStringLiteral("filterdialog")); } mFilterEditDialog->show(); mFilterEditDialog->raise(); mFilterEditDialog->activateWindow(); } void KMKernel::createFilter(const QByteArray &field, const QString &value) { mFilterEditDialog->createFilter(field, value); } void KMKernel::checkFolderFromResources(const Akonadi::Collection::List &collectionList) { const Akonadi::AgentInstance::List lst = MailCommon::Util::agentInstances(); for (const Akonadi::AgentInstance &type : lst) { if (type.status() == Akonadi::AgentInstance::Broken) { continue; } const QString typeIdentifier(type.identifier()); if (PimCommon::Util::isImapResource(typeIdentifier)) { OrgKdeAkonadiImapSettingsInterface *iface = PimCommon::Util::createImapSettingsInterface(typeIdentifier); if (iface && iface->isValid()) { const Akonadi::Collection::Id imapTrashId = iface->trashCollection(); for (const Akonadi::Collection &collection : collectionList) { const Akonadi::Collection::Id collectionId = collection.id(); if (imapTrashId == collectionId) { //Use default trash iface->setTrashCollection(CommonKernel->trashCollectionFolder().id()); iface->save(); break; } } } delete iface; } else if (typeIdentifier.contains(POP3_RESOURCE_IDENTIFIER)) { OrgKdeAkonadiPOP3SettingsInterface *iface = MailCommon::Util::createPop3SettingsInterface(typeIdentifier); if (iface->isValid()) { for (const Akonadi::Collection &collection : qAsConst(collectionList)) { const Akonadi::Collection::Id collectionId = collection.id(); if (iface->targetCollection() == collectionId) { //Use default inbox iface->setTargetCollection(CommonKernel->inboxCollectionFolder().id()); iface->save(); break; } } } delete iface; } } } const QAbstractItemModel *KMKernel::treeviewModelSelection() { if (getKMMainWidget()) { return getKMMainWidget()->folderTreeView()->selectionModel()->model(); } else { return entityTreeModel(); } } void KMKernel::slotInstanceWarning(const Akonadi::AgentInstance &instance, const QString &message) { const QString summary = i18nc(": ", "%1: %2", instance.name(), message); KNotification::event(QStringLiteral("akonadi-instance-warning"), summary, QPixmap(), nullptr, KNotification::CloseOnTimeout); } void KMKernel::slotInstanceError(const Akonadi::AgentInstance &instance, const QString &message) { const QString summary = i18nc(": ", "%1: %2", instance.name(), message); KNotification::event(QStringLiteral("akonadi-instance-error"), summary, QPixmap(), nullptr, KNotification::CloseOnTimeout); } void KMKernel::slotInstanceRemoved(const Akonadi::AgentInstance &instance) { const QString identifier(instance.identifier()); const QString resourceGroup = QStringLiteral("Resource %1").arg(identifier); if (KMKernel::config()->hasGroup(resourceGroup)) { KConfigGroup group(KMKernel::config(), resourceGroup); group.deleteGroup(); group.sync(); } if (mResourceCryptoSettingCache.contains(identifier)) { mResourceCryptoSettingCache.remove(identifier); } mFolderArchiveManager->slotInstanceRemoved(instance); } void KMKernel::savePaneSelection() { KMMainWidget *widget = getKMMainWidget(); if (widget) { widget->savePaneSelection(); } } void KMKernel::updatePaneTagComboBox() { KMMainWidget *widget = getKMMainWidget(); if (widget) { widget->updatePaneTagComboBox(); } } void KMKernel::resourceGoOnLine() { KMMainWidget *widget = getKMMainWidget(); if (widget) { if (widget->currentCollection().isValid()) { Akonadi::Collection collection = widget->currentCollection(); Akonadi::AgentInstance instance = Akonadi::AgentManager::self()->instance(collection.resource()); instance.setIsOnline(true); widget->refreshMessageListSelection(); } } } void KMKernel::makeResourceOnline(MessageViewer::Viewer::ResourceOnlineMode mode) { switch (mode) { case MessageViewer::Viewer::AllResources: resumeNetworkJobs(); break; case MessageViewer::Viewer::SelectedResource: resourceGoOnLine(); break; } } PimCommon::AutoCorrection *KMKernel::composerAutoCorrection() { return mAutoCorrection; } void KMKernel::toggleSystemTray() { KMMainWidget *widget = getKMMainWidget(); if (widget) { mUnityServiceManager->toggleSystemTray(widget); } } void KMKernel::showFolder(const QString &collectionId) { if (!collectionId.isEmpty()) { const Akonadi::Collection::Id id = collectionId.toLongLong(); selectCollectionFromId(id); } } void KMKernel::reloadFolderArchiveConfig() { mFolderArchiveManager->reloadConfig(); } void KMKernel::slotCollectionChanged(const Akonadi::Collection &, const QSet &set) { if (set.contains("newmailnotifierattribute")) { mUnityServiceManager->initListOfCollection(); } } FolderArchiveManager *KMKernel::folderArchiveManager() const { return mFolderArchiveManager; } bool KMKernel::allowToDebug() const { return mDebug; } bool KMKernel::firstStart() const { return the_firstStart; } bool KMKernel::shuttingDown() const { return the_shuttingDown; } void KMKernel::setShuttingDown(bool flag) { the_shuttingDown = flag; } void KMKernel::expunge(Akonadi::Collection::Id col, bool sync) { Q_UNUSED(col); Q_UNUSED(sync); }