/*============================================================================

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 "QmitkExtWorkbenchWindowAdvisor.h"
#include "QmitkExtActionBarAdvisor.h"

#include <QApplication>
#include <QLayout>
#include <QMenu>
#include <QMenuBar>
#include <QMainWindow>
#include <QStatusBar>
#include <QString>
#include <QFile>
#include <QRegularExpression>
#include <QTextStream>
#include <QSettings>

#include <ctkPluginException.h>
#include <service/event/ctkEventAdmin.h>

#include <berryPlatform.h>
#include <berryPlatformUI.h>
#include <berryIActionBarConfigurer.h>
#include <berryIWorkbenchWindow.h>
#include <berryIWorkbenchPage.h>
#include <berryIPerspectiveRegistry.h>
#include <berryIPerspectiveDescriptor.h>
#include <berryIProduct.h>
#include <berryIWorkbenchPartConstants.h>
#include <berryQtPreferences.h>
#include <berryQtStyleManager.h>
#include <berryWorkbenchPlugin.h>

#include <internal/berryQtShowViewAction.h>
#include <internal/berryQtOpenPerspectiveAction.h>

#include <QmitkFileOpenAction.h>
#include <QmitkFileSaveAction.h>
#include "QmitkExtFileSaveProjectAction.h"
#include <QmitkFileExitAction.h>
#include <QmitkCloseProjectAction.h>
#include <QmitkUndoAction.h>
#include <QmitkRedoAction.h>
#include <QmitkDefaultDropTargetListener.h>
#include <QmitkStatusBar.h>
#include <QmitkProgressBar.h>
#include <QmitkMemoryUsageIndicatorView.h>
#include <QmitkPreferencesDialog.h>
#include "QmitkOpenDicomEditorAction.h"
#include "QmitkOpenMxNMultiWidgetEditorAction.h"
#include "QmitkOpenStdMultiWidgetEditorAction.h"
#include <QmitkApplicationConstants.h>

#include <itkConfigure.h>
#include <mitkBaseApplication.h>
#include <mitkVersion.h>
#include <mitkCoreServices.h>
#include <mitkDataStorageEditorInput.h>
#include <mitkDataStorageReference.h>
#include <mitkIDataStorageService.h>
#include <mitkWorkbenchUtil.h>
#include <mitkCoreServices.h>
#include <mitkIPreferencesService.h>
#include <mitkIPreferences.h>
#include <vtkVersionMacros.h>

// UGLYYY
#include "internal/QmitkExtWorkbenchWindowAdvisorHack.h"
#include "internal/QmitkCommonExtPlugin.h"
#include "internal/QmitkThemedStyle.h"
#include <mitkUndoController.h>
#include <mitkVerboseLimitedLinearUndo.h>
#include <QToolBar>
#include <QToolButton>
#include <QMessageBox>
#include <QMouseEvent>
#include <QLabel>
#include <QmitkAboutDialog.h>
#include "QmitkStartupDialog.h"

QmitkExtWorkbenchWindowAdvisorHack* QmitkExtWorkbenchWindowAdvisorHack::undohack =
  new QmitkExtWorkbenchWindowAdvisorHack();

QString QmitkExtWorkbenchWindowAdvisor::QT_SETTINGS_FILENAME = "QtSettings.ini";

static bool USE_EXPERIMENTAL_COMMAND_CONTRIBUTIONS = false;

namespace
{
  void ExecuteStartupDialog()
  {
    QmitkStartupDialog startupDialog;

    if (startupDialog.SkipDialog())
      return;

    try
    {
      if (startupDialog.exec() != QDialog::Accepted)
        return;
    }
    catch (const mitk::Exception& e)
    {
      MITK_ERROR << e.GetDescription();
      return;
    }

    // Create two lists: one for all categories and one subset for visible categories.

    QStringList allCategories = berry::PlatformUI::GetWorkbench()->GetViewRegistry()->GetViewsByCategory().uniqueKeys();
    QStringList visibleCategories;

    if (startupDialog.UsePreset())
    {
      visibleCategories = startupDialog.GetPresetCategories();

      if (visibleCategories.isEmpty()) // "Custom" preset
      {
        // Early-out and show the "Tool Bars" preference page instead.
        QmitkExtWorkbenchWindowAdvisorHack::undohack->onEditPreferences("org.mitk.ToolBarsPreferencePage");
        return;
      }
    }
    else
    {
      visibleCategories = allCategories;
    }

    // Now set the visibility preferences for all categories and apply them instantly.

    auto prefsService = mitk::CoreServices::GetPreferencesService();
    auto prefs = prefsService->GetSystemPreferences()->Node(QmitkApplicationConstants::TOOL_BARS_PREFERENCES);
    const auto toolBars = berry::PlatformUI::GetWorkbench()->GetWorkbenchWindows().first()->GetToolBars();

    for (const auto& category : allCategories)
    {
      bool isVisible = visibleCategories.contains(category);
      prefs->PutBool(category.toStdString(), isVisible);

      auto toolBarIter = std::find_if(toolBars.cbegin(), toolBars.cend(), [&category](const QToolBar* toolBar) {
        return toolBar->objectName() == category;
      });

      if (toolBarIter != toolBars.cend())
        (*toolBarIter)->setVisible(isVisible);
    }

    prefs->Flush();
  }
}

class PartListenerForTitle: public berry::IPartListener
{
public:

  PartListenerForTitle(QmitkExtWorkbenchWindowAdvisor* wa)
    : windowAdvisor(wa)
  {
  }

  Events::Types GetPartEventTypes() const override
  {
    return Events::ACTIVATED | Events::BROUGHT_TO_TOP | Events::CLOSED
      | Events::HIDDEN | Events::VISIBLE;
  }

  void PartActivated(const berry::IWorkbenchPartReference::Pointer& ref) override
  {
    if (ref.Cast<berry::IEditorReference> ())
    {
      windowAdvisor->UpdateTitle(false);
    }
  }

  void PartBroughtToTop(const berry::IWorkbenchPartReference::Pointer& ref) override
  {
    if (ref.Cast<berry::IEditorReference> ())
    {
      windowAdvisor->UpdateTitle(false);
    }
  }

  void PartClosed(const berry::IWorkbenchPartReference::Pointer& /*ref*/) override
  {
    windowAdvisor->UpdateTitle(false);
  }

  void PartHidden(const berry::IWorkbenchPartReference::Pointer& ref) override
  {
    auto lockedLastActiveEditor = windowAdvisor->lastActiveEditor.Lock();

    if (lockedLastActiveEditor.IsNotNull() && ref->GetPart(false) == lockedLastActiveEditor)
    {
      windowAdvisor->UpdateTitle(true);
    }
  }

  void PartVisible(const berry::IWorkbenchPartReference::Pointer& ref) override
  {
    auto lockedLastActiveEditor = windowAdvisor->lastActiveEditor.Lock();

    if (lockedLastActiveEditor.IsNotNull() && ref->GetPart(false) == lockedLastActiveEditor)
    {
      windowAdvisor->UpdateTitle(false);
    }
  }

private:
  QmitkExtWorkbenchWindowAdvisor* windowAdvisor;
};

class PartListenerForViewNavigator: public berry::IPartListener
{
public:

  PartListenerForViewNavigator(QAction* act)
    : viewNavigatorAction(act)
  {
  }

  Events::Types GetPartEventTypes() const override
  {
    return Events::OPENED | Events::CLOSED | Events::HIDDEN |
      Events::VISIBLE;
  }

  void PartOpened(const berry::IWorkbenchPartReference::Pointer& ref) override
  {
    if (ref->GetId()=="org.mitk.views.viewnavigator")
    {
      viewNavigatorAction->setChecked(true);
    }
  }

  void PartClosed(const berry::IWorkbenchPartReference::Pointer& ref) override
  {
    if (ref->GetId()=="org.mitk.views.viewnavigator")
    {
      viewNavigatorAction->setChecked(false);
    }
  }

  void PartVisible(const berry::IWorkbenchPartReference::Pointer& ref) override
  {
    if (ref->GetId()=="org.mitk.views.viewnavigator")
    {
      viewNavigatorAction->setChecked(true);
    }
  }

  void PartHidden(const berry::IWorkbenchPartReference::Pointer& ref) override
  {
    if (ref->GetId()=="org.mitk.views.viewnavigator")
    {
      viewNavigatorAction->setChecked(false);
    }
  }

private:
  QAction* viewNavigatorAction;
};

class PartListenerForImageNavigator: public berry::IPartListener
{
public:

  PartListenerForImageNavigator(QAction* act)
    : imageNavigatorAction(act)
  {
  }

  Events::Types GetPartEventTypes() const override
  {
    return Events::OPENED | Events::CLOSED | Events::HIDDEN |
      Events::VISIBLE;
  }

  void PartOpened(const berry::IWorkbenchPartReference::Pointer& ref) override
  {
    if (ref->GetId()=="org.mitk.views.imagenavigator")
    {
      imageNavigatorAction->setChecked(true);
    }
  }

  void PartClosed(const berry::IWorkbenchPartReference::Pointer& ref) override
  {
    if (ref->GetId()=="org.mitk.views.imagenavigator")
    {
      imageNavigatorAction->setChecked(false);
    }
  }

  void PartVisible(const berry::IWorkbenchPartReference::Pointer& ref) override
  {
    if (ref->GetId()=="org.mitk.views.imagenavigator")
    {
      imageNavigatorAction->setChecked(true);
    }
  }

  void PartHidden(const berry::IWorkbenchPartReference::Pointer& ref) override
  {
    if (ref->GetId()=="org.mitk.views.imagenavigator")
    {
      imageNavigatorAction->setChecked(false);
    }
  }

private:
  QAction* imageNavigatorAction;
};

class PerspectiveListenerForTitle: public berry::IPerspectiveListener
{
public:

  PerspectiveListenerForTitle(QmitkExtWorkbenchWindowAdvisor* wa)
    : windowAdvisor(wa)
    , perspectivesClosed(false)
  {
  }

  Events::Types GetPerspectiveEventTypes() const override
  {
    if (USE_EXPERIMENTAL_COMMAND_CONTRIBUTIONS)
    {
      return Events::ACTIVATED | Events::SAVED_AS | Events::DEACTIVATED;
    }
    else
    {
      return Events::ACTIVATED | Events::SAVED_AS | Events::DEACTIVATED
        | Events::CLOSED | Events::OPENED;
    }
  }

  void PerspectiveActivated(const berry::IWorkbenchPage::Pointer& /*page*/,
    const berry::IPerspectiveDescriptor::Pointer& /*perspective*/) override
  {
    windowAdvisor->UpdateTitle(false);
  }

  void PerspectiveSavedAs(const berry::IWorkbenchPage::Pointer& /*page*/,
    const berry::IPerspectiveDescriptor::Pointer& /*oldPerspective*/,
    const berry::IPerspectiveDescriptor::Pointer& /*newPerspective*/) override
  {
    windowAdvisor->UpdateTitle(false);
  }

  void PerspectiveDeactivated(const berry::IWorkbenchPage::Pointer& /*page*/,
    const berry::IPerspectiveDescriptor::Pointer& /*perspective*/) override
  {
    windowAdvisor->UpdateTitle(false);
  }

  void PerspectiveOpened(const berry::IWorkbenchPage::Pointer& /*page*/,
    const berry::IPerspectiveDescriptor::Pointer& /*perspective*/) override
  {
    if (perspectivesClosed)
    {
      QListIterator<QAction*> i(windowAdvisor->viewActions);
      while (i.hasNext())
      {
        i.next()->setEnabled(true);
      }

      //GetViewRegistry()->Find("org.mitk.views.imagenavigator");
      if(windowAdvisor->GetWindowConfigurer()->GetWindow()->GetWorkbench()->GetEditorRegistry()->FindEditor("org.mitk.editors.dicombrowser"))
      {
        windowAdvisor->openDicomEditorAction->setEnabled(true);
      }
      if (windowAdvisor->GetWindowConfigurer()->GetWindow()->GetWorkbench()->GetEditorRegistry()->FindEditor("org.mitk.editors.stdmultiwidget"))
      {
        windowAdvisor->openStdMultiWidgetEditorAction->setEnabled(true);
      }
      if (windowAdvisor->GetWindowConfigurer()->GetWindow()->GetWorkbench()->GetEditorRegistry()->FindEditor("org.mitk.editors.mxnmultiwidget"))
      {
        windowAdvisor->openMxNMultiWidgetEditorAction->setEnabled(true);
      }

      windowAdvisor->fileSaveProjectAction->setEnabled(true);
      windowAdvisor->closeProjectAction->setEnabled(true);
      windowAdvisor->undoAction->setEnabled(true);
      windowAdvisor->redoAction->setEnabled(true);
      windowAdvisor->imageNavigatorAction->setEnabled(true);
      windowAdvisor->viewNavigatorAction->setEnabled(true);
      windowAdvisor->resetPerspAction->setEnabled(true);
      if( windowAdvisor->GetShowClosePerspectiveMenuItem() )
      {
        windowAdvisor->closePerspAction->setEnabled(true);
      }
    }

    perspectivesClosed = false;
  }

  void PerspectiveClosed(const berry::IWorkbenchPage::Pointer& /*page*/,
    const berry::IPerspectiveDescriptor::Pointer& /*perspective*/) override
  {
    berry::IWorkbenchWindow::Pointer wnd = windowAdvisor->GetWindowConfigurer()->GetWindow();
    bool allClosed = true;
    if (wnd->GetActivePage())
    {
      QList<berry::IPerspectiveDescriptor::Pointer> perspectives(wnd->GetActivePage()->GetOpenPerspectives());
      allClosed = perspectives.empty();
    }

    if (allClosed)
    {
      perspectivesClosed = true;

      QListIterator<QAction*> i(windowAdvisor->viewActions);
      while (i.hasNext())
      {
        i.next()->setEnabled(false);
      }

      if(windowAdvisor->GetWindowConfigurer()->GetWindow()->GetWorkbench()->GetEditorRegistry()->FindEditor("org.mitk.editors.dicombrowser"))
      {
        windowAdvisor->openDicomEditorAction->setEnabled(false);
      }
      if (windowAdvisor->GetWindowConfigurer()->GetWindow()->GetWorkbench()->GetEditorRegistry()->FindEditor("org.mitk.editors.stdmultiwidget"))
      {
        windowAdvisor->openStdMultiWidgetEditorAction->setEnabled(false);
      }
      if (windowAdvisor->GetWindowConfigurer()->GetWindow()->GetWorkbench()->GetEditorRegistry()->FindEditor("org.mitk.editors.mxnmultiwidget"))
      {
        windowAdvisor->openMxNMultiWidgetEditorAction->setEnabled(false);
      }

      windowAdvisor->fileSaveProjectAction->setEnabled(false);
      windowAdvisor->closeProjectAction->setEnabled(false);
      windowAdvisor->undoAction->setEnabled(false);
      windowAdvisor->redoAction->setEnabled(false);
      windowAdvisor->imageNavigatorAction->setEnabled(false);
      windowAdvisor->viewNavigatorAction->setEnabled(false);
      windowAdvisor->resetPerspAction->setEnabled(false);
      if( windowAdvisor->GetShowClosePerspectiveMenuItem() )
      {
        windowAdvisor->closePerspAction->setEnabled(false);
      }
    }
  }

private:
  QmitkExtWorkbenchWindowAdvisor* windowAdvisor;
  bool perspectivesClosed;
};

class PerspectiveListenerForMenu: public berry::IPerspectiveListener
{
public:

  PerspectiveListenerForMenu(QmitkExtWorkbenchWindowAdvisor* wa)
    : windowAdvisor(wa)
  {
  }

  Events::Types GetPerspectiveEventTypes() const override
  {
    return Events::ACTIVATED | Events::DEACTIVATED;
  }

  void PerspectiveActivated(const berry::IWorkbenchPage::Pointer& /*page*/,
    const berry::IPerspectiveDescriptor::Pointer& perspective) override
  {
    QAction* action = windowAdvisor->mapPerspIdToAction[perspective->GetId()];
    if (action)
    {
      action->setChecked(true);
    }
  }

  void PerspectiveDeactivated(const berry::IWorkbenchPage::Pointer& /*page*/,
    const berry::IPerspectiveDescriptor::Pointer& perspective) override
  {
    QAction* action = windowAdvisor->mapPerspIdToAction[perspective->GetId()];
    if (action)
    {
      action->setChecked(false);
    }
  }

private:
  QmitkExtWorkbenchWindowAdvisor* windowAdvisor;
};

QmitkExtWorkbenchWindowAdvisor::QmitkExtWorkbenchWindowAdvisor(berry::WorkbenchAdvisor* wbAdvisor,
                                                               berry::IWorkbenchWindowConfigurer::Pointer configurer)
  : berry::WorkbenchWindowAdvisor(configurer)
  , lastInput(nullptr)
  , wbAdvisor(wbAdvisor)
  , showViewToolbar(true)
  , showPerspectiveToolbar(false)
  , showVersionInfo(true)
  , showMitkVersionInfo(true)
  , showViewMenuItem(true)
  , showNewWindowMenuItem(false)
  , showClosePerspectiveMenuItem(true)
  , viewNavigatorFound(false)
  , showMemoryIndicator(true)
  , dropTargetListener(new QmitkDefaultDropTargetListener)
{
  productName = QCoreApplication::applicationName();
  viewExcludeList.push_back("org.mitk.views.viewnavigator");
}

QmitkExtWorkbenchWindowAdvisor::~QmitkExtWorkbenchWindowAdvisor()
{
}

berry::ActionBarAdvisor::Pointer QmitkExtWorkbenchWindowAdvisor::CreateActionBarAdvisor(berry::IActionBarConfigurer::Pointer configurer)
{
  if (USE_EXPERIMENTAL_COMMAND_CONTRIBUTIONS)
  {
    berry::ActionBarAdvisor::Pointer actionBarAdvisor(new QmitkExtActionBarAdvisor(configurer));
    return actionBarAdvisor;
  }
  else
  {
    return berry::WorkbenchWindowAdvisor::CreateActionBarAdvisor(configurer);
  }
}

QWidget* QmitkExtWorkbenchWindowAdvisor::CreateEmptyWindowContents(QWidget* parent)
{
  QWidget* parentWidget = static_cast<QWidget*>(parent);
  auto   label = new QLabel(parentWidget);
  label->setText("<b>No perspectives are open. Open a perspective in the <i>Window->Open Perspective</i> menu.</b>");
  label->setContentsMargins(10,10,10,10);
  label->setAlignment(Qt::AlignTop);
  label->setEnabled(false);
  parentWidget->layout()->addWidget(label);
  return label;
}

void QmitkExtWorkbenchWindowAdvisor::ShowClosePerspectiveMenuItem(bool show)
{
  showClosePerspectiveMenuItem = show;
}

bool QmitkExtWorkbenchWindowAdvisor::GetShowClosePerspectiveMenuItem()
{
  return showClosePerspectiveMenuItem;
}

void QmitkExtWorkbenchWindowAdvisor::ShowMemoryIndicator(bool show)
{
  showMemoryIndicator = show;
}

bool QmitkExtWorkbenchWindowAdvisor::GetShowMemoryIndicator()
{
  return showMemoryIndicator;
}

void QmitkExtWorkbenchWindowAdvisor::ShowNewWindowMenuItem(bool show)
{
  showNewWindowMenuItem = show;
}

void QmitkExtWorkbenchWindowAdvisor::ShowViewToolbar(bool show)
{
  showViewToolbar = show;
}

void QmitkExtWorkbenchWindowAdvisor::ShowViewMenuItem(bool show)
{
  showViewMenuItem = show;
}

void QmitkExtWorkbenchWindowAdvisor::ShowPerspectiveToolbar(bool show)
{
  showPerspectiveToolbar = show;
}

void QmitkExtWorkbenchWindowAdvisor::ShowVersionInfo(bool show)
{
  showVersionInfo = show;
}

void QmitkExtWorkbenchWindowAdvisor::ShowMitkVersionInfo(bool show)
{
  showMitkVersionInfo = show;
}

void QmitkExtWorkbenchWindowAdvisor::SetProductName(const QString& product)
{
  productName = product;
}

void QmitkExtWorkbenchWindowAdvisor::SetWindowIcon(const QString& wndIcon)
{
  windowIcon = wndIcon;
}

namespace
{
#ifndef __APPLE__
  // Off macOS, use a borderless window instead of true full-screen. The render
  // views are OpenGL widgets, so Qt composites the whole window through OpenGL;
  // on Windows a GL window that exactly fills the screen makes the OS bypass
  // desktop composition (exclusive full-screen), throttling Qt widget repaints
  // to a few FPS. A frameless window covering the screen keeps composition
  // active. Replace the flags (do not just add the frameless hint) so no title
  // bar survives on X11, and overflow the screen by one pixel on Windows to
  // avoid the exclusive path; the overflow is clamped by X11 window managers,
  // so it is applied on Windows only.
  void SetBorderlessFullScreen(QMainWindow* window)
  {
    window->setWindowFlags(Qt::FramelessWindowHint);
    QRect bounds = window->screen()->geometry();
#ifdef Q_OS_WIN
    bounds.adjust(-1, -1, 1, 1);
#endif
    window->setGeometry(bounds);
  }
#endif
}

void QmitkExtWorkbenchWindowAdvisor::PostWindowCreate()
{
  // very bad hack...
  berry::IWorkbenchWindow::Pointer window = this->GetWindowConfigurer()->GetWindow();
  QMainWindow* mainWindow = qobject_cast<QMainWindow*> (window->GetShell()->GetControl());

  if (!windowIcon.isEmpty())
  {
    mainWindow->setWindowIcon(QIcon(windowIcon));
  }
  mainWindow->setContextMenuPolicy(Qt::PreventContextMenu);

  // Load icon theme
  QIcon::setThemeSearchPaths(QStringList() << QStringLiteral(":/org_mitk_icons/icons/"));
  QIcon::setThemeName(QStringLiteral("awesome"));

  // Style icons of Qt's standard message boxes
  QApplication::setStyle(new QmitkThemedStyle(QApplication::style()));

  // Start in full-screen (kiosk) mode when requested on the command line.
  const bool fullScreenMode = static_cast<mitk::BaseApplication*>(&mitk::BaseApplication::instance())->getFullScreenMode();
  if (fullScreenMode)
  {
#ifdef __APPLE__
    // Native full-screen (uses the full-screen button hint set in the shell
    // factory); correctly clears the menu bar and notch.
    mainWindow->setWindowState(mainWindow->windowState() | Qt::WindowFullScreen);
#else
    SetBorderlessFullScreen(mainWindow);
#endif
  }

  // ==== Application menu ============================

  QMenuBar* menuBar = mainWindow->menuBar();
  menuBar->setContextMenuPolicy(Qt::PreventContextMenu);

#ifdef __APPLE__
  menuBar->setNativeMenuBar(true);
#else
  menuBar->setNativeMenuBar(false);
#endif

  auto basePath = QStringLiteral(":/org_mitk_icons/icons/awesome/scalable/actions/");

  auto fileOpenAction = new QmitkFileOpenAction(berry::QtStyleManager::ThemeIcon(basePath + "document-open.svg"), window);
  fileOpenAction->setShortcut(QKeySequence::Open);
  auto fileSaveAction = new QmitkFileSaveAction(berry::QtStyleManager::ThemeIcon(basePath + "document-save.svg"), window);
  fileSaveAction->setShortcut(QKeySequence::Save);
  fileSaveProjectAction = new QmitkExtFileSaveProjectAction(window);
  fileSaveProjectAction->setIcon(berry::QtStyleManager::ThemeIcon(basePath + "document-save.svg"));
  closeProjectAction = new QmitkCloseProjectAction(window);
  closeProjectAction->setIcon(berry::QtStyleManager::ThemeIcon(basePath + "edit-delete.svg"));

  auto   perspGroup = new QActionGroup(menuBar);
  std::map<QString, berry::IViewDescriptor::Pointer> VDMap;

  // sort elements (converting vector to map...)
  QList<berry::IViewDescriptor::Pointer>::const_iterator iter;

  berry::IViewRegistry* viewRegistry =
    berry::PlatformUI::GetWorkbench()->GetViewRegistry();
  const QList<berry::IViewDescriptor::Pointer> viewDescriptors = viewRegistry->GetViews();

  bool skip = false;
  for (iter = viewDescriptors.begin(); iter != viewDescriptors.end(); ++iter)
  {
    // if viewExcludeList is set, it contains the id-strings of view, which
    // should not appear as an menu-entry in the menu
    if (viewExcludeList.size() > 0)
    {
      for (int i=0; i<viewExcludeList.size(); i++)
      {
        if (viewExcludeList.at(i) == (*iter)->GetId())
        {
          skip = true;
          break;
        }
      }
      if (skip)
      {
        skip = false;
        continue;
      }
    }

    if ((*iter)->GetId() == "org.blueberry.ui.internal.introview")
      continue;
    if ((*iter)->GetId() == "org.mitk.views.imagenavigator")
      continue;
    if ((*iter)->GetId() == "org.mitk.views.viewnavigator")
      continue;

    std::pair<QString, berry::IViewDescriptor::Pointer> p((*iter)->GetLabel(), (*iter));
    VDMap.insert(p);
  }

  std::map<QString, berry::IViewDescriptor::Pointer>::const_iterator MapIter;
  for (MapIter = VDMap.begin(); MapIter != VDMap.end(); ++MapIter)
  {
    berry::QtShowViewAction* viewAction = new berry::QtShowViewAction(window, (*MapIter).second);
    viewActions.push_back(viewAction);
  }

  if (!USE_EXPERIMENTAL_COMMAND_CONTRIBUTIONS)
  {
    QMenu* fileMenu = menuBar->addMenu("&File");
    fileMenu->setObjectName("FileMenu");
    fileMenu->addAction(fileOpenAction);
    fileMenu->addAction(fileSaveAction);
    fileMenu->addAction(fileSaveProjectAction);
    fileMenu->addAction(closeProjectAction);
    fileMenu->addSeparator();

    QAction* fileExitAction = new QmitkFileExitAction(window);
    fileExitAction->setIcon(berry::QtStyleManager::ThemeIcon(basePath + "system-log-out.svg"));
    fileExitAction->setShortcut(QKeySequence::Quit);
    fileExitAction->setObjectName("QmitkFileExitAction");
    fileMenu->addAction(fileExitAction);

    // another bad hack to get an edit/undo menu...
    QMenu* editMenu = menuBar->addMenu("&Edit");
    undoAction = editMenu->addAction(
      berry::QtStyleManager::ThemeIcon(basePath + "edit-undo.svg"),
      "&Undo",
      QKeySequence("CTRL+Z"),
      QmitkExtWorkbenchWindowAdvisorHack::undohack,
      SLOT(onUndo()));
    undoAction->setToolTip("Undo the last action (not supported by all modules)");
    redoAction = editMenu->addAction(
      berry::QtStyleManager::ThemeIcon(basePath + "edit-redo.svg"),
      "&Redo",
      QKeySequence("CTRL+Y"),
      QmitkExtWorkbenchWindowAdvisorHack::undohack,
      SLOT(onRedo()));
    redoAction->setToolTip("execute the last action that was undone again (not supported by all modules)");

    // ==== Window Menu ==========================
    QMenu* windowMenu = menuBar->addMenu("Window");
    if (showNewWindowMenuItem)
    {
      windowMenu->addAction("&New Window", QmitkExtWorkbenchWindowAdvisorHack::undohack, SLOT(onNewWindow()));
      windowMenu->addSeparator();
    }

    if (!fullScreenMode)
    {
      windowMenu->addAction("&Full Screen", QKeySequence(QKeySequence::FullScreen),
        QmitkExtWorkbenchWindowAdvisorHack::undohack, SLOT(onFullScreen()));
      windowMenu->addSeparator();
    }

    QMenu* perspMenu = windowMenu->addMenu("&Open Perspective");

    QMenu* viewMenu = nullptr;
    if (showViewMenuItem)
    {
      viewMenu = windowMenu->addMenu("Show &View");
      viewMenu->setObjectName("Show View");
    }
    windowMenu->addSeparator();
    resetPerspAction = windowMenu->addAction("&Reset Perspective",
      QmitkExtWorkbenchWindowAdvisorHack::undohack, SLOT(onResetPerspective()));

    if(showClosePerspectiveMenuItem)
      closePerspAction = windowMenu->addAction("&Close Perspective", QmitkExtWorkbenchWindowAdvisorHack::undohack, SLOT(onClosePerspective()));

    windowMenu->addSeparator();
    windowMenu->addAction("&Preferences...", QKeySequence("CTRL+P"),
      QmitkExtWorkbenchWindowAdvisorHack::undohack, SLOT(onEditPreferences()));

    // fill perspective menu
    berry::IPerspectiveRegistry* perspRegistry =
      window->GetWorkbench()->GetPerspectiveRegistry();

    QList<berry::IPerspectiveDescriptor::Pointer> perspectives(
      perspRegistry->GetPerspectives());

    skip = false;
    for (QList<berry::IPerspectiveDescriptor::Pointer>::iterator perspIt =
      perspectives.begin(); perspIt != perspectives.end(); ++perspIt)
    {
      // if perspectiveExcludeList is set, it contains the id-strings of perspectives, which
      // should not appear as an menu-entry in the perspective menu
      if (perspectiveExcludeList.size() > 0)
      {
        for (int i=0; i<perspectiveExcludeList.size(); i++)
        {
          if (perspectiveExcludeList.at(i) == (*perspIt)->GetId())
          {
            skip = true;
            break;
          }
        }
        if (skip)
        {
          skip = false;
          continue;
        }
      }

      QAction* perspAction = new berry::QtOpenPerspectiveAction(window, *perspIt, perspGroup);
      mapPerspIdToAction.insert((*perspIt)->GetId(), perspAction);
    }
    perspMenu->addActions(perspGroup->actions());

    if (showViewMenuItem)
    {
      for (auto viewAction : std::as_const(viewActions))
      {
        viewMenu->addAction(viewAction);
      }
    }

    // ===== Help menu ====================================
    QMenu* helpMenu = menuBar->addMenu("&Help");
    helpMenu->addAction("&Welcome",this, SLOT(onIntro()));
    helpMenu->addAction("&Open Help Perspective", this, SLOT(onHelpOpenHelpPerspective()));
    helpMenu->addAction("&Context Help", QKeySequence("F1"), this, SLOT(onHelp()));
    helpMenu->addAction("&About",this, SLOT(onAbout()));
    // =====================================================
  }
  else
  {
    undoAction = new QmitkUndoAction(berry::QtStyleManager::ThemeIcon(basePath + "edit-undo.svg"), nullptr);
    undoAction->setShortcut(QKeySequence::Undo);
    redoAction = new QmitkRedoAction(berry::QtStyleManager::ThemeIcon(basePath + "edit-redo.svg"), nullptr);
    redoAction->setShortcut(QKeySequence::Redo);
  }

  // toolbar for showing file open, undo, redo and other main actions
  auto   mainActionsToolBar = new QToolBar;
  mainActionsToolBar->setObjectName("mainActionsToolBar");
  mainActionsToolBar->setContextMenuPolicy(Qt::PreventContextMenu);
#ifdef __APPLE__
  mainActionsToolBar->setToolButtonStyle ( Qt::ToolButtonTextUnderIcon );
#else
  mainActionsToolBar->setToolButtonStyle ( Qt::ToolButtonTextBesideIcon );
#endif

  basePath = QStringLiteral(":/org.mitk.gui.qt.ext/");
  imageNavigatorAction = new QAction(berry::QtStyleManager::ThemeIcon(basePath + "image_navigator.svg"), "&Image Navigator", nullptr);
  bool imageNavigatorViewFound = window->GetWorkbench()->GetViewRegistry()->Find("org.mitk.views.imagenavigator");

  if (this->GetWindowConfigurer()->GetWindow()->GetWorkbench()->GetEditorRegistry()->FindEditor("org.mitk.editors.dicombrowser"))
  {
    openDicomEditorAction = new QmitkOpenDicomEditorAction(berry::QtStyleManager::ThemeIcon(basePath + "dicom.svg"), window);
  }
  if (this->GetWindowConfigurer()->GetWindow()->GetWorkbench()->GetEditorRegistry()->FindEditor("org.mitk.editors.stdmultiwidget"))
  {
    openStdMultiWidgetEditorAction = new QmitkOpenStdMultiWidgetEditorAction(berry::QtStyleManager::ThemeIcon(basePath + "Editor.svg"), window);
  }
  if (this->GetWindowConfigurer()->GetWindow()->GetWorkbench()->GetEditorRegistry()->FindEditor("org.mitk.editors.mxnmultiwidget"))
  {
    openMxNMultiWidgetEditorAction = new QmitkOpenMxNMultiWidgetEditorAction(berry::QtStyleManager::ThemeIcon(basePath + "Editor.svg"), window);
  }

  if (imageNavigatorViewFound)
  {
    QObject::connect(imageNavigatorAction, SIGNAL(triggered(bool)), QmitkExtWorkbenchWindowAdvisorHack::undohack, SLOT(onImageNavigator()));
    imageNavigatorAction->setCheckable(true);

    // add part listener for image navigator
    imageNavigatorPartListener.reset(new PartListenerForImageNavigator(imageNavigatorAction));
    window->GetPartService()->AddPartListener(imageNavigatorPartListener.data());
    berry::IViewPart::Pointer imageNavigatorView = window->GetActivePage()->FindView("org.mitk.views.imagenavigator");
    imageNavigatorAction->setChecked(false);
    if (imageNavigatorView)
    {
      bool isImageNavigatorVisible = window->GetActivePage()->IsPartVisible(imageNavigatorView);
      if (isImageNavigatorVisible)
        imageNavigatorAction->setChecked(true);
    }
    imageNavigatorAction->setToolTip("Toggle image navigator for navigating through image");
  }

  viewNavigatorAction = new QAction(berry::QtStyleManager::ThemeIcon(QStringLiteral(":/org.mitk.gui.qt.ext/view-manager.svg")),"&View Navigator", nullptr);
  viewNavigatorFound = window->GetWorkbench()->GetViewRegistry()->Find("org.mitk.views.viewnavigator");
  if (viewNavigatorFound)
  {
    QObject::connect(viewNavigatorAction, SIGNAL(triggered(bool)), QmitkExtWorkbenchWindowAdvisorHack::undohack, SLOT(onViewNavigator()));
    viewNavigatorAction->setCheckable(true);

    // add part listener for view navigator
    viewNavigatorPartListener.reset(new PartListenerForViewNavigator(viewNavigatorAction));
    window->GetPartService()->AddPartListener(viewNavigatorPartListener.data());
    berry::IViewPart::Pointer viewnavigatorview = window->GetActivePage()->FindView("org.mitk.views.viewnavigator");
    viewNavigatorAction->setChecked(false);
    if (viewnavigatorview)
    {
      bool isViewNavigatorVisible = window->GetActivePage()->IsPartVisible(viewnavigatorview);
      if (isViewNavigatorVisible)
        viewNavigatorAction->setChecked(true);
    }
    viewNavigatorAction->setToolTip("Toggle View Navigator");
  }

  mainActionsToolBar->addAction(fileOpenAction);
  mainActionsToolBar->addAction(fileSaveProjectAction);
  mainActionsToolBar->addAction(closeProjectAction);
  mainActionsToolBar->addAction(undoAction);
  mainActionsToolBar->addAction(redoAction);
  if(this->GetWindowConfigurer()->GetWindow()->GetWorkbench()->GetEditorRegistry()->FindEditor("org.mitk.editors.dicombrowser"))
  {
    mainActionsToolBar->addAction(openDicomEditorAction);
  }
  if (this->GetWindowConfigurer()->GetWindow()->GetWorkbench()->GetEditorRegistry()->FindEditor("org.mitk.editors.stdmultiwidget"))
  {
    mainActionsToolBar->addAction(openStdMultiWidgetEditorAction);
  }
  if (this->GetWindowConfigurer()->GetWindow()->GetWorkbench()->GetEditorRegistry()->FindEditor("org.mitk.editors.mxnmultiwidget"))
  {
    mainActionsToolBar->addAction(openMxNMultiWidgetEditorAction);
  }

  if (imageNavigatorViewFound)
  {
    mainActionsToolBar->addAction(imageNavigatorAction);
  }
  if (viewNavigatorFound)
  {
    mainActionsToolBar->addAction(viewNavigatorAction);
  }
  mainWindow->addToolBar(mainActionsToolBar);

  // ==== Undo/Redo update handling ==================================
  auto undoModel = dynamic_cast<mitk::VerboseLimitedLinearUndo*>(mitk::UndoController::GetCurrentUndoModel());
  if (nullptr != undoModel)
  {
    m_UndoStackObserverGuard.Reset(undoModel, mitk::UndoStackEvent(), [this](const itk::EventObject&) {this->OnUndoStackChanged(); });
  }
  this->OnUndoStackChanged(); //ensure the enable state of undoAction/redoAction is correct

  // ==== Perspective Toolbar ==================================
  auto   qPerspectiveToolbar = new QToolBar;
  qPerspectiveToolbar->setObjectName("perspectiveToolBar");

  if (showPerspectiveToolbar)
  {
    qPerspectiveToolbar->addActions(perspGroup->actions());
    mainWindow->addToolBar(qPerspectiveToolbar);
  }
  else
    delete qPerspectiveToolbar;

  if (showViewToolbar)
  {
    auto* prefService = mitk::CoreServices::GetPreferencesService();
    auto* toolBarsPrefs = prefService->GetSystemPreferences()->Node(QmitkApplicationConstants::TOOL_BARS_PREFERENCES);
    bool showCategories = toolBarsPrefs->GetBool(QmitkApplicationConstants::TOOL_BARS_SHOW_CATEGORIES, true);

    // Order view descriptors by category

    QMultiMap<QString, berry::IViewDescriptor::Pointer> categoryViewDescriptorMap;

    for (const auto &labelViewDescriptorPair : VDMap)
    {
      auto viewDescriptor = labelViewDescriptorPair.second;
      auto category = !viewDescriptor->GetCategoryPath().isEmpty()
        ? viewDescriptor->GetCategoryPath().back()
        : QString();

      categoryViewDescriptorMap.insert(category, viewDescriptor);
    }

    // Create a separate toolbar for each category

    for (const auto &category : categoryViewDescriptorMap.uniqueKeys())
    {
      auto viewDescriptorsInCurrentCategory = categoryViewDescriptorMap.values(category);

      if (!viewDescriptorsInCurrentCategory.isEmpty())
      {
        auto toolbar = new QToolBar;
        toolbar->setObjectName(category);
        mainWindow->addToolBar(toolbar);

        toolbar->setVisible(toolBarsPrefs->GetBool(category.toStdString(), true));

        if (!category.isEmpty())
        {
          auto categoryButton = new QToolButton;
          categoryButton->setToolButtonStyle(Qt::ToolButtonTextOnly);
          categoryButton->setText(category);
          categoryButton->setStyleSheet("background: transparent; margin: 0; padding: 0;");

          auto action = toolbar->addWidget(categoryButton);
          action->setObjectName("category");
          action->setVisible(showCategories);

          connect(categoryButton, &QToolButton::clicked, [toolbar]()
          {
            for (QWidget* widget : toolbar->findChildren<QWidget*>())
            {
              if (QStringLiteral("qt_toolbar_ext_button") == widget->objectName() && widget->isVisible())
              {
                QMouseEvent pressEvent(QEvent::MouseButtonPress, QPointF(0.0f, 0.0f), QCursor::pos(), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
                QMouseEvent releaseEvent(QEvent::MouseButtonRelease, QPointF(0.0f, 0.0f), QCursor::pos(), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
                QApplication::sendEvent(widget, &pressEvent);
                QApplication::sendEvent(widget, &releaseEvent);
              }
            }
          });
        }

        for (const auto &viewDescriptor : std::as_const(viewDescriptorsInCurrentCategory))
        {
          auto viewAction = new berry::QtShowViewAction(window, viewDescriptor);
          toolbar->addAction(viewAction);
        }


      }
    }
  }

  QSettings settings(GetQSettingsFile(), QSettings::IniFormat);
  mainWindow->restoreState(settings.value("ToolbarPosition").toByteArray());

  auto   qStatusBar = new QStatusBar();

  //creating a QmitkStatusBar for Output on the QStatusBar and connecting it with the MainStatusBar
  auto  statusBar = new QmitkStatusBar(qStatusBar);
  //disabling the SizeGrip in the lower right corner
  statusBar->SetSizeGripEnabled(false);

  auto  progBar = new QmitkProgressBar();

  qStatusBar->addPermanentWidget(progBar, 0);
  progBar->hide();
  // progBar->AddStepsToDo(2);
  // progBar->Progress(1);

  mainWindow->setStatusBar(qStatusBar);

  if (showMemoryIndicator)
  {
    auto   memoryIndicator = new QmitkMemoryUsageIndicatorView();
    qStatusBar->addPermanentWidget(memoryIndicator, 0);
  }
}

void QmitkExtWorkbenchWindowAdvisor::PreWindowOpen()
{
  berry::IWorkbenchWindowConfigurer::Pointer configurer = GetWindowConfigurer();

  // show the shortcut bar and progress indicator, which are hidden by
  // default
  //configurer->SetShowPerspectiveBar(true);
  //configurer->SetShowFastViewBars(true);
  //configurer->SetShowProgressIndicator(true);

  //  // add the drag and drop support for the editor area
  //  configurer.addEditorAreaTransfer(EditorInputTransfer.getInstance());
  //  configurer.addEditorAreaTransfer(ResourceTransfer.getInstance());
  //  configurer.addEditorAreaTransfer(FileTransfer.getInstance());
  //  configurer.addEditorAreaTransfer(MarkerTransfer.getInstance());
  //  configurer.configureEditorAreaDropListener(new EditorAreaDropAdapter(
  //      configurer.getWindow()));

  this->HookTitleUpdateListeners(configurer);

  menuPerspectiveListener.reset(new PerspectiveListenerForMenu(this));
  configurer->GetWindow()->AddPerspectiveListener(menuPerspectiveListener.data());

  configurer->AddEditorAreaTransfer(QStringList("text/uri-list"));
  configurer->ConfigureEditorAreaDropListener(dropTargetListener.data());
}

void QmitkExtWorkbenchWindowAdvisor::PostWindowOpen()
{
  berry::WorkbenchWindowAdvisor::PostWindowOpen();
  // Force Rendering Window Creation on startup.
  berry::IWorkbenchWindowConfigurer::Pointer configurer = GetWindowConfigurer();

  mitk::CoreServicePointer<mitk::IDataStorageService> dsService(mitk::CoreServices::GetDataStorageService());
  if (dsService)
  {
    mitk::DataStorageReference dsRef = dsService->GetActiveDataStorageReference();
    mitk::DataStorageEditorInput::Pointer dsInput(new mitk::DataStorageEditorInput(dsRef));
    mitk::WorkbenchUtil::OpenEditor(configurer->GetWindow()->GetActivePage(), dsInput);
  }

  auto introPart = configurer->GetWindow()->GetWorkbench()->GetIntroManager()->GetIntro();
  if (introPart.IsNotNull())
  {
    configurer->GetWindow()->GetWorkbench()->GetIntroManager()->ShowIntro(GetWindowConfigurer()->GetWindow(), false);
  }

  ExecuteStartupDialog();
}

void QmitkExtWorkbenchWindowAdvisor::onIntro()
{
  QmitkExtWorkbenchWindowAdvisorHack::undohack->onIntro();
}

void QmitkExtWorkbenchWindowAdvisor::onHelp()
{
  QmitkExtWorkbenchWindowAdvisorHack::undohack->onHelp();
}

void QmitkExtWorkbenchWindowAdvisor::onHelpOpenHelpPerspective()
{
  QmitkExtWorkbenchWindowAdvisorHack::undohack->onHelpOpenHelpPerspective();
}

void QmitkExtWorkbenchWindowAdvisor::onAbout()
{
  QmitkExtWorkbenchWindowAdvisorHack::undohack->onAbout();
}

//--------------------------------------------------------------------------------
// Ugly hack from here on. Feel free to delete when command framework
// and undo buttons are done.
//--------------------------------------------------------------------------------

QmitkExtWorkbenchWindowAdvisorHack::QmitkExtWorkbenchWindowAdvisorHack()
  : QObject()
{
}

QmitkExtWorkbenchWindowAdvisorHack::~QmitkExtWorkbenchWindowAdvisorHack()
{
}

void QmitkExtWorkbenchWindowAdvisorHack::onUndo()
{
  mitk::UndoModel* model = mitk::UndoController::GetCurrentUndoModel();
  if (model)
  {
    if (mitk::VerboseLimitedLinearUndo* verboseundo = dynamic_cast<mitk::VerboseLimitedLinearUndo*>( model ))
    {
      mitk::VerboseLimitedLinearUndo::StackDescription descriptions = verboseundo->GetUndoDescriptions();
      if (descriptions.size() >= 1)
      {
        MITK_INFO << "Undo " << descriptions.front().second;
      }
    }
    model->Undo();
  }
  else
  {
    MITK_ERROR << "No undo model instantiated";
  }
}

void QmitkExtWorkbenchWindowAdvisorHack::onRedo()
{
  mitk::UndoModel* model = mitk::UndoController::GetCurrentUndoModel();
  if (model)
  {
    if (mitk::VerboseLimitedLinearUndo* verboseundo = dynamic_cast<mitk::VerboseLimitedLinearUndo*>( model ))
    {
      mitk::VerboseLimitedLinearUndo::StackDescription descriptions = verboseundo->GetRedoDescriptions();
      if (descriptions.size() >= 1)
      {
        MITK_INFO << "Redo " << descriptions.front().second;
      }
    }
    model->Redo();
  }
  else
  {
    MITK_ERROR << "No undo model instantiated";
  }
}

// safe calls to the complete chain
// berry::PlatformUI::GetWorkbench()->GetActiveWorkbenchWindow()->GetActivePage()->FindView("org.mitk.views.imagenavigator");
// to cover for all possible cases of closed pages etc.
static void SafeHandleNavigatorView(QString view_query_name)
{
  berry::IWorkbench* wbench = berry::PlatformUI::GetWorkbench();
  if( wbench == nullptr )
    return;

  berry::IWorkbenchWindow::Pointer wbench_window = wbench->GetActiveWorkbenchWindow();
  if( wbench_window.IsNull() )
    return;

  berry::IWorkbenchPage::Pointer wbench_page = wbench_window->GetActivePage();
  if( wbench_page.IsNull() )
    return;

  auto wbench_view = wbench_page->FindView( view_query_name );

  if( wbench_view.IsNotNull() )
  {
    bool isViewVisible = wbench_page->IsPartVisible( wbench_view );
    if( isViewVisible )
    {
      wbench_page->HideView( wbench_view );
      return;
    }

  }

  wbench_page->ShowView( view_query_name );
}

void QmitkExtWorkbenchWindowAdvisorHack::onImageNavigator()
{
  // show/hide ImageNavigatorView
  SafeHandleNavigatorView("org.mitk.views.imagenavigator");
}

void QmitkExtWorkbenchWindowAdvisorHack::onViewNavigator()
{
  // show/hide viewnavigatorView
  SafeHandleNavigatorView("org.mitk.views.viewnavigator");
}

void QmitkExtWorkbenchWindowAdvisorHack::onEditPreferences(const QString& selectedPage)
{
  QmitkPreferencesDialog _PreferencesDialog(QApplication::activeWindow());

  if (!selectedPage.isEmpty())
    _PreferencesDialog.SetSelectedPage(selectedPage);

  _PreferencesDialog.exec();
}

void QmitkExtWorkbenchWindowAdvisorHack::onQuit()
{
  berry::PlatformUI::GetWorkbench()->Close();
}

void QmitkExtWorkbenchWindowAdvisorHack::onResetPerspective()
{
  berry::PlatformUI::GetWorkbench()->GetActiveWorkbenchWindow()->GetActivePage()->ResetPerspective();
}

void QmitkExtWorkbenchWindowAdvisorHack::onClosePerspective()
{
  berry::IWorkbenchPage::Pointer page =
    berry::PlatformUI::GetWorkbench()->GetActiveWorkbenchWindow()->GetActivePage();
  page->ClosePerspective(page->GetPerspective(), true, true);
}

void QmitkExtWorkbenchWindowAdvisorHack::onNewWindow()
{
  berry::PlatformUI::GetWorkbench()->OpenWorkbenchWindow(nullptr);
}

void QmitkExtWorkbenchWindowAdvisorHack::onFullScreen()
{
  auto window = berry::PlatformUI::GetWorkbench()->GetActiveWorkbenchWindow();
  auto* mainWindow = qobject_cast<QMainWindow*>(window->GetShell()->GetControl());
  if (nullptr == mainWindow)
    return;

#ifdef __APPLE__
  mainWindow->isFullScreen() ? mainWindow->showNormal() : mainWindow->showFullScreen();
#else
  // Toggle borderless full-screen. See SetBorderlessFullScreen for why true
  // full-screen is avoided off macOS. The original flags are saved so the
  // decorations can be restored on exit.
  if (mainWindow->property("mitkBorderlessFullScreen").toBool())
  {
    mainWindow->setProperty("mitkBorderlessFullScreen", false);
    mainWindow->setWindowFlags(Qt::WindowFlags(mainWindow->property("mitkWindowedFlags").toInt()));
    mainWindow->setGeometry(mainWindow->property("mitkWindowedGeometry").toRect());
  }
  else
  {
    mainWindow->setProperty("mitkBorderlessFullScreen", true);
    mainWindow->setProperty("mitkWindowedFlags", static_cast<int>(mainWindow->windowFlags()));
    mainWindow->setProperty("mitkWindowedGeometry", mainWindow->geometry());
    SetBorderlessFullScreen(mainWindow);
  }

  mainWindow->show(); // setWindowFlags() hides the window
#endif
}

void QmitkExtWorkbenchWindowAdvisorHack::onIntro()
{
  if (berry::PlatformUI::GetWorkbench()->GetIntroManager()->HasIntro())
  {
    berry::PlatformUI::GetWorkbench()->GetIntroManager()->ShowIntro(
      berry::PlatformUI::GetWorkbench()->GetActiveWorkbenchWindow(), false);
  }
}

void QmitkExtWorkbenchWindowAdvisorHack::onHelp()
{
  ctkPluginContext* context = QmitkCommonExtPlugin::getContext();
  if (context == nullptr)
  {
    MITK_WARN << "Plugin context not set, unable to open context help";
    return;
  }

  // Check if the org.blueberry.ui.qt.help plug-in is installed and started
  QList<QSharedPointer<ctkPlugin> > plugins = context->getPlugins();
  foreach(QSharedPointer<ctkPlugin> p, plugins)
  {
    if (p->getSymbolicName() == "org.blueberry.ui.qt.help")
    {
      if (p->getState() != ctkPlugin::ACTIVE)
      {
        // try to activate the plug-in explicitly
        try
        {
          p->start(ctkPlugin::START_TRANSIENT);
        }
        catch (const ctkPluginException& pe)
        {
          MITK_ERROR << "Activating org.blueberry.ui.qt.help failed: " << pe.what();
          return;
        }
      }
    }
  }

  ctkServiceReference eventAdminRef = context->getServiceReference<ctkEventAdmin>();
  ctkEventAdmin* eventAdmin = nullptr;
  if (eventAdminRef)
  {
    eventAdmin = context->getService<ctkEventAdmin>(eventAdminRef);
  }
  if (eventAdmin == nullptr)
  {
    MITK_WARN << "ctkEventAdmin service not found. Unable to open context help";
  }
  else
  {
    ctkEvent ev("org/blueberry/ui/help/CONTEXTHELP_REQUESTED");
    eventAdmin->postEvent(ev);
  }
}

void QmitkExtWorkbenchWindowAdvisorHack::onHelpOpenHelpPerspective()
{
  berry::PlatformUI::GetWorkbench()->ShowPerspective("org.blueberry.perspectives.help",
    berry::PlatformUI::GetWorkbench()->GetActiveWorkbenchWindow());
}

void QmitkExtWorkbenchWindowAdvisorHack::onAbout()
{
  auto aboutDialog = new QmitkAboutDialog(QApplication::activeWindow(), {});
  aboutDialog->open();
}

void QmitkExtWorkbenchWindowAdvisor::HookTitleUpdateListeners(berry::IWorkbenchWindowConfigurer::Pointer configurer)
{
  // hook up the listeners to update the window title
  titlePartListener.reset(new PartListenerForTitle(this));
  titlePerspectiveListener.reset(new PerspectiveListenerForTitle(this));
  editorPropertyListener.reset(new berry::PropertyChangeIntAdapter<
    QmitkExtWorkbenchWindowAdvisor>(this,
    &QmitkExtWorkbenchWindowAdvisor::PropertyChange));

  //    configurer.getWindow().addPageListener(new IPageListener() {
  //      public void pageActivated(IWorkbenchPage page) {
  //        updateTitle(false);
  //      }
  //
  //      public void pageClosed(IWorkbenchPage page) {
  //        updateTitle(false);
  //      }
  //
  //      public void pageOpened(IWorkbenchPage page) {
  //        // do nothing
  //      }
  //    });

  configurer->GetWindow()->AddPerspectiveListener(titlePerspectiveListener.data());
  configurer->GetWindow()->GetPartService()->AddPartListener(titlePartListener.data());
}

QString QmitkExtWorkbenchWindowAdvisor::ComputeTitle()
{
  berry::IWorkbenchWindowConfigurer::Pointer configurer = GetWindowConfigurer();
  berry::IWorkbenchPage::Pointer currentPage = configurer->GetWindow()->GetActivePage();
  berry::IEditorPart::Pointer activeEditor;
  if (currentPage)
  {
    activeEditor = lastActiveEditor.Lock();
  }

  QString title;
  berry::IProduct::Pointer product = berry::Platform::GetProduct();
  if (product.IsNotNull())
  {
    title = product->GetName();
  }
  if (title.isEmpty())
  {
    // instead of the product name, we use a custom variable for now
    title = productName;
  }

  if(showMitkVersionInfo)
  {
    QString mitkVersionInfo = MITK_REVISION_DESC;

    if(mitkVersionInfo.isEmpty())
      mitkVersionInfo = MITK_VERSION_STRING;

    title += " " + mitkVersionInfo;
  }

  if (showVersionInfo)
  {
    // add version informatioin
    QString versions = QString(" (ITK %1.%2.%3 | VTK %4.%5.%6 | Qt %7)")
      .arg(ITK_VERSION_MAJOR).arg(ITK_VERSION_MINOR).arg(ITK_VERSION_PATCH)
      .arg(VTK_MAJOR_VERSION).arg(VTK_MINOR_VERSION).arg(VTK_BUILD_VERSION)
      .arg(QT_VERSION_STR);

    title += versions;
  }

  if (currentPage)
  {
    if (activeEditor)
    {
      lastEditorTitle = activeEditor->GetTitleToolTip();
      if (!lastEditorTitle.isEmpty())
        title = lastEditorTitle + " - " + title;
    }
    berry::IPerspectiveDescriptor::Pointer persp = currentPage->GetPerspective();
    QString label = "";
    if (persp)
    {
      label = persp->GetLabel();
    }
    berry::IAdaptable* input = currentPage->GetInput();
    if (input && input != wbAdvisor->GetDefaultPageInput())
    {
      label = currentPage->GetLabel();
    }
    if (!label.isEmpty())
    {
      title = label + " - " + title;
    }
  }

  title += " (Not for use in diagnosis or treatment of patients)";

  return title;
}

void QmitkExtWorkbenchWindowAdvisor::RecomputeTitle()
{
  berry::IWorkbenchWindowConfigurer::Pointer configurer = GetWindowConfigurer();
  QString oldTitle = configurer->GetTitle();
  QString newTitle = ComputeTitle();
  if (newTitle != oldTitle)
  {
    configurer->SetTitle(newTitle);
  }
}

void QmitkExtWorkbenchWindowAdvisor::UpdateTitle(bool editorHidden)
{
  berry::IWorkbenchWindowConfigurer::Pointer configurer = GetWindowConfigurer();
  berry::IWorkbenchWindow::Pointer window = configurer->GetWindow();
  berry::IEditorPart::Pointer activeEditor;
  berry::IWorkbenchPage::Pointer currentPage = window->GetActivePage();
  berry::IPerspectiveDescriptor::Pointer persp;
  berry::IAdaptable* input = nullptr;

  if (currentPage)
  {
    activeEditor = currentPage->GetActiveEditor();
    persp = currentPage->GetPerspective();
    input = currentPage->GetInput();
  }

  if (editorHidden)
  {
    activeEditor = nullptr;
  }

  // Nothing to do if the editor hasn't changed
  if (activeEditor == lastActiveEditor.Lock() && currentPage == lastActivePage.Lock()
    && persp == lastPerspective.Lock() && input == lastInput)
  {
    return;
  }

  auto lockedLastActiveEditor = lastActiveEditor.Lock();

  if (lockedLastActiveEditor.IsNotNull())
  {
    lockedLastActiveEditor->RemovePropertyListener(editorPropertyListener.data());
  }

  lastActiveEditor = activeEditor;
  lastActivePage = currentPage;
  lastPerspective = persp;
  lastInput = input;

  if (activeEditor)
  {
    activeEditor->AddPropertyListener(editorPropertyListener.data());
  }

  RecomputeTitle();
}

void QmitkExtWorkbenchWindowAdvisor::PropertyChange(const berry::Object::Pointer& /*source*/, int propId)
{
  if (propId == berry::IWorkbenchPartConstants::PROP_TITLE)
  {
    auto lockedLastActiveEditor = lastActiveEditor.Lock();

    if (lockedLastActiveEditor.IsNotNull())
    {
      QString newTitle = lockedLastActiveEditor->GetPartName();
      if (lastEditorTitle != newTitle)
      {
        RecomputeTitle();
      }
    }
  }
}

void QmitkExtWorkbenchWindowAdvisor::SetPerspectiveExcludeList(const QList<QString>& v)
{
  this->perspectiveExcludeList = v;
}

QList<QString> QmitkExtWorkbenchWindowAdvisor::GetPerspectiveExcludeList()
{
  return this->perspectiveExcludeList;
}

void QmitkExtWorkbenchWindowAdvisor::SetViewExcludeList(const QList<QString>& v)
{
  this->viewExcludeList = v;
}

QList<QString> QmitkExtWorkbenchWindowAdvisor::GetViewExcludeList()
{
  return this->viewExcludeList;
}

void QmitkExtWorkbenchWindowAdvisor::PostWindowClose()
{
  berry::IWorkbenchWindow::Pointer window = this->GetWindowConfigurer()->GetWindow();
  QMainWindow* mainWindow = static_cast<QMainWindow*> (window->GetShell()->GetControl());

  auto fileName = this->GetQSettingsFile();

  if (!fileName.isEmpty())
  {
    QSettings settings(fileName, QSettings::IniFormat);
    settings.setValue("ToolbarPosition", mainWindow->saveState());
  }
}

QString QmitkExtWorkbenchWindowAdvisor::GetQSettingsFile() const
{
  QFileInfo settingsInfo = QmitkCommonExtPlugin::getContext()->getDataFile(QT_SETTINGS_FILENAME);
  return settingsInfo.canonicalFilePath();
}

void QmitkExtWorkbenchWindowAdvisor::OnUndoStackChanged()
{
  auto undoModel = dynamic_cast<mitk::VerboseLimitedLinearUndo*>(mitk::UndoController::GetCurrentUndoModel());

  this->undoAction->setEnabled(nullptr != undoModel && !undoModel->UndoListEmpty());
  this->redoAction->setEnabled(nullptr != undoModel && !undoModel->RedoListEmpty());
}
