QGIS API Documentation  2.8.2-Wien
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
qgsoptionsdialogbase.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsoptionsdialogbase.cpp - base vertical tabs option dialog
3 
4  ---------------------
5  begin : March 24, 2013
6  copyright : (C) 2013 by Larry Shaffer
7  email : larrys at dakcarto dot com
8  ***************************************************************************
9  * *
10  * This program is free software; you can redistribute it and/or modify *
11  * it under the terms of the GNU General Public License as published by *
12  * the Free Software Foundation; either version 2 of the License, or *
13  * (at your option) any later version. *
14  * *
15  ***************************************************************************/
16 
17 #include "qgsoptionsdialogbase.h"
18 
19 #include <QDialog>
20 #include <QDialogButtonBox>
21 #include <QLayout>
22 #include <QListWidget>
23 #include <QMessageBox>
24 #include <QScrollBar>
25 #include <QStackedWidget>
26 #include <QSplitter>
27 #include <QTimer>
28 
29 
30 QgsOptionsDialogBase::QgsOptionsDialogBase( QString settingsKey, QWidget* parent, Qt::WindowFlags fl, QSettings* settings )
31  : QDialog( parent, fl )
32  , mOptsKey( settingsKey )
33  , mInit( false )
34  , mOptListWidget( NULL )
35  , mOptStackedWidget( NULL )
36  , mOptSplitter( NULL )
37  , mOptButtonBox( NULL )
38  , mDialogTitle( "" )
39  , mIconOnly( false )
40  , mSettings( settings )
41  , mDelSettings( false )
42 {
43 }
44 
46 {
47  if ( mInit )
48  {
49  mSettings->setValue( QString( "/Windows/%1/geometry" ).arg( mOptsKey ), saveGeometry() );
50  mSettings->setValue( QString( "/Windows/%1/splitState" ).arg( mOptsKey ), mOptSplitter->saveState() );
51  mSettings->setValue( QString( "/Windows/%1/tab" ).arg( mOptsKey ), mOptStackedWidget->currentIndex() );
52  }
53 
54  if ( mDelSettings ) // local settings obj to delete
55  {
56  delete mSettings;
57  }
58 
59  mSettings = 0; // null the pointer (in case of outside settings obj)
60 }
61 
62 void QgsOptionsDialogBase::initOptionsBase( bool restoreUi, QString title )
63 {
64  // use pointer to app QSettings if no custom QSettings specified
65  // custom QSettings object may be from Python plugin
66  mDelSettings = false;
67 
68  if ( !mSettings )
69  {
70  mSettings = new QSettings();
71  mDelSettings = true; // only delete obj created by class
72  }
73 
74  // save dialog title so it can be used to be concatenated
75  // with category title in icon-only mode
76  if ( title.isEmpty() )
77  mDialogTitle = windowTitle();
78  else
79  mDialogTitle = title;
80 
81  // don't add to dialog margins
82  // redefine now, or those in inherited .ui file will be added
83  if ( layout() )
84  {
85  layout()->setContentsMargins( 0, 0, 0, 0 ); // Qt default spacing
86  }
87 
88  // start with copy of qgsoptionsdialog_template.ui to ensure existence of these objects
89  mOptListWidget = findChild<QListWidget*>( "mOptionsListWidget" );
90  QFrame* optionsFrame = findChild<QFrame*>( "mOptionsFrame" );
91  mOptStackedWidget = findChild<QStackedWidget*>( "mOptionsStackedWidget" );
92  mOptSplitter = findChild<QSplitter*>( "mOptionsSplitter" );
93  mOptButtonBox = findChild<QDialogButtonBox*>( "buttonBox" );
94  QFrame* buttonBoxFrame = findChild<QFrame*>( "mButtonBoxFrame" );
95 
96  if ( !mOptListWidget || !mOptStackedWidget || !mOptSplitter || !optionsFrame )
97  {
98  return;
99  }
100 
101  int size = mSettings->value( "/IconSize", 24 ).toInt();
102  // buffer size to match displayed icon size in toolbars, and expected geometry restore
103  // newWidth (above) may need adjusted if you adjust iconBuffer here
104  int iconBuffer = 4;
105  mOptListWidget->setIconSize( QSize( size + iconBuffer, size + iconBuffer ) );
106  mOptListWidget->setFrameStyle( QFrame::NoFrame );
107 
108  optionsFrame->layout()->setContentsMargins( 0, 3, 3, 3 );
109  QVBoxLayout* layout = static_cast<QVBoxLayout*>( optionsFrame->layout() );
110 
111  if ( buttonBoxFrame )
112  {
113  buttonBoxFrame->layout()->setContentsMargins( 0, 0, 0, 0 );
114  layout->insertWidget( layout->count() + 1, buttonBoxFrame );
115  }
116  else
117  {
118  layout->insertWidget( layout->count() + 1, mOptButtonBox );
119  }
120 
121  if ( mOptButtonBox )
122  {
123  // enforce only one connection per signal, in case added in Qt Designer
124  disconnect( mOptButtonBox, SIGNAL( accepted() ), this, SLOT( accept() ) );
125  connect( mOptButtonBox, SIGNAL( accepted() ), this, SLOT( accept() ) );
126  disconnect( mOptButtonBox, SIGNAL( rejected() ), this, SLOT( reject() ) );
127  connect( mOptButtonBox, SIGNAL( rejected() ), this, SLOT( reject() ) );
128  }
129  connect( mOptSplitter, SIGNAL( splitterMoved( int, int ) ), this, SLOT( updateOptionsListVerticalTabs() ) );
130  connect( mOptStackedWidget, SIGNAL( currentChanged( int ) ), this, SLOT( optionsStackedWidget_CurrentChanged( int ) ) );
131  connect( mOptStackedWidget, SIGNAL( widgetRemoved( int ) ), this, SLOT( optionsStackedWidget_WidgetRemoved( int ) ) );
132 
133  mInit = true;
134 
135  if ( restoreUi )
137 }
138 
139 void QgsOptionsDialogBase::setSettings( QSettings* settings )
140 {
141  if ( mDelSettings ) // local settings obj to delete
142  {
143  delete mSettings;
144  }
145 
146  mSettings = settings;
147  mDelSettings = false; // don't delete outside obj
148 }
149 
151 {
152  if ( !mInit )
153  {
154  return;
155  }
156 
157  if ( !title.isEmpty() )
158  {
159  mDialogTitle = title;
161  }
162 
163  // re-save original dialog title in case it was changed after dialog initialization
164  mDialogTitle = windowTitle();
165 
166  restoreGeometry( mSettings->value( QString( "/Windows/%1/geometry" ).arg( mOptsKey ) ).toByteArray() );
167  // mOptListWidget width is fixed to take up less space in QtDesigner
168  // revert it now unless the splitter's state hasn't been saved yet
169  mOptListWidget->setMaximumWidth(
170  mSettings->value( QString( "/Windows/%1/splitState" ).arg( mOptsKey ) ).isNull() ? 150 : 16777215 );
171  mOptSplitter->restoreState( mSettings->value( QString( "/Windows/%1/splitState" ).arg( mOptsKey ) ).toByteArray() );
172  int curIndx = mSettings->value( QString( "/Windows/%1/tab" ).arg( mOptsKey ), 0 ).toInt();
173 
174  // if the last used tab is out of range or not enabled display the first enabled one
175  if ( mOptStackedWidget->count() < ( curIndx + 1 )
176  || !mOptStackedWidget->widget( curIndx )->isEnabled() )
177  {
178  curIndx = 0;
179  for ( int i = 0; i < mOptStackedWidget->count(); i++ )
180  {
181  if ( mOptStackedWidget->widget( i )->isEnabled() )
182  {
183  curIndx = i;
184  break;
185  }
186  }
187  }
188 
189  if ( mOptStackedWidget->count() != 0 && mOptListWidget->count() != 0 )
190  {
191  mOptStackedWidget->setCurrentIndex( curIndx );
192  mOptListWidget->setCurrentRow( curIndx );
193  }
194 
195  // get rid of annoying outer focus rect on Mac
196  mOptListWidget->setAttribute( Qt::WA_MacShowFocusRect, false );
197 }
198 
199 void QgsOptionsDialogBase::showEvent( QShowEvent* e )
200 {
201  if ( mInit )
202  {
205  }
206  else
207  {
208  QTimer::singleShot( 0, this, SLOT( warnAboutMissingObjects() ) );
209  }
210 
211  QDialog::showEvent( e );
212 }
213 
214 void QgsOptionsDialogBase::paintEvent( QPaintEvent* e )
215 {
216  if ( mInit )
217  QTimer::singleShot( 0, this, SLOT( updateOptionsListVerticalTabs() ) );
218 
219  QDialog::paintEvent( e );
220 }
221 
223 {
224  QListWidgetItem *curitem = mOptListWidget->currentItem();
225  if ( curitem )
226  {
227  setWindowTitle( QString( "%1 | %2" ).arg( mDialogTitle ).arg( curitem->text() ) );
228  }
229  else
230  {
231  setWindowTitle( mDialogTitle );
232  }
233 }
234 
236 {
237  if ( !mInit )
238  return;
239 
240  if ( mOptListWidget->maximumWidth() != 16777215 )
241  mOptListWidget->setMaximumWidth( 16777215 );
242  // auto-resize splitter for vert scrollbar without covering icons in icon-only mode
243  // TODO: mOptListWidget has fixed 32px wide icons for now, allow user-defined
244  // Note: called on splitter resize and dialog paint event, so only update when necessary
245  int iconWidth = mOptListWidget->iconSize().width();
246  int snapToIconWidth = iconWidth + 32;
247 
248  QList<int> splitSizes = mOptSplitter->sizes();
249  mIconOnly = ( splitSizes.at( 0 ) <= snapToIconWidth );
250 
251  // iconBuffer (above) may need adjusted if you adjust iconWidth here
252  int newWidth = mOptListWidget->verticalScrollBar()->isVisible() ? iconWidth + 22 : iconWidth + 9;
253  bool diffWidth = mOptListWidget->minimumWidth() != newWidth;
254 
255  if ( diffWidth )
256  mOptListWidget->setMinimumWidth( newWidth );
257 
258  if ( mIconOnly && ( diffWidth || mOptListWidget->width() != newWidth ) )
259  {
260  splitSizes[1] = splitSizes.at( 1 ) - ( splitSizes.at( 0 ) - newWidth );
261  splitSizes[0] = newWidth;
262  mOptSplitter->setSizes( splitSizes );
263  }
264 
265  if ( mOptListWidget->wordWrap() && mIconOnly )
266  mOptListWidget->setWordWrap( false );
267  if ( !mOptListWidget->wordWrap() && !mIconOnly )
268  mOptListWidget->setWordWrap( true );
269 }
270 
272 {
273  mOptListWidget->blockSignals( true );
274  mOptListWidget->setCurrentRow( indx );
275  mOptListWidget->blockSignals( false );
276 
278 }
279 
281 {
282  // will need to take item first, if widgets are set for item in future
283  delete mOptListWidget->item( indx );
284 }
285 
287 {
288  QMessageBox::warning( 0, tr( "Missing objects" ),
289  tr( "Base options dialog could not be initialized.\n\n"
290  "Missing some of the .ui template objects:\n" )
291  + " mOptionsListWidget,\n mOptionsStackedWidget,\n mOptionsSplitter",
292  QMessageBox::Ok,
293  QMessageBox::Ok );
294 }