diff --git a/Plugins/org.blueberry.ui.qt.help/src/internal/berryHelpIndexView.cpp b/Plugins/org.blueberry.ui.qt.help/src/internal/berryHelpIndexView.cpp
index d027955804..77a22144d0 100644
--- a/Plugins/org.blueberry.ui.qt.help/src/internal/berryHelpIndexView.cpp
+++ b/Plugins/org.blueberry.ui.qt.help/src/internal/berryHelpIndexView.cpp
@@ -1,310 +1,325 @@
 /*============================================================================
 
 The Medical Imaging Interaction Toolkit (MITK)
 
 Copyright (c) German Cancer Research Center (DKFZ)
 All rights reserved.
 
 Use of this source code is governed by a 3-clause BSD license that can be
 found in the LICENSE file.
 
 ============================================================================*/
 
 #include "berryHelpIndexView.h"
 
 #include "berryHelpPluginActivator.h"
 #include "berryHelpEditor.h"
 #include "berryHelpEditorInput.h"
 #include "berryQHelpEngineWrapper.h"
 #include "berryHelpTopicChooser.h"
 
 #include <berryIWorkbenchPage.h>
 
 #include <ctkSearchBox.h>
 
 #include <QHelpIndexWidget>
+#include <QHelpLink>
 #include <QLayout>
 #include <QLabel>
 #include <QMenu>
 #include <QKeyEvent>
 
 namespace berry {
 HelpIndexWidget::HelpIndexWidget()
   : QListView(nullptr)
 {
   setEditTriggers(QAbstractItemView::NoEditTriggers);
   setUniformItemSizes(true);
   connect(this, SIGNAL(activated(QModelIndex)),
           this, SLOT(showLink(QModelIndex)));
 }
 
 void HelpIndexWidget::showLink(const QModelIndex &index)
 {
   if (!index.isValid())
     return;
 
   QHelpIndexModel *indexModel = qobject_cast<QHelpIndexModel*>(model());
   if (!indexModel)
     return;
   QVariant v = indexModel->data(index, Qt::DisplayRole);
   QString name;
   if (v.isValid())
     name = v.toString();
 
-  QMap<QString, QUrl> links = indexModel->linksForKeyword(name);
+  QHelpEngineWrapper& helpEngine = HelpPluginActivator::getInstance()->getQHelpEngine();
+  auto links = helpEngine.documentsForKeyword(name);
+
   if (links.count() == 1)
   {
-    emit linkActivated(links.constBegin().value(), name);
+    emit linkActivated(links[0].url, name);
   }
   else if (links.count() > 1)
   {
-    emit linksActivated(links, name);
+    QMap<QString, QUrl> legacyLinks;
+
+    for (const auto& link : links)
+      legacyLinks.insert(link.title, link.url);
+
+    emit linksActivated(legacyLinks, name);
   }
 }
 
 void HelpIndexWidget::activateCurrentItem()
 {
   showLink(currentIndex());
 }
 
 void HelpIndexWidget::filterIndices(const QString &filter, const QString &wildcard)
 {
   QHelpIndexModel *indexModel = qobject_cast<QHelpIndexModel*>(model());
   if (!indexModel)
     return;
   QModelIndex idx = indexModel->filter(filter, wildcard);
   if (idx.isValid())
     setCurrentIndex(idx);
 }
 
 HelpIndexView::HelpIndexView()
   : m_IndexWidget(nullptr)
 {
 }
 
 HelpIndexView::~HelpIndexView()
 {
 }
 
 void HelpIndexView::CreateQtPartControl(QWidget* parent)
 {
   if (m_IndexWidget == nullptr)
   {
     auto  layout = new QVBoxLayout(parent);
     //QLabel *l = new QLabel(tr("&Look for:"));
     //layout->addWidget(l);
 
     m_SearchLineEdit = new ctkSearchBox(parent);
     m_SearchLineEdit->setClearIcon(QIcon(":/org.blueberry.ui.qt.help/clear.png"));
     m_SearchLineEdit->setPlaceholderText("Filter...");
     m_SearchLineEdit->setContentsMargins(2,2,2,0);
     //l->setBuddy(m_SearchLineEdit);
     connect(m_SearchLineEdit, SIGNAL(textChanged(QString)), this,
             SLOT(filterIndices(QString)));
     m_SearchLineEdit->installEventFilter(this);
     layout->setContentsMargins({});
     layout->setSpacing(2);
     layout->addWidget(m_SearchLineEdit);
 
     QHelpEngineWrapper& helpEngine = HelpPluginActivator::getInstance()->getQHelpEngine();
     m_IndexWidget = new HelpIndexWidget();
     m_IndexWidget->setModel(helpEngine.indexModel());
     connect(helpEngine.indexModel(), SIGNAL(indexCreationStarted()),
             this, SLOT(setIndexWidgetBusy()));
     connect(helpEngine.indexModel(), SIGNAL(indexCreated()),
             this, SLOT(unsetIndexWidgetBusy()));
     m_IndexWidget->installEventFilter(this);
 
     connect(helpEngine.indexModel(), SIGNAL(indexCreationStarted()), this,
             SLOT(disableSearchLineEdit()));
     connect(helpEngine.indexModel(), SIGNAL(indexCreated()), this,
             SLOT(enableSearchLineEdit()));
     connect(m_IndexWidget, SIGNAL(linkActivated(QUrl,QString)), this,
             SLOT(linkActivated(QUrl)));
     connect(m_IndexWidget, SIGNAL(linksActivated(QMap<QString,QUrl>,QString)),
             this, SLOT(linksActivated(QMap<QString,QUrl>,QString)));
     connect(m_SearchLineEdit, SIGNAL(returnPressed()), m_IndexWidget,
             SLOT(activateCurrentItem()));
     layout->addWidget(m_IndexWidget);
 
     m_IndexWidget->viewport()->installEventFilter(this);
   }
 }
 
 void HelpIndexView::SetFocus()
 {
   if (!(m_IndexWidget->hasFocus() || m_SearchLineEdit->hasFocus()))
   {
     m_SearchLineEdit->setFocus();
   }
 }
 
 void HelpIndexView::filterIndices(const QString &filter)
 {
   if (filter.contains(QLatin1Char('*')))
     m_IndexWidget->filterIndices(filter, filter);
   else
     m_IndexWidget->filterIndices(filter, QString());
 }
 
 bool HelpIndexView::eventFilter(QObject *obj, QEvent *e)
 {
   if (obj == m_SearchLineEdit && e->type() == QEvent::KeyPress)
   {
     QKeyEvent *ke = static_cast<QKeyEvent*>(e);
     QModelIndex idx = m_IndexWidget->currentIndex();
     switch (ke->key())
     {
     case Qt::Key_Up:
       idx = m_IndexWidget->model()->index(idx.row()-1,
                                           idx.column(), idx.parent());
       if (idx.isValid())
       {
         m_IndexWidget->setCurrentIndex(idx);
         return true;
       }
       break;
     case Qt::Key_Down:
       idx = m_IndexWidget->model()->index(idx.row()+1,
                                           idx.column(), idx.parent());
       if (idx.isValid())
       {
         m_IndexWidget->setCurrentIndex(idx);
         return true;
       }
       break;
     default: ; // stop complaining
     }
   }
   else if (obj == m_IndexWidget && e->type() == QEvent::ContextMenu)
   {
     QContextMenuEvent *ctxtEvent = static_cast<QContextMenuEvent*>(e);
     QModelIndex idx = m_IndexWidget->indexAt(ctxtEvent->pos());
     if (idx.isValid())
     {
       QMenu menu;
       QAction *curTab = menu.addAction(tr("Open Link"));
       QAction *newTab = menu.addAction(tr("Open Link in New Tab"));
       menu.move(m_IndexWidget->mapToGlobal(ctxtEvent->pos()));
 
       QAction *action = menu.exec();
       if (curTab == action)
         m_IndexWidget->activateCurrentItem();
       else if (newTab == action)
       {
         open(m_IndexWidget, idx);
       }
     }
   }
   else if (m_IndexWidget && obj == m_IndexWidget->viewport()
            && e->type() == QEvent::MouseButtonRelease)
   {
     QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(e);
     QModelIndex idx = m_IndexWidget->indexAt(mouseEvent->pos());
     if (idx.isValid())
     {
       Qt::MouseButtons button = mouseEvent->button();
       if (((button == Qt::LeftButton) && (mouseEvent->modifiers() & Qt::ControlModifier))
-          || (button == Qt::MidButton))
+          || (button == Qt::MiddleButton))
       {
         open(m_IndexWidget, idx);
       }
     }
   }
 #ifdef Q_OS_MAC
   else if (obj == m_IndexWidget && e->type() == QEvent::KeyPress)
   {
     QKeyEvent *ke = static_cast<QKeyEvent*>(e);
     if (ke->key() == Qt::Key_Return || ke->key() == Qt::Key_Enter)
       m_IndexWidget->activateCurrentItem();
   }
 #endif
   return QObject::eventFilter(obj, e);
 }
 
 void HelpIndexView::enableSearchLineEdit()
 {
   m_SearchLineEdit->setDisabled(false);
   filterIndices(m_SearchLineEdit->text());
 }
 
 void HelpIndexView::disableSearchLineEdit()
 {
   m_SearchLineEdit->setDisabled(true);
 }
 
 void HelpIndexView::setIndexWidgetBusy()
 {
   m_IndexWidget->setCursor(Qt::WaitCursor);
 }
 
 void HelpIndexView::unsetIndexWidgetBusy()
 {
   m_IndexWidget->unsetCursor();
 }
 
 void HelpIndexView::setSearchLineEditText(const QString &text)
 {
   m_SearchLineEdit->setText(text);
 }
 
 QString HelpIndexView::searchLineEditText() const
 {
   return m_SearchLineEdit->text();
 }
 
 void HelpIndexView::focusInEvent(QFocusEvent *e)
 {
   if (e->reason() != Qt::MouseFocusReason)
   {
     m_SearchLineEdit->selectAll();
     m_SearchLineEdit->setFocus();
   }
 }
 
 void HelpIndexView::open(HelpIndexWidget* indexWidget, const QModelIndex &index)
 {
   QHelpIndexModel *model = qobject_cast<QHelpIndexModel*>(indexWidget->model());
   if (model)
   {
     QString keyword = model->data(index, Qt::DisplayRole).toString();
-    QMap<QString, QUrl> links = model->linksForKeyword(keyword);
+
+    QHelpEngineWrapper& helpEngine = HelpPluginActivator::getInstance()->getQHelpEngine();
+    auto links = helpEngine.documentsForKeyword(keyword);
 
     QUrl url;
     if (links.count() > 1)
     {
-      HelpTopicChooser tc(m_IndexWidget, keyword, links);
+      QMap<QString, QUrl> legacyLinks;
+
+      for (const auto& link : links)
+        legacyLinks.insert(link.title, link.url);
+
+      HelpTopicChooser tc(m_IndexWidget, keyword, legacyLinks);
       if (tc.exec() == QDialog::Accepted)
         url = tc.link();
     }
     else if (links.count() == 1)
     {
-      url = links.constBegin().value();
+      url = links[0].url;
     }
     else
     {
       return;
     }
 
     IEditorInput::Pointer input(new HelpEditorInput(url));
     this->GetSite()->GetPage()->OpenEditor(input, HelpEditor::EDITOR_ID);
   }
 }
 
 void HelpIndexView::linkActivated(const QUrl& link)
 {
   IWorkbenchPage::Pointer page = this->GetSite()->GetPage();
 
   HelpPluginActivator::linkActivated(page, link);
 }
 
 void HelpIndexView::linksActivated(const QMap<QString,QUrl>& links, const QString& keyword)
 {
   HelpTopicChooser tc(m_IndexWidget, keyword, links);
   if (tc.exec() == QDialog::Accepted)
   {
     IWorkbenchPage::Pointer page = this->GetSite()->GetPage();
     HelpPluginActivator::linkActivated(page, tc.link());
   }
 }
 }
diff --git a/Plugins/org.blueberry.ui.qt.help/src/internal/berryHelpPluginActivator.cpp b/Plugins/org.blueberry.ui.qt.help/src/internal/berryHelpPluginActivator.cpp
index 1ef2c93bca..38acff3ef9 100644
--- a/Plugins/org.blueberry.ui.qt.help/src/internal/berryHelpPluginActivator.cpp
+++ b/Plugins/org.blueberry.ui.qt.help/src/internal/berryHelpPluginActivator.cpp
@@ -1,474 +1,475 @@
 /*============================================================================
 
 The Medical Imaging Interaction Toolkit (MITK)
 
 Copyright (c) German Cancer Research Center (DKFZ)
 All rights reserved.
 
 Use of this source code is governed by a 3-clause BSD license that can be
 found in the LICENSE file.
 
 ============================================================================*/
 
 #include "berryHelpPluginActivator.h"
 
 #include "berryHelpContentView.h"
 #include "berryHelpIndexView.h"
 #include "berryHelpSearchView.h"
 #include "berryHelpEditor.h"
 #include "berryHelpEditorInput.h"
 #include "berryHelpEditorInputFactory.h"
 #include "berryHelpPerspective.h"
 
 #include "berryQHelpEngineConfiguration.h"
 #include "berryQHelpEngineWrapper.h"
 
 #include <berryPlatformUI.h>
 
 #include <service/event/ctkEventConstants.h>
 
 #include <QDir>
 #include <QDateTime>
 
 namespace berry {
 
 class HelpPerspectiveListener : public IPerspectiveListener
 {
 public:
 
   Events::Types GetPerspectiveEventTypes() const override;
 
   using IPerspectiveListener::PerspectiveChanged;
 
   void PerspectiveOpened(const SmartPointer<IWorkbenchPage>& page, const IPerspectiveDescriptor::Pointer& perspective) override;
   void PerspectiveChanged(const SmartPointer<IWorkbenchPage>& page, const IPerspectiveDescriptor::Pointer& perspective, const QString &changeId) override;
 };
 
 class HelpWindowListener : public IWindowListener
 {
 public:
 
   HelpWindowListener();
   ~HelpWindowListener() override;
 
   void WindowClosed(const IWorkbenchWindow::Pointer& window) override;
   void WindowOpened(const IWorkbenchWindow::Pointer& window) override;
 
 private:
 
   // We use the same perspective listener for every window
   QScopedPointer<IPerspectiveListener> perspListener;
 };
 
 
 HelpPluginActivator* HelpPluginActivator::instance = nullptr;
 
 HelpPluginActivator::HelpPluginActivator()
   : pluginListener(nullptr)
 {
   this->instance = this;
 }
 
 HelpPluginActivator::~HelpPluginActivator()
 {
   instance = nullptr;
 }
 
 void
 HelpPluginActivator::start(ctkPluginContext* context)
 {
   BERRY_REGISTER_EXTENSION_CLASS(berry::HelpContentView, context)
   BERRY_REGISTER_EXTENSION_CLASS(berry::HelpIndexView, context)
   BERRY_REGISTER_EXTENSION_CLASS(berry::HelpSearchView, context)
   BERRY_REGISTER_EXTENSION_CLASS(berry::HelpEditor, context)
   BERRY_REGISTER_EXTENSION_CLASS(berry::HelpEditorInputFactory, context)
   BERRY_REGISTER_EXTENSION_CLASS(berry::HelpPerspective, context)
 
   QFileInfo qhcInfo = context->getDataFile("qthelpcollection.qhc");
   helpEngine.reset(new QHelpEngineWrapper(qhcInfo.absoluteFilePath()));
+  helpEngine->setReadOnly(false);
   if (!helpEngine->setupData())
   {
     BERRY_ERROR << "QHelpEngine set-up failed: " << helpEngine->error().toStdString();
     return;
   }
 
   helpEngineConfiguration.reset(new QHelpEngineConfiguration(context, *helpEngine.data()));
 
   delete pluginListener;
   pluginListener = new QCHPluginListener(context, helpEngine.data());
   context->connectPluginListener(pluginListener, SLOT(pluginChanged(ctkPluginEvent)));
 
   // register all QCH files from all the currently installed plugins
   pluginListener->processPlugins();
 
   helpEngine->initialDocSetupDone();
 
   // Register a wnd listener which registers a perspective listener for each
   // new window. The perspective listener opens the help home page in the window
   // if no other help page is opened yet.
   wndListener.reset(new HelpWindowListener());
   PlatformUI::GetWorkbench()->AddWindowListener(wndListener.data());
 
   // Register an event handler for CONTEXTHELP_REQUESTED events
   helpContextHandler.reset(new HelpContextHandler);
   ctkDictionary helpHandlerProps;
   helpHandlerProps.insert(ctkEventConstants::EVENT_TOPIC, "org/blueberry/ui/help/CONTEXTHELP_REQUESTED");
   context->registerService<ctkEventHandler>(helpContextHandler.data(), helpHandlerProps);
 }
 
 void
 HelpPluginActivator::stop(ctkPluginContext* /*context*/)
 {
   delete pluginListener;
   pluginListener = nullptr;
 
   if (PlatformUI::IsWorkbenchRunning())
   {
     PlatformUI::GetWorkbench()->RemoveWindowListener(wndListener.data());
   }
   wndListener.reset();
 
   helpEngineConfiguration.reset();
   helpEngine.reset();
 }
 
 HelpPluginActivator *HelpPluginActivator::getInstance()
 {
   return instance;
 }
 
 QHelpEngineWrapper& HelpPluginActivator::getQHelpEngine()
 {
   return *helpEngine;
 }
 
 void HelpPluginActivator::linkActivated(IWorkbenchPage::Pointer page, const QUrl &link)
 {
   IEditorInput::Pointer input(new HelpEditorInput(link));
 
   // see if an editor with the same input is already open
   IEditorPart::Pointer reuseEditor = page->FindEditor(input);
   if (reuseEditor)
   {
     // just activate it
     page->Activate(reuseEditor);
   }
   else
   {
     // reuse the currently active editor, if it is a HelpEditor
     reuseEditor = page->GetActiveEditor();
     if (reuseEditor.IsNotNull() && page->GetReference(reuseEditor)->GetId() == HelpEditor::EDITOR_ID)
     {
       page->ReuseEditor(reuseEditor.Cast<IReusableEditor>(), input);
       page->Activate(reuseEditor);
     }
     else
     {
       // get the last used HelpEditor instance
       QList<IEditorReference::Pointer> editors =
           page->FindEditors(IEditorInput::Pointer(nullptr), HelpEditor::EDITOR_ID, IWorkbenchPage::MATCH_ID);
       if (editors.empty())
       {
         // no HelpEditor is currently open, create a new one
         page->OpenEditor(input, HelpEditor::EDITOR_ID);
       }
       else
       {
         // reuse an existing editor
         reuseEditor = editors.front()->GetEditor(false);
         page->ReuseEditor(reuseEditor.Cast<IReusableEditor>(), input);
         page->Activate(reuseEditor);
       }
     }
   }
 }
 
 QCHPluginListener::QCHPluginListener(ctkPluginContext* context, QHelpEngine* helpEngine)
   : delayRegistration(true), context(context), helpEngine(helpEngine)
 {}
 
 void QCHPluginListener::processPlugins()
 {
   QMutexLocker lock(&mutex);
   processPlugins_unlocked();
 }
 
 void QCHPluginListener::pluginChanged(const ctkPluginEvent& event)
 {
   QMutexLocker lock(&mutex);
   if (delayRegistration)
   {
     this->processPlugins_unlocked();
     return;
   }
 
   /* Only should listen for RESOLVED and UNRESOLVED events.
    *
    * When a plugin is updated the Framework will publish an UNRESOLVED and
    * then a RESOLVED event which should cause the plugin to be removed
    * and then added back into the registry.
    *
    * When a plugin is uninstalled the Framework should publish an UNRESOLVED
    * event and then an UNINSTALLED event so the plugin will have been removed
    * by the UNRESOLVED event before the UNINSTALLED event is published.
    */
   QSharedPointer<ctkPlugin> plugin = event.getPlugin();
   switch (event.getType())
   {
   case ctkPluginEvent::RESOLVED :
     addPlugin(plugin);
     break;
   case ctkPluginEvent::UNRESOLVED :
     removePlugin(plugin);
     break;
   default:
     break;
   }
 }
 
 void QCHPluginListener::processPlugins_unlocked()
 {
   if (!delayRegistration) return;
 
   foreach (QSharedPointer<ctkPlugin> plugin, context->getPlugins())
   {
     if (isPluginResolved(plugin))
       addPlugin(plugin);
     else
       removePlugin(plugin);
   }
 
   delayRegistration = false;
 }
 
 bool QCHPluginListener::isPluginResolved(QSharedPointer<ctkPlugin> plugin)
 {
   return (plugin->getState() & (ctkPlugin::RESOLVED | ctkPlugin::ACTIVE | ctkPlugin::STARTING | ctkPlugin::STOPPING)) != 0;
 }
 
 void QCHPluginListener::removePlugin(QSharedPointer<ctkPlugin> plugin)
 {
   // bail out if system plugin
   if (plugin->getPluginId() == 0) return;
 
   QFileInfo qchDirInfo = context->getDataFile("qch_files/" + QString::number(plugin->getPluginId()));
   if (qchDirInfo.exists())
   {
     QDir qchDir(qchDirInfo.absoluteFilePath());
     QStringList qchEntries = qchDir.entryList(QStringList("*.qch"));
     QStringList qchFiles;
     foreach(QString qchEntry, qchEntries)
     {
       qchFiles << qchDir.absoluteFilePath(qchEntry);
     }
     // unregister the cached qch files
     foreach(QString qchFile, qchFiles)
     {
       QString namespaceName = QHelpEngineCore::namespaceName(qchFile);
       if (namespaceName.isEmpty())
       {
         BERRY_ERROR << "Could not get the namespace for qch file " << qchFile.toStdString();
         continue;
       }
       else
       {
         if (!helpEngine->unregisterDocumentation(namespaceName))
         {
           BERRY_ERROR << "Unregistering qch namespace " << namespaceName.toStdString() << " failed: " << helpEngine->error().toStdString();
         }
       }
     }
     // clean the directory
     foreach(QString qchEntry, qchEntries)
     {
       qchDir.remove(qchEntry);
     }
   }
 }
 
 void QCHPluginListener::addPlugin(QSharedPointer<ctkPlugin> plugin)
 {
   // bail out if system plugin
   if (plugin->getPluginId() == 0) return;
 
   QFileInfo qchDirInfo = context->getDataFile("qch_files/" + QString::number(plugin->getPluginId()));
   QUrl location(plugin->getLocation());
   QFileInfo pluginFileInfo(location.toLocalFile());
 
   if (!qchDirInfo.exists() || qchDirInfo.lastModified() < pluginFileInfo.lastModified())
   {
     removePlugin(plugin);
 
     if (!qchDirInfo.exists())
     {
       QDir().mkpath(qchDirInfo.absoluteFilePath());
     }
 
     QStringList localQCHFiles;
     QStringList resourceList = plugin->findResources("/", "*.qch", true);
     foreach(QString resource, resourceList)
     {
       QByteArray content = plugin->getResource(resource);
       QFile localFile(qchDirInfo.absoluteFilePath() + "/" + resource.section('/', -1));
       localFile.open(QIODevice::WriteOnly);
       localFile.write(content);
       localFile.close();
       if (localFile.error() != QFile::NoError)
       {
         BERRY_WARN << "Error writing " << localFile.fileName().toStdString()
                    << ": " << localFile.errorString().toStdString();
       }
       else
       {
         localQCHFiles << localFile.fileName();
       }
     }
 
     foreach(QString qchFile, localQCHFiles)
     {
       if (!helpEngine->registerDocumentation(qchFile))
       {
         BERRY_ERROR << "Registering qch file " << qchFile.toStdString() << " failed: " << helpEngine->error().toStdString();
       }
     }
   }
 
 }
 
 IPerspectiveListener::Events::Types HelpPerspectiveListener::GetPerspectiveEventTypes() const
 {
   return Events::OPENED | Events::CHANGED;
 }
 
 void HelpPerspectiveListener::PerspectiveOpened(const SmartPointer<IWorkbenchPage>& page, const IPerspectiveDescriptor::Pointer& perspective)
 {
   // if no help editor is opened, open one showing the home page
   if (perspective->GetId() == HelpPerspective::ID &&
       page->FindEditors(IEditorInput::Pointer(nullptr), HelpEditor::EDITOR_ID, IWorkbenchPage::MATCH_ID).empty())
   {
     IEditorInput::Pointer input(new HelpEditorInput());
     page->OpenEditor(input, HelpEditor::EDITOR_ID);
   }
 }
 
 void HelpPerspectiveListener::PerspectiveChanged(const SmartPointer<IWorkbenchPage>& page, const IPerspectiveDescriptor::Pointer& perspective, const QString &changeId)
 {
   if (perspective->GetId() == HelpPerspective::ID && changeId == IWorkbenchPage::CHANGE_RESET)
   {
     PerspectiveOpened(page, perspective);
   }
 }
 
 HelpWindowListener::HelpWindowListener()
   : perspListener(new HelpPerspectiveListener())
 {
   // Register perspective listener for already opened windows
   typedef QList<IWorkbenchWindow::Pointer> WndVec;
   WndVec windows = PlatformUI::GetWorkbench()->GetWorkbenchWindows();
   for (WndVec::iterator i = windows.begin(); i != windows.end(); ++i)
   {
     (*i)->AddPerspectiveListener(perspListener.data());
   }
 }
 
 HelpWindowListener::~HelpWindowListener()
 {
   if (!PlatformUI::IsWorkbenchRunning()) return;
 
   typedef QList<IWorkbenchWindow::Pointer> WndVec;
   WndVec windows = PlatformUI::GetWorkbench()->GetWorkbenchWindows();
   for (WndVec::iterator i = windows.begin(); i != windows.end(); ++i)
   {
     (*i)->RemovePerspectiveListener(perspListener.data());
   }
 }
 
 void HelpWindowListener::WindowClosed(const IWorkbenchWindow::Pointer& window)
 {
   window->RemovePerspectiveListener(perspListener.data());
 }
 
 void HelpWindowListener::WindowOpened(const IWorkbenchWindow::Pointer& window)
 {
   window->AddPerspectiveListener(perspListener.data());
 }
 
 void HelpContextHandler::handleEvent(const ctkEvent &event)
 {
   struct _runner : public Poco::Runnable
   {
     _runner(const ctkEvent& ev) : ev(ev) {}
 
     void run() override
     {
       QUrl helpUrl;
       if (ev.containsProperty("url"))
       {
         helpUrl = QUrl(ev.getProperty("url").toString());
       }
       else
       {
         helpUrl = contextUrl();
       }
 
       HelpPluginActivator::linkActivated(PlatformUI::GetWorkbench()->GetActiveWorkbenchWindow()->GetActivePage(),
                                          helpUrl);
       delete this;
     }
 
     QUrl contextUrl() const
     {
       berry::IWorkbench* currentWorkbench = berry::PlatformUI::GetWorkbench();
       if (currentWorkbench)
       {
         berry::IWorkbenchWindow::Pointer currentWorkbenchWindow = currentWorkbench->GetActiveWorkbenchWindow();
         if (currentWorkbenchWindow)
         {
           berry::IWorkbenchPage::Pointer currentPage = currentWorkbenchWindow->GetActivePage();
           if (currentPage)
           {
             berry::IWorkbenchPart::Pointer currentPart = currentPage->GetActivePart();
             if (currentPart)
             {
               QString pluginID = currentPart->GetSite()->GetPluginId();
               QString viewID = currentPart->GetSite()->GetId();
               QString loc = "qthelp://" + pluginID + "/bundle/%1.html";
 
               QHelpEngineWrapper& helpEngine = HelpPluginActivator::getInstance()->getQHelpEngine();
               // Get view help page if available
               QUrl contextUrl(loc.arg(viewID.replace(".", "_")));
               QUrl url = helpEngine.findFile(contextUrl);
               if (url.isValid()) return url;
               else
               {
                 BERRY_INFO << "Context help url invalid: " << contextUrl.toString().toStdString();
               }
               // If no view help exists get plugin help if available
               QUrl pluginContextUrl(loc.arg(pluginID.replace(".", "_")));
               url = helpEngine.findFile(pluginContextUrl);
               if (url.isValid()) return url;
               // Try to get the index.html file of the plug-in contributing the
               // currently active part.
               QUrl pluginIndexUrl(loc.arg("index"));
               url = helpEngine.findFile(pluginIndexUrl);
               if (url != pluginIndexUrl)
               {
                 // Use the default page instead of another index.html
                 // (merged via the virtual folder property).
                 url = QUrl();
               }
               return url;
             }
           }
         }
       }
       return QUrl();
     }
 
     ctkEvent ev;
   };
   // sync with GUI thread
   Display::GetDefault()->AsyncExec(new _runner(event));
 }
 
 }