VirtualBox

source: vbox/trunk/src/VBox/Frontends/VirtualBox4/src/VBoxVMSettingsDlg.cpp@ 9784

Last change on this file since 9784 was 9784, checked in by vboxsync, 17 years ago

FE/Qt4: Fixed adjustment of the vmsettings selector width.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Revision Author Id
File size: 17.4 KB
Line 
1/** @file
2 *
3 * VBox frontends: Qt4 GUI ("VirtualBox"):
4 * VBoxVMSettingsDlg class implementation
5 */
6
7/*
8 * Copyright (C) 2006-2008 Sun Microsystems, Inc.
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.215389.xyz. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 *
18 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
19 * Clara, CA 95054 USA or visit http://www.sun.com if you need
20 * additional information or have any questions.
21 */
22
23#include "VBoxVMSettingsDlg.h"
24#include "VBoxVMSettingsGeneral.h"
25#include "VBoxVMSettingsHD.h"
26#include "VBoxVMSettingsCD.h"
27#include "VBoxVMSettingsFD.h"
28#include "VBoxVMSettingsAudio.h"
29#include "VBoxVMSettingsNetwork.h"
30#include "VBoxVMSettingsSerial.h"
31#include "VBoxVMSettingsParallel.h"
32#include "VBoxVMSettingsUSB.h"
33#include "VBoxVMSettingsSF.h"
34#include "VBoxVMSettingsVRDP.h"
35
36#include "VBoxGlobal.h"
37#include "VBoxProblemReporter.h"
38#include "QIWidgetValidator.h"
39
40#include <QTimer>
41
42/**
43 * Returns the path to the item in the form of 'grandparent > parent > item'
44 * using the text of the first column of every item.
45 */
46static QString path (QTreeWidgetItem *aItem)
47{
48 static QString sep = ": ";
49 QString p;
50 QTreeWidgetItem *cur = aItem;
51 while (cur)
52 {
53 if (!p.isNull())
54 p = sep + p;
55 p = cur->text (0).simplified() + p;
56 cur = cur->parent();
57 }
58 return p;
59}
60
61static QTreeWidgetItem* findItem (QTreeWidget *aView,
62 const QString &aMatch, int aColumn)
63{
64 QList<QTreeWidgetItem*> list =
65 aView->findItems (aMatch, Qt::MatchExactly, aColumn);
66
67 return list.count() ? list [0] : 0;
68}
69
70VBoxVMSettingsDlg::VBoxVMSettingsDlg (QWidget *aParent,
71 const QString &aCategory,
72 const QString &aControl)
73 : QIWithRetranslateUI<QIMainDialog> (aParent)
74 , mPolished (false)
75 , mAllowResetFirstRunFlag (false)
76 , mValid (true)
77 , mWhatsThisTimer (new QTimer (this))
78 , mWhatsThisCandidate (NULL)
79{
80 /* Apply UI decorations */
81 Ui::VBoxVMSettingsDlg::setupUi (this);
82
83#ifndef Q_WS_MAC
84 setWindowIcon (QIcon (":/settings_16px.png"));
85#endif /* Q_WS_MAC */
86
87 mWarnIconLabel = new VBoxWarnIconLabel();
88
89 /* Setup warning icon */
90 QIcon icon = vboxGlobal().standardIcon (QStyle::SP_MessageBoxWarning, this);
91 if (!icon.isNull())
92 mWarnIconLabel->setWarningPixmap (icon.pixmap (16, 16));
93
94 mButtonBox->addExtraWidget (mWarnIconLabel);
95
96 /* Page title font is derived from the system font */
97 QFont f = font();
98 f.setBold (true);
99 f.setPointSize (f.pointSize() + 2);
100 mLbTitle->setFont (f);
101
102 /* Setup the what's this label */
103 qApp->installEventFilter (this);
104 mWhatsThisTimer->setSingleShot (true);
105 connect (mWhatsThisTimer, SIGNAL (timeout()), this, SLOT (updateWhatsThis()));
106
107 mLbWhatsThis->setFixedHeight (mLbWhatsThis->frameWidth() * 2 +
108 6 /* seems that RichText adds some margin */ +
109 mLbWhatsThis->fontMetrics().lineSpacing() * 4);
110 mLbWhatsThis->setMinimumWidth (mLbWhatsThis->frameWidth() * 2 +
111 6 /* seems that RichText adds some margin */ +
112 mLbWhatsThis->fontMetrics().width ('m') * 40);
113
114 /* Common connections */
115 connect (mButtonBox, SIGNAL (accepted()), this, SLOT (accept()));
116 connect (mButtonBox, SIGNAL (rejected()), this, SLOT (reject()));
117 connect (mButtonBox, SIGNAL (helpRequested()), &vboxProblem(), SLOT (showHelpHelpDialog()));
118 connect (mTwSelector, SIGNAL (currentItemChanged (QTreeWidgetItem*, QTreeWidgetItem*)),
119 this, SLOT (settingsGroupChanged (QTreeWidgetItem *, QTreeWidgetItem*)));
120 connect (&vboxGlobal(), SIGNAL (mediaEnumFinished (const VBoxMediaList &)),
121 this, SLOT (onMediaEnumerationDone()));
122
123 /* Parallel Port Page (currently disabled) */
124 //QTreeWidgetItem *item = findItem (mTwSelector, "#parallelPorts", listView_Link);
125 //Assert (item);
126 //if (item) item->setHidden (true);
127
128 /* Hide unnecessary columns and header */
129 mTwSelector->header()->hide();
130 mTwSelector->hideColumn (listView_Id);
131 mTwSelector->hideColumn (listView_Link);
132
133 /* Initially select the first settings page */
134 mTwSelector->setCurrentItem (mTwSelector->topLevelItem (0));
135
136 /* Setup Settings Dialog */
137 if (!aCategory.isNull())
138 {
139 /* Search for a list view item corresponding to the category */
140 QTreeWidgetItem *item = findItem (mTwSelector, aCategory, listView_Link);
141 if (item)
142 {
143 mTwSelector->setCurrentItem (item);
144
145 /* Search for a widget with the given name */
146 if (!aControl.isNull())
147 {
148 QObject *obj = mPageStack->currentWidget()->findChild<QWidget*> (aControl);
149 if (obj && obj->isWidgetType())
150 {
151 QWidget *w = static_cast<QWidget*> (obj);
152 QList<QWidget*> parents;
153 QWidget *p = w;
154 while ((p = p->parentWidget()) != NULL)
155 {
156 if (p->inherits ("QTabWidget"))
157 {
158 /* The tab contents widget is two steps down
159 * (QTabWidget -> QStackedWidget -> QWidget) */
160 QWidget *c = parents [parents.count() - 1];
161 if (c)
162 c = parents [parents.count() - 2];
163 if (c)
164 static_cast<QTabWidget*> (p)->setCurrentWidget (c);
165 }
166 parents.append (p);
167 }
168
169 w->setFocus();
170 }
171 }
172 }
173 }
174
175 /* Applying language settings */
176 retranslateUi();
177}
178
179void VBoxVMSettingsDlg::getFromMachine (const CMachine &aMachine)
180{
181 mMachine = aMachine;
182
183 setWindowTitle (dialogTitle());
184
185 CVirtualBox vbox = vboxGlobal().virtualBox();
186
187 /* General Page */
188 VBoxVMSettingsGeneral::getFromMachine (aMachine, mPageGeneral,
189 this, pagePath (mPageGeneral));
190
191 /* HD */
192 VBoxVMSettingsHD::getFromMachine (aMachine, mPageHD,
193 this, pagePath (mPageHD));
194
195 /* CD */
196 VBoxVMSettingsCD::getFromMachine (aMachine, mPageCD,
197 this, pagePath (mPageCD));
198
199 /* FD */
200 VBoxVMSettingsFD::getFromMachine (aMachine, mPageFD,
201 this, pagePath (mPageFD));
202
203 /* Audio */
204 VBoxVMSettingsAudio::getFromMachine (aMachine, mPageAudio);
205
206 /* Network */
207 VBoxVMSettingsNetwork::getFromMachine (aMachine, mPageNetwork,
208 this, pagePath (mPageNetwork));
209
210 /* Serial Ports */
211 VBoxVMSettingsSerial::getFromMachine (aMachine, mPageSerial,
212 this, pagePath (mPageSerial));
213
214 /* Parallel Ports */
215 VBoxVMSettingsParallel::getFromMachine (aMachine, mPageParallel,
216 this, pagePath (mPageParallel));
217
218 /* USB */
219 VBoxVMSettingsUSB::getFromMachine (aMachine, mPageUSB,
220 this, pagePath (mPageUSB));
221
222 /* Shared Folders */
223 VBoxVMSettingsSF::getFromMachineEx (aMachine, mPageShared, this);
224
225 /* Vrdp */
226 VBoxVMSettingsVRDP::getFromMachine (aMachine, mPageVrdp,
227 this, pagePath (mPageVrdp));
228
229 /* Finally set the reset First Run Wizard flag to "false" to make sure
230 * user will see this dialog if he hasn't change the boot-order
231 * and/or mounted images configuration */
232 mResetFirstRunFlag = false;
233}
234
235COMResult VBoxVMSettingsDlg::putBackToMachine()
236{
237 CVirtualBox vbox = vboxGlobal().virtualBox();
238
239 /* General Page */
240 VBoxVMSettingsGeneral::putBackToMachine();
241
242 /* HD */
243 VBoxVMSettingsHD::putBackToMachine();
244
245 /* CD */
246 VBoxVMSettingsCD::putBackToMachine();
247
248 /* FD */
249 VBoxVMSettingsFD::putBackToMachine();
250
251 /* Clear the "GUI_FirstRun" extra data key in case if the boot order
252 * and/or disk configuration were changed */
253 if (mResetFirstRunFlag)
254 mMachine.SetExtraData (VBoxDefs::GUI_FirstRun, QString::null);
255
256 /* Audio */
257 VBoxVMSettingsAudio::putBackToMachine();
258
259 /* Network */
260 VBoxVMSettingsNetwork::putBackToMachine();
261
262 /* Serial ports */
263 VBoxVMSettingsSerial::putBackToMachine();
264
265 /* Parallel ports */
266 VBoxVMSettingsParallel::putBackToMachine();
267
268 /* USB */
269 VBoxVMSettingsUSB::putBackToMachine();
270
271 /* Shared folders */
272 VBoxVMSettingsSF::putBackToMachineEx();
273
274 /* Vrdp */
275 VBoxVMSettingsVRDP::putBackToMachine();
276
277 return COMResult();
278}
279
280
281void VBoxVMSettingsDlg::retranslateUi()
282{
283 /* Unfortunately retranslateUi clears the QTreeWidget to do the
284 * translation. So save the current selected index. */
285 int ci = mPageStack->currentIndex();
286 /* Translate uic generated strings */
287 Ui::VBoxVMSettingsDlg::retranslateUi (this);
288 /* Set the old index */
289 mTwSelector->setCurrentItem (mTwSelector->topLevelItem (ci));
290
291 /* Adjust selector list */
292 mTwSelector->setFixedWidth (static_cast<QAbstractItemView*> (mTwSelector)->sizeHintForColumn (0) + 2 * mTwSelector->frameWidth());
293
294 /* Sort selector by the id column (to have pages in the desired order) */
295 mTwSelector->sortItems (listView_Id, Qt::AscendingOrder);
296 mTwSelector->resizeColumnToContents (0);
297
298 mWarnIconLabel->setWarningText (tr ("Invalid settings detected"));
299 mButtonBox->button (QDialogButtonBox::Ok)->setWhatsThis (tr ("Accepts (saves) changes and closes the dialog."));
300 mButtonBox->button (QDialogButtonBox::Cancel)->setWhatsThis (tr ("Cancels changes and closes the dialog."));
301 mButtonBox->button (QDialogButtonBox::Help)->setWhatsThis (tr ("Displays the dialog help."));
302
303 setWindowTitle (dialogTitle());
304
305 /* We have to make sure that the Serial & Network subpages are retranslated
306 * before they are revalidated. Cause: They do string comparing within
307 * vboxGlobal which is retranslated at that point already. */
308 QEvent* event = new QEvent (QEvent::LanguageChange);
309 qApp->sendEvent (mPageSerial, event);
310 qApp->sendEvent (mPageNetwork, event);
311
312 /* Revalidate all pages to retranslate the warning messages also. */
313 QList<QIWidgetValidator*> l = this->findChildren<QIWidgetValidator*>();
314 foreach (QIWidgetValidator *wval, l)
315 if (!wval->isValid())
316 revalidate (wval);
317}
318
319void VBoxVMSettingsDlg::enableOk (const QIWidgetValidator*)
320{
321 setWarning (QString::null);
322 QString wvalWarning;
323
324 /* Detect the overall validity */
325 bool newValid = true;
326 {
327 QList<QIWidgetValidator*> l = this->findChildren<QIWidgetValidator*>();
328 foreach (QIWidgetValidator *wval, l)
329 {
330 newValid = wval->isValid();
331 if (!newValid)
332 {
333 wvalWarning = wval->warningText();
334 break;
335 }
336 }
337 }
338
339 if (mWarnString.isNull() && !wvalWarning.isNull())
340 {
341 /* Try to set the generic error message when invalid but no specific
342 * message is provided */
343 setWarning (wvalWarning);
344 }
345
346 if (mValid != newValid)
347 {
348 mValid = newValid;
349 mButtonBox->button (QDialogButtonBox::Ok)->setEnabled (mValid);
350 mWarnIconLabel->setVisible (!mValid);
351 }
352}
353
354void VBoxVMSettingsDlg::revalidate (QIWidgetValidator *aWval)
355{
356 /* do individual validations for pages */
357 QWidget *pg = aWval->widget();
358 bool valid = aWval->isOtherValid();
359
360 QString warningText;
361 QString pageTitle = pagePath (pg);
362
363 if (pg == mPageHD)
364 valid = VBoxVMSettingsHD::revalidate (warningText);
365 else if (pg == mPageCD)
366 valid = VBoxVMSettingsCD::revalidate (warningText);
367 else if (pg == mPageFD)
368 valid = VBoxVMSettingsFD::revalidate (warningText);
369 else if (pg == mPageNetwork)
370 valid = VBoxVMSettingsNetwork::revalidate (warningText, pageTitle);
371 else if (pg == mPageSerial)
372 valid = VBoxVMSettingsSerial::revalidate (warningText, pageTitle);
373 else if (pg == mPageParallel)
374 valid = VBoxVMSettingsParallel::revalidate (warningText, pageTitle);
375
376 if (!valid)
377 setWarning (tr ("%1 on the <b>%2</b> page.")
378 .arg (warningText, pageTitle));
379
380 aWval->setOtherValid (valid);
381}
382
383void VBoxVMSettingsDlg::onMediaEnumerationDone()
384{
385 mAllowResetFirstRunFlag = true;
386}
387
388void VBoxVMSettingsDlg::settingsGroupChanged (QTreeWidgetItem *aItem,
389 QTreeWidgetItem *)
390{
391 if (aItem)
392 {
393 int id = aItem->text (1).toInt();
394 Assert (id >= 0);
395 mLbTitle->setText (::path (aItem));
396 mPageStack->setCurrentIndex (id);
397 }
398}
399
400void VBoxVMSettingsDlg::updateWhatsThis (bool aGotFocus /* = false */)
401{
402 QString text;
403
404 QWidget *widget = 0;
405 if (!aGotFocus)
406 {
407 if (mWhatsThisCandidate && mWhatsThisCandidate != this)
408 widget = mWhatsThisCandidate;
409 }
410 else
411 {
412 widget = QApplication::focusWidget();
413 }
414 /* If the given widget lacks the whats'this text, look at its parent */
415 while (widget && widget != this)
416 {
417 text = widget->whatsThis();
418 if (!text.isEmpty())
419 break;
420 widget = widget->parentWidget();
421 }
422
423 if (text.isEmpty() && !mWarnString.isEmpty())
424 text = mWarnString;
425 if (text.isEmpty())
426 text = whatsThis();
427
428 mLbWhatsThis->setText (text);
429}
430
431void VBoxVMSettingsDlg::resetFirstRunFlag()
432{
433 if (mAllowResetFirstRunFlag)
434 mResetFirstRunFlag = true;
435}
436
437void VBoxVMSettingsDlg::whatsThisCandidateDestroyed (QObject *aObj /*= NULL*/)
438{
439 /* sanity */
440 Assert (mWhatsThisCandidate == aObj);
441
442 if (mWhatsThisCandidate == aObj)
443 mWhatsThisCandidate = NULL;
444}
445
446bool VBoxVMSettingsDlg::eventFilter (QObject *aObject, QEvent *aEvent)
447{
448 if (!aObject->isWidgetType())
449 return QIMainDialog::eventFilter (aObject, aEvent);
450
451 QWidget *widget = static_cast<QWidget*> (aObject);
452 if (widget->topLevelWidget() != this)
453 return QIMainDialog::eventFilter (aObject, aEvent);
454
455 switch (aEvent->type())
456 {
457 case QEvent::Enter:
458 case QEvent::Leave:
459 {
460 if (aEvent->type() == QEvent::Enter)
461 {
462 /* What if Qt sends Enter w/o Leave... */
463 if (mWhatsThisCandidate)
464 disconnect (mWhatsThisCandidate, SIGNAL (destroyed (QObject *)),
465 this, SLOT (whatsThisCandidateDestroyed (QObject *)));
466
467 mWhatsThisCandidate = widget;
468 /* make sure we don't reference a deleted object after the
469 * timer is shot */
470 connect (mWhatsThisCandidate, SIGNAL (destroyed (QObject *)),
471 this, SLOT (whatsThisCandidateDestroyed (QObject *)));
472 }
473 else
474 {
475 /* cleanup */
476 if (mWhatsThisCandidate)
477 disconnect (mWhatsThisCandidate, SIGNAL (destroyed (QObject *)),
478 this, SLOT (whatsThisCandidateDestroyed (QObject *)));
479 mWhatsThisCandidate = NULL;
480 }
481
482 mWhatsThisTimer->start (100);
483 break;
484 }
485 case QEvent::FocusIn:
486 {
487 updateWhatsThis (true /* aGotFocus */);
488 break;
489 }
490 default:
491 break;
492 }
493
494 return QIMainDialog::eventFilter (aObject, aEvent);
495}
496
497void VBoxVMSettingsDlg::showEvent (QShowEvent *aEvent)
498{
499 QIMainDialog::showEvent (aEvent);
500
501 /* One may think that QWidget::polish() is the right place to do things
502 * below, but apparently, by the time when QWidget::polish() is called,
503 * the widget style & layout are not fully done, at least the minimum
504 * size hint is not properly calculated. Since this is sometimes necessary,
505 * we provide our own "polish" implementation. */
506
507 if (mPolished)
508 return;
509
510 mPolished = true;
511
512 /* Resize to the minimum possible size */
513 resize (minimumSize());
514
515 VBoxGlobal::centerWidget (this, parentWidget());
516}
517
518/**
519 * Returns a path to the given page of this settings dialog. See ::path() for
520 * details.
521 */
522QString VBoxVMSettingsDlg::pagePath (QWidget *aPage)
523{
524 QTreeWidgetItem *li =
525 findItem (mTwSelector,
526 QString ("%1")
527 .arg (mPageStack->indexOf (aPage), 2, 10, QChar ('0')),
528 1);
529 return ::path (li);
530}
531
532void VBoxVMSettingsDlg::setWarning (const QString &aWarning)
533{
534 mWarnString = aWarning;
535 if (!aWarning.isEmpty())
536 mWarnString = QString ("<font color=red>%1</font>").arg (aWarning);
537
538 if (!mWarnString.isEmpty())
539 mLbWhatsThis->setText (mWarnString);
540 else
541 updateWhatsThis (true);
542}
543
544QString VBoxVMSettingsDlg::dialogTitle() const
545{
546 QString dialogTitle;
547 if (!mMachine.isNull())
548 dialogTitle = tr ("%1 - Settings").arg (mMachine.GetName());
549 return dialogTitle;
550}
551
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette