VirtualBox

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

Last change on this file since 9951 was 9951, checked in by vboxsync, 16 years ago

Fe/Qt4: VBox VM Network Settings (for windows) ported to qt4.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Revision Author Id
File size: 19.0 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.virtualbox.org. 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 /* Hide unnecessary columns and header */
124 mTwSelector->header()->hide();
125 mTwSelector->hideColumn (listView_Id);
126 mTwSelector->hideColumn (listView_Link);
127
128 /* Initially select the first settings page */
129 mTwSelector->setCurrentItem (mTwSelector->topLevelItem (0));
130
131 /* Setup Settings Dialog */
132 if (!aCategory.isNull())
133 {
134 /* Search for a list view item corresponding to the category */
135 QTreeWidgetItem *item = findItem (mTwSelector, aCategory, listView_Link);
136 if (item)
137 {
138 mTwSelector->setCurrentItem (item);
139
140 /* Search for a widget with the given name */
141 if (!aControl.isNull())
142 {
143 QObject *obj = mPageStack->currentWidget()->findChild<QWidget*> (aControl);
144 if (obj && obj->isWidgetType())
145 {
146 QWidget *w = static_cast<QWidget*> (obj);
147 QList<QWidget*> parents;
148 QWidget *p = w;
149 while ((p = p->parentWidget()) != NULL)
150 {
151 if (p->inherits ("QTabWidget"))
152 {
153 /* The tab contents widget is two steps down
154 * (QTabWidget -> QStackedWidget -> QWidget) */
155 QWidget *c = parents [parents.count() - 1];
156 if (c)
157 c = parents [parents.count() - 2];
158 if (c)
159 static_cast<QTabWidget*> (p)->setCurrentWidget (c);
160 }
161 parents.append (p);
162 }
163
164 w->setFocus();
165 }
166 }
167 }
168 }
169
170 /* Applying language settings */
171 retranslateUi();
172}
173
174void VBoxVMSettingsDlg::getFromMachine (const CMachine &aMachine)
175{
176 mMachine = aMachine;
177
178 updateAvailability();
179
180 setWindowTitle (dialogTitle());
181
182 CVirtualBox vbox = vboxGlobal().virtualBox();
183
184 /* General Page */
185 VBoxVMSettingsGeneral::getFromMachine (aMachine, mPageGeneral,
186 this, pagePath (mPageGeneral));
187
188 /* HD */
189 VBoxVMSettingsHD::getFromMachine (aMachine, mPageHD,
190 this, pagePath (mPageHD));
191
192 /* CD */
193 VBoxVMSettingsCD::getFromMachine (aMachine, mPageCD,
194 this, pagePath (mPageCD));
195
196 /* FD */
197 VBoxVMSettingsFD::getFromMachine (aMachine, mPageFD,
198 this, pagePath (mPageFD));
199
200 /* Audio */
201 VBoxVMSettingsAudio::getFromMachine (aMachine, mPageAudio);
202
203 /* Network */
204 VBoxVMSettingsNetworkPage::getFromMachine (aMachine, mPageNetwork,
205 this, pagePath (mPageNetwork));
206
207 /* Serial Ports */
208 VBoxVMSettingsSerial::getFromMachine (aMachine, mPageSerial,
209 this, pagePath (mPageSerial));
210
211 /* Parallel Ports */
212 if (mPageParallel->isEnabled())
213 VBoxVMSettingsParallel::getFromMachine (aMachine, mPageParallel,
214 this, pagePath (mPageParallel));
215
216 /* USB */
217 if (mPageUSB->isEnabled())
218 VBoxVMSettingsUSB::getFrom (aMachine, mPageUSB,
219 this, pagePath (mPageUSB));
220
221 /* Shared Folders */
222 VBoxVMSettingsSF::getFromMachineEx (aMachine, mPageShared, this);
223
224 /* Vrdp */
225 if (mPageVrdp->isEnabled())
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 VBoxVMSettingsNetworkPage::putBackToMachine();
261
262 /* Serial ports */
263 VBoxVMSettingsSerial::putBackToMachine();
264
265 /* Parallel ports */
266 VBoxVMSettingsParallel::putBackToMachine();
267
268 /* USB */
269 VBoxVMSettingsUSB::putBackTo();
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 /* Update QTreeWidget with available items */
292 updateAvailability();
293
294 /* Adjust selector list */
295 mTwSelector->setFixedWidth (static_cast<QAbstractItemView*> (mTwSelector)->sizeHintForColumn (0) + 2 * mTwSelector->frameWidth());
296
297 /* Sort selector by the id column (to have pages in the desired order) */
298 mTwSelector->sortItems (listView_Id, Qt::AscendingOrder);
299 mTwSelector->resizeColumnToContents (0);
300
301 mWarnIconLabel->setWarningText (tr ("Invalid settings detected"));
302 mButtonBox->button (QDialogButtonBox::Ok)->setWhatsThis (tr ("Accepts (saves) changes and closes the dialog."));
303 mButtonBox->button (QDialogButtonBox::Cancel)->setWhatsThis (tr ("Cancels changes and closes the dialog."));
304 mButtonBox->button (QDialogButtonBox::Help)->setWhatsThis (tr ("Displays the dialog help."));
305
306 setWindowTitle (dialogTitle());
307
308 /* We have to make sure that the Serial & Network subpages are retranslated
309 * before they are revalidated. Cause: They do string comparing within
310 * vboxGlobal which is retranslated at that point already. */
311 QEvent* event = new QEvent (QEvent::LanguageChange);
312 qApp->sendEvent (mPageSerial, event);
313 qApp->sendEvent (mPageNetwork, event);
314
315 /* Revalidate all pages to retranslate the warning messages also. */
316 QList<QIWidgetValidator*> l = this->findChildren<QIWidgetValidator*>();
317 foreach (QIWidgetValidator *wval, l)
318 if (!wval->isValid())
319 revalidate (wval);
320}
321
322void VBoxVMSettingsDlg::enableOk (const QIWidgetValidator*)
323{
324 setWarning (QString::null);
325 QString wvalWarning;
326
327 /* Detect the overall validity */
328 bool newValid = true;
329 {
330 QList<QIWidgetValidator*> l = this->findChildren<QIWidgetValidator*>();
331 foreach (QIWidgetValidator *wval, l)
332 {
333 newValid = wval->isValid();
334 if (!newValid)
335 {
336 wvalWarning = wval->warningText();
337 break;
338 }
339 }
340 }
341
342 if (mWarnString.isNull() && !wvalWarning.isNull())
343 {
344 /* Try to set the generic error message when invalid but no specific
345 * message is provided */
346 setWarning (wvalWarning);
347 }
348
349 if (mValid != newValid)
350 {
351 mValid = newValid;
352 mButtonBox->button (QDialogButtonBox::Ok)->setEnabled (mValid);
353 mWarnIconLabel->setVisible (!mValid);
354 }
355}
356
357void VBoxVMSettingsDlg::revalidate (QIWidgetValidator *aWval)
358{
359 /* do individual validations for pages */
360 QWidget *pg = aWval->widget();
361 bool valid = aWval->isOtherValid();
362
363 QString warningText;
364 QString pageTitle = pagePath (pg);
365
366 if (pg == mPageHD)
367 valid = VBoxVMSettingsHD::revalidate (warningText);
368 else if (pg == mPageCD)
369 valid = VBoxVMSettingsCD::revalidate (warningText);
370 else if (pg == mPageFD)
371 valid = VBoxVMSettingsFD::revalidate (warningText);
372 else if (pg == mPageNetwork)
373 valid = VBoxVMSettingsNetworkPage::revalidate (warningText, pageTitle);
374 else if (pg == mPageSerial)
375 valid = VBoxVMSettingsSerial::revalidate (warningText, pageTitle);
376 else if (pg == mPageParallel)
377 valid = VBoxVMSettingsParallel::revalidate (warningText, pageTitle);
378
379 if (!valid)
380 setWarning (tr ("%1 on the <b>%2</b> page.")
381 .arg (warningText, pageTitle));
382
383 aWval->setOtherValid (valid);
384}
385
386void VBoxVMSettingsDlg::onMediaEnumerationDone()
387{
388 mAllowResetFirstRunFlag = true;
389}
390
391void VBoxVMSettingsDlg::settingsGroupChanged (QTreeWidgetItem *aItem,
392 QTreeWidgetItem *)
393{
394 if (aItem)
395 {
396 int id = aItem->text (1).toInt();
397 Assert (id >= 0);
398 mLbTitle->setText (::path (aItem));
399 mPageStack->setCurrentIndex (id);
400 }
401}
402
403void VBoxVMSettingsDlg::updateWhatsThis (bool aGotFocus /* = false */)
404{
405 QString text;
406
407 QWidget *widget = 0;
408 if (!aGotFocus)
409 {
410 if (mWhatsThisCandidate && mWhatsThisCandidate != this)
411 widget = mWhatsThisCandidate;
412 }
413 else
414 {
415 widget = QApplication::focusWidget();
416 }
417 /* If the given widget lacks the whats'this text, look at its parent */
418 while (widget && widget != this)
419 {
420 text = widget->whatsThis();
421 if (!text.isEmpty())
422 break;
423 widget = widget->parentWidget();
424 }
425
426 if (text.isEmpty() && !mWarnString.isEmpty())
427 text = mWarnString;
428 if (text.isEmpty())
429 text = whatsThis();
430
431 mLbWhatsThis->setText (text);
432}
433
434void VBoxVMSettingsDlg::resetFirstRunFlag()
435{
436 if (mAllowResetFirstRunFlag)
437 mResetFirstRunFlag = true;
438}
439
440void VBoxVMSettingsDlg::whatsThisCandidateDestroyed (QObject *aObj /*= NULL*/)
441{
442 /* sanity */
443 Assert (mWhatsThisCandidate == aObj);
444
445 if (mWhatsThisCandidate == aObj)
446 mWhatsThisCandidate = NULL;
447}
448
449bool VBoxVMSettingsDlg::eventFilter (QObject *aObject, QEvent *aEvent)
450{
451 if (!aObject->isWidgetType())
452 return QIMainDialog::eventFilter (aObject, aEvent);
453
454 QWidget *widget = static_cast<QWidget*> (aObject);
455 if (widget->topLevelWidget() != this)
456 return QIMainDialog::eventFilter (aObject, aEvent);
457
458 switch (aEvent->type())
459 {
460 case QEvent::Enter:
461 case QEvent::Leave:
462 {
463 if (aEvent->type() == QEvent::Enter)
464 {
465 /* What if Qt sends Enter w/o Leave... */
466 if (mWhatsThisCandidate)
467 disconnect (mWhatsThisCandidate, SIGNAL (destroyed (QObject *)),
468 this, SLOT (whatsThisCandidateDestroyed (QObject *)));
469
470 mWhatsThisCandidate = widget;
471 /* make sure we don't reference a deleted object after the
472 * timer is shot */
473 connect (mWhatsThisCandidate, SIGNAL (destroyed (QObject *)),
474 this, SLOT (whatsThisCandidateDestroyed (QObject *)));
475 }
476 else
477 {
478 /* cleanup */
479 if (mWhatsThisCandidate)
480 disconnect (mWhatsThisCandidate, SIGNAL (destroyed (QObject *)),
481 this, SLOT (whatsThisCandidateDestroyed (QObject *)));
482 mWhatsThisCandidate = NULL;
483 }
484
485 mWhatsThisTimer->start (100);
486 break;
487 }
488 case QEvent::FocusIn:
489 {
490 updateWhatsThis (true /* aGotFocus */);
491 break;
492 }
493 default:
494 break;
495 }
496
497 return QIMainDialog::eventFilter (aObject, aEvent);
498}
499
500void VBoxVMSettingsDlg::showEvent (QShowEvent *aEvent)
501{
502 QIMainDialog::showEvent (aEvent);
503
504 /* One may think that QWidget::polish() is the right place to do things
505 * below, but apparently, by the time when QWidget::polish() is called,
506 * the widget style & layout are not fully done, at least the minimum
507 * size hint is not properly calculated. Since this is sometimes necessary,
508 * we provide our own "polish" implementation. */
509
510 if (mPolished)
511 return;
512
513 mPolished = true;
514
515 /* Resize to the minimum possible size */
516 resize (minimumSize());
517
518 VBoxGlobal::centerWidget (this, parentWidget());
519}
520
521/**
522 * Returns a path to the given page of this settings dialog. See ::path() for
523 * details.
524 */
525QString VBoxVMSettingsDlg::pagePath (QWidget *aPage)
526{
527 QTreeWidgetItem *li =
528 findItem (mTwSelector,
529 QString ("%1")
530 .arg (mPageStack->indexOf (aPage), 2, 10, QChar ('0')),
531 1);
532 return ::path (li);
533}
534
535void VBoxVMSettingsDlg::setWarning (const QString &aWarning)
536{
537 mWarnString = aWarning;
538 if (!aWarning.isEmpty())
539 mWarnString = QString ("<font color=red>%1</font>").arg (aWarning);
540
541 if (!mWarnString.isEmpty())
542 mLbWhatsThis->setText (mWarnString);
543 else
544 updateWhatsThis (true);
545}
546
547QString VBoxVMSettingsDlg::dialogTitle() const
548{
549 QString dialogTitle;
550 if (!mMachine.isNull())
551 dialogTitle = tr ("%1 - Settings").arg (mMachine.GetName());
552 return dialogTitle;
553}
554
555void VBoxVMSettingsDlg::updateAvailability()
556{
557 if (mMachine.isNull())
558 return;
559
560 /* Parallel Port Page (currently disabled) */
561 QTreeWidgetItem *parallelItem =
562 findItem (mTwSelector, "#parallelPorts", listView_Link);
563 Assert (parallelItem);
564 if (parallelItem)
565 parallelItem->setHidden (true);
566 mPageParallel->setEnabled (false);
567
568 /* USB Stuff */
569 CUSBController ctl = mMachine.GetUSBController();
570 /* Show an error message (if there is any).
571 * Note that we don't use the generic cannotLoadMachineSettings()
572 * call here because we want this message to be suppressable. */
573 if (!mMachine.isReallyOk())
574 vboxProblem().cannotAccessUSB (mMachine);
575 if (ctl.isNull())
576 {
577 /* Disable the USB controller category if the USB controller is
578 * not available (i.e. in VirtualBox OSE) */
579 QTreeWidgetItem *usbItem =
580 findItem (mTwSelector, "#usb", listView_Link);
581 Assert (usbItem);
582 if (usbItem)
583 usbItem->setHidden (true);
584 mPageUSB->setEnabled (false);
585 }
586
587 /* VRDP Stuff */
588 CVRDPServer vrdp = mMachine.GetVRDPServer();
589 if (vrdp.isNull())
590 {
591 /* Disable the VRDP category if VRDP is
592 * not available (i.e. in VirtualBox OSE) */
593 QTreeWidgetItem *vrdpItem =
594 findItem (mTwSelector, "#vrdp", listView_Link);
595 Assert (vrdpItem);
596 if (vrdpItem)
597 vrdpItem->setHidden (true);
598 mPageVrdp->setEnabled (false);
599 /* If mMachine has something to say, show the message */
600 vboxProblem().cannotLoadMachineSettings (mMachine, false /* strict */);
601 }
602}
603
Note: See TracBrowser for help on using the repository browser.

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