1 /***************************************************************************
2     qgsbrowserdockwidget.cpp
3     ---------------------
4     begin                : July 2011
5     copyright            : (C) 2011 by Martin Dobias
6     email                : wonder dot sk at gmail dot com
7  ***************************************************************************
8  *                                                                         *
9  *   This program is free software; you can redistribute it and/or modify  *
10  *   it under the terms of the GNU General Public License as published by  *
11  *   the Free Software Foundation; either version 2 of the License, or     *
12  *   (at your option) any later version.                                   *
13  *                                                                         *
14  ***************************************************************************/
15 #include "qgsbrowserdockwidget.h"
16 #include "qgsbrowserdockwidget_p.h"
17 
18 #include <QAbstractTextDocumentLayout>
19 #include <QHeaderView>
20 #include <QTreeView>
21 #include <QMenu>
22 #include <QToolButton>
23 #include <QFileDialog>
24 #include <QPlainTextDocumentLayout>
25 #include <QSortFilterProxyModel>
26 
27 #include "qgsbrowserguimodel.h"
28 #include "qgsbrowsertreeview.h"
29 #include "qgslogger.h"
30 #include "qgsrasterlayer.h"
31 #include "qgsvectorlayer.h"
32 #include "qgsproject.h"
33 #include "qgssettings.h"
34 #include "qgsnewnamedialog.h"
35 #include "qgsbrowserproxymodel.h"
36 #include "qgsgui.h"
37 #include "qgswindowmanagerinterface.h"
38 #include "qgsnative.h"
39 #include "qgsdataitemguiproviderregistry.h"
40 #include "qgsdataitemguiprovider.h"
41 
42 // browser layer properties dialog
43 #include "qgsapplication.h"
44 #include "qgsmapcanvas.h"
45 
46 #include <QDragEnterEvent>
47 
QgsBrowserDockWidget(const QString & name,QgsBrowserGuiModel * browserModel,QWidget * parent)48 QgsBrowserDockWidget::QgsBrowserDockWidget( const QString &name, QgsBrowserGuiModel *browserModel, QWidget *parent )
49   : QgsDockWidget( parent )
50   , mModel( browserModel )
51   , mPropertiesWidgetEnabled( false )
52   , mPropertiesWidgetHeight( 0 )
53 {
54   setupUi( this );
55 
56   mContents->layout()->setContentsMargins( 0, 0, 0, 0 );
57   static_cast< QVBoxLayout * >( mContents->layout() )->setSpacing( 0 );
58 
59   setWindowTitle( name );
60 
61   mBrowserView = new QgsDockBrowserTreeView( this );
62   mLayoutBrowser->addWidget( mBrowserView );
63 
64   mWidgetFilter->hide();
65   mLeFilter->setPlaceholderText( tr( "Type here to filter visible items…" ) );
66   // icons from http://www.fatcow.com/free-icons License: CC Attribution 3.0
67 
68   QMenu *menu = new QMenu( this );
69   menu->setSeparatorsCollapsible( false );
70   mBtnFilterOptions->setMenu( menu );
71   QAction *action = new QAction( tr( "Case Sensitive" ), menu );
72   action->setData( "case" );
73   action->setCheckable( true );
74   action->setChecked( false );
75   connect( action, &QAction::toggled, this, &QgsBrowserDockWidget::setCaseSensitive );
76   menu->addAction( action );
77   QActionGroup *group = new QActionGroup( menu );
78   action = new QAction( tr( "Filter Pattern Syntax" ), group );
79   action->setSeparator( true );
80   menu->addAction( action );
81   action = new QAction( tr( "Normal" ), group );
82   action->setData( QgsBrowserProxyModel::Normal );
83   action->setCheckable( true );
84   action->setChecked( true );
85   menu->addAction( action );
86   action = new QAction( tr( "Wildcard(s)" ), group );
87   action->setData( QgsBrowserProxyModel::Wildcards );
88   action->setCheckable( true );
89   menu->addAction( action );
90   action = new QAction( tr( "Regular Expression" ), group );
91   action->setData( QgsBrowserProxyModel::RegularExpression );
92   action->setCheckable( true );
93   menu->addAction( action );
94 
95   mBrowserView->setExpandsOnDoubleClick( false );
96 
97   connect( mActionRefresh, &QAction::triggered, this, &QgsBrowserDockWidget::refresh );
98   connect( mActionAddLayers, &QAction::triggered, this, &QgsBrowserDockWidget::addSelectedLayers );
99   connect( mActionCollapse, &QAction::triggered, mBrowserView, &QgsDockBrowserTreeView::collapseAll );
100   connect( mActionShowFilter, &QAction::triggered, this, &QgsBrowserDockWidget::showFilterWidget );
101   connect( mActionPropertiesWidget, &QAction::triggered, this, &QgsBrowserDockWidget::enablePropertiesWidget );
102   connect( mLeFilter, &QgsFilterLineEdit::returnPressed, this, &QgsBrowserDockWidget::setFilter );
103   connect( mLeFilter, &QgsFilterLineEdit::cleared, this, &QgsBrowserDockWidget::setFilter );
104   connect( mLeFilter, &QgsFilterLineEdit::textChanged, this, &QgsBrowserDockWidget::setFilter );
105   connect( group, &QActionGroup::triggered, this, &QgsBrowserDockWidget::setFilterSyntax );
106   connect( mBrowserView, &QgsDockBrowserTreeView::customContextMenuRequested, this, &QgsBrowserDockWidget::showContextMenu );
107   connect( mBrowserView, &QgsDockBrowserTreeView::doubleClicked, this, &QgsBrowserDockWidget::itemDoubleClicked );
108   connect( mSplitter, &QSplitter::splitterMoved, this, &QgsBrowserDockWidget::splitterMoved );
109 }
110 
~QgsBrowserDockWidget()111 QgsBrowserDockWidget::~QgsBrowserDockWidget()
112 {
113   QgsSettings settings;
114   settings.setValue( settingsSection() + "/propertiesWidgetEnabled", mPropertiesWidgetEnabled );
115   //settings.setValue(settingsSection() + "/propertiesWidgetHeight", mPropertiesWidget->size().height() );
116   settings.setValue( settingsSection() + "/propertiesWidgetHeight", mPropertiesWidgetHeight );
117 }
118 
showEvent(QShowEvent * e)119 void QgsBrowserDockWidget::showEvent( QShowEvent *e )
120 {
121   // delayed initialization of the model
122   if ( !mModel->initialized( ) )
123   {
124     mModel->initialize();
125   }
126   if ( ! mProxyModel )
127   {
128     mProxyModel = new QgsBrowserProxyModel( this );
129     mProxyModel->setBrowserModel( mModel );
130     mProxyModel->setHiddenDataItemProviderKeyFilter( mDisabledDataItemsKeys );
131     mBrowserView->setSettingsSection( objectName().toLower() ); // to distinguish 2 or more instances of the browser
132     mBrowserView->setBrowserModel( mModel );
133     mBrowserView->setModel( mProxyModel );
134     mBrowserView->setSortingEnabled( true );
135     mBrowserView->sortByColumn( 0, Qt::AscendingOrder );
136     // provide a horizontal scroll bar instead of using ellipse (...) for longer items
137     mBrowserView->setTextElideMode( Qt::ElideNone );
138     mBrowserView->header()->setSectionResizeMode( 0, QHeaderView::ResizeToContents );
139     mBrowserView->header()->setStretchLastSection( false );
140 
141     // selectionModel is created when model is set on tree
142     connect( mBrowserView->selectionModel(), &QItemSelectionModel::selectionChanged,
143              this, &QgsBrowserDockWidget::selectionChanged );
144 
145     // Forward the model changed signals to the widget
146     connect( mModel, &QgsBrowserModel::connectionsChanged,
147              this, &QgsBrowserDockWidget::connectionsChanged );
148 
149 
150     // objectName used by settingsSection() is not yet set in constructor
151     QgsSettings settings;
152     mPropertiesWidgetEnabled = settings.value( settingsSection() + "/propertiesWidgetEnabled", false ).toBool();
153     mActionPropertiesWidget->setChecked( mPropertiesWidgetEnabled );
154     mPropertiesWidget->setVisible( false ); // false until item is selected
155 
156     mPropertiesWidgetHeight = settings.value( settingsSection() + "/propertiesWidgetHeight" ).toFloat();
157     QList<int> sizes = mSplitter->sizes();
158     int total = sizes.value( 0 ) + sizes.value( 1 );
159     int height = static_cast<int>( total * mPropertiesWidgetHeight );
160     sizes.clear();
161     sizes << total - height << height;
162     mSplitter->setSizes( sizes );
163   }
164 
165   QgsDockWidget::showEvent( e );
166 }
167 
itemDoubleClicked(const QModelIndex & index)168 void QgsBrowserDockWidget::itemDoubleClicked( const QModelIndex &index )
169 {
170   QgsDataItem *item = mModel->dataItem( mProxyModel->mapToSource( index ) );
171   if ( !item )
172     return;
173 
174   QgsDataItemGuiContext context = createContext();
175 
176   const QList< QgsDataItemGuiProvider * > providers = QgsGui::instance()->dataItemGuiProviderRegistry()->providers();
177   for ( QgsDataItemGuiProvider *provider : providers )
178   {
179     if ( provider->handleDoubleClick( item, context ) )
180       return;
181   }
182 
183   // if no providers overrode the double-click handling for this item, we give the item itself a chance
184   if ( !item->handleDoubleClick() )
185   {
186     // double-click not handled by browser model, so use as default view expand behavior
187     if ( mBrowserView->isExpanded( index ) )
188       mBrowserView->collapse( index );
189     else
190       mBrowserView->expand( index );
191   }
192 }
193 
showContextMenu(QPoint pt)194 void QgsBrowserDockWidget::showContextMenu( QPoint pt )
195 {
196   QModelIndex index = mProxyModel->mapToSource( mBrowserView->indexAt( pt ) );
197   QgsDataItem *item = mModel->dataItem( index );
198   if ( !item )
199     return;
200 
201   const QModelIndexList selection = mBrowserView->selectionModel()->selectedIndexes();
202   QList< QgsDataItem * > selectedItems;
203   selectedItems.reserve( selection.size() );
204   for ( const QModelIndex &selectedIndex : selection )
205   {
206     QgsDataItem *selectedItem = mProxyModel->dataItem( selectedIndex );
207     if ( selectedItem )
208       selectedItems << selectedItem;
209   }
210 
211   QMenu *menu = new QMenu( this );
212 
213   const QList<QMenu *> menus = item->menus( menu );
214   QList<QAction *> actions = item->actions( menu );
215 
216   if ( !menus.isEmpty() )
217   {
218     for ( QMenu *mn : menus )
219     {
220       menu->addMenu( mn );
221     }
222   }
223 
224   if ( !actions.isEmpty() )
225   {
226     if ( !menu->actions().isEmpty() )
227       menu->addSeparator();
228     // add action to the menu
229     menu->addActions( actions );
230   }
231 
232   QgsDataItemGuiContext context = createContext();
233 
234   const QList< QgsDataItemGuiProvider * > providers = QgsGui::instance()->dataItemGuiProviderRegistry()->providers();
235   for ( QgsDataItemGuiProvider *provider : providers )
236   {
237     provider->populateContextMenu( item, menu, selectedItems, context );
238   }
239 
240   if ( menu->actions().isEmpty() )
241   {
242     delete menu;
243     return;
244   }
245 
246   menu->popup( mBrowserView->mapToGlobal( pt ) );
247 }
248 
addFavorite()249 void QgsBrowserDockWidget::addFavorite()
250 {
251   QModelIndex index = mProxyModel->mapToSource( mBrowserView->currentIndex() );
252   QgsDataItem *item = mModel->dataItem( index );
253   if ( !item )
254     return;
255 
256   QgsDirectoryItem *dirItem = qobject_cast<QgsDirectoryItem *>( item );
257   if ( !dirItem )
258     return;
259 
260   Q_NOWARN_DEPRECATED_PUSH
261   addFavoriteDirectory( dirItem->dirPath() );
262   Q_NOWARN_DEPRECATED_POP
263 }
264 
addFavoriteDirectory()265 void QgsBrowserDockWidget::addFavoriteDirectory()
266 {
267   QString directory = QFileDialog::getExistingDirectory( this, tr( "Add directory to favorites" ) );
268   if ( !directory.isEmpty() )
269   {
270     Q_NOWARN_DEPRECATED_PUSH
271     addFavoriteDirectory( directory );
272     Q_NOWARN_DEPRECATED_POP
273   }
274 }
275 
addFavoriteDirectory(const QString & favDir,const QString & name)276 void QgsBrowserDockWidget::addFavoriteDirectory( const QString &favDir, const QString &name )
277 {
278   mModel->addFavoriteDirectory( favDir, name );
279 }
280 
setMessageBar(QgsMessageBar * bar)281 void QgsBrowserDockWidget::setMessageBar( QgsMessageBar *bar )
282 {
283   mMessageBar = bar;
284   mModel->setMessageBar( bar );
285 }
286 
messageBar()287 QgsMessageBar *QgsBrowserDockWidget::messageBar()
288 {
289   return mMessageBar;
290 }
291 
setDisabledDataItemsKeys(const QStringList & filter)292 void QgsBrowserDockWidget::setDisabledDataItemsKeys( const QStringList &filter )
293 {
294   mDisabledDataItemsKeys = filter;
295 
296   if ( !mProxyModel )
297     return;
298 
299   mProxyModel->setHiddenDataItemProviderKeyFilter( mDisabledDataItemsKeys );
300 }
301 
removeFavorite()302 void QgsBrowserDockWidget::removeFavorite()
303 {
304   mModel->removeFavorite( mProxyModel->mapToSource( mBrowserView->currentIndex() ) );
305 }
306 
refresh()307 void QgsBrowserDockWidget::refresh()
308 {
309   refreshModel( QModelIndex() );
310 }
311 
refreshModel(const QModelIndex & index)312 void QgsBrowserDockWidget::refreshModel( const QModelIndex &index )
313 {
314   if ( mModel && mProxyModel )
315   {
316     QgsDataItem *item = mModel->dataItem( index );
317     if ( item )
318     {
319       QgsDebugMsgLevel( "path = " + item->path(), 4 );
320     }
321     else
322     {
323       QgsDebugMsgLevel( QStringLiteral( "invalid item" ), 4 );
324     }
325 
326     if ( item && ( item->capabilities2() & QgsDataItem::Fertile ) )
327     {
328       mModel->refresh( index );
329     }
330 
331     for ( int i = 0; i < mModel->rowCount( index ); i++ )
332     {
333       QModelIndex idx = mModel->index( i, 0, index );
334       QModelIndex proxyIdx = mProxyModel->mapFromSource( idx );
335       QgsDataItem *child = mModel->dataItem( idx );
336 
337       // Check also expanded descendants so that the whole expanded path does not get collapsed if one item is collapsed.
338       // Fast items (usually root items) are refreshed so that when collapsed, it is obvious they are if empty (no expand symbol).
339       if ( mBrowserView->isExpanded( proxyIdx ) || mBrowserView->hasExpandedDescendant( proxyIdx ) || ( child && child->capabilities2() & QgsDataItem::Fast ) )
340       {
341         refreshModel( idx );
342       }
343       else
344       {
345         if ( child && ( child->capabilities2() & QgsDataItem::Fertile ) )
346         {
347           child->depopulate();
348         }
349       }
350     }
351   }
352 }
353 
addLayer(QgsLayerItem * layerItem)354 void QgsBrowserDockWidget::addLayer( QgsLayerItem *layerItem )
355 {
356   if ( !layerItem )
357     return;
358 
359   QgsMimeDataUtils::UriList list;
360   list << layerItem->mimeUri();
361   emit handleDropUriList( list );
362 }
363 
addLayerAtIndex(const QModelIndex & index)364 bool QgsBrowserDockWidget::addLayerAtIndex( const QModelIndex &index )
365 {
366   QgsDebugMsg( QStringLiteral( "rowCount() = %1" ).arg( mModel->rowCount( mProxyModel->mapToSource( index ) ) ) );
367   QgsDataItem *item = mModel->dataItem( mProxyModel->mapToSource( index ) );
368 
369   if ( item && item->type() == QgsDataItem::Project )
370   {
371     QgsProjectItem *projectItem = qobject_cast<QgsProjectItem *>( item );
372     if ( projectItem )
373     {
374       QApplication::setOverrideCursor( Qt::WaitCursor );
375       emit openFile( projectItem->path(), QStringLiteral( "project" ) );
376       QApplication::restoreOverrideCursor();
377     }
378     return true;
379   }
380   else if ( item && item->type() == QgsDataItem::Layer )
381   {
382     QgsLayerItem *layerItem = qobject_cast<QgsLayerItem *>( item );
383     if ( layerItem )
384     {
385       QApplication::setOverrideCursor( Qt::WaitCursor );
386       addLayer( layerItem );
387       QApplication::restoreOverrideCursor();
388     }
389     return true;
390   }
391   return false;
392 }
393 
addSelectedLayers()394 void QgsBrowserDockWidget::addSelectedLayers()
395 {
396   QApplication::setOverrideCursor( Qt::WaitCursor );
397 
398   // get a sorted list of selected indexes
399   QModelIndexList list = mBrowserView->selectionModel()->selectedIndexes();
400   std::sort( list.begin(), list.end() );
401 
402   // If any of the layer items are QGIS we just open and exit the loop
403   const auto constList = list;
404   for ( const QModelIndex &index : constList )
405   {
406     QgsDataItem *item = mModel->dataItem( mProxyModel->mapToSource( index ) );
407     if ( item && item->type() == QgsDataItem::Project )
408     {
409       QgsProjectItem *projectItem = qobject_cast<QgsProjectItem *>( item );
410       if ( projectItem )
411         emit openFile( projectItem->path(), QStringLiteral( "project" ) );
412 
413       QApplication::restoreOverrideCursor();
414       return;
415     }
416   }
417 
418   // add items in reverse order so they are in correct order in the layers dock
419   for ( int i = list.size() - 1; i >= 0; i-- )
420   {
421     QgsDataItem *item = mModel->dataItem( mProxyModel->mapToSource( list[i] ) );
422     if ( item && item->type() == QgsDataItem::Layer )
423     {
424       QgsLayerItem *layerItem = qobject_cast<QgsLayerItem *>( item );
425       if ( layerItem )
426         addLayer( layerItem );
427     }
428   }
429 
430   QApplication::restoreOverrideCursor();
431 }
432 
hideItem()433 void QgsBrowserDockWidget::hideItem()
434 {
435   QModelIndex index = mProxyModel->mapToSource( mBrowserView->currentIndex() );
436   QgsDataItem *item = mModel->dataItem( index );
437   if ( ! item )
438     return;
439 
440   if ( item->type() == QgsDataItem::Directory )
441   {
442     mModel->hidePath( item );
443   }
444 }
445 
showProperties()446 void QgsBrowserDockWidget::showProperties()
447 {
448   QModelIndex index = mProxyModel->mapToSource( mBrowserView->currentIndex() );
449   QgsDataItem *item = mModel->dataItem( index );
450   if ( ! item )
451     return;
452 
453   if ( item->type() == QgsDataItem::Layer || item->type() == QgsDataItem::Directory )
454   {
455     QgsBrowserPropertiesDialog *dialog = new QgsBrowserPropertiesDialog( settingsSection(), this );
456     dialog->setAttribute( Qt::WA_DeleteOnClose );
457     dialog->setItem( item, createContext() );
458     dialog->show();
459   }
460 }
461 
toggleFastScan()462 void QgsBrowserDockWidget::toggleFastScan()
463 {
464   QModelIndex index = mProxyModel->mapToSource( mBrowserView->currentIndex() );
465   QgsDataItem *item = mModel->dataItem( index );
466   if ( ! item )
467     return;
468 
469   if ( item->type() == QgsDataItem::Directory )
470   {
471     QgsSettings settings;
472     QStringList fastScanDirs = settings.value( QStringLiteral( "qgis/scanItemsFastScanUris" ),
473                                QStringList() ).toStringList();
474     int idx = fastScanDirs.indexOf( item->path() );
475     if ( idx != -1 )
476     {
477       fastScanDirs.removeAt( idx );
478     }
479     else
480     {
481       fastScanDirs << item->path();
482     }
483     settings.setValue( QStringLiteral( "qgis/scanItemsFastScanUris" ), fastScanDirs );
484   }
485 }
486 
showFilterWidget(bool visible)487 void QgsBrowserDockWidget::showFilterWidget( bool visible )
488 {
489   mWidgetFilter->setVisible( visible );
490   if ( ! visible )
491   {
492     mLeFilter->setText( QString() );
493     setFilter();
494   }
495   else
496   {
497     mLeFilter->setFocus();
498   }
499 }
500 
setFilter()501 void QgsBrowserDockWidget::setFilter()
502 {
503   QString filter = mLeFilter->text();
504   if ( mProxyModel )
505     mProxyModel->setFilterString( filter );
506 }
507 
updateProjectHome()508 void QgsBrowserDockWidget::updateProjectHome()
509 {
510   if ( mModel )
511     mModel->updateProjectHome();
512 }
513 
setFilterSyntax(QAction * action)514 void QgsBrowserDockWidget::setFilterSyntax( QAction *action )
515 {
516   if ( !action || ! mProxyModel )
517     return;
518 
519   mProxyModel->setFilterSyntax( static_cast< QgsBrowserProxyModel::FilterSyntax >( action->data().toInt() ) );
520 }
521 
setCaseSensitive(bool caseSensitive)522 void QgsBrowserDockWidget::setCaseSensitive( bool caseSensitive )
523 {
524   if ( ! mProxyModel )
525     return;
526   mProxyModel->setFilterCaseSensitivity( caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive );
527 }
528 
selectedItemsCount()529 int QgsBrowserDockWidget::selectedItemsCount()
530 {
531   QItemSelectionModel *selectionModel = mBrowserView->selectionModel();
532   if ( selectionModel )
533   {
534     return selectionModel->selectedIndexes().size();
535   }
536   return 0;
537 }
538 
createContext()539 QgsDataItemGuiContext QgsBrowserDockWidget::createContext()
540 {
541   QgsDataItemGuiContext context;
542   context.setMessageBar( mMessageBar );
543   return context;
544 }
545 
selectionChanged(const QItemSelection & selected,const QItemSelection & deselected)546 void QgsBrowserDockWidget::selectionChanged( const QItemSelection &selected, const QItemSelection &deselected )
547 {
548   Q_UNUSED( selected )
549   Q_UNUSED( deselected )
550   if ( mPropertiesWidgetEnabled )
551   {
552     setPropertiesWidget();
553   }
554 }
555 
clearPropertiesWidget()556 void QgsBrowserDockWidget::clearPropertiesWidget()
557 {
558   while ( mPropertiesLayout->count() > 0 )
559   {
560     delete mPropertiesLayout->itemAt( 0 )->widget();
561   }
562   mPropertiesWidget->setVisible( false );
563 }
564 
setPropertiesWidget()565 void QgsBrowserDockWidget::setPropertiesWidget()
566 {
567   clearPropertiesWidget();
568   QItemSelectionModel *selectionModel = mBrowserView->selectionModel();
569   if ( selectionModel )
570   {
571     QModelIndexList indexes = selectionModel->selectedIndexes();
572     if ( indexes.size() == 1 )
573     {
574       QModelIndex index = mProxyModel->mapToSource( indexes.value( 0 ) );
575       QgsDataItem *item = mModel->dataItem( index );
576       QgsDataItemGuiContext context = createContext();
577       QgsBrowserPropertiesWidget *propertiesWidget = QgsBrowserPropertiesWidget::createWidget( item, context, mPropertiesWidget );
578       if ( propertiesWidget )
579       {
580         propertiesWidget->setCondensedMode( true );
581         mPropertiesLayout->addWidget( propertiesWidget );
582       }
583     }
584   }
585   mPropertiesWidget->setVisible( mPropertiesLayout->count() > 0 );
586 }
587 
enablePropertiesWidget(bool enable)588 void QgsBrowserDockWidget::enablePropertiesWidget( bool enable )
589 {
590   mPropertiesWidgetEnabled = enable;
591   if ( enable && selectedItemsCount() == 1 )
592   {
593     setPropertiesWidget();
594   }
595   else
596   {
597     clearPropertiesWidget();
598   }
599 }
600 
setActiveIndex(const QModelIndex & index)601 void QgsBrowserDockWidget::setActiveIndex( const QModelIndex &index )
602 {
603   if ( index.isValid() )
604   {
605     QModelIndex proxyIndex = mProxyModel->mapFromSource( index );
606     mBrowserView->expand( proxyIndex );
607     mBrowserView->setCurrentIndex( proxyIndex );
608   }
609 }
610 
splitterMoved()611 void QgsBrowserDockWidget::splitterMoved()
612 {
613   QList<int> sizes = mSplitter->sizes();
614   float total = sizes.value( 0 ) + sizes.value( 1 );
615   mPropertiesWidgetHeight = total > 0 ? sizes.value( 1 ) / total : 0;
616 }
617