// Created on: 2017-06-16
// Created by: Natalia ERMOLAEVA
// Copyright (c) 2017 OPEN CASCADE SAS
//
// This file is part of Open CASCADE Technology software library.
//
// This library is free software; you can redistribute it and/or modify it under
// the terms of the GNU Lesser General Public License version 2.1 as published
// by the Free Software Foundation, with special exception defined in the file
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
// distribution for complete text of the license and disclaimer of any warranty.
//
// Alternatively, this file may be used under the terms of Open CASCADE
// commercial license or contractual agreement. 

#include <inspector/DFBrowser_Window.hxx>

#include <AIS_InteractiveContext.hxx>
#include <AIS_InteractiveObject.hxx>
#include <AIS_ListOfInteractive.hxx>

#include <inspector/DFBrowserPane_AttributePaneAPI.hxx>

#include <inspector/DFBrowser_AttributePaneStack.hxx>
#include <inspector/DFBrowser_AttributePaneType.hxx>
#include <inspector/DFBrowser_DumpView.hxx>
#include <inspector/DFBrowser_Item.hxx>
#include <inspector/DFBrowser_ItemApplication.hxx>
#include <inspector/DFBrowser_Module.hxx>
#include <inspector/DFBrowser_OpenApplication.hxx>
#include <inspector/DFBrowser_PropertyPanel.hxx>
#include <inspector/DFBrowser_SearchLine.hxx>
#include <inspector/DFBrowser_SearchView.hxx>
#include <inspector/DFBrowser_Tools.hxx>
#include <inspector/DFBrowser_TreeLevelLine.hxx>
#include <inspector/DFBrowser_TreeLevelView.hxx>
#include <inspector/DFBrowser_TreeModel.hxx>

#include <inspector/DFBrowserPane_AttributePaneSelector.hxx>
#include <inspector/DFBrowserPane_SelectionKind.hxx>
#include <inspector/DFBrowserPane_Tools.hxx>

#include <inspector/TreeModel_ContextMenu.hxx>
#include <inspector/TreeModel_Tools.hxx>

#include <inspector/ViewControl_PropertyView.hxx>
#include <inspector/ViewControl_TreeView.hxx>

#include <OSD_Directory.hxx>
#include <OSD_Environment.hxx>
#include <OSD_Protection.hxx>

#include <inspector/View_Displayer.hxx>
#include <inspector/View_ToolBar.hxx>
#include <inspector/View_Viewer.hxx>
#include <inspector/View_Window.hxx>

#include <TDF_Tool.hxx>
#include <inspector/ViewControl_MessageDialog.hxx>
#include <inspector/ViewControl_Tools.hxx>

#include <Standard_WarningsDisable.hxx>
#include <QAction>
#include <QApplication>
#include <QComboBox>
#include <QDir>
#include <QDockWidget>
#include <QGridLayout>
#include <QList>
#include <QMainWindow>
#include <QItemSelectionModel>
#include <QPushButton>
#include <QTabWidget>
#include <QToolBar>
#include <QTreeView>
#include <QMenu>
#include <QMessageBox>
#include <QStatusBar>
#include <QWidget>
#if QT_VERSION < 0x050000
#include <QWindowsStyle>
#else
#include <QStyleFactory>
#endif
#include <Standard_WarningsRestore.hxx>

const int DFBROWSER_DEFAULT_TREE_VIEW_WIDTH = 325;
const int DFBROWSER_DEFAULT_TREE_VIEW_HEIGHT = 500;
const int DFBROWSER_DEFAULT_VIEW_WIDTH = 400;
const int DFBROWSER_DEFAULT_VIEW_HEIGHT = 300;

static Standard_Boolean MyIsUseDumpJson = Standard_False;

// =======================================================================
// function : Constructor
// purpose :
// =======================================================================
DFBrowser_Window::DFBrowser_Window()
: myModule (0), myParent (0), myPropertyPanel (0), myExportToShapeViewDialog (0)
{
  myMainWindow = new QMainWindow (0);

  // tree view
  myTreeView = new ViewControl_TreeView (myMainWindow);
  myTreeView->setContextMenuPolicy (Qt::CustomContextMenu);
  connect (myTreeView, SIGNAL (customContextMenuRequested (const QPoint&)),
           this, SLOT (onTreeViewContextMenuRequested (const QPoint&)));
  new TreeModel_ContextMenu (myTreeView);
  ((ViewControl_TreeView*)myTreeView)->SetPredefinedSize (QSize (DFBROWSER_DEFAULT_TREE_VIEW_WIDTH,
                                                                 DFBROWSER_DEFAULT_TREE_VIEW_HEIGHT));
  myTreeView->setHeaderHidden (true);
  myTreeView->setSortingEnabled (Standard_False);
  myMainWindow->setCentralWidget (myTreeView);

#if QT_VERSION < 0x050000
  myTreeView->setStyle (new QWindowsStyle);
#else
  myTreeView->setStyle (QStyleFactory::create("Windows"));
#endif

  myTreeLevelLine = new DFBrowser_TreeLevelLine (myMainWindow);
  connect (myTreeLevelLine->GetSearchLine(), SIGNAL (searchActivated()), this, SLOT (onSearchActivated()));
  connect (myTreeLevelLine, SIGNAL (indexSelected (const QModelIndex&)),
          this, SLOT (onTreeLevelLineSelected (const QModelIndex&)));
  connect (myTreeLevelLine, SIGNAL (updateClicked()), this, SLOT (onUpdateClicked()));

  QDockWidget* aTreeLineDockWidget = new QDockWidget (tr ("Tree Level Line"), myMainWindow);
  aTreeLineDockWidget->setObjectName (aTreeLineDockWidget->windowTitle());
  aTreeLineDockWidget->setTitleBarWidget (new QWidget(myMainWindow));
  aTreeLineDockWidget->setWidget (myTreeLevelLine->GetControl());
  myMainWindow->addDockWidget (Qt::TopDockWidgetArea, aTreeLineDockWidget);

  // property panel
  myPropertyPanel = new DFBrowser_PropertyPanel (myMainWindow);
  DFBrowser_AttributePaneStack* anAttributePaneStack = myPropertyPanel->GetAttributesStack();
  anAttributePaneStack->GetSearchView()->SetSearchLine (myTreeLevelLine->GetSearchLine());

  connect (anAttributePaneStack->GetPaneSelector(),
           SIGNAL (tableSelectionChanged (const QItemSelection&, const QItemSelection&, QItemSelectionModel*)),
           this, SLOT (onPaneSelectionChanged (const QItemSelection&, const QItemSelection&, QItemSelectionModel*)));

  DFBrowser_SearchView* aSearchView = anAttributePaneStack->GetSearchView();
  connect (aSearchView, SIGNAL (pathSelected (const QStringList&, const QString&)),
           this, SLOT (onSearchPathSelected (const QStringList&, const QString&)));
  connect (aSearchView, SIGNAL (pathDoubleClicked (const QStringList&, const QString&)),
           this, SLOT (onSearchPathDoubleClicked (const QStringList&, const QString&)));

  DFBrowser_TreeLevelView* aLevelView = anAttributePaneStack->GetTreeLevelView();
  connect (aLevelView, SIGNAL (indexSelected (const QModelIndex&)), this, SLOT (onLevelSelected (const QModelIndex&)));
  connect (aLevelView, SIGNAL (indexDoubleClicked (const QModelIndex&)),
           this, SLOT (onLevelDoubleClicked (const QModelIndex&)));

  // property custom panel with specific parameters of attributes
  QDockWidget* aPropertyPanelWidget = new QDockWidget (tr ("PropertyPanel (custom)"), myMainWindow);
  aPropertyPanelWidget->setObjectName (aPropertyPanelWidget->windowTitle());
  aPropertyPanelWidget->setTitleBarWidget (new QWidget(myMainWindow));
  aPropertyPanelWidget->setWidget (myPropertyPanel->GetControl());
  myMainWindow->addDockWidget (Qt::RightDockWidgetArea, aPropertyPanelWidget);

  // property panel
  myUseDumpJson = new QWidget(myMainWindow);
  QVBoxLayout* aLay = new QVBoxLayout(myUseDumpJson);
  QPushButton* aUseDumpJson = new QPushButton ("Use DumpJson", myMainWindow);
  aLay->addWidget (aUseDumpJson);
  aLay->addStretch(1);
  connect(aUseDumpJson, SIGNAL (clicked (bool)), this, SLOT (onUseDumpJson()));
  myUseDumpJson->setVisible (false);

  myPropertyPanelWidget = new QDockWidget (tr ("PropertyPanel"), myMainWindow);
  myPropertyView = new ViewControl_PropertyView (myMainWindow,
    QSize(DFBROWSER_DEFAULT_VIEW_WIDTH, DFBROWSER_DEFAULT_VIEW_HEIGHT));
  myPropertyPanelWidget->setObjectName (myPropertyPanelWidget->windowTitle());
  myPropertyPanelWidget->setTitleBarWidget (new QWidget(myMainWindow));
  myPropertyPanelWidget->setWidget (myPropertyView->GetControl());
  updatePropertyPanelWidget();
  myMainWindow->addDockWidget (Qt::RightDockWidgetArea, myPropertyPanelWidget);

  // dump view window
  QWidget* aDumpWidget = new QWidget(myMainWindow);
  QVBoxLayout* aDumpLay = new QVBoxLayout(aDumpWidget);
  aDumpLay->setMargin(0);
  myDumpView = new DFBrowser_DumpView(aDumpWidget);
  aDumpLay->addWidget(myDumpView->GetControl());
  QDockWidget* aDumpDockWidget = new QDockWidget(tr("Dump"), myMainWindow);
  aDumpDockWidget->setObjectName(aDumpDockWidget->windowTitle());

  aDumpDockWidget->setWidget(aDumpWidget);
  myMainWindow->addDockWidget(Qt::RightDockWidgetArea, aDumpDockWidget);

  // view
  myViewWindow = new View_Window (myMainWindow);
  myViewWindow->SetPredefinedSize (DFBROWSER_DEFAULT_VIEW_WIDTH, DFBROWSER_DEFAULT_VIEW_HEIGHT);

  QDockWidget* aViewDockWidget = new QDockWidget (tr ("View"), myMainWindow);
  aViewDockWidget->setObjectName (aViewDockWidget->windowTitle());
  aViewDockWidget->setTitleBarWidget (myViewWindow->ViewToolBar()->GetControl());
  aViewDockWidget->setWidget (myViewWindow);
  myMainWindow->addDockWidget (Qt::RightDockWidgetArea, aViewDockWidget);

  QColor aHColor (229, 243, 255);
  myViewWindow->Displayer()->SetAttributeColor (Quantity_Color(aHColor.red() / 255., aHColor.green() / 255.,
                                                aHColor.blue() / 255., Quantity_TOC_sRGB), View_PresentationType_Additional);

  myMainWindow->splitDockWidget (myPropertyPanelWidget, aViewDockWidget, Qt::Vertical);
  myMainWindow->tabifyDockWidget (myPropertyPanelWidget, aPropertyPanelWidget);

  myMainWindow->tabifyDockWidget (aDumpDockWidget, aViewDockWidget);

  myTreeView->resize (DFBROWSER_DEFAULT_TREE_VIEW_WIDTH, DFBROWSER_DEFAULT_TREE_VIEW_HEIGHT);
}

// =======================================================================
// function : Destructor
// purpose :
// =======================================================================
DFBrowser_Window::~DFBrowser_Window()
{
  delete myModule;
}

// =======================================================================
// function : SetParent
// purpose :
// =======================================================================
void DFBrowser_Window::SetParent (void* theParent)
{
  myParent = (QWidget*)theParent;
  if (myParent)
  {
    QLayout* aLayout = myParent->layout();
    if (aLayout)
      aLayout->addWidget (GetMainWindow());

    if (!myOpenedFileName.isEmpty())
      myParent->setObjectName(myOpenedFileName);
  }
}

// =======================================================================
// function : FillActionsMenu
// purpose :
// =======================================================================
void DFBrowser_Window::FillActionsMenu (void* theMenu)
{
  QMenu* aMenu = (QMenu*)theMenu;
  QList<QDockWidget*> aDockwidgets = myMainWindow->findChildren<QDockWidget*>();
  for (QList<QDockWidget*>::iterator it = aDockwidgets.begin(); it != aDockwidgets.end(); ++it)
  {
    QDockWidget* aDockWidget = *it;
    if (aDockWidget->parentWidget() == myMainWindow)
      aMenu->addAction (aDockWidget->toggleViewAction());
  }
}

// =======================================================================
// function : GetPreferences
// purpose :
// =======================================================================
void DFBrowser_Window::GetPreferences (TInspectorAPI_PreferencesDataMap& theItem)
{
  theItem.Bind ("geometry",  TreeModel_Tools::ToString (myMainWindow->saveState()).toStdString().c_str());

  QMap<QString, QString> anItems;
  TreeModel_Tools::SaveState (myTreeView, anItems);
  View_Window::SaveState(myViewWindow, anItems);

  for (QMap<QString, QString>::const_iterator anItemsIt = anItems.begin(); anItemsIt != anItems.end(); anItemsIt++)
    theItem.Bind (anItemsIt.key().toStdString().c_str(), anItemsIt.value().toStdString().c_str());
}

// =======================================================================
// function : SetPreferences
// purpose :
// =======================================================================
void DFBrowser_Window::SetPreferences (const TInspectorAPI_PreferencesDataMap& theItem)
{
  if (theItem.IsEmpty())
  {
    TreeModel_Tools::SetDefaultHeaderSections (myTreeView);
    return;
  }

  for (TInspectorAPI_IteratorOfPreferencesDataMap anItemIt (theItem); anItemIt.More(); anItemIt.Next())
  {
    if (anItemIt.Key().IsEqual ("geometry"))
      myMainWindow->restoreState (TreeModel_Tools::ToByteArray (anItemIt.Value().ToCString()));
    else if (TreeModel_Tools::RestoreState (myTreeView, anItemIt.Key().ToCString(), anItemIt.Value().ToCString()))
      continue;
    else if (View_Window::RestoreState(myViewWindow, anItemIt.Key().ToCString(), anItemIt.Value().ToCString()))
      continue;
  }
}

// =======================================================================
// function : UpdateContent
// purpose :
// =======================================================================
void DFBrowser_Window::UpdateContent()
{
  TCollection_AsciiString aName = "TKDFBrowser";

  if (myParameters->FindParameters (aName))
    Init(myParameters->Parameters (aName));
  else
    Init(NCollection_List<Handle(Standard_Transient)>());

  if (myParameters->FindFileNames(aName))
  {
    NCollection_List<TCollection_AsciiString> aFileNames = myParameters->FileNames (aName);
    if (aFileNames.Extent() > 0) // only one document file might be opened
      OpenFile (aFileNames.First());
    myParameters->SetFileNames(aName, NCollection_List<TCollection_AsciiString>());
  }
  onUpdateClicked();

  // make parameter items selected if defined
  if (myParameters->FindSelectedNames(aName))
  {
    const NCollection_List<TCollection_AsciiString>& aSelected = myParameters->GetSelectedNames (aName);

    DFBrowser_TreeModel* aTreeModel = dynamic_cast<DFBrowser_TreeModel*> (myTreeView->model());
    Handle(TDocStd_Application) anApplication = aTreeModel->GetTDocStdApplication();

    QItemSelectionModel* aSelectionModel = myTreeView->selectionModel();
    aSelectionModel->clear();

    NCollection_List<TCollection_AsciiString>::Iterator aSelectedIt (aSelected);
    if (aSelectedIt.More())
    {
      TCollection_AsciiString aLabelEntry = aSelectedIt.Value();

      TDF_Label aLabel;
      for (Standard_Integer aDocId = 1, aNbDoc = anApplication->NbDocuments(); aDocId <= aNbDoc; aDocId++)
      {
        Handle(TDocStd_Document) aDocument;
        anApplication->GetDocument (aDocId, aDocument);

        TDF_Tool::Label(aDocument->GetData(), aLabelEntry.ToCString(), aLabel, Standard_False);
        if (!aLabel.IsNull())
          break;
      }
      if (!aLabel.IsNull())
      {
        QModelIndex anIndexToBeSelected = aTreeModel->FindIndex (aLabel);

        TCollection_AsciiString anAttributeType;
        aSelectedIt.Next();
        // find attribute by attribute type on the given label
        if  (aSelectedIt.More())
        {
          anAttributeType = aSelectedIt.Value();

          TreeModel_ItemBasePtr aLabelItem = TreeModel_ModelBase::GetItemByIndex (anIndexToBeSelected);
          //DFBrowser_ItemPtr anItem = itemDynamicCast<DFBrowser_Item> (anItemBase);
          for (int aChildId = 0, aNbChildren = aLabelItem->rowCount(); aChildId < aNbChildren; aChildId++)
          {
            QModelIndex anIndex = aTreeModel->index (aChildId, 0, anIndexToBeSelected);
            TreeModel_ItemBasePtr anItemBase = TreeModel_ModelBase::GetItemByIndex (anIndex);
            DFBrowser_ItemPtr anItem = itemDynamicCast<DFBrowser_Item> (anItemBase);
            if (anItem->HasAttribute())
            {
              // processing attribute in theValue
              DFBrowser_ItemApplicationPtr aRootAppItem = itemDynamicCast<DFBrowser_ItemApplication>(aTreeModel->RootItem (0));
              QString anAttributeInfo = DFBrowser_Module::GetAttributeInfo (anItem->GetAttribute(), aRootAppItem->GetModule(),
                                                      Qt::DisplayRole, 0).toString();
              if (anAttributeInfo == anAttributeType.ToCString())
              {
                anIndexToBeSelected = anIndex;
                break;
              }
            }
          }
        }
        aSelectionModel->select (anIndexToBeSelected, QItemSelectionModel::Select);
        myTreeView->scrollTo (anIndexToBeSelected);
      }
    }

    myParameters->SetSelectedNames(aName, NCollection_List<TCollection_AsciiString>());
  }
}

// =======================================================================
// function : Init
// purpose :
// =======================================================================
void DFBrowser_Window::Init (const NCollection_List<Handle(Standard_Transient)>& theParameters)
{
  Handle(TDocStd_Application) anApplication;
  if (myModule)
  {
    DFBrowser_TreeModel* anOCAFViewModel = dynamic_cast<DFBrowser_TreeModel*> (myModule->GetOCAFViewModel());
    if (anOCAFViewModel)
      anApplication = anOCAFViewModel->GetTDocStdApplication();
  }
  Handle(AIS_InteractiveContext) aContext;
  if (myModule)
    aContext = myModule->GetExternalContext();

  bool aSameApplication = !anApplication.IsNull(), aSameContext = !aContext.IsNull();
  for (NCollection_List<Handle(Standard_Transient)>::Iterator aParametersIt (theParameters);
       aParametersIt.More(); aParametersIt.Next())
  {
    Handle(Standard_Transient) anObject = aParametersIt.Value();
    // check if the object is an application
    Handle(TDocStd_Application) anIApplication = Handle(TDocStd_Application)::DownCast (anObject);
    if (!anIApplication.IsNull())
    {
      aSameApplication = anApplication == anIApplication;
      if (!aSameApplication)
        anApplication = anIApplication;
    }
    // check if the object is an interactive context
    Handle(AIS_InteractiveContext) anIContext = Handle(AIS_InteractiveContext)::DownCast (anObject);
    if (!anIContext.IsNull())
    {
      aSameContext = aContext == anIContext;
      if (!aSameContext)
        aContext = anIContext;
    }
  }
  if (aSameApplication)
  {
    if (!aSameContext && !aContext.IsNull())
    {
      myModule->SetExternalContext (aContext);
      myViewWindow->SetContext (View_ContextType_External, aContext);
    }
    return;
  }

  myModule = new DFBrowser_Module();
  myModule->CreateViewModel (myMainWindow);

  myPropertyPanel->GetAttributesStack()->SetModule (myModule);

  // model should be set after the attribute pane stack is initialized by module
  QAbstractItemModel* aModel = myModule->GetOCAFViewModel();
  setOCAFModel (aModel);
  myModule->SetOCAFViewSelectionModel (myTreeView->selectionModel());
  myTreeLevelLine->GetSearchLine()->SetModule (myModule);
  myPropertyPanel->GetAttributesStack()->GetSearchView()->InitModels();

  connect (myModule, SIGNAL (beforeUpdateTreeModel()), this, SLOT (onBeforeUpdateTreeModel()));

  if (!aContext.IsNull())
  {
    myModule->SetExternalContext (aContext);
    myViewWindow->SetContext (View_ContextType_External, aContext);
  }

  myModule->SetApplication (anApplication);
  //! expand first three levels: CUSTOM
  QModelIndex aParentIndex = aModel->index (0, 0);
  setExpandedLevels (myTreeView, aParentIndex, 3/*levels*/);
  
  myModule->SetInitialTreeViewSelection();
}

// =======================================================================
// function : OpenFile
// purpose :
// =======================================================================
void DFBrowser_Window::OpenFile (const TCollection_AsciiString& theFileName)
{
  QApplication::setOverrideCursor (Qt::WaitCursor);

  myTreeLevelLine->ClearHistory();
  QItemSelectionModel* aSelectionModel = myModule->GetOCAFViewSelectionModel();
  if (aSelectionModel)
  {
    aSelectionModel->clearSelection();
    QModelIndex anIndex;
    aSelectionModel->select (anIndex, QItemSelectionModel::ClearAndSelect);
  }

  myTreeLevelLine->ClearHistory();

  DFBrowser_TreeModel* anOCAFViewModel = dynamic_cast<DFBrowser_TreeModel*> (myModule->GetOCAFViewModel());
  anOCAFViewModel->Reset();

  //! close previous documents to open new document
  Handle(TDocStd_Application) anApplication = myModule->GetTDocStdApplication();
  if (!anApplication.IsNull())
  {
    for (int aDocId = 1, aNbDocuments = anApplication->NbDocuments(); aDocId <= aNbDocuments; aDocId++)
    {
      Handle(TDocStd_Document) aDocument;
      anApplication->GetDocument (aDocId, aDocument);
      if (!aDocument.IsNull())
        anApplication->Close (aDocument);
    }
  }

  //! open new document
  bool isSTEPFileName = false;
  anApplication = DFBrowser_OpenApplication::OpenApplication (theFileName, isSTEPFileName);
  
  if (myParent)
    myParent->setObjectName (isSTEPFileName ? QString (TCollection_AsciiString (theFileName).ToCString()) : getWindowTitle());
  else
    myOpenedFileName = isSTEPFileName ? QString(TCollection_AsciiString(theFileName).ToCString()) : getWindowTitle();

  if (anApplication.IsNull())
  {
    QApplication::restoreOverrideCursor();
    QMessageBox::information (0, "Error", QString ("File %1 can't be opened by OCAF application")
                                                   .arg(TCollection_AsciiString (theFileName).ToCString()));
  }
  else {
    myModule->SetApplication (anApplication);
    //! expand first three levels: CUSTOM
    QModelIndex aParentIndex = anOCAFViewModel->index (0, 0);
    setExpandedLevels (myTreeView, aParentIndex, 3/*levels*/);

    myModule->SetInitialTreeViewSelection();
    QApplication::restoreOverrideCursor();
  }
}

// =======================================================================
// function : getWindowTitle
// purpose :
// =======================================================================
QString DFBrowser_Window::getWindowTitle() const
{
  DFBrowser_TreeModel* anOCAFViewModel = dynamic_cast<DFBrowser_TreeModel*> (myModule->GetOCAFViewModel());
  if (!anOCAFViewModel)
    return "";

  Handle(TDocStd_Application) anApplication = anOCAFViewModel->GetTDocStdApplication();
  if (anApplication.IsNull() || anApplication->NbDocuments() == 0)
    return "";

  Handle(TDocStd_Document) aDocument;
  anApplication->GetDocument (1, aDocument);
  if (aDocument.IsNull() || !aDocument->IsSaved())
    return "";

  return DFBrowserPane_Tools::ToString (aDocument->GetPath());
}

// =======================================================================
// function : setExpandedLevels
// purpose :
// =======================================================================
void DFBrowser_Window::setExpandedLevels (QTreeView* theTreeView, const QModelIndex& theParentIndex, const int theLevels)
{
  if (theLevels <= 0)
    return;

  QAbstractItemModel* aModel = theTreeView->model();
  if (!aModel)
    return;

  theTreeView->setExpanded (theParentIndex, true);
  for (int aRowId = 0, aRows = aModel->rowCount (theParentIndex); aRowId < aRows; aRowId++)
    setExpandedLevels (theTreeView, aModel->index (aRowId, 0, theParentIndex), theLevels - 1);
}

// =======================================================================
// function : setOCAFModel
// purpose :
// =======================================================================
void DFBrowser_Window::setOCAFModel (QAbstractItemModel* theModel)
{
  myTreeView->setModel (theModel);

  QItemSelectionModel* aSelectionModel = new QItemSelectionModel (theModel);
  myTreeView->setSelectionModel (aSelectionModel);

  connect (aSelectionModel, SIGNAL (selectionChanged (const QItemSelection&, const QItemSelection&)),
           myTreeLevelLine, SLOT (OnTreeViewSelectionChanged (const QItemSelection&, const QItemSelection&)));
  connect(aSelectionModel, SIGNAL (selectionChanged (const QItemSelection&, const QItemSelection&)),
          myDumpView, SLOT (OnTreeViewSelectionChanged (const QItemSelection&, const QItemSelection&)));
  connect (aSelectionModel, SIGNAL (selectionChanged (const QItemSelection&, const QItemSelection&)),
           this, SLOT (onTreeViewSelectionChanged (const QItemSelection&, const QItemSelection&)));
}

// =======================================================================
// function : onBeforeUpdateTreeModel
// purpose :
// =======================================================================
void DFBrowser_Window::onBeforeUpdateTreeModel()
{
  myTreeLevelLine->ClearHistory();
}

// =======================================================================
// function : TmpDirectory
// purpose :
// =======================================================================
TCollection_AsciiString DFBrowser_Window::TmpDirectory()
{
  TCollection_AsciiString aTmpDir;
#ifdef _WIN32
  OSD_Environment anEnvironment ("TEMP");
  aTmpDir = anEnvironment.Value();
  if (aTmpDir.IsEmpty() )
  {
    anEnvironment.SetName("TMP");
    aTmpDir = anEnvironment.Value();
    if (aTmpDir.IsEmpty())
      aTmpDir = "C:\\";
  }
  OSD_Path aTmpPath (aTmpDir);
  OSD_Directory aTmpDirectory (aTmpPath);
  if (!aTmpDirectory.Exists())
    aTmpDirectory.Build(OSD_Protection());
#else
  OSD_Directory aTmpDirectory = OSD_Directory::BuildTemporary();
  OSD_Path aTmpPath;
  aTmpDirectory.Path (aTmpPath);
  aTmpPath.SystemName(aTmpDir);
#endif

  return aTmpDir;
}

// =======================================================================
// function : SetUseDumpJson
// purpose :
// =======================================================================
void DFBrowser_Window::SetUseDumpJson (const Standard_Boolean theValue)
{
  MyIsUseDumpJson = theValue;
}

// =======================================================================
// function : IsUseDumpJson
// purpose :
// =======================================================================
Standard_Boolean DFBrowser_Window::IsUseDumpJson()
{
  return MyIsUseDumpJson;
}

// =======================================================================
// function : onTreeViewContextMenuRequested
// purpose :
// =======================================================================
void DFBrowser_Window::onTreeViewContextMenuRequested (const QPoint& thePosition)
{
  QMenu* aMenu = new QMenu(GetMainWindow());
  aMenu->addAction (ViewControl_Tools::CreateAction (tr ("Expand"), SLOT (onExpand()), GetMainWindow(), this));
  aMenu->addAction (ViewControl_Tools::CreateAction (tr ("Expand All"), SLOT (onExpandAll()), GetMainWindow(), this));
  aMenu->addAction (ViewControl_Tools::CreateAction (tr ("Collapse All"), SLOT (onCollapseAll()), GetMainWindow(), this));

  aMenu->addSeparator();
  QAction* aUseDumpJsonAction = ViewControl_Tools::CreateAction (tr ("Use DumpJson"), SLOT (onUseDumpJson()), GetMainWindow(), this);
  aUseDumpJsonAction->setCheckable(true);
  aUseDumpJsonAction->setChecked (IsUseDumpJson());
  aMenu->addAction (aUseDumpJsonAction);

  QPoint aPoint = myTreeView->mapToGlobal (thePosition);
  aMenu->exec (aPoint);
}

// =======================================================================
// function : onExpand
// purpose :
// =======================================================================
void DFBrowser_Window::onExpand()
{
  QApplication::setOverrideCursor (Qt::WaitCursor);

  QItemSelectionModel* aSelectionModel = myTreeView->selectionModel();
  QModelIndexList aSelectedIndices = aSelectionModel->selectedIndexes();
  for (int aSelectedId = 0, aSize = aSelectedIndices.size(); aSelectedId < aSize; aSelectedId++)
  {
    int aLevels = 2;
    TreeModel_Tools::SetExpanded (myTreeView, aSelectedIndices[aSelectedId], true, aLevels);
  }
  QApplication::restoreOverrideCursor();
}

// =======================================================================
// function : onExpandAll
// purpose :
// =======================================================================
void DFBrowser_Window::onExpandAll()
{
  QApplication::setOverrideCursor (Qt::WaitCursor);

  QItemSelectionModel* aSelectionModel = myTreeView->selectionModel();
  QModelIndexList aSelectedIndices = aSelectionModel->selectedIndexes();
  for (int  aSelectedId = 0, aSize = aSelectedIndices.size(); aSelectedId < aSize; aSelectedId++)
  {
    int aLevels = -1;
    TreeModel_Tools::SetExpanded (myTreeView, aSelectedIndices[aSelectedId], true, aLevels);
  }
  QApplication::restoreOverrideCursor();
}

// =======================================================================
// function : onCollapseAll
// purpose :
// =======================================================================
void DFBrowser_Window::onCollapseAll()
{
  QItemSelectionModel* aSelectionModel = myTreeView->selectionModel();
  QModelIndexList aSelectedIndices = aSelectionModel->selectedIndexes();
  for (int aSelectedId = 0, aSize = aSelectedIndices.size(); aSelectedId < aSize; aSelectedId++) {
    int aLevels = -1;
    TreeModel_Tools::SetExpanded (myTreeView, aSelectedIndices[aSelectedId], false, aLevels);
  }
}

// =======================================================================
// function : onUseDumpJson
// purpose :
// =======================================================================
void DFBrowser_Window::onUseDumpJson()
{
  SetUseDumpJson(!IsUseDumpJson());
  updatePropertyPanelWidget();

  QApplication::setOverrideCursor (Qt::WaitCursor);
  myModule->UpdateTreeModel();
  QApplication::restoreOverrideCursor();
}

// =======================================================================
// function : onTreeViewSelectionChanged
// purpose :
// =======================================================================
void DFBrowser_Window::onTreeViewSelectionChanged (const QItemSelection& theSelected,
                                                   const QItemSelection& theDeselected)
{
  if (!myModule)
    return;

  if (IsUseDumpJson() && myPropertyPanelWidget->toggleViewAction()->isChecked())
  {
      myPropertyView->Init (ViewControl_Tools::CreateTableModelValues (myTreeView->selectionModel()));
  }

  // previuos selection should be cleared in the panel selectors
  DFBrowser_AttributePaneStack* anAttributePaneStack = myPropertyPanel->GetAttributesStack();
  anAttributePaneStack->GetPaneSelector()->ClearSelected();

  myPropertyPanel->UpdateBySelectionChanged (theSelected, theDeselected);
  anAttributePaneStack->GetTreeLevelView()->UpdateByTreeSelectionChanged (theSelected, theDeselected);

  QModelIndexList aSelectedIndices = theSelected.indexes();
  QModelIndex aSelectedIndex = TreeModel_ModelBase::SingleSelected (aSelectedIndices, 0);

  myTreeView->scrollTo (aSelectedIndex);
  View_Displayer* aDisplayer = myViewWindow->Displayer();
  
  aDisplayer->ErasePresentations (View_PresentationType_Additional, false);
  aDisplayer->DisplayPresentation (findPresentation (aSelectedIndex), View_PresentationType_Main);
}

// =======================================================================
// function : onSearchActivated
// purpose :
// =======================================================================
void DFBrowser_Window::onSearchActivated()
{
  myPropertyPanel->GetAttributesStack()->SetPaneMode ((myTreeLevelLine->GetSearchLine()->Text().isEmpty()
                        ? DFBrowser_AttributePaneType_ItemView : DFBrowser_AttributePaneType_SearchView));
}

// =======================================================================
// function : onPaneSelectionChanged
// purpose :
// =======================================================================
void DFBrowser_Window::onPaneSelectionChanged (const QItemSelection&,
                                               const QItemSelection&,
                                               QItemSelectionModel* theModel)
{
  DFBrowserPane_AttributePaneAPI* anAttributePane = myPropertyPanel->GetAttributesStack()->GetCurrentPane();
  switch (anAttributePane->GetSelectionKind (theModel))
  {
    case DFBrowserPane_SelectionKind_ExportToShapeViewer:
    {
      QItemSelectionModel* aSelectionModel = theModel;
      QModelIndexList aSelectedIndices = aSelectionModel->selectedIndexes();
      if (aSelectedIndices.size() != 1)
        return;

      TCollection_AsciiString aPluginName ("TKShapeView");
      NCollection_List<Handle(Standard_Transient)> aParameters;
      if (myParameters->FindParameters (aPluginName))
        aParameters = myParameters->Parameters (aPluginName);

      NCollection_List<TCollection_AsciiString> anItemNames;
      if (myParameters->FindSelectedNames (aPluginName))
        anItemNames = myParameters->GetSelectedNames (aPluginName);

      int aParametersCount = aParameters.Extent();
      anAttributePane->GetSelectionParameters (aSelectionModel, aParameters, anItemNames);
      if (aParametersCount != aParameters.Extent()) // some TShapes are added
      {
        TCollection_AsciiString aPluginShortName = aPluginName.SubString (3, aPluginName.Length());
        QString aMessage = QString ("TShape %1 is sent to %2.")
          .arg (Standard_Dump::GetPointerInfo (aParameters.Last()).ToCString())
          .arg (aPluginShortName.ToCString());
        QString aQuestion = QString ("Would you like to activate %1 immediately?\n")
          .arg (aPluginShortName.ToCString()).toStdString().c_str();
        if (!myExportToShapeViewDialog)
          myExportToShapeViewDialog = new ViewControl_MessageDialog (myParent, aMessage, aQuestion);
        else
          myExportToShapeViewDialog->SetInformation (aMessage);
        myExportToShapeViewDialog->Start();

        myParameters->SetSelectedNames (aPluginName, anItemNames);
        myParameters->SetParameters (aPluginName, aParameters, myExportToShapeViewDialog->IsAccepted());
      }
      return;
    }
    case DFBrowserPane_SelectionKind_ExportToBREP:
    case DFBrowserPane_SelectionKind_LabelReferences:
    case DFBrowserPane_SelectionKind_AttributeReferences:
    default: break;
  }

  QItemSelectionModel* aSelectionModel = myTreeView->selectionModel();
  QModelIndexList aSelectedIndices = aSelectionModel->selectedIndexes();
  if (aSelectedIndices.size() != 1)
    return;

  // make the shape visualized
  QModelIndex aSelectedIndex = aSelectedIndices.first();
  View_Displayer* aDisplayer = myViewWindow->Displayer();
  aDisplayer->DisplayPresentation (findPresentation (aSelectedIndex), View_PresentationType_Main);

  // highlight and scroll to the referenced item if it exists
  Handle(TDF_Attribute) anAttribute = myModule->FindAttribute (aSelectedIndex);
  NCollection_List<TDF_Label> aReferences;
  Handle(Standard_Transient) aPresentation;
  anAttributePane->GetReferences (anAttribute, aReferences, aPresentation);
  QModelIndexList anIndices;
  DFBrowser_TreeModel* aTreeModel = dynamic_cast<DFBrowser_TreeModel*> (myTreeView->model());
  if (!aReferences.IsEmpty())
    aTreeModel->ConvertToIndices (aReferences, anIndices);
  else {
    NCollection_List<Handle(TDF_Attribute)> anAttributeReferences;
    anAttributePane->GetAttributeReferences (anAttribute, anAttributeReferences, aPresentation);
    aTreeModel->ConvertToIndices (anAttributeReferences, anIndices);
  }
  highlightIndices (anIndices);
  // display either the reference presentation of the panel or find a presentation if the reference
  // is an attribute
  if (!aPresentation.IsNull())
    aDisplayer->DisplayPresentation (aPresentation, View_PresentationType_Additional);
  else {
    aDisplayer->ErasePresentations (View_PresentationType_Additional, false);
    AIS_ListOfInteractive aDisplayed;
    findPresentations (anIndices, aDisplayed);
    for (AIS_ListIteratorOfListOfInteractive aDisplayedIt (aDisplayed); aDisplayedIt.More(); aDisplayedIt.Next())
      aDisplayer->DisplayPresentation (aDisplayedIt.Value(), View_PresentationType_Additional, false);

    aDisplayer->UpdateViewer();
  }
}

// =======================================================================
// function : onTreeLevelLineSelected
// purpose :
// =======================================================================
void DFBrowser_Window::onTreeLevelLineSelected (const QModelIndex& theIndex)
{
  QItemSelectionModel* aSelectionModel = myTreeView->selectionModel();
  if (theIndex.isValid())
    aSelectionModel->select (theIndex, QItemSelectionModel::ClearAndSelect);
  else
    aSelectionModel->clearSelection();
}

// =======================================================================
// function : onUpdateClicked
// purpose :
// =======================================================================
void DFBrowser_Window::onUpdateClicked()
{
  if (myModule)
    myModule->UpdateTreeModel();
}

// =======================================================================
// function : onSearchPathSelected
// purpose :
// =======================================================================
void DFBrowser_Window::onSearchPathSelected (const QStringList& thePath, const QString& theValue)
{
  DFBrowser_TreeModel* aDFBrowserModel = dynamic_cast<DFBrowser_TreeModel*> (myTreeView->model());
  const QModelIndex& anIndex = aDFBrowserModel->FindIndexByPath (thePath, theValue);

  if (anIndex.isValid())
  {
    QModelIndexList anIndices;
    anIndices.append (anIndex);
    highlightIndices (anIndices);
  }
}

// =======================================================================
// function : onSearchPathDoubleClicked
// purpose :
// =======================================================================
void DFBrowser_Window::onSearchPathDoubleClicked (const QStringList& thePath, const QString& theValue)
{
  DFBrowser_TreeModel* aDFBrowserModel = dynamic_cast<DFBrowser_TreeModel*> (myTreeView->model());
  const QModelIndex& anIndex = aDFBrowserModel->FindIndexByPath (thePath, theValue);

  QItemSelectionModel* aSelectionModel = myTreeView->selectionModel();
  if (anIndex.isValid())
    aSelectionModel->select (anIndex, QItemSelectionModel::ClearAndSelect);
  else
    aSelectionModel->clearSelection();
}

// =======================================================================
// function : onLevelSelected
// purpose :
// =======================================================================
void DFBrowser_Window::onLevelSelected (const QModelIndex& theIndex)
{
  if (!theIndex.isValid())
    return;

  QModelIndexList anIndices;
  anIndices.append (theIndex);
  highlightIndices (anIndices);
  View_Displayer* aDisplayer = myViewWindow->Displayer();
  aDisplayer->ErasePresentations (View_PresentationType_Additional, false);
  aDisplayer->DisplayPresentation (findPresentation (theIndex), View_PresentationType_Main);
}

// =======================================================================
// function : onLevelDoubleClicked
// purpose :
// =======================================================================
void DFBrowser_Window::onLevelDoubleClicked (const QModelIndex& theIndex)
{
  QItemSelectionModel* aSelectionModel = myTreeView->selectionModel();
  if (theIndex.isValid())
    aSelectionModel->select (theIndex, QItemSelectionModel::ClearAndSelect);
  else
    aSelectionModel->clearSelection();
}

// =======================================================================
// function : highlightIndices
// purpose :
// =======================================================================
void DFBrowser_Window::highlightIndices (const QModelIndexList& theIndices)
{
  QAbstractItemModel* aModel = myTreeView->model();
  if (!aModel)
    return;

  DFBrowser_TreeModel* aTreeModel = dynamic_cast<DFBrowser_TreeModel*> (aModel);
  if (!aTreeModel)
    return;

  aTreeModel->SetHighlighted (theIndices);

  QModelIndex anIndexToScroll;
  if (!theIndices.isEmpty())
    anIndexToScroll = theIndices.last(); // scroll to last selected index
  else
  {
    // scroll to tree selected item
    QItemSelectionModel* aSelectionModel = myTreeView->selectionModel();
    QModelIndexList aSelectedIndices = aSelectionModel->selectedIndexes();
    if (aSelectedIndices.size() == 1)
      anIndexToScroll = aSelectedIndices.first();
  }
  if (anIndexToScroll.isValid())
    myTreeView->scrollTo (anIndexToScroll);

  if (theIndices.isEmpty())
    myTreeView->setFocus(); // to see the selected item in active palette color

  aTreeModel->EmitLayoutChanged();
}

// =======================================================================
// function : findPresentation
// purpose :
// =======================================================================
Handle(AIS_InteractiveObject) DFBrowser_Window::findPresentation (const QModelIndex& theIndex)
{
  Handle(AIS_InteractiveObject) aPresentation;

  QModelIndexList anIndices;
  anIndices.append (theIndex);
  AIS_ListOfInteractive aDisplayed;
  findPresentations (anIndices, aDisplayed);
  if (!aDisplayed.IsEmpty())
    aPresentation = aDisplayed.First();

  return aPresentation;
}

// =======================================================================
// function : findPresentations
// purpose :
// =======================================================================
void DFBrowser_Window::findPresentations (const QModelIndexList& theIndices, AIS_ListOfInteractive& thePresentations)
{
  for (int anIndexId = 0, aCount = theIndices.size(); anIndexId < aCount; anIndexId++)
  {
    Handle(AIS_InteractiveObject) aPresentation;
    Handle(TDF_Attribute) anAttribute = myModule->FindAttribute (theIndices[anIndexId]);
    if (anAttribute.IsNull())
      continue;
    DFBrowserPane_AttributePaneAPI* anAttributePane = myModule->GetAttributePane (anAttribute);
    if (!anAttributePane)
      continue;
    aPresentation = Handle(AIS_InteractiveObject)::DownCast (anAttributePane->GetPresentation (anAttribute));
    if (aPresentation.IsNull())
      continue;

    thePresentations.Append (aPresentation);
  }
}

// =======================================================================
// function : updatePropertyPanelWidget
// purpose :
// =======================================================================
void DFBrowser_Window::updatePropertyPanelWidget()
{
  bool aUseDumpJson = IsUseDumpJson();

  myUseDumpJson->setVisible (!aUseDumpJson);
  myPropertyPanelWidget->setWidget (aUseDumpJson ? myPropertyView->GetControl() : myUseDumpJson);
}
