diff --git a/plugins/messageviewer/bodypartformatter/autotests/diffdata/diff-akonadiconsole-16.12-master-ref.diff b/plugins/messageviewer/bodypartformatter/autotests/diffdata/diff-akonadiconsole-16.12-master-ref.diff index 8c7d0ef7..98932356 100644 --- a/plugins/messageviewer/bodypartformatter/autotests/diffdata/diff-akonadiconsole-16.12-master-ref.diff +++ b/plugins/messageviewer/bodypartformatter/autotests/diffdata/diff-akonadiconsole-16.12-master-ref.diff @@ -1,1100 +1,1099 @@ -
diff --git a/CMakeLists.txt b/CMakeLists.txt
+
diff --git a/CMakeLists.txt b/CMakeLists.txt
 index b4ef75c..258dcc1 100644
 --- a/CMakeLists.txt
 +++ b/CMakeLists.txt
 @@ -22,10 +22,10 @@ include(KDECMakeSettings)
  include(KDECompilerSettings NO_POLICY_SCOPE)
  include(ECMAddAppIcon)
  include(ECMQtDeclareLoggingCategory)
 -
 +include(ECMCoverageOption)
  
  # Do NOT add quote
 -set(KDEPIM_DEV_VERSION )
 +set(KDEPIM_DEV_VERSION alpha1)
  
  # add an extra space
  if(DEFINED KDEPIM_DEV_VERSION)
 @@ -33,15 +33,15 @@ if(DEFINED KDEPIM_DEV_VERSION)
  endif()
  
  
 -set(KDEPIM_VERSION_NUMBER "5.4.2")
 +set(KDEPIM_VERSION_NUMBER "5.4.40")
  set(KDEPIM_VERSION "${KDEPIM_VERSION_NUMBER}${KDEPIM_DEV_VERSION}")
  
  
 -set(AKONADI_MIMELIB_VERSION "5.4.2")
 -set(AKONADI_CONTACT_VERSION "5.4.2")
 -set(CALENDARSUPPORT_LIB_VERSION_LIB "5.4.2")
 -set(KPIMTEXTEDIT_LIB_VERSION "5.4.2")
 -set(AKONADI_VERSION "5.4.2")
 +set(AKONADI_MIMELIB_VERSION "5.4.40")
 +set(AKONADI_CONTACT_VERSION "5.4.40")
 +set(CALENDARSUPPORT_LIB_VERSION_LIB "5.4.40")
 +set(KPIMTEXTEDIT_LIB_VERSION "5.4.40")
 +set(AKONADI_VERSION "5.4.40")
  
  set(KDEPIM_LIB_VERSION "${KDEPIM_VERSION_NUMBER}")
  set(KDEPIM_LIB_SOVERSION "5")
 @@ -49,12 +49,12 @@ set(KDEPIM_LIB_SOVERSION "5")
  
  set(QT_REQUIRED_VERSION "5.6.0")
  find_package(Qt5 ${QT_REQUIRED_VERSION} CONFIG REQUIRED Widgets DBus Sql)
 -set(MESSAGELIB_LIB_VERSION_LIB "5.4.2")
 -set(LIBKLEO_LIB_VERSION_LIB "5.4.2")
 -set(LIBKDEPIM_LIB_VERSION_LIB "5.4.2")
 -set(KCALENDARCORE_LIB_VERSION "5.4.2")
 -set(KCONTACTS_LIB_VERSION "5.4.2")
 -set(KMIME_LIB_VERSION "5.4.2")
 +set(MESSAGELIB_LIB_VERSION_LIB "5.4.40")
 +set(LIBKLEO_LIB_VERSION_LIB "5.4.40")
 +set(LIBKDEPIM_LIB_VERSION_LIB "5.4.40")
 +set(KCALENDARCORE_LIB_VERSION "5.4.40")
 +set(KCONTACTS_LIB_VERSION "5.4.40")
 +set(KMIME_LIB_VERSION "5.4.40")
  
  # Find KF5 package
  find_package(KF5Completion ${KF5_VERSION} CONFIG REQUIRED)
 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
 index 4986439..9f3e884 100644
 --- a/src/CMakeLists.txt
 +++ b/src/CMakeLists.txt
 @@ -39,6 +39,7 @@ set(akonadiconsole_bin_SRCS
      jobtracker.cpp
      jobtrackerwidget.cpp
      jobtrackermodel.cpp
 +    jobtrackerfilterproxymodel.cpp
      tagpropertiesdialog.cpp
      )
  
 @@ -89,6 +90,7 @@ target_link_libraries(akonadiconsole_bin
      KF5::AkonadiCore
      KF5::AkonadiPrivate
      KF5::AkonadiContact
 +    KF5::AkonadiXml
      KF5::CalendarSupport
      KF5::CalendarCore
      KF5::Libkdepim
 diff --git a/src/agentconfigdialog.h b/src/agentconfigdialog.h
 index ff27c29..2ff0c62 100644
 --- a/src/agentconfigdialog.h
 +++ b/src/agentconfigdialog.h
 @@ -31,7 +31,7 @@ class AgentConfigDialog : public QDialog
  {
      Q_OBJECT
  public:
 -    explicit AgentConfigDialog(QWidget *parent = Q_NULLPTR);
 +    explicit AgentConfigDialog(QWidget *parent = nullptr);
      void setAgentInstance(const Akonadi::AgentInstance &instance);
  
  private Q_SLOTS:
 diff --git a/src/agentconfigmodel.cpp b/src/agentconfigmodel.cpp
 index e50da4a..f0b9f69 100644
 --- a/src/agentconfigmodel.cpp
 +++ b/src/agentconfigmodel.cpp
 @@ -24,7 +24,7 @@
  #include <KLocalizedString>
  #include "akonadiconsole_debug.h"
  
 -AgentConfigModel::AgentConfigModel(QObject *parent): QAbstractTableModel(parent), m_interface(Q_NULLPTR)
 +AgentConfigModel::AgentConfigModel(QObject *parent): QAbstractTableModel(parent), m_interface(nullptr)
  {
  }
  
 diff --git a/src/agentconfigmodel.h b/src/agentconfigmodel.h
 index 9d4bbc1..2496bc1 100644
 --- a/src/agentconfigmodel.h
 +++ b/src/agentconfigmodel.h
 @@ -31,7 +31,7 @@ class AgentConfigModel : public QAbstractTableModel
  {
      Q_OBJECT
  public:
 -    explicit AgentConfigModel(QObject *parent = Q_NULLPTR);
 +    explicit AgentConfigModel(QObject *parent = nullptr);
      ~AgentConfigModel();
      void setAgentInstance(const Akonadi::AgentInstance &instance);
  
 diff --git a/src/agentwidget.cpp b/src/agentwidget.cpp
 index 1c8d1fd..73f8a8a 100644
 --- a/src/agentwidget.cpp
 +++ b/src/agentwidget.cpp
 @@ -54,7 +54,7 @@
  class TextDialog : public QDialog
  {
  public:
 -    TextDialog(QWidget *parent = Q_NULLPTR)
 +    TextDialog(QWidget *parent = nullptr)
          : QDialog(parent)
      {
          QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok);
 diff --git a/src/agentwidget.h b/src/agentwidget.h
 index 3ab14ff..22d72d0 100644
 --- a/src/agentwidget.h
 +++ b/src/agentwidget.h
 @@ -37,7 +37,7 @@ class AgentWidget : public QWidget
      Q_OBJECT
  
  public:
 -    explicit AgentWidget(QWidget *parent = Q_NULLPTR);
 +    explicit AgentWidget(QWidget *parent = nullptr);
      Akonadi::AgentInstanceWidget *widget() const
      {
          return ui.instanceWidget;
 diff --git a/src/akonadibrowsermodel.cpp b/src/akonadibrowsermodel.cpp
 index c5c6545..97eaec6 100644
 --- a/src/akonadibrowsermodel.cpp
 +++ b/src/akonadibrowsermodel.cpp
 @@ -293,7 +293,7 @@ void AkonadiBrowserModel::setItemDisplayMode(AkonadiBrowserModel::ItemDisplayMod
  {
      const int oldColumnCount = columnCount();
      m_itemDisplayMode = itemDisplayMode;
 -    AkonadiBrowserModel::State *newState = Q_NULLPTR;
 +    AkonadiBrowserModel::State *newState = nullptr;
      switch (itemDisplayMode) {
      case MailMode:
          newState = m_mailState;
 diff --git a/src/akonadibrowsermodel.h b/src/akonadibrowsermodel.h
 index 30b44a1..4205346 100644
 --- a/src/akonadibrowsermodel.h
 +++ b/src/akonadibrowsermodel.h
 @@ -31,7 +31,7 @@ class AkonadiBrowserModel : public EntityTreeModel
  {
      Q_OBJECT
  public:
 -    explicit AkonadiBrowserModel(ChangeRecorder *monitor, QObject *parent = Q_NULLPTR);
 +    explicit AkonadiBrowserModel(ChangeRecorder *monitor, QObject *parent = nullptr);
      ~AkonadiBrowserModel();
  
      enum ItemDisplayMode {
 @@ -70,7 +70,7 @@ class AkonadiBrowserSortModel : public QSortFilterProxyModel
  {
      Q_OBJECT
  public:
 -    explicit AkonadiBrowserSortModel(AkonadiBrowserModel *browserModel, QObject *parent = Q_NULLPTR);
 +    explicit AkonadiBrowserSortModel(AkonadiBrowserModel *browserModel, QObject *parent = nullptr);
      ~AkonadiBrowserSortModel();
  
  protected:
 diff --git a/src/browserwidget.cpp b/src/browserwidget.cpp
 index c12a9b3..1180c3e 100644
 --- a/src/browserwidget.cpp
 +++ b/src/browserwidget.cpp
 @@ -48,6 +48,7 @@
  #include <AkonadiCore/tagmodel.h>
  #include <AkonadiCore/statisticsproxymodel.h>
  #include <AkonadiCore/tagdeletejob.h>
 +#include <AkonadiXml/XmlWriteJob>
  #include <kviewstatemaintainer.h>
  
  #include <kcontacts/addressee.h>
 @@ -86,10 +87,10 @@ Q_DECLARE_METATYPE(QSet<QByteArray>)
  
  BrowserWidget::BrowserWidget(KXmlGuiWindow *xmlGuiWindow, QWidget *parent) :
      QWidget(parent),
 -    mAttrModel(Q_NULLPTR),
 -    mStdActionManager(Q_NULLPTR),
 -    mMonitor(Q_NULLPTR),
 -    mCacheOnlyAction(Q_NULLPTR)
 +    mAttrModel(nullptr),
 +    mStdActionManager(nullptr),
 +    mMonitor(nullptr),
 +    mCacheOnlyAction(nullptr)
  {
      Q_ASSERT(xmlGuiWindow);
      QVBoxLayout *layout = new QVBoxLayout(this);
 @@ -256,7 +257,7 @@ void BrowserWidget::clear()
      contentUi.modificationtime->clear();
      contentUi.flags->clear();
      contentUi.tags->clear();
 -    contentUi.attrView->setModel(Q_NULLPTR);
 +    contentUi.attrView->setModel(nullptr);
  }
  
  void BrowserWidget::itemActivated(const QModelIndex &index)
 @@ -472,10 +473,9 @@ void BrowserWidget::dumpToXml()
      if (fileName.isEmpty()) {
          return;
      }
 -#if 0 // TODO: port me, can't use XmlWriteJob here, it's in runtime, call the akonadi2xml cli tool instead
 +
      XmlWriteJob *job = new XmlWriteJob(root, fileName, this);
      connect(job, &XmlWriteJob::result, this, &BrowserWidget::dumpToXmlResult);
 -#endif
  }
  
  void BrowserWidget::dumpToXmlResult(KJob *job)
 diff --git a/src/browserwidget.h b/src/browserwidget.h
 index b78038f..e161533 100644
 --- a/src/browserwidget.h
 +++ b/src/browserwidget.h
 @@ -56,7 +56,7 @@ class BrowserWidget: public QWidget
      Q_OBJECT
  
  public:
 -    explicit BrowserWidget(KXmlGuiWindow *xmlGuiWindow, QWidget *parent = Q_NULLPTR);
 +    explicit BrowserWidget(KXmlGuiWindow *xmlGuiWindow, QWidget *parent = nullptr);
      ~BrowserWidget();
  
  public Q_SLOTS:
 diff --git a/src/browserwidget_contentview.ui b/src/browserwidget_contentview.ui
 index 27b4bbd..d5e679a 100644
 --- a/src/browserwidget_contentview.ui
 +++ b/src/browserwidget_contentview.ui
 @@ -7,7 +7,7 @@
      <x>0</x>
      <y>0</y>
      <width>622</width>
 -    <height>496</height>
 +    <height>604</height>
     </rect>
    </property>
    <layout class="QGridLayout">
 @@ -34,14 +34,23 @@
     <item row="0" column="0" colspan="2">
      <widget class="QTabWidget" name="mainTabWidget">
       <property name="currentIndex">
 -      <number>3</number>
 +      <number>0</number>
       </property>
       <widget class="QWidget" name="tab">
        <attribute name="title">
         <string>Payload</string>
        </attribute>
        <layout class="QVBoxLayout">
 -       <property name="margin">
 +       <property name="leftMargin">
 +        <number>0</number>
 +       </property>
 +       <property name="topMargin">
 +        <number>0</number>
 +       </property>
 +       <property name="rightMargin">
 +        <number>0</number>
 +       </property>
 +       <property name="bottomMargin">
          <number>0</number>
         </property>
         <item>
 @@ -54,7 +63,16 @@
           </property>
           <widget class="QWidget" name="unsupportedTypePage">
            <layout class="QVBoxLayout">
 -           <property name="margin">
 +           <property name="leftMargin">
 +            <number>0</number>
 +           </property>
 +           <property name="topMargin">
 +            <number>0</number>
 +           </property>
 +           <property name="rightMargin">
 +            <number>0</number>
 +           </property>
 +           <property name="bottomMargin">
              <number>0</number>
             </property>
             <item>
 @@ -92,7 +110,16 @@
           </widget>
           <widget class="QWidget" name="incidenceViewPage">
            <layout class="QVBoxLayout">
 -           <property name="margin">
 +           <property name="leftMargin">
 +            <number>0</number>
 +           </property>
 +           <property name="topMargin">
 +            <number>0</number>
 +           </property>
 +           <property name="rightMargin">
 +            <number>0</number>
 +           </property>
 +           <property name="bottomMargin">
              <number>0</number>
             </property>
             <item>
 @@ -102,7 +129,16 @@
           </widget>
           <widget class="QWidget" name="mailViewPage">
            <layout class="QGridLayout" name="gridLayout1">
 -           <property name="margin">
 +           <property name="leftMargin">
 +            <number>0</number>
 +           </property>
 +           <property name="topMargin">
 +            <number>0</number>
 +           </property>
 +           <property name="rightMargin">
 +            <number>0</number>
 +           </property>
 +           <property name="bottomMargin">
              <number>0</number>
             </property>
             <item row="0" column="0">
 @@ -154,9 +190,6 @@
         <string>Raw Payload</string>
        </attribute>
        <layout class="QGridLayout">
 -       <property name="margin">
 -        <number>0</number>
 -       </property>
         <item row="0" column="0" colspan="2">
          <widget class="KTextEdit" name="dataView">
           <property name="lineWrapMode">
 @@ -369,17 +402,17 @@
   </widget>
   <customwidgets>
    <customwidget>
 +   <class>KTextEdit</class>
 +   <extends>QTextEdit</extends>
 +   <header>ktextedit.h</header>
 +  </customwidget>
 +  <customwidget>
     <class>KEditListWidget</class>
     <extends>QWidget</extends>
     <header>keditlistwidget.h</header>
     <container>1</container>
    </customwidget>
    <customwidget>
 -   <class>KTextEdit</class>
 -   <extends>QTextEdit</extends>
 -   <header>ktextedit.h</header>
 -  </customwidget>
 -  <customwidget>
     <class>CalendarSupport::IncidenceViewer</class>
     <extends>QWidget</extends>
     <header location="global">CalendarSupport/IncidenceViewer</header>
 diff --git a/src/collectionaclpage.h b/src/collectionaclpage.h
 index a29e324..fc9e6a9 100644
 --- a/src/collectionaclpage.h
 +++ b/src/collectionaclpage.h
 @@ -28,7 +28,7 @@ class CollectionAclPage : public Akonadi::CollectionPropertiesPage
  {
      Q_OBJECT
  public:
 -    explicit CollectionAclPage(QWidget *parent = Q_NULLPTR);
 +    explicit CollectionAclPage(QWidget *parent = nullptr);
  
      void load(const Akonadi::Collection &col) Q_DECL_OVERRIDE;
      void save(Akonadi::Collection &col) Q_DECL_OVERRIDE;
 diff --git a/src/collectionattributespage.cpp b/src/collectionattributespage.cpp
 index 2bc9fa2..5d6291e 100644
 --- a/src/collectionattributespage.cpp
 +++ b/src/collectionattributespage.cpp
 @@ -30,7 +30,7 @@ using namespace Akonadi;
  
  CollectionAttributePage::CollectionAttributePage(QWidget *parent) :
      CollectionPropertiesPage(parent),
 -    mModel(Q_NULLPTR)
 +    mModel(nullptr)
  {
      setPageTitle(QStringLiteral("Attributes"));
      ui.setupUi(this);
 diff --git a/src/collectionattributespage.h b/src/collectionattributespage.h
 index 7f4876c..d20d970 100644
 --- a/src/collectionattributespage.h
 +++ b/src/collectionattributespage.h
 @@ -31,7 +31,7 @@ class CollectionAttributePage : public Akonadi::CollectionPropertiesPage
  {
      Q_OBJECT
  public:
 -    explicit CollectionAttributePage(QWidget *parent = Q_NULLPTR);
 +    explicit CollectionAttributePage(QWidget *parent = nullptr);
  
      void load(const Akonadi::Collection &col) Q_DECL_OVERRIDE;
      void save(Akonadi::Collection &col) Q_DECL_OVERRIDE;
 diff --git a/src/collectioninternalspage.h b/src/collectioninternalspage.h
 index e2fb5b7..c56b755 100644
 --- a/src/collectioninternalspage.h
 +++ b/src/collectioninternalspage.h
 @@ -28,7 +28,7 @@ class CollectionInternalsPage : public Akonadi::CollectionPropertiesPage
  {
      Q_OBJECT
  public:
 -    explicit CollectionInternalsPage(QWidget *parent = Q_NULLPTR);
 +    explicit CollectionInternalsPage(QWidget *parent = nullptr);
  
      void load(const Akonadi::Collection &col) Q_DECL_OVERRIDE;
      void save(Akonadi::Collection &col) Q_DECL_OVERRIDE;
 diff --git a/src/connectionpage.h b/src/connectionpage.h
 index bcf4501..dc4d242 100644
 --- a/src/connectionpage.h
 +++ b/src/connectionpage.h
 @@ -31,7 +31,7 @@ class ConnectionPage : public QWidget
      Q_OBJECT
  
  public:
 -    explicit ConnectionPage(const QString &identifier, QWidget *parent = Q_NULLPTR);
 +    explicit ConnectionPage(const QString &identifier, QWidget *parent = nullptr);
  
      void showAllConnections(bool);
  
 diff --git a/src/dbaccess.cpp b/src/dbaccess.cpp
 index c4b11be..b20ab8e 100644
 --- a/src/dbaccess.cpp
 +++ b/src/dbaccess.cpp
 @@ -55,7 +55,7 @@ public:
          database.setPassword(settings.value(QStringLiteral("Password"), QString()).toString());
          database.setConnectOptions(settings.value(QStringLiteral("Options"), QString()).toString());
          if (!database.open()) {
 -            KMessageBox::error(Q_NULLPTR, QStringLiteral("Failed to connect to database: %1").arg(database.lastError().text()));
 +            KMessageBox::error(nullptr, QStringLiteral("Failed to connect to database: %1").arg(database.lastError().text()));
          }
      }
  
 diff --git a/src/dbbrowser.cpp b/src/dbbrowser.cpp
 index a734663..b264d9f 100644
 --- a/src/dbbrowser.cpp
 +++ b/src/dbbrowser.cpp
 @@ -26,7 +26,7 @@
  
  DbBrowser::DbBrowser(QWidget *parent) :
      QWidget(parent),
 -    mTableModel(Q_NULLPTR)
 +    mTableModel(nullptr)
  {
      ui.setupUi(this);
  
 diff --git a/src/dbbrowser.h b/src/dbbrowser.h
 index 5fce3f0..c4ac80d 100644
 --- a/src/dbbrowser.h
 +++ b/src/dbbrowser.h
 @@ -28,7 +28,7 @@ class DbBrowser : public QWidget
  {
      Q_OBJECT
  public:
 -    explicit DbBrowser(QWidget *parent = Q_NULLPTR);
 +    explicit DbBrowser(QWidget *parent = nullptr);
  
  private Q_SLOTS:
      void refreshClicked();
 diff --git a/src/dbconsole.cpp b/src/dbconsole.cpp
 index 1a4dd6d..c88743e 100644
 --- a/src/dbconsole.cpp
 +++ b/src/dbconsole.cpp
 @@ -35,7 +35,7 @@
  
  DbConsole::DbConsole(QWidget *parent) :
      QWidget(parent),
 -    mQueryModel(Q_NULLPTR)
 +    mQueryModel(nullptr)
  {
      ui.setupUi(this);
  
 diff --git a/src/dbconsole.h b/src/dbconsole.h
 index 9bd485a..399fde1 100644
 --- a/src/dbconsole.h
 +++ b/src/dbconsole.h
 @@ -28,7 +28,7 @@ class DbConsole : public QWidget
  {
      Q_OBJECT
  public:
 -    explicit DbConsole(QWidget *parent = Q_NULLPTR);
 +    explicit DbConsole(QWidget *parent = nullptr);
  
  private Q_SLOTS:
      void execClicked();
 diff --git a/src/debugwidget.h b/src/debugwidget.h
 index bedf5a5..0e38af3 100644
 --- a/src/debugwidget.h
 +++ b/src/debugwidget.h
 @@ -37,7 +37,7 @@ class DebugWidget : public QWidget
      Q_OBJECT
  
  public:
 -    explicit DebugWidget(QWidget *parent = Q_NULLPTR);
 +    explicit DebugWidget(QWidget *parent = nullptr);
  
  private Q_SLOTS:
      void connectionStarted(const QString &, const QString &);
 diff --git a/src/instanceselector.cpp b/src/instanceselector.cpp
 index d10b601..a902bf7 100644
 --- a/src/instanceselector.cpp
 +++ b/src/instanceselector.cpp
 @@ -43,7 +43,7 @@ InstanceSelector::InstanceSelector(const QString &remoteHost, QWidget *parent, Q
      : QDialog(parent, flags),
        ui(new Ui::InstanceSelector),
        m_remoteHost(remoteHost),
 -      mWindow(Q_NULLPTR)
 +      mWindow(nullptr)
  {
      QWidget *mainWidget = new QWidget(this);
      QVBoxLayout *mainLayout = new QVBoxLayout(this);
 @@ -76,7 +76,7 @@ InstanceSelector::InstanceSelector(const QString &remoteHost, QWidget *parent, Q
              item->setData(inst, Qt::UserRole);
              model->appendRow(item);
          }
 -        connect(ui->listView, &QAbstractItemView::doubleClicked, this, &QDialog::accept);
 +        connect(ui->listView, &QAbstractItemView::doubleClicked, this, &InstanceSelector::slotAccept);
          ui->listView->setModel(model);
          show();
      }
 diff --git a/src/instanceselector.h b/src/instanceselector.h
 index 99fdd4d..565dc74 100644
 --- a/src/instanceselector.h
 +++ b/src/instanceselector.h
 @@ -39,7 +39,7 @@ class InstanceSelector : public QDialog
  {
      Q_OBJECT
  public:
 -    explicit InstanceSelector(const QString &remoteHost, QWidget *parent = Q_NULLPTR, Qt::WindowFlags flags = Q_NULLPTR);
 +    explicit InstanceSelector(const QString &remoteHost, QWidget *parent = nullptr, Qt::WindowFlags flags = nullptr);
      virtual ~InstanceSelector();
  
  private Q_SLOTS:
 diff --git a/src/jobtracker.cpp b/src/jobtracker.cpp
 index 5a6280f..ede37c9 100644
 --- a/src/jobtracker.cpp
 +++ b/src/jobtracker.cpp
 @@ -22,6 +22,7 @@
  
  #include "jobtracker.h"
  #include "jobtrackeradaptor.h"
 +#include <akonadi/private/instance_p.h>
  #include "akonadiconsole_debug.h"
  #include <QString>
  #include <QStringList>
 @@ -87,7 +88,8 @@ JobTracker::JobTracker(const char *name, QObject *parent)
      : QObject(parent), d(new Private(this))
  {
      new JobTrackerAdaptor(this);
 -    QDBusConnection::sessionBus().registerService(QStringLiteral("org.kde.akonadiconsole"));
 +    const QString suffix = Akonadi::Instance::identifier().isEmpty() ? QString() : QLatin1Char('-') + Akonadi::Instance::identifier();
 +    QDBusConnection::sessionBus().registerService(QStringLiteral("org.kde.akonadiconsole") + suffix);
      QDBusConnection::sessionBus().registerObject(QLatin1Char('/') + QLatin1String(name),
              this, QDBusConnection::ExportAdaptors);
  
 @@ -243,7 +245,8 @@ QList<JobInfo> JobTracker::jobs(const QString &parent) const
      assert(d->jobs.contains(parent));
      const QStringList jobs = d->jobs.value(parent);
      QList<JobInfo> infoList;
 -    Q_FOREACH (const QString &job, jobs) {
 +    infoList.reserve(jobs.count());
 +    for (const QString &job : jobs) {
          infoList << d->infoList.value(job);
      }
      return infoList;
 diff --git a/src/jobtracker.h b/src/jobtracker.h
 index 25e36b2..028c404 100644
 --- a/src/jobtracker.h
 +++ b/src/jobtracker.h
 @@ -66,7 +66,7 @@ class JobTracker : public QObject
      Q_CLASSINFO("D-Bus Interface", "org.freedesktop.Akonadi.JobTracker")
  
  public:
 -    explicit JobTracker(const char *name, QObject *parent = Q_NULLPTR);
 +    explicit JobTracker(const char *name, QObject *parent = nullptr);
      ~JobTracker();
      QStringList sessions() const;
  
 diff --git a/src/jobtrackerfilterproxymodel.cpp b/src/jobtrackerfilterproxymodel.cpp
 new file mode 100644
 index 0000000..3841e3c
 --- /dev/null
 +++ b/src/jobtrackerfilterproxymodel.cpp
 @@ -0,0 +1,63 @@
 +/*
 +  Copyright (c) 2017 Montel Laurent <montel@kde.org>
 +
 +  This program is free software; you can redistribute it and/or modify it
 +  under the terms of the GNU General Public License, version 2, as
 +  published by the Free Software Foundation.
 +
 +  This program is distributed in the hope that it will be useful, but
 +  WITHOUT ANY WARRANTY; without even the implied warranty of
 +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 +  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 "jobtrackerfilterproxymodel.h"
 +//#include <QDebug>
 +
 +JobTrackerFilterProxyModel::JobTrackerFilterProxyModel(QObject *parent)
 +    : QSortFilterProxyModel(parent),
 +      mSearchColumn(0)
 +{
 +
 +}
 +
 +JobTrackerFilterProxyModel::~JobTrackerFilterProxyModel()
 +{
 +
 +}
 +
 +bool JobTrackerFilterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
 +{
 +    if (filterRegExp().isEmpty()) {
 +        return true;
 +    }
 +    QRegExp reg = filterRegExp();
 +    reg.setCaseSensitivity(Qt::CaseInsensitive);
 +    if (mSearchColumn == -1) {//Search on All Column
 +        for (int i = 0; i < 7; i++) {
 +            QModelIndex index = sourceModel()->index(sourceRow, i, sourceParent);
 +            if (index.isValid()) {
 +                //qDebug() << " sourceModel()->data(index).toString( "<<sourceModel()->data(index).toString() << " column " <<i << "reg" << reg
 +                if (sourceModel()->data(index).toString().contains(reg)) {
 +                    return true;
 +                }
 +            } else {
 +                return true;
 +            }
 +        }
 +        return false;
 +    } else {
 +        QModelIndex index = sourceModel()->index(sourceRow, mSearchColumn, sourceParent);
 +        return sourceModel()->data(index).toString().contains(reg);
 +    }
 +}
 +
 +void JobTrackerFilterProxyModel::setSearchColumn(int column)
 +{
 +    mSearchColumn = column;
 +}
 diff --git a/src/jobtrackerfilterproxymodel.h b/src/jobtrackerfilterproxymodel.h
 new file mode 100644
 index 0000000..b2f9326
 --- /dev/null
 +++ b/src/jobtrackerfilterproxymodel.h
 @@ -0,0 +1,39 @@
 +/*
 +  Copyright (c) 2017 Montel Laurent <montel@kde.org>
 +
 +  This program is free software; you can redistribute it and/or modify it
 +  under the terms of the GNU General Public License, version 2, as
 +  published by the Free Software Foundation.
 +
 +  This program is distributed in the hope that it will be useful, but
 +  WITHOUT ANY WARRANTY; without even the implied warranty of
 +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 +  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
 +*/
 +
 +#ifndef JOBTRACKERFILTERPROXYMODEL_H
 +#define JOBTRACKERFILTERPROXYMODEL_H
 +
 +#include <QSortFilterProxyModel>
 +
 +class JobTrackerFilterProxyModel : public QSortFilterProxyModel
 +{
 +    Q_OBJECT
 +public:
 +    explicit JobTrackerFilterProxyModel(QObject *parent = nullptr);
 +    ~JobTrackerFilterProxyModel();
 +
 +    void setSearchColumn(int column);
 +
 +protected:
 +    bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const Q_DECL_OVERRIDE;
 +
 +private:
 +    int mSearchColumn;
 +};
 +
 +#endif // JOBTRACKERFILTERPROXYMODEL_H
 diff --git a/src/jobtrackermodel.cpp b/src/jobtrackermodel.cpp
 index 2264d3b..31017f7 100644
 --- a/src/jobtrackermodel.cpp
 +++ b/src/jobtrackermodel.cpp
 @@ -89,7 +89,7 @@ QModelIndex JobTrackerModel::index(int row, int column, const QModelIndex &paren
      }
      // non-toplevel job
      const QStringList jobs = d->tracker.jobNames(parent.internalId());
 -    if (row >= jobs.size()) {
 +    if (row < 0 || row >= jobs.size()) {
          return QModelIndex();
      }
      return createIndex(row, column, d->tracker.idForJob(jobs.at(row)));
 diff --git a/src/jobtrackerwidget.cpp b/src/jobtrackerwidget.cpp
 index e344735..e08b630 100644
 --- a/src/jobtrackerwidget.cpp
 +++ b/src/jobtrackerwidget.cpp
 @@ -23,6 +23,7 @@
  #include <QCheckBox>
  
  #include "jobtrackermodel.h"
 +#include "jobtrackerfilterproxymodel.h"
  
  #include <AkonadiWidgets/controlgui.h>
  
 @@ -36,11 +37,17 @@
  #include <QPushButton>
  #include <QFile>
  #include <QFileDialog>
 +#include <QApplication>
 +#include <QClipboard>
 +#include <QLineEdit>
  
  class JobTrackerWidget::Private
  {
  public:
      JobTrackerModel *model;
 +    QTreeView *tv;
 +    QLineEdit *searchLineEdit;
 +    JobTrackerFilterProxyModel *filterProxyModel;
  };
  
  JobTrackerWidget::JobTrackerWidget(const char *name, QWidget *parent, const QString &checkboxText)
 @@ -56,16 +63,25 @@ JobTrackerWidget::JobTrackerWidget(const char *name, QWidget *parent, const QStr
      connect(enableCB, &QAbstractButton::toggled, d->model, &JobTrackerModel::setEnabled);
      layout->addWidget(enableCB);
  
 -    QTreeView *tv = new QTreeView(this);
 -    tv->setModel(d->model);
 -    tv->expandAll();
 -    tv->setAlternatingRowColors(true);
 -    tv->setContextMenuPolicy(Qt::CustomContextMenu);
 +    d->searchLineEdit = new QLineEdit(this);
 +    d->searchLineEdit->setClearButtonEnabled(true);
 +    d->searchLineEdit->setPlaceholderText(QStringLiteral("Search..."));
 +    layout->addWidget(d->searchLineEdit);
 +    connect(d->searchLineEdit, &QLineEdit::textChanged, this, &JobTrackerWidget::textFilterChanged);
 +
 +    d->filterProxyModel = new JobTrackerFilterProxyModel(this);
 +    d->filterProxyModel->setSourceModel(d->model);
 +
 +    d->tv = new QTreeView(this);
 +    d->tv->setModel(d->filterProxyModel);
 +    d->tv->expandAll();
 +    d->tv->setAlternatingRowColors(true);
 +    d->tv->setContextMenuPolicy(Qt::CustomContextMenu);
      // too slow with many jobs:
      // tv->header()->setResizeMode( QHeaderView::ResizeToContents );
 -    connect(d->model, &JobTrackerModel::modelReset, tv, &QTreeView::expandAll);
 -    connect(tv, &QTreeView::customContextMenuRequested, this, &JobTrackerWidget::contextMenu);
 -    layout->addWidget(tv);
 +    connect(d->model, &JobTrackerModel::modelReset, d->tv, &QTreeView::expandAll);
 +    connect(d->tv, &QTreeView::customContextMenuRequested, this, &JobTrackerWidget::contextMenu);
 +    layout->addWidget(d->tv);
      d->model->setEnabled(false);   // since it can be slow, default to off
  
      QHBoxLayout *layout2 = new QHBoxLayout;
 @@ -84,13 +100,45 @@ JobTrackerWidget::~JobTrackerWidget()
      delete d;
  }
  
 +void JobTrackerWidget::textFilterChanged()
 +{
 +    d->filterProxyModel->setFilterFixedString(d->searchLineEdit->text());
 +    d->tv->expandAll();
 +}
 +
  void JobTrackerWidget::contextMenu(const QPoint &/*pos*/)
  {
      QMenu menu;
      menu.addAction(QStringLiteral("Clear View"), d->model, &JobTrackerModel::resetTracker);
 +    menu.addSeparator();
 +    menu.addAction(QStringLiteral("Copy Info"), this, &JobTrackerWidget::copyJobInfo);
 +    menu.addSeparator();
 +    menu.addAction(QStringLiteral("Collapse All"), this, &JobTrackerWidget::collapseAll);
 +    menu.addAction(QStringLiteral("Expand All"), this, &JobTrackerWidget::expandAll);
 +
      menu.exec(QCursor::pos());
  }
  
 +void JobTrackerWidget::expandAll()
 +{
 +    d->tv->expandAll();
 +}
 +
 +void JobTrackerWidget::collapseAll()
 +{
 +    d->tv->collapseAll();
 +}
 +
 +void JobTrackerWidget::copyJobInfo()
 +{
 +    QModelIndex index = d->tv->currentIndex();
 +    if (index.isValid()) {
 +        const QModelIndex newIndex = d->model->index(index.row(), 6, index.parent());
 +        QClipboard *cb = QApplication::clipboard();
 +        cb->setText(newIndex.data().toString(), QClipboard::Clipboard);
 +    }
 +}
 +
  void JobTrackerWidget::slotSaveToFile()
  {
      const QString fileName = QFileDialog::getSaveFileName(this, QString(), QString(), QString());
 diff --git a/src/jobtrackerwidget.h b/src/jobtrackerwidget.h
 index 94166bd..ca06215 100644
 --- a/src/jobtrackerwidget.h
 +++ b/src/jobtrackerwidget.h
 @@ -38,6 +38,10 @@ private Q_SLOTS:
      void slotSaveToFile();
  
  private:
 +    void expandAll();
 +    void copyJobInfo();
 +    void collapseAll();
 +    void textFilterChanged();
      void writeRows(const QModelIndex &parent, QFile &file, int indentLevel);
  
      class Private;
 diff --git a/src/mainwidget.h b/src/mainwidget.h
 index 27d6f58..6870dea 100644
 --- a/src/mainwidget.h
 +++ b/src/mainwidget.h
 @@ -33,7 +33,7 @@ class MainWidget : public QWidget
      Q_OBJECT
  
  public:
 -    explicit MainWidget(KXmlGuiWindow *parent = Q_NULLPTR);
 +    explicit MainWidget(KXmlGuiWindow *parent = nullptr);
      ~MainWidget();
  
  private Q_SLOTS:
 diff --git a/src/mainwindow.h b/src/mainwindow.h
 index 8d993b4..6bc824d 100644
 --- a/src/mainwindow.h
 +++ b/src/mainwindow.h
 @@ -31,7 +31,7 @@ class MainWindow : public KXmlGuiWindow
      Q_OBJECT
  
  public:
 -    explicit MainWindow(QWidget *parent = Q_NULLPTR);
 +    explicit MainWindow(QWidget *parent = nullptr);
      ~MainWindow();
  
  protected:
 diff --git a/src/monitorsmodel.cpp b/src/monitorsmodel.cpp
 index ce4767d..138dfc6 100644
 --- a/src/monitorsmodel.cpp
 +++ b/src/monitorsmodel.cpp
 @@ -27,7 +27,7 @@
  
  MonitorsModel::MonitorsModel(QObject *parent):
      QAbstractItemModel(parent),
 -    mMonitor(Q_NULLPTR)
 +    mMonitor(nullptr)
  {
      QTimer::singleShot(0, this, &MonitorsModel::init);
  }
 diff --git a/src/monitorsmodel.h b/src/monitorsmodel.h
 index c0b0ad1..0ed6bb3 100644
 --- a/src/monitorsmodel.h
 +++ b/src/monitorsmodel.h
 @@ -51,7 +51,7 @@ public:
          ColumnsCount
      };
  
 -    explicit MonitorsModel(QObject *parent = Q_NULLPTR);
 +    explicit MonitorsModel(QObject *parent = nullptr);
      virtual ~MonitorsModel();
  
      void setEnabled(bool enabled);
 diff --git a/src/monitorswidget.cpp b/src/monitorswidget.cpp
 index f5e5cb7..2db947a 100644
 --- a/src/monitorswidget.cpp
 +++ b/src/monitorswidget.cpp
 @@ -38,6 +38,7 @@ MonitorsWidget::MonitorsWidget(QWidget *parent):
      mTreeView = new QTreeView(this);
      mTreeView->setModel(mModel);
      mTreeView->setAlternatingRowColors(true);
 +    mTreeView->setRootIsDecorated(false);
      layout->addWidget(mTreeView);
  
      Akonadi::ControlGui::widgetNeedsAkonadi(this);
 diff --git a/src/monitorswidget.h b/src/monitorswidget.h
 index 8289836..f59d0fe 100644
 --- a/src/monitorswidget.h
 +++ b/src/monitorswidget.h
 @@ -30,7 +30,7 @@ class MonitorsWidget : public QWidget
      Q_OBJECT
  
  public:
 -    explicit MonitorsWidget(QWidget *parent = Q_NULLPTR);
 +    explicit MonitorsWidget(QWidget *parent = nullptr);
      virtual ~MonitorsWidget();
  
  private:
 diff --git a/src/notificationmodel.cpp b/src/notificationmodel.cpp
 index c8b367e..0f8d8b2 100644
 --- a/src/notificationmodel.cpp
 +++ b/src/notificationmodel.cpp
 @@ -305,24 +305,24 @@ public:
      QVariant data(int column) const Q_DECL_OVERRIDE
      {
          switch (column) {
 -            case 0: {
 -                switch (msg.operation()) {
 -                case Protocol::SubscriptionChangeNotification::Add:
 -                    return QStringLiteral("Add");
 -                case Protocol::SubscriptionChangeNotification::Modify:
 -                    return QStringLiteral("Modify");
 -                case Protocol::SubscriptionChangeNotification::Remove:
 -                    return QStringLiteral("Remove");
 -                default:
 -                    return QStringLiteral("Invalid");
 -                }
 -            }
 -            case 1:
 -                return QStringLiteral("Subscription");
 -            case 2:
 -                return msg.subscriber();
 +        case 0: {
 +            switch (msg.operation()) {
 +            case Protocol::SubscriptionChangeNotification::Add:
 +                return QStringLiteral("Add");
 +            case Protocol::SubscriptionChangeNotification::Modify:
 +                return QStringLiteral("Modify");
 +            case Protocol::SubscriptionChangeNotification::Remove:
 +                return QStringLiteral("Remove");
              default:
 -                return QString();
 +                return QStringLiteral("Invalid");
 +            }
 +        }
 +        case 1:
 +            return QStringLiteral("Subscription");
 +        case 2:
 +            return msg.subscriber();
 +        default:
 +            return QString();
          }
      }
  
 @@ -333,7 +333,7 @@ class NotificationModel::NotificationBlock: public NotificationModel::Item
  {
  public:
      NotificationBlock(const Akonadi::ChangeNotification &msg)
 -        : NotificationModel::Item(-2, Q_NULLPTR)
 +        : NotificationModel::Item(-2, nullptr)
      {
          timestamp = msg.timestamp();
          QStringList list;
 @@ -388,7 +388,7 @@ public:
  
  NotificationModel::NotificationModel(QObject *parent) :
      QAbstractItemModel(parent),
 -    m_monitor(Q_NULLPTR)
 +    m_monitor(nullptr)
  {
  }
  
 @@ -513,19 +513,19 @@ void NotificationModel::clear()
  
  Protocol::ChangeNotification NotificationModel::notification(const QModelIndex &index) const
  {
 -    Item *item = static_cast<Item*>(index.internalPointer());
 +    Item *item = static_cast<Item *>(index.internalPointer());
      Q_ASSERT(item);
      switch (item->type) {
      case Protocol::Command::ItemChangeNotification:
 -        return static_cast<ItemNotificationNode*>(item)->msg;
 +        return static_cast<ItemNotificationNode *>(item)->msg;
      case Protocol::Command::CollectionChangeNotification:
 -        return static_cast<CollectionNotificationNode*>(item)->msg;
 +        return static_cast<CollectionNotificationNode *>(item)->msg;
      case Protocol::Command::TagChangeNotification:
 -        return static_cast<TagNotificationNode*>(item)->msg;
 +        return static_cast<TagNotificationNode *>(item)->msg;
      case Protocol::Command::RelationChangeNotification:
 -        return static_cast<RelationNotificationNode*>(item)->msg;
 +        return static_cast<RelationNotificationNode *>(item)->msg;
      case Protocol::Command::SubscriptionChangeNotification:
 -        return static_cast<SubscriptionNotificationNode*>(item)->msg;
 +        return static_cast<SubscriptionNotificationNode *>(item)->msg;
      default:
          return Protocol::ChangeNotification();
      }
 @@ -541,7 +541,7 @@ void NotificationModel::setEnabled(bool enable)
                  this, &NotificationModel::slotNotify);
      } else if (m_monitor) {
          m_monitor->deleteLater();
 -        m_monitor = Q_NULLPTR;
 +        m_monitor = nullptr;
      }
  }
  
 diff --git a/src/notificationmodel.h b/src/notificationmodel.h
 index 9b6c8af..a45304b 100644
 --- a/src/notificationmodel.h
 +++ b/src/notificationmodel.h
 @@ -27,8 +27,10 @@
  #include <AkonadiCore/Monitor>
  #include <AkonadiCore/ChangeNotification>
  
 -namespace Akonadi {
 -namespace Protocol {
 +namespace Akonadi
 +{
 +namespace Protocol
 +{
  class ChangeNotification;
  }
  }
 diff --git a/src/org.kde.akonadiconsole.desktop b/src/org.kde.akonadiconsole.desktop
 index 9abdeee..716e0cd 100755
 --- a/src/org.kde.akonadiconsole.desktop
 +++ b/src/org.kde.akonadiconsole.desktop
 @@ -1,5 +1,6 @@
  [Desktop Entry]
  Name=Akonadi Console
 +Name[ast]=Consola d'Akonadi
  Name[ca]=Consola de l'Akonadi
  Name[ca@valencia]=Consola de l'Akonadi
  Name[cs]=Konzole Akonadi
 diff --git a/src/querydebugger.h b/src/querydebugger.h
 index c1a43bf..1d7a60c 100644
 --- a/src/querydebugger.h
 +++ b/src/querydebugger.h
 @@ -38,7 +38,7 @@ class QueryDebugger : public QWidget
      Q_OBJECT
  
  public:
 -    explicit QueryDebugger(QWidget *parent = Q_NULLPTR);
 +    explicit QueryDebugger(QWidget *parent = nullptr);
      virtual ~QueryDebugger();
  
  private Q_SLOTS:
 diff --git a/src/searchdialog.h b/src/searchdialog.h
 index 8cb5357..64063ea 100644
 --- a/src/searchdialog.h
 +++ b/src/searchdialog.h
 @@ -29,7 +29,7 @@ class KTextEdit;
  class SearchDialog : public QDialog
  {
  public:
 -    explicit SearchDialog(QWidget *parent = Q_NULLPTR);
 +    explicit SearchDialog(QWidget *parent = nullptr);
      ~SearchDialog();
  
      QString searchName() const;
 diff --git a/src/searchwidget.h b/src/searchwidget.h
 index 2589355..842c3cd 100644
 --- a/src/searchwidget.h
 +++ b/src/searchwidget.h
 @@ -41,7 +41,7 @@ class SearchWidget : public QWidget
      Q_OBJECT
  
  public:
 -    explicit SearchWidget(QWidget *parent = Q_NULLPTR);
 +    explicit SearchWidget(QWidget *parent = nullptr);
      ~SearchWidget();
  
  private Q_SLOTS:
 diff --git a/src/tagpropertiesdialog.h b/src/tagpropertiesdialog.h
 index 389683b..d17dffe 100644
 --- a/src/tagpropertiesdialog.h
 +++ b/src/tagpropertiesdialog.h
 @@ -33,8 +33,8 @@ class TagPropertiesDialog : public QDialog
      Q_OBJECT
  
  public:
 -    explicit TagPropertiesDialog(QWidget *parent = Q_NULLPTR);
 -    explicit TagPropertiesDialog(const Akonadi::Tag &tag, QWidget *parent = Q_NULLPTR);
 +    explicit TagPropertiesDialog(QWidget *parent = nullptr);
 +    explicit TagPropertiesDialog(const Akonadi::Tag &tag, QWidget *parent = nullptr);
      virtual ~TagPropertiesDialog();
  
      Akonadi::Tag tag() const;
-
-
\ No newline at end of file + diff --git a/plugins/messageviewer/bodypartformatter/autotests/diffdata/kcontact1-ref.diff b/plugins/messageviewer/bodypartformatter/autotests/diffdata/kcontact1-ref.diff index 7cd9fa0e..05e0f4a0 100644 --- a/plugins/messageviewer/bodypartformatter/autotests/diffdata/kcontact1-ref.diff +++ b/plugins/messageviewer/bodypartformatter/autotests/diffdata/kcontact1-ref.diff @@ -1,479 +1,478 @@ -
commit 93f6aaf446133e05826a4d8e50e2da8ffac7c28f
+
commit 93f6aaf446133e05826a4d8e50e2da8ffac7c28f
 Author: David Faure <faure@kde.org>
 Date:   Mon Apr 17 20:41:55 2017 +0200
 
     VCardParser: rewrite parser to use a state machine instead of split()
     
     This is both faster and more correct: the parser now handles correctly
     double-quoted values (not splitting at ':' inside the double quotes
     or comma -- well except for type, where it's apparently needed).
     
     The speed gain is 10% according to vcardtool_benchmark with -O2.
 
 diff --git a/autotests/importexportvcardtest.cpp b/autotests/importexportvcardtest.cpp
 index 5cb2ef24..331b194d 100644
 --- a/autotests/importexportvcardtest.cpp
 +++ b/autotests/importexportvcardtest.cpp
 @@ -21,6 +21,7 @@
  #include "importexportvcardtest.h"
  #include <QTest>
  #include "vcardtool.h"
 +#include <QDebug>
  
  ImportExportVCardTest::ImportExportVCardTest(QObject *parent)
      : QObject(parent)
 @@ -33,6 +34,27 @@ ImportExportVCardTest::~ImportExportVCardTest()
  
  }
  
 +static void compareBuffers(const QByteArray &outputData, const QByteArray &expected)
 +{
 +    if (outputData != expected) {
 +        qDebug() << " outputData " << outputData;
 +        qDebug() << " expected " << expected;
 +    }
 +    const QList<QByteArray> outputLines = outputData.split('\n');
 +    const QList<QByteArray> outputRefLines = expected.split('\n');
 +    for (int i = 0; i < qMin(outputLines.count(), outputRefLines.count()); ++i) {
 +        const QByteArray actual = outputLines.at(i);
 +        const QByteArray expect = outputRefLines.at(i);
 +        if (actual != expect) {
 +            qCritical() << "Mismatch at output line" << (i + 1);
 +            QCOMPARE(actual, expect);
 +            QCOMPARE(actual.count(), expect.count());
 +        }
 +    }
 +    QCOMPARE(outputLines.count(), outputRefLines.count());
 +    QCOMPARE(outputData.size(), expected.size());
 +}
 +
  void ImportExportVCardTest::shouldExportFullTestVcard4()
  {
      QByteArray vcarddata("BEGIN:VCARD\r\n"
 @@ -71,8 +93,9 @@ void ImportExportVCardTest::shouldExportFullTestVcard4()
                           "END:VCARD\r\n\r\n");
      QByteArray vcardexpected("BEGIN:VCARD\r\n"
                               "VERSION:4.0\r\n"
 -                             "ADR;GEO=\"geo:51.523701,0.158500\";LABEL=\"Mr Sherlock Holmes\";TYPE:;;221B Bak\r\n"
 -                             " er Street;London;;NW1;United Kingdom\r\n"
 +                             "ADR;GEO=\"geo:51.523701,0.158500\";LABEL=\"Mr Sherlock Holmes, 221B Baker Stre\r\n"
 +                             " et, London NW1, England, United Kingdom\";TYPE:;;221B Baker Street;London;;\r\n"
 +                             " NW1;United Kingdom\r\n"
                               "ANNIVERSARY:19960415\r\n"
                               "BDAY:19531015T231000Z\r\n"
                               "CALADRURI;PREF=1:mailto:detective@sherlockholmes.com\r\n"
 @@ -93,7 +116,7 @@ void ImportExportVCardTest::shouldExportFullTestVcard4()
                               "PRODID:-//KADDRESSBOOK//NONSGML Version 1//EN\r\n"
                               "REV:20140722T222710Z\r\n"
                               "ROLE:Detective\r\n"
 -                             "TEL;PREF=1;VALUE=uri:ext=5555\r\n"
 +                             "TEL;TYPE=\"home,voice\";PREF=1;VALUE=uri:tel:+44-555-555-5555;ext=5555\r\n"
                               "TEL;TYPE=\"cell,voice\";VALUE=uri:tel:+44-555-555-6666\r\n"
                               "TEL;TYPE=\"voice,work\";VALUE=uri:tel:+44-555-555-7777\r\n"
                               "TITLE;ALTID=1;LANGUAGE=fr:Patron\r\n"
 @@ -107,8 +130,7 @@ void ImportExportVCardTest::shouldExportFullTestVcard4()
      const KContacts::AddresseeList lst = vcard.parseVCards(vcarddata);
  
      const QByteArray result = vcard.exportVCards(lst, KContacts::VCard::v4_0);
 -    //qDebug() << " result " << result;
 -    QCOMPARE(result, vcardexpected);
 +    compareBuffers(result, vcardexpected);
  }
  
  void ImportExportVCardTest::shouldExportMiscElementVcard4()
 @@ -140,9 +162,7 @@ void ImportExportVCardTest::shouldExportMiscElementVcard4()
      const KContacts::AddresseeList lst = vcard.parseVCards(vcarddata);
  
      const QByteArray result = vcard.exportVCards(lst, KContacts::VCard::v4_0);
 -    //qDebug() << " result " << result;
 -    QCOMPARE(result, vcardexpected);
 -
 +    compareBuffers(result, vcardexpected);
  }
  
  void ImportExportVCardTest::shouldExportMemberElementVcard4()
 @@ -174,8 +194,9 @@ void ImportExportVCardTest::shouldExportMemberElementVcard4()
      const KContacts::AddresseeList lst = vcard.parseVCards(vcarddata);
  
      const QByteArray result = vcard.exportVCards(lst, KContacts::VCard::v4_0);
 -    //qDebug() << " result " << result;
 -    QCOMPARE(result, vcardexpected);
 +    compareBuffers(result, vcardexpected);
  }
  
 +// TODO please make this data driven before copy/pasting more methods here...
 +
  QTEST_MAIN(ImportExportVCardTest)
 diff --git a/autotests/testroundtrip.cpp b/autotests/testroundtrip.cpp
 index 77a9aca6..cc3a4aca 100644
 --- a/autotests/testroundtrip.cpp
 +++ b/autotests/testroundtrip.cpp
 @@ -99,6 +99,27 @@ void RoundtripTest::testVCardRoundtrip_data()
      }
  }
  
 +static void compareBuffers(const char *version, const QByteArray &outputData, const QByteArray &outputRefData)
 +{
 +    if (outputData != outputRefData) {
 +        qDebug() << " outputData " << outputData;
 +        qDebug() << " outputRefData " << outputRefData;
 +    }
 +    const QList<QByteArray> outputLines = outputData.split('\n');
 +    const QList<QByteArray> outputRefLines = outputRefData.split('\n');
 +    for (int i = 0; i < qMin(outputLines.count(), outputRefLines.count()); ++i) {
 +        const QByteArray actual = outputLines.at(i);
 +        const QByteArray expect = outputRefLines.at(i);
 +        if (actual != expect) {
 +            qCritical() << "Mismatch in" << version << "output line" << (i + 1);
 +            QCOMPARE(actual, expect);
 +            QCOMPARE(actual.count(), expect.count());
 +        }
 +    }
 +    QCOMPARE(outputLines.count(), outputRefLines.count());
 +    QCOMPARE(outputData.size(), outputRefData.size());
 +}
 +
  void RoundtripTest::testVCardRoundtrip()
  {
      QFETCH(QString, inputFile);
 @@ -128,23 +149,7 @@ void RoundtripTest::testVCardRoundtrip()
          QVERIFY(outputFile.open(QIODevice::ReadOnly));
  
          const QByteArray outputRefData = outputFile.readAll();
 -        QCOMPARE(outputData.size(), outputRefData.size());
 -
 -        const QList<QByteArray> outputLines = outputData.split('\n');
 -        const QList<QByteArray> outputRefLines = outputRefData.split('\n');
 -        QCOMPARE(outputLines.count(), outputRefLines.count());
 -        for (int i = 0; i < outputLines.count(); ++i) {
 -            const QByteArray actual = outputLines[i];
 -            const QByteArray expect = outputRefLines[i];
 -
 -            if (actual != expect) {
 -                qCritical() << "Mismatch in v2.1 output line" << (i + 1);
 -                QCOMPARE(actual.count(), expect.count());
 -
 -                qCritical() << "\nActual:" << actual << "\nExpect:" << expect;
 -                QCOMPARE(actual, expect);
 -            }
 -        }
 +        compareBuffers("v2.1", outputData, outputRefData);
      }
  
      if (!output3_0File.isEmpty()) {
 @@ -154,26 +159,7 @@ void RoundtripTest::testVCardRoundtrip()
          QVERIFY(outputFile.open(QIODevice::ReadOnly));
  
          const QByteArray outputRefData = outputFile.readAll();
 -        if (outputData.size() != outputRefData.size()) {
 -            qDebug() << " outputRefData " << outputRefData << endl;
 -            qDebug() << " outputData " << outputData;
 -        }
 -        const QList<QByteArray> outputLines = outputData.split('\n');
 -        const QList<QByteArray> outputRefLines = outputRefData.split('\n');
 -        for (int i = 0; i < qMin(outputLines.count(), outputRefLines.count()); ++i) {
 -            const QByteArray actual = outputLines[i];
 -            const QByteArray expect = outputRefLines[i];
 -
 -            if (actual != expect) {
 -                qCritical() << "Mismatch in v3.0 output line" << (i + 1);
 -
 -                qCritical() << "\nActual:" << actual << "\nExpect:" << expect;
 -                QCOMPARE(actual.count(), expect.count());
 -                QCOMPARE(actual, expect);
 -            }
 -        }
 -        QCOMPARE(outputLines.count(), outputRefLines.count());
 -        QCOMPARE(outputData.size(), outputRefData.size());
 +        compareBuffers("v3.0", outputData, outputRefData);
      }
  #if 0
      if (!output4_0File.isEmpty()) {
 @@ -183,28 +169,7 @@ void RoundtripTest::testVCardRoundtrip()
          QVERIFY(outputFile.open(QIODevice::ReadOnly));
  
          const QByteArray outputRefData = outputFile.readAll();
 -        if (outputData.size() != outputRefData.size()) {
 -            qDebug() << " outputRefData " << outputRefData << endl;
 -            qDebug() << " outputData " << outputData;
 -        }
 -        //QCOMPARE( outputData.size(), outputRefData.size() );
 -
 -        const QList<QByteArray> outputLines = outputData.split('\n');
 -        const QList<QByteArray> outputRefLines = outputRefData.split('\n');
 -        //QCOMPARE(outputLines.count(), outputRefLines.count());
 -
 -        for (int i = 0; i < outputLines.count(); ++i) {
 -            const QByteArray actual = outputLines[i];
 -            const QByteArray expect = outputRefLines[i];
 -
 -            if (actual != expect) {
 -                qCritical() << "Mismatch in v4.0 output line" << (i + 1);
 -
 -                qCritical() << "\nActual:" << actual << "\nExpect:" << expect;
 -                QCOMPARE(actual.count(), expect.count());
 -                QCOMPARE(actual, expect);
 -            }
 -        }
 +        compareBuffers("v4.0", outputData, outputRefData);
      }
  #endif
  }
 diff --git a/src/vcardparser/vcardparser.cpp b/src/vcardparser/vcardparser.cpp
 index a16f320f..bc91299c 100644
 --- a/src/vcardparser/vcardparser.cpp
 +++ b/src/vcardparser/vcardparser.cpp
 @@ -80,96 +80,152 @@ public:
      {
      }
  
 -    void parseLine(const QByteArray &currentLine, VCardLine &vCardLine);
 +    void parseLine(const QByteArray &currentLine, VCardLine *vCardLine);
 +
 +private:
 +    void addParameter(const QByteArray &paramKey, const QByteArray &paramValue);
  
  private:
      StringCache &m_cache;
      std::function<QByteArray()> m_fetchAnotherLine;
 +
 +    VCardLine *m_vCardLine;
 +    QByteArray m_encoding;
 +    QByteArray m_charset;
  };
  
 -void VCardLineParser::parseLine(const QByteArray& currentLine, KContacts::VCardLine& vCardLine)
 +void VCardLineParser::addParameter(const QByteArray& paramKey, const QByteArray& paramValue)
  {
 -    // ### The syntax is key:value, but the key can contain semicolon-separated parameters, which can contain a ':', so indexOf(':') is wrong.
 -    // EXAMPLE: "ADR;GEO=\"geo:22.500000,45.099998\";LABEL=\"My Label\";TYPE=home:P.O. Box 101;;;Any Town;CA;91921-1234;
 -    const int colon = currentLine.indexOf(':');
 -    if (colon == -1) {   // invalid line
 -        return;
 -    }
 -    const QByteArray key = currentLine.left(colon).trimmed();
 -    QByteArray value = currentLine.mid(colon + 1);
 -    const QList<QByteArray> params = key.split(';');
 -    //qDebug() << "key=" << QString::fromLatin1(key) << "params=" << params;
 -    // check for group
 -    const QByteArray firstParam = params.at(0);
 -    const int groupPos = firstParam.indexOf('.');
 -    if (groupPos != -1) {
 -        vCardLine.setGroup(m_cache.fromLatin1(firstParam.left(groupPos)));
 -        vCardLine.setIdentifier(m_cache.fromLatin1(firstParam.mid(groupPos + 1)));
 -        //qDebug() << "group" << vCardLine.group() << "identifier" << vCardLine.identifier();
 -    } else {
 -        vCardLine.setIdentifier(m_cache.fromLatin1(firstParam));
 -        //qDebug() << "identifier" << vCardLine.identifier();
 +    if (paramKey == "encoding") {
 +        m_encoding = paramValue.toLower();
 +    } else if (paramKey == "charset") {
 +        m_charset = paramValue.toLower();
      }
 +    //qDebug() << "  add parameter" << paramKey << "    =    " << paramValue;
 +    m_vCardLine->addParameter(m_cache.fromLatin1(paramKey), m_cache.fromLatin1(paramValue));
 +}
  
 -    if (params.count() > 1) {   // find all parameters
 -        QList<QByteArray>::ConstIterator paramIt(params.constBegin());
 -        for (++paramIt; paramIt != params.constEnd(); ++paramIt) {
 -            //qDebug() << "param" << QString::fromLatin1(*paramIt);
 -            QList<QByteArray> pair = (*paramIt).split('=');
 -            QByteArray first = pair.at(0).toLower();
 -            if (pair.count() == 1) {
 -                // correct the fucking 2.1 'standard'
 -                if (first == "quoted-printable") {
 -                    pair[ 0 ] = "encoding";
 -                    pair.append("quoted-printable");
 -                } else if (first == "base64") {
 -                    pair[ 0 ] = "encoding";
 -                    pair.append("base64");
 +void VCardLineParser::parseLine(const QByteArray& currentLine, KContacts::VCardLine* vCardLine)
 +{
 +    //qDebug() << currentLine;
 +    m_vCardLine = vCardLine;
 +    // The syntax is key:value, but the key can contain semicolon-separated parameters, which can contain a ':', so indexOf(':') is wrong.
 +    // EXAMPLE: ADR;GEO="geo:22.500000,45.099998";LABEL="My Label";TYPE=home:P.O. Box 101;;;Any Town;CA;91921-1234;
 +    // Therefore we need a small state machine, just the way I like it.
 +    enum State { StateInitial, StateParamKey, StateParamValue, StateQuotedValue, StateAfterParamValue, StateValue };
 +    State state = StateInitial;
 +    const int lineLength = currentLine.length();
 +    const char *lineData = currentLine.constData(); // to skip length checks from at() in debug mode
 +    QByteArray paramKey;
 +    QByteArray paramValue;
 +    int start = 0;
 +    int pos = 0;
 +    for (; pos < lineLength; ++pos) {
 +        const char ch = lineData[pos];
 +        const bool colonOrSemicolon = (ch == ';' || ch == ':');
 +        switch (state) {
 +        case StateInitial:
 +            if (colonOrSemicolon) {
 +                const QByteArray identifier = currentLine.mid(start, pos - start);
 +                //qDebug() << " identifier" << identifier;
 +                vCardLine->setIdentifier(m_cache.fromLatin1(identifier));
 +                start = pos + 1;
 +            }
 +            if (ch == ';') {
 +                state = StateParamKey;
 +            } else if (ch == ':') {
 +                state = StateValue;
 +            } else if (ch == '.') {
 +                vCardLine->setGroup(m_cache.fromLatin1(currentLine.mid(start, pos - start)));
 +                start = pos + 1;
 +            }
 +            break;
 +        case StateParamKey:
 +            if (colonOrSemicolon || ch == '=') {
 +                paramKey = currentLine.mid(start, pos - start);
 +                start = pos + 1;
 +            }
 +            if (colonOrSemicolon) {
 +                // correct the so-called 2.1 'standard'
 +                paramValue = paramKey;
 +                const QByteArray lowerKey = paramKey.toLower();
 +                if (lowerKey == "quoted-printable" || lowerKey == "base64") {
 +                    paramKey = "encoding";
                  } else {
 -                    pair.prepend("type");
 +                    paramKey = "type";
                  }
 -                first = pair.at(0);
 +                addParameter(paramKey, paramValue);
              }
 -            const QByteArray second = pair.at(1);
 -            if (second.contains(',')) {     // parameter in type=x,y,z format
 -                const QList<QByteArray> args = second.split(',');
 -                for (QByteArray tmpArg : args) {
 -                    if (tmpArg.startsWith('"')) {
 -                        tmpArg = tmpArg.mid(1);
 -                    }
 -                    if (tmpArg.endsWith('"')) {
 -                        tmpArg.chop(1);
 -                    }
 -                    vCardLine.addParameter(m_cache.fromLatin1(first),
 -                                           m_cache.fromLatin1(tmpArg));
 +            if (ch == ';') {
 +                state = StateParamKey;
 +            } else if (ch == ':') {
 +                state = StateValue;
 +            } else if (ch == '=') {
 +                state = StateParamValue;
 +            }
 +            break;
 +        case StateQuotedValue:
 +            if (ch == '"' || (ch == ',' && paramKey.toLower() == "type")) {
 +                // TODO the hack above is for TEL;TYPE=\"voice,home\":... without breaking GEO.... TODO: check spec
 +                paramValue = currentLine.mid(start, pos - start);
 +                addParameter(paramKey.toLower(), paramValue);
 +                start = pos + 1;
 +                if (ch == '"') {
 +                    state = StateAfterParamValue; // to avoid duplicating code with StateParamValue, we use this intermediate state for one char
                  }
 -            } else {
 -                vCardLine.addParameter(m_cache.fromLatin1(first),
 -                                       m_cache.fromLatin1(second));
              }
 +            break;
 +        case StateParamValue:
 +            if (colonOrSemicolon || ch == ',') {
 +                paramValue = currentLine.mid(start, pos - start);
 +                addParameter(paramKey.toLower(), paramValue);
 +                start = pos + 1;
 +            }
 +            // fall-through intended
 +        case StateAfterParamValue:
 +            if (ch == ';') {
 +                state = StateParamKey;
 +                start = pos + 1;
 +            } else if (ch == ':') {
 +                state = StateValue;
 +            } else if (pos == start && ch == '"') { // don't treat foo"bar" as quoted - TODO check the vCard 3.0 spec.
 +                state = StateQuotedValue;
 +                start = pos + 1;
 +            }
 +            break;
 +        case StateValue:
 +            Q_UNREACHABLE();
 +            break;
 +        }
 +
 +        if (state == StateValue) {
 +            break;
          }
      }
  
 +    if (state != StateValue) {   // invalid line, no ':'
 +        return;
 +    }
 +
 +    QByteArray value = currentLine.mid(pos + 1);
      removeEscapes(value);
  
      QByteArray output;
      bool wasBase64Encoded = false;
  
 -    const QString encoding = vCardLine.parameter(QStringLiteral("encoding")).toLower();
 -    if (!encoding.isEmpty()) {
 -
 +    if (!m_encoding.isEmpty()) {
          // have to decode the data
 -        if (encoding == QLatin1String("b") || encoding == QLatin1String("base64")) {
 +        if (m_encoding == "b" || m_encoding == "base64") {
              output = QByteArray::fromBase64(value);
              wasBase64Encoded = true;
 -        } else if (encoding == QLatin1String("quoted-printable")) {
 +        } else if (m_encoding == "quoted-printable") {
              // join any qp-folded lines
              while (value.endsWith('=')) {
                  value.chop(1);   // remove the '='
                  value.append(m_fetchAnotherLine());
              }
              KCodecs::quotedPrintableDecode(value, output);
 -        } else if (encoding == QLatin1String("8bit")) {
 +        } else if (m_encoding == "8bit") {
              output = value;
          } else {
              qDebug("Unknown vcard encoding type!");
 @@ -178,22 +234,23 @@ void VCardLineParser::parseLine(const QByteArray& currentLine, KContacts::VCardL
          output = value;
      }
  
 -    const QString charset = vCardLine.parameter(QStringLiteral("charset"));
 -    if (!charset.isEmpty()) {
 +    if (!m_charset.isEmpty()) {
          // have to convert the data
 -        QTextCodec *codec = QTextCodec::codecForName(charset.toLatin1());
 +        QTextCodec *codec = QTextCodec::codecForName(m_charset);
          if (codec) {
 -            vCardLine.setValue(codec->toUnicode(output));
 +            vCardLine->setValue(codec->toUnicode(output));
          } else {
 -            vCardLine.setValue(QString::fromUtf8(output));
 +            vCardLine->setValue(QString::fromUtf8(output));
          }
      } else if (wasBase64Encoded) {
 -        vCardLine.setValue(output);
 +        vCardLine->setValue(output);
      } else {
 -        vCardLine.setValue(QString::fromUtf8(output));
 +        vCardLine->setValue(QString::fromUtf8(output));
      }
  }
  
 +////
 +
  VCardParser::VCardParser()
      : d(nullptr)
  {
 @@ -248,7 +305,7 @@ VCard::List VCardParser::parseVCards(const QByteArray &text)
  
                  VCardLineParser lineParser(cache, fetchAnotherLine);
  
 -                lineParser.parseLine(currentLine, vCardLine);
 +                lineParser.parseLine(currentLine, &vCardLine);
  
                  currentVCard.addLine(vCardLine);
              }
-
-
\ No newline at end of file + diff --git a/plugins/messageviewer/bodypartformatter/autotests/diffdata/test1-ref.diff b/plugins/messageviewer/bodypartformatter/autotests/diffdata/test1-ref.diff index 53cad71d..1a15117a 100644 --- a/plugins/messageviewer/bodypartformatter/autotests/diffdata/test1-ref.diff +++ b/plugins/messageviewer/bodypartformatter/autotests/diffdata/test1-ref.diff @@ -1,15 +1,14 @@ -
diff --git a/plugins/messageviewer/bodypartformatter/ms-tnef/application_ms-tnef.cpp b/plugins/messageviewer/bodypartformatter/ms-tnef/application_ms-tnef.cpp
+
diff --git a/plugins/messageviewer/bodypartformatter/ms-tnef/application_ms-tnef.cpp b/plugins/messageviewer/bodypartformatter/ms-tnef/application_ms-tnef.cpp
 index b69f56c0..2cc4ab92 100644
 --- a/plugins/messageviewer/bodypartformatter/ms-tnef/application_ms-tnef.cpp
 +++ b/plugins/messageviewer/bodypartformatter/ms-tnef/application_ms-tnef.cpp
 @@ -166,7 +166,7 @@ public:
                  attFileName = att->name();
              }
              bodyPart->nodeHelper()->addTempFile(dir + QDir::separator() + attFileName);
 -            const QString href = QStringLiteral("file:") + QString::fromLatin1(QUrl::toPercentEncoding(dir + QDir::separator() + att->name()));
 +            const QString href = QStringLiteral("file:") + dir + QDir::separator() + attFileName;
  
              const QString iconName = QUrl::fromLocalFile(MessageViewer::Util::iconPathForMimetype(att->mimeTag(),
                                                                                                    KIconLoader::Desktop, attFileName)).url();
-
-
\ No newline at end of file + diff --git a/plugins/messageviewer/bodypartformatter/autotests/diffhighlightertest.cpp b/plugins/messageviewer/bodypartformatter/autotests/diffhighlightertest.cpp index 3b62961b..d755a97e 100644 --- a/plugins/messageviewer/bodypartformatter/autotests/diffhighlightertest.cpp +++ b/plugins/messageviewer/bodypartformatter/autotests/diffhighlightertest.cpp @@ -1,80 +1,86 @@ /* Copyright (C) 2017 Montel Laurent 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 MERCHANTABILITY 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; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "diffhighlightertest.h" #include "../xdiff/diffhighlighter.h" #include +#include #include DiffHighlighterTest::DiffHighlighterTest(QObject *parent) : QObject(parent) { + QStandardPaths::setTestModeEnabled(true); + // trick the highlighter into using the light color theme + auto pal = QGuiApplication::palette(); + pal.setColor(QPalette::Base, Qt::white); + QGuiApplication::setPalette(pal); } QString readDiffFile(const QString &diffFile) { QFile file(diffFile); file.open(QIODevice::ReadOnly); Q_ASSERT(file.isOpen()); const QString data = QString::fromUtf8(file.readAll()); return data; } void DiffHighlighterTest::shouldGenerateDiff_data() { QTest::addColumn("input"); QTest::newRow("test1") << QStringLiteral("test1"); QTest::newRow("kcontact1") << QStringLiteral("kcontact1"); QTest::newRow("diff-akonadiconsole-16.12-master") << QStringLiteral("diff-akonadiconsole-16.12-master"); } void DiffHighlighterTest::shouldGenerateDiff() { QFETCH(QString, input); const QString originalFile = QStringLiteral(DIFF_DATA_DIR) + QLatin1Char('/') + input + QStringLiteral(".diff"); const QString refFile = QStringLiteral(DIFF_DATA_DIR) + QLatin1Char('/') + input + QStringLiteral("-ref.diff"); const QString generatedFile = QStringLiteral(DIFF_DATA_DIR) + QLatin1Char('/') + input + QStringLiteral("-generated.diff"); QString diff = readDiffFile(originalFile); DiffHighlighter highLighter; highLighter.highlightDiff(diff); const QString html = highLighter.outputDiff(); //Create generated file QFile f(generatedFile); QVERIFY(f.open(QIODevice::WriteOnly | QIODevice::Truncate)); f.write(html.toUtf8()); f.close(); // compare to reference file QStringList args = QStringList() << QStringLiteral("-u") << refFile << generatedFile; QProcess proc; proc.setProcessChannelMode(QProcess::ForwardedChannels); proc.start(QStringLiteral("diff"), args); QVERIFY(proc.waitForFinished()); QCOMPARE(proc.exitCode(), 0); } QTEST_MAIN(DiffHighlighterTest) diff --git a/plugins/messageviewer/bodypartformatter/xdiff/CMakeLists.txt b/plugins/messageviewer/bodypartformatter/xdiff/CMakeLists.txt index 00f8240f..a31dde4b 100644 --- a/plugins/messageviewer/bodypartformatter/xdiff/CMakeLists.txt +++ b/plugins/messageviewer/bodypartformatter/xdiff/CMakeLists.txt @@ -1,9 +1,14 @@ add_definitions(-DTRANSLATION_DOMAIN=\"messageviewer_text_xdiff_plugin\") set(messageviewer_bodypartformatter_text_xdiff_PART_SRCS text_xdiff.cpp diffhighlighter.cpp) add_library(messageviewer_bodypartformatter_text_xdiff MODULE ${messageviewer_bodypartformatter_text_xdiff_PART_SRCS}) -target_link_libraries(messageviewer_bodypartformatter_text_xdiff Qt5::Core KF5::I18n KF5::CoreAddons KF5::IconThemes KF5::WidgetsAddons KF5::MessageViewer KF5::SyntaxHighlighting) +target_link_libraries(messageviewer_bodypartformatter_text_xdiff + Qt5::Core + KF5::MessageViewer + KF5::SyntaxHighlighting + Grantlee5::Templates +) install(TARGETS messageviewer_bodypartformatter_text_xdiff DESTINATION ${KDE_INSTALL_PLUGINDIR}/messageviewer/bodypartformatter) diff --git a/plugins/messageviewer/bodypartformatter/xdiff/diffhighlighter.cpp b/plugins/messageviewer/bodypartformatter/xdiff/diffhighlighter.cpp index 3e3a18cf..d7379f1e 100644 --- a/plugins/messageviewer/bodypartformatter/xdiff/diffhighlighter.cpp +++ b/plugins/messageviewer/bodypartformatter/xdiff/diffhighlighter.cpp @@ -1,103 +1,95 @@ /* Copyright (C) 2017 Montel Laurent 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 MERCHANTABILITY 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; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "diffhighlighter.h" #include #include #include + +#include #include DiffHighlighter::DiffHighlighter() { mDef = mRepo.definitionForName(QStringLiteral("Diff")); setDefinition(mDef); - setTheme(/*(palette().color(QPalette::Base).lightness() < 128) - ? mRepo.defaultTheme(KSyntaxHighlighting::Repository::DarkTheme)*/ - /*:*/ mRepo.defaultTheme(KSyntaxHighlighting::Repository::LightTheme)); + setTheme(QGuiApplication::palette().color(QPalette::Base).lightness() < 128 + ? mRepo.defaultTheme(KSyntaxHighlighting::Repository::DarkTheme) + : mRepo.defaultTheme(KSyntaxHighlighting::Repository::LightTheme)); } DiffHighlighter::~DiffHighlighter() { } void DiffHighlighter::highlightDiff(const QString &str) { mOutputDiff.clear(); - - static const QLatin1String tableStyle( - "style=\"" - "text-align: left; " - "border: solid black 1px; " - "padding: 0.5em; " - "margin: 0em;\""); - - mOutputDiff = QStringLiteral("
"); - mOutputDiff += QStringLiteral("
');
+    mOutputDiff = QStringLiteral("
");
 
     KSyntaxHighlighting::State state;
 
     int lineStart = 0;
     int lineEnd = str.indexOf(QLatin1Char('\n'));
 
     for (; lineEnd != -1; lineStart = lineEnd + 1, lineEnd = str.indexOf(QLatin1Char('\n'), lineStart)) {
         mCurrentLine = str.mid(lineStart, lineEnd - lineStart);
         state = highlightLine(mCurrentLine, state);
         mOutputDiff += QLatin1Char('\n');
     }
-    mOutputDiff += QLatin1Char('\n');
-    mOutputDiff += QLatin1String("
"); + mOutputDiff += QLatin1String("\n"); } void DiffHighlighter::applyFormat(int offset, int length, const KSyntaxHighlighting::Format &format) { if (!format.isDefaultTextStyle(theme())) { mOutputDiff += QStringLiteral(""); } mOutputDiff += mCurrentLine.mid(offset, length).toHtmlEscaped(); if (!format.isDefaultTextStyle(theme())) { mOutputDiff += QStringLiteral(""); } } QString DiffHighlighter::outputDiff() const { return mOutputDiff; } diff --git a/plugins/messageviewer/bodypartformatter/xdiff/text_xdiff.cpp b/plugins/messageviewer/bodypartformatter/xdiff/text_xdiff.cpp index ec5b3fd2..5aff1b4e 100644 --- a/plugins/messageviewer/bodypartformatter/xdiff/text_xdiff.cpp +++ b/plugins/messageviewer/bodypartformatter/xdiff/text_xdiff.cpp @@ -1,91 +1,93 @@ /* This file is part of kdepim. Copyright (c) 2004 Till Adam 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 MERCHANTABILITY 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 In addition, as a special exception, the copyright holders give permission to link the code of this program with any edition of the Qt library by Trolltech AS, Norway (or with modified versions of Qt that use the same license as Qt), and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than Qt. If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ #include "diffhighlighter.h" -#include -#include -#include +#include +#include +#include + +#include #include -#include +#include namespace { -// TODO: Show filename header to make it possible to save the patch. // FIXME: The box should only be as wide as necessary. -class Formatter : public MimeTreeParser::Interface::BodyPartFormatter +class Formatter : public MessageViewer::MessagePartRendererBase { public: - Result format(MimeTreeParser::Interface::BodyPart *bodyPart, MimeTreeParser::HtmlWriter *writer) const override + bool render(const MimeTreeParser::MessagePartPtr &msgPart, MimeTreeParser::HtmlWriter *htmlWriter, MessageViewer::RenderContext *context) const override { - if (!writer) { - return Ok; - } - - if (bodyPart->defaultDisplay() == MimeTreeParser::Interface::BodyPart::AsIcon) { - return AsIcon; - } - - const QString diff = bodyPart->asText(); - if (diff.isEmpty()) { - return AsIcon; - } - - DiffHighlighter highLighter; - highLighter.highlightDiff(diff); - const QString html = highLighter.outputDiff(); - writer->write(html); - - return Ok; + Q_UNUSED(context); + auto mp = msgPart.dynamicCast(); + if (!mp || mp->isHidden() || mp->text().isEmpty() || mp->asIcon() != MimeTreeParser::NoIcon) + return false; + + const bool diffMimeType = msgPart->content()->contentType()->mimeType() == "text/x-patch" + || msgPart->content()->contentType()->mimeType() == "text/x-diff"; + const bool diffFileName = msgPart->content()->contentType()->name().endsWith(QLatin1String(".diff")) + || msgPart->content()->contentType()->name().endsWith(QLatin1String(".patch")); + if (!diffMimeType && !diffFileName) + return false; + + auto c = MessageViewer::MessagePartRendererManager::self()->createContext(); + c.insert(QStringLiteral("block"), msgPart.data()); + c.insert(QStringLiteral("content"), QVariant::fromValue([msgPart](Grantlee::OutputStream *stream) { + DiffHighlighter highLighter; + highLighter.highlightDiff(msgPart->text()); + *stream << highLighter.outputDiff(); + })); + + auto t = MessageViewer::MessagePartRendererManager::self()->loadByName(QStringLiteral(":/textmessagepart.html")); + Grantlee::OutputStream s(htmlWriter->stream()); + t->render(&s, &c); + return true; } - - // unhide the overload with three arguments - using MimeTreeParser::Interface::BodyPartFormatter::format; }; -class Plugin : public QObject, public MimeTreeParser::Interface::BodyPartFormatterPlugin +class Plugin : public QObject, public MessageViewer::MessagePartRenderPlugin { Q_OBJECT - Q_INTERFACES(MimeTreeParser::Interface::BodyPartFormatterPlugin) + Q_INTERFACES(MessageViewer::MessagePartRenderPlugin) Q_PLUGIN_METADATA(IID "com.kde.messageviewer.bodypartformatter" FILE "text_xdiff.json") public: - const MimeTreeParser::Interface::BodyPartFormatter *bodyPartFormatter(int idx) const override + MessageViewer::MessagePartRendererBase* renderer(int index) override { - if (idx == 0) { + if (index == 0) return new Formatter(); - } return nullptr; } }; } #include "text_xdiff.moc" diff --git a/plugins/messageviewer/bodypartformatter/xdiff/text_xdiff.json b/plugins/messageviewer/bodypartformatter/xdiff/text_xdiff.json index 50417141..4d063207 100644 --- a/plugins/messageviewer/bodypartformatter/xdiff/text_xdiff.json +++ b/plugins/messageviewer/bodypartformatter/xdiff/text_xdiff.json @@ -1,5 +1,3 @@ -{ "formatter": - [ - { "mimetype": "text/x-patch" } - ] +{ + "renderer": [ { "type": "MimeTreeParser::AttachmentMessagePart" } ] }