3
* VBox frontends: Qt4 GUI ("VirtualBox"):
4
* VBoxVMSettingsHD class implementation
8
* Copyright (C) 2006-2008 Sun Microsystems, Inc.
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.
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.
24
#include "VBoxVMSettingsHD.h"
25
#include "VBoxGlobal.h"
26
#include "VBoxProblemReporter.h"
27
#include "QIWidgetValidator.h"
28
#include "VBoxToolBar.h"
29
#include "VBoxMediaManagerDlg.h"
30
#include "VBoxNewHDWzd.h"
33
#include <QHeaderView>
34
#include <QItemEditorFactory>
35
#include <QMetaProperty>
37
#include <QStylePainter>
40
* Clear the focus from the current focus owner on guard creation.
41
* And put it into the desired object on guard deletion.
43
* Here this is used to temporary remove the focus from the attachments
44
* table to close the temporary editor of this table to prevent
45
* any side-process (enumeration) influencing model's data.
50
FocusGuardBlock (QWidget *aReturnTo) : mReturnTo (aReturnTo)
52
if (QApplication::focusWidget())
54
QApplication::focusWidget()->clearFocus();
55
qApp->processEvents();
60
mReturnTo->setFocus();
61
qApp->processEvents();
68
/** Type to store disk data */
69
DiskValue::DiskValue (const QString &aId)
71
, name (QString::null), tip (QString::null), pix (QPixmap())
76
VBoxMedium medium = vboxGlobal().getMedium (
77
CMedium (vboxGlobal().virtualBox().GetHardDisk(aId)));
79
bool noDiffs = !HDSettings::instance()->showDiffs();
80
name = medium.details (noDiffs);
81
tip = medium.toolTipCheckRO (noDiffs);
82
pix = medium.iconCheckRO (noDiffs);
86
* QAbstractTableModel class reimplementation.
87
* Used to feat slot/disk selection mechanism.
89
Qt::ItemFlags AttachmentsModel::flags (const QModelIndex &aIndex) const
91
return aIndex.row() == rowCount() - 1 ?
92
QAbstractItemModel::flags (aIndex) ^ Qt::ItemIsSelectable :
93
QAbstractItemModel::flags (aIndex) | Qt::ItemIsEditable;
96
QVariant AttachmentsModel::data (const QModelIndex &aIndex, int aRole) const
98
if (!aIndex.isValid())
101
if (aIndex.row() < 0 || aIndex.row() >= rowCount())
106
case Qt::DisplayRole:
108
if (aIndex.row() == rowCount() - 1)
110
else if (aIndex.column() == 0)
111
return QVariant (mUsedSlotsList [aIndex.row()].name);
112
else if (aIndex.column() == 1)
113
return QVariant (mUsedDisksList [aIndex.row()].name);
118
case Qt::DecorationRole:
120
return aIndex.row() != rowCount() - 1 &&
121
aIndex.column() == 1 &&
122
(aIndex != mParent->currentIndex() ||
123
!DiskEditor::activeEditor())
124
? QVariant (mUsedDisksList [aIndex.row()].pix) : QVariant();
128
if (aIndex.column() == 0)
129
return QVariant (mSlotId, &mUsedSlotsList [aIndex.row()]);
130
else if (aIndex.column() == 1)
131
return QVariant (mDiskId, &mUsedDisksList [aIndex.row()]);
136
case Qt::ToolTipRole:
138
if (aIndex.row() == rowCount() - 1)
139
return QVariant (tr ("Double-click to add a new attachment"));
141
return QVariant (mUsedDisksList [aIndex.row()].tip);
150
bool AttachmentsModel::setData (const QModelIndex &aIndex,
151
const QVariant &aValue,
152
int /* aRole = Qt::EditRole */)
154
if (!aIndex.isValid())
157
if (aIndex.row() < 0 || aIndex.row() >= rowCount())
160
if (aIndex.column() == 0)
162
SlotValue newSlot = aValue.isValid() ?
163
aValue.value <SlotValue>() : SlotValue();
164
if (mUsedSlotsList [aIndex.row()] != newSlot)
166
mUsedSlotsList [aIndex.row()] = newSlot;
167
emit dataChanged (aIndex, aIndex);
172
if (aIndex.column() == 1)
174
DiskValue newDisk = aValue.isValid() ?
175
aValue.value <DiskValue>() : DiskValue();
176
if (mUsedDisksList [aIndex.row()] != newDisk)
178
mUsedDisksList [aIndex.row()] = newDisk;
179
emit dataChanged (aIndex, aIndex);
188
QVariant AttachmentsModel::headerData (int aSection,
189
Qt::Orientation aOrientation,
192
if (aRole != Qt::DisplayRole)
195
if (aOrientation == Qt::Horizontal)
196
return aSection ? tr ("Hard Disk") : tr ("Slot");
201
void AttachmentsModel::addItem (const SlotValue &aSlot, const DiskValue &aDisk)
203
beginInsertRows (QModelIndex(), rowCount() - 1, rowCount() - 1);
204
mUsedSlotsList.append (aSlot);
205
mUsedDisksList.append (aDisk);
209
void AttachmentsModel::delItem (int aIndex)
211
beginRemoveRows (QModelIndex(), aIndex, aIndex);
212
mUsedSlotsList.removeAt (aIndex);
213
mUsedDisksList.removeAt (aIndex);
217
QList <Attachment> AttachmentsModel::fullUsedList()
219
QList <Attachment> list;
220
QList <SlotValue> slts = usedSlotsList();
221
QList <DiskValue> dsks = usedDisksList();
222
for (int i = 0; i < slts.size(); ++ i)
223
list << Attachment (slts [i], dsks [i]);
224
qSort (list.begin(), list.end());
228
void AttachmentsModel::removeAddController()
231
while (i < mUsedSlotsList.size())
233
if (mUsedSlotsList.at (i).bus == KStorageBus_SATA ||
234
mUsedSlotsList.at (i).bus == KStorageBus_SCSI)
235
/* We have to use delItem cause then all views are informed about
243
void AttachmentsModel::updateDisks()
245
QList <DiskValue> newDisks (HDSettings::instance()->disksList());
246
for (int i = 0; i < mUsedDisksList.size(); ++ i)
248
if (newDisks.isEmpty())
249
mUsedDisksList [i] = DiskValue();
250
else if (newDisks.contains (mUsedDisksList [i]))
251
mUsedDisksList [i] = DiskValue (mUsedDisksList [i].id);
253
mUsedDisksList [i] = DiskValue (newDisks [0].id);
255
emit dataChanged (index (0, 1), index (rowCount() - 1, 1));
259
* QComboBox class reimplementation.
260
* Used as editor for HD Attachment SLOT field.
262
SlotEditor::SlotEditor (QWidget *aParent)
263
: QComboBox (aParent)
265
connect (this, SIGNAL (currentIndexChanged (int)), this, SLOT (onActivate()));
266
connect (this, SIGNAL (readyToCommit (QWidget*)),
267
parent()->parent(), SLOT (commitData (QWidget*)));
270
QVariant SlotEditor::slot() const
272
int current = currentIndex();
274
if (current >= 0 && current < mList.size())
275
result.setValue (mList [current]);
279
void SlotEditor::setSlot (QVariant aSlot)
281
SlotValue val (aSlot.value <SlotValue>());
283
int current = findText (val.name);
284
setCurrentIndex (current == -1 ? 0 : current);
287
void SlotEditor::onActivate()
289
emit readyToCommit (this);
292
#if 0 /* F2 key binding left for future releases... */
293
void SlotEditor::keyPressEvent (QKeyEvent *aEvent)
295
/* Make F2 key to show the popup. */
296
if (aEvent->key() == Qt::Key_F2)
303
QComboBox::keyPressEvent (aEvent);
307
void SlotEditor::populate (const SlotValue &aIncluding)
309
clear(), mList.clear();
310
QList <SlotValue> list (HDSettings::instance()->slotsList (aIncluding, true));
311
for (int i = 0; i < list.size() ; ++ i)
313
insertItem (i, list [i].name);
319
* VBoxMediaComboBox class reimplementation.
320
* Used as editor for HD Attachment DISK field.
322
DiskEditor* DiskEditor::mInstance = 0;
323
DiskEditor* DiskEditor::activeEditor()
328
DiskEditor::DiskEditor (QWidget *aParent)
329
: VBoxMediaComboBox (aParent)
332
setIconSize (QSize (iconSize().width() * 2 + 2, iconSize().height()));
333
Assert (!HDSettings::instance()->machine().isNull());
334
setType (VBoxDefs::MediaType_HardDisk);
335
setMachineId (HDSettings::instance()->machine().GetId());
336
setShowDiffs (HDSettings::instance()->showDiffs());
337
connect (this, SIGNAL (currentIndexChanged (int)), this, SLOT (onActivate()));
338
connect (this, SIGNAL (readyToCommit (QWidget *)),
339
parent()->parent(), SLOT (commitData (QWidget *)));
342
DiskEditor::~DiskEditor()
344
if (mInstance == this)
348
QVariant DiskEditor::disk() const
350
int current = currentIndex();
352
if (current >= 0 && current < count())
353
result.setValue (DiskValue (id (current)));
357
void DiskEditor::setDisk (QVariant aDisk)
359
setCurrentItem (DiskValue (aDisk.value <DiskValue>()).id);
362
void DiskEditor::paintEvent (QPaintEvent*)
364
/* Create the style painter to paint the elements. */
365
QStylePainter painter (this);
366
painter.setPen (palette().color (QPalette::Text));
367
/* Initialize combo-box options and draw the elements. */
368
QStyleOptionComboBox options;
369
initStyleOption (&options);
370
painter.drawComplexControl (QStyle::CC_ComboBox, options);
371
painter.drawControl (QStyle::CE_ComboBoxLabel, options);
374
void DiskEditor::initStyleOption (QStyleOptionComboBox *aOption) const
376
/* The base version of Qt4::QComboBox ignores the fact what each
377
* combo-box item can have the icon of different size and uses the
378
* maximum possible icon-size to draw the icon then performing
379
* paintEvent(). As a result, stand-alone icons are painted using
380
* the same huge region as the merged paired icons, so we have to
381
* perform the size calculation ourself... */
383
/* Init all style option by default... */
384
VBoxMediaComboBox::initStyleOption (aOption);
385
/* But calculate the icon size ourself. */
386
QIcon currentItemIcon (itemIcon (currentIndex()));
387
QPixmap realPixmap (currentItemIcon.pixmap (iconSize()));
388
aOption->iconSize = realPixmap.size();
391
void DiskEditor::onActivate()
393
emit readyToCommit (this);
396
#if 0 /* F2 key binding left for future releases... */
397
void DiskEditor::keyPressEvent (QKeyEvent *aEvent)
399
/* Make F2 key to show the popup. */
400
if (aEvent->key() == Qt::Key_F2)
407
VBoxMediaComboBox::keyPressEvent (aEvent);
412
* Singleton QObject class reimplementation.
413
* Used to make selected HD Attachments slots unique &
414
* stores some local data used for HD Settings.
416
HDSettings* HDSettings::mInstance = 0;
417
HDSettings* HDSettings::instance (QWidget *aParent,
418
AttachmentsModel *aWatched)
422
Assert (aParent && aWatched);
423
mInstance = new HDSettings (aParent, aWatched);
428
HDSettings::HDSettings (QWidget *aParent, AttachmentsModel *aWatched)
432
, mAddBus (KStorageBus_Null)
436
makeAddControllerList();
439
HDSettings::~HDSettings()
444
QList <SlotValue> HDSettings::slotsList (const SlotValue &aIncluding,
445
bool aFilter /* = false */) const
447
/* Compose the full slots list */
448
QList <SlotValue> list (mIDEList + mAddControllerList);
452
/* Current used list */
453
QList <SlotValue> usedList (mModel->usedSlotsList());
455
/* Filter the list */
456
foreach (SlotValue value, usedList)
457
if (value != aIncluding)
458
list.removeAll (value);
463
QList <DiskValue> HDSettings::disksList() const
468
bool HDSettings::tryToChooseUniqueDisk (DiskValue &aResult) const
472
/* Current used list */
473
QList <DiskValue> usedList (mModel->usedDisksList());
475
/* Select the first available disk initially */
476
aResult = mDisksList.isEmpty() ? DiskValue() : mDisksList [0];
478
/* Search for first not busy disk */
479
for (int i = 0; i < mDisksList.size(); ++ i)
480
if (!usedList.contains (mDisksList [i]))
482
aResult = mDisksList [i];
490
void HDSettings::makeIDEList()
494
/* IDE Primary Master */
495
mIDEList << SlotValue (KStorageBus_IDE, 0, 0);
496
/* IDE Primary Slave */
497
mIDEList << SlotValue (KStorageBus_IDE, 0, 1);
498
/* IDE Secondary Slave */
499
mIDEList << SlotValue (KStorageBus_IDE, 1, 1);
502
void HDSettings::makeAddControllerList()
504
mAddControllerList.clear();
506
for (int i = 0; i < mAddCount; ++ i)
507
mAddControllerList << SlotValue (mAddBus, i, 0);
510
void HDSettings::makeMediumList()
513
VBoxMediaList list (vboxGlobal().currentMediaList());
514
foreach (VBoxMedium medium, list)
516
/* Filter out unnecessary mediums */
517
if (medium.type() != VBoxDefs::MediaType_HardDisk)
520
/* If !mShowDiffs we ignore all diffs except ones that are
521
* directly attached to the related VM in the current state */
522
if (!mShowDiffs && medium.parent() &&
523
!medium.isAttachedInCurStateTo (mMachine.GetId()))
526
/* If !mShowDiffs we have to replace the root medium with his
527
* differencing child which is directly used if the parent is found. */
528
if (!mShowDiffs && medium.parent())
530
int index = mDisksList.indexOf (DiskValue (medium.root().id()));
533
mDisksList.replace (index, DiskValue (medium.id()));
538
mDisksList.append (DiskValue (medium.id()));
543
* QWidget class reimplementation.
544
* Used as HD Settings widget.
546
VBoxVMSettingsHD::VBoxVMSettingsHD()
548
, mWasTableSelected (false)
550
, mLastSelAddControllerIndex (0)
552
/* Apply UI decorations */
553
Ui::VBoxVMSettingsHD::setupUi (this);
555
/* Setup model/view factory */
556
int idHDSlot = qRegisterMetaType <SlotValue>();
557
int idHDDisk = qRegisterMetaType <DiskValue>();
558
QItemEditorFactory *factory = new QItemEditorFactory;
559
QItemEditorCreatorBase *slotCreator =
560
new QStandardItemEditorCreator <SlotEditor>();
561
QItemEditorCreatorBase *diskCreator =
562
new QStandardItemEditorCreator <DiskEditor>();
563
factory->registerEditor ((QVariant::Type)idHDSlot, slotCreator);
564
factory->registerEditor ((QVariant::Type)idHDDisk, diskCreator);
565
QItemEditorFactory::setDefaultFactory (factory);
567
/* Setup view-model */
568
mModel = new AttachmentsModel (mTwAts, idHDSlot, idHDDisk);
569
connect (mModel, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)),
570
this, SIGNAL (hdChanged()));
572
/* Initialize HD Settings */
573
HDSettings::instance (mTwAts, mModel);
575
/* Setup table-view */
576
mTwAts->setMinimumHeight (100);
577
mTwAts->verticalHeader()->setDefaultSectionSize (
578
(int) (mTwAts->fontMetrics().height() * 1.30 /* 130% of font height */));
579
mTwAts->verticalHeader()->hide();
580
mTwAts->horizontalHeader()->setStretchLastSection (true);
581
mTwAts->setModel (mModel);
582
mTwAts->setToolTip (mModel->data (mModel->index (mModel->rowCount() - 1, 0),
583
Qt::ToolTipRole).toString());
585
/* Prepare actions */
586
mNewAction = new QAction (mTwAts);
587
mDelAction = new QAction (mTwAts);
588
mVdmAction = new QAction (mTwAts);
590
mTwAts->addAction (mNewAction);
591
mTwAts->addAction (mDelAction);
592
mTwAts->addAction (mVdmAction);
594
mNewAction->setShortcut (QKeySequence ("Ins"));
595
mDelAction->setShortcut (QKeySequence ("Del"));
596
mVdmAction->setShortcut (QKeySequence ("Ctrl+Space"));
598
mNewAction->setIcon (VBoxGlobal::iconSet (":/vdm_add_16px.png",
599
":/vdm_add_disabled_16px.png"));
600
mDelAction->setIcon (VBoxGlobal::iconSet (":/vdm_remove_16px.png",
601
":/vdm_remove_disabled_16px.png"));
602
mVdmAction->setIcon (VBoxGlobal::iconSet (":/select_file_16px.png",
603
":/select_file_dis_16px.png"));
605
/* Prepare toolbar */
606
VBoxToolBar *toolBar = new VBoxToolBar (mGbAts);
607
toolBar->setUsesTextLabel (false);
608
toolBar->setIconSize (QSize (16, 16));
609
toolBar->setOrientation (Qt::Vertical);
610
toolBar->addAction (mNewAction);
611
toolBar->addAction (mDelAction);
612
toolBar->addAction (mVdmAction);
613
mGbAts->layout()->addWidget (toolBar);
615
/* Setup connections */
616
connect (mNewAction, SIGNAL (triggered (bool)),
617
this, SLOT (addAttachment()));
618
connect (mDelAction, SIGNAL (triggered (bool)),
619
this, SLOT (delAttachment()));
620
connect (mVdmAction, SIGNAL (triggered (bool)),
621
this, SLOT (showMediaManager()));
623
connect (mAddControllerCheck, SIGNAL (stateChanged (int)),
624
this, SLOT (onAddControllerCheckToggled (int)));
625
connect (mCbControllerType, SIGNAL (currentIndexChanged (int)),
626
this, SLOT (onAddControllerTypeChanged (int)));
627
connect (mShowDiffsCheck, SIGNAL (stateChanged (int)),
628
this, SLOT (onShowDiffsCheckToggled (int)));
630
connect (mTwAts, SIGNAL (currentChanged (const QModelIndex &)),
631
this, SLOT (updateActions (const QModelIndex &)));
633
connect (&vboxGlobal(), SIGNAL (mediumAdded (const VBoxMedium &)),
634
HDSettings::instance(), SLOT (update()));
635
connect (&vboxGlobal(), SIGNAL (mediumUpdated (const VBoxMedium &)),
636
HDSettings::instance(), SLOT (update()));
637
connect (&vboxGlobal(), SIGNAL (mediumRemoved (VBoxDefs::MediaType, const QString &)),
638
HDSettings::instance(), SLOT (update()));
640
/* IDE Controller Type */
641
mCbIDEController->addItem (""); /* KIDEControllerType_PIIX3 */
642
mCbIDEController->addItem (""); /* KIDEControllerType_PIIX4 */
643
mCbIDEController->addItem (""); /* KIDEControllerType_ICH6 */
646
/* Additional Controller Type */
647
mCbControllerType->addItem ("", KStorageControllerType_IntelAhci);
648
mCbControllerType->addItem ("", KStorageControllerType_LsiLogic);
649
mCbControllerType->addItem ("", KStorageControllerType_BusLogic);
651
/* Install global event filter */
652
qApp->installEventFilter (this);
654
/* Applying language settings */
658
void VBoxVMSettingsHD::getFrom (const CMachine &aMachine)
661
HDSettings::instance()->setMachine (mMachine);
663
/* IDE controller type */
664
const QString ideName = QString ("IDE");
665
CStorageController ideCtl = aMachine.GetStorageControllerByName (ideName);
666
mCbIDEController->setCurrentIndex (mCbIDEController->
667
findText (vboxGlobal().toString (ideCtl.GetControllerType())));
669
/* For now we search for the first one which isn't IDE */
670
CStorageController addController;
671
QVector<CStorageController> scs = mMachine.GetStorageControllers();
672
foreach (const CStorageController &sc, scs)
673
if (sc.GetBus() != KStorageBus_IDE)
678
if (!addController.isNull())
679
mCbControllerType->setCurrentIndex (mCbControllerType->findData (addController.GetControllerType()));
681
mCbControllerType->setCurrentIndex (0);
683
mAddControllerCheck->setChecked (!addController.isNull());
685
onAddControllerCheckToggled (mAddControllerCheck->checkState());
686
onShowDiffsCheckToggled (mShowDiffsCheck->checkState());
688
/* Load attachments list */
689
CHardDiskAttachmentVector vec = mMachine.GetHardDiskAttachments();
690
for (int i = 0; i < vec.size(); ++ i)
692
CHardDiskAttachment hda = vec [i];
693
CStorageController ctl = mMachine.GetStorageControllerByName(hda.GetController());
695
SlotValue slot (ctl.GetBus(), hda.GetPort(), hda.GetDevice());
696
DiskValue disk (hda.GetHardDisk().GetId());
697
mModel->addItem (slot, disk);
700
/* Initially select the first table item & update the actions */
701
mTwAts->setCurrentIndex (mModel->index (0, 1));
702
updateActions (mTwAts->currentIndex());
704
/* Validate if possible */
706
mValidator->revalidate();
709
void VBoxVMSettingsHD::putBackTo()
711
/* IDE controller type */
712
const QString ideName = QString ("IDE");
713
CStorageController ideCtl = mMachine.GetStorageControllerByName(ideName);
714
ideCtl.SetControllerType (vboxGlobal().toIDEControllerType (mCbIDEController->currentText()));
716
/* Detach all attached Hard Disks */
717
CHardDiskAttachmentVector vec = mMachine.GetHardDiskAttachments();
718
for (int i = 0; i < vec.size(); ++ i)
720
CHardDiskAttachment hda = vec [i];
722
mMachine.DetachHardDisk(hda.GetController(), hda.GetPort(), hda.GetDevice());
724
/* [dsen] check this */
725
if (!mMachine.isOk())
727
CStorageController ctl = mMachine.GetStorageControllerByName (hda.GetController());
728
vboxProblem().cannotDetachHardDisk (this, mMachine,
729
vboxGlobal().getMedium (CMedium (hda.GetHardDisk())).location(),
730
ctl.GetBus(), hda.GetPort(), hda.GetDevice());
734
/* Clear all storage controllers beside the IDE one */
735
CStorageController addController;
736
QVector <CStorageController> scs = mMachine.GetStorageControllers();
737
foreach (const CStorageController &sc, scs)
738
if (sc.GetBus() != KStorageBus_IDE)
739
mMachine.RemoveStorageController (sc.GetName());
741
/* Now add an additional controller if the user has enabled this */
742
CStorageController addCtl;
743
if (mAddControllerCheck->isChecked())
745
KStorageControllerType sct = currentControllerType();
746
KStorageBus sv = currentBusType();
747
addCtl = mMachine.AddStorageController (vboxGlobal().toString (sv), sv);
748
addCtl.SetControllerType (sct);
751
/* On SATA it is possible to set the max port count. We want not wasting resources
752
* so we try to find the port with the highest number & set it as max port count.
753
* But first set it to the maximum because we get errors if there are more hard
754
* disks than currently activated ports. */
755
if (!addCtl.isNull() &&
756
addCtl.GetBus() == KStorageBus_SATA)
757
addCtl.SetPortCount (30);
759
/* Attach all listed Hard Disks */
760
LONG maxSATAPort = 1;
762
QList <Attachment> list (mModel->fullUsedList());
763
for (int i = 0; i < list.size(); ++ i)
765
ctrlName = vboxGlobal().toString (list [i].slot.bus);
766
if (list [i].slot.bus == KStorageBus_SATA)
768
maxSATAPort = maxSATAPort < (list [i].slot.channel + 1) ?
769
(list [i].slot.channel + 1) : maxSATAPort;
772
mMachine.AttachHardDisk (list [i].disk.id,
773
ctrlName, list [i].slot.channel, list [i].slot.device);
774
/* [dsen] check this */
775
if (!mMachine.isOk())
776
vboxProblem().cannotAttachHardDisk (this, mMachine,
777
vboxGlobal().getMedium (CMedium (vboxGlobal().virtualBox()
778
.GetHardDisk (list [i].disk.id))).location(),
779
list [i].slot.bus, list [i].slot.channel, list [i].slot.device);
782
/* Set the maximum port count if the additional controller is a SATA controller. */
783
if (!addCtl.isNull() &&
784
addCtl.GetBus() == KStorageBus_SATA)
785
addCtl.SetPortCount (maxSATAPort);
788
void VBoxVMSettingsHD::setValidator (QIWidgetValidator *aVal)
791
connect (mModel, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)),
792
mValidator, SLOT (revalidate()));
795
bool VBoxVMSettingsHD::revalidate (QString &aWarning, QString &)
797
QList <SlotValue> slotList (mModel->usedSlotsList());
798
QList <DiskValue> diskList (mModel->usedDisksList());
799
for (int i = 0; i < diskList.size(); ++ i)
801
/* Check for emptiness */
802
if (diskList [i].id.isNull())
804
aWarning = tr ("No hard disk is selected for <i>%1</i>")
805
.arg (slotList [i].name);
809
/* Check for coincidence */
810
if (diskList.count (diskList [i]) > 1)
812
int first = diskList.indexOf (diskList [i]);
813
int second = diskList.indexOf (diskList [i], first + 1);
814
Assert (first != -1 && second != -1);
815
aWarning = tr ("<i>%1</i> uses the hard disk that is "
816
"already attached to <i>%2</i>")
817
.arg (slotList [second].name,
818
slotList [first].name);
823
return aWarning.isNull();
826
void VBoxVMSettingsHD::setOrderAfter (QWidget *aWidget)
828
setTabOrder (aWidget, mCbIDEController);
829
setTabOrder (mCbIDEController, mAddControllerCheck);
830
setTabOrder (mAddControllerCheck, mCbControllerType);
831
setTabOrder (mCbControllerType, mTwAts);
832
setTabOrder (mTwAts, mShowDiffsCheck);
835
void VBoxVMSettingsHD::retranslateUi()
837
/* Translate uic generated strings */
838
Ui::VBoxVMSettingsHD::retranslateUi (this);
840
/* IDE Controller Type */
841
mCbIDEController->setItemText (0, vboxGlobal().toString (KStorageControllerType_PIIX3));
842
mCbIDEController->setItemText (1, vboxGlobal().toString (KStorageControllerType_PIIX4));
843
mCbIDEController->setItemText (2, vboxGlobal().toString (KStorageControllerType_ICH6));
845
/* Additional Controller Type */
846
mCbControllerType->setItemText (0, QString ("%1 (%2)").arg (vboxGlobal().toString (KStorageBus_SATA))
847
.arg (vboxGlobal().toString (KStorageControllerType_IntelAhci)));
848
mCbControllerType->setItemText (1, QString ("%1 (%2)").arg (vboxGlobal().toString (KStorageBus_SCSI))
849
.arg (vboxGlobal().toString (KStorageControllerType_LsiLogic)));
850
mCbControllerType->setItemText (2, QString ("%1 (%2)").arg (vboxGlobal().toString (KStorageBus_SCSI))
851
.arg (vboxGlobal().toString (KStorageControllerType_BusLogic)));
853
/* Attachments List */
854
mNewAction->setText (tr ("&Add Attachment"));
855
mDelAction->setText (tr ("&Remove Attachment"));
856
mVdmAction->setText (tr ("&Select Hard Disk"));
858
mNewAction->setToolTip (mNewAction->text().remove ('&') +
859
QString (" (%1)").arg (mNewAction->shortcut().toString()));
860
mDelAction->setToolTip (mDelAction->text().remove ('&') +
861
QString (" (%1)").arg (mDelAction->shortcut().toString()));
862
mVdmAction->setToolTip (mVdmAction->text().remove ('&') +
863
QString (" (%1)").arg (mVdmAction->shortcut().toString()));
865
mNewAction->setWhatsThis (tr ("Adds a new hard disk attachment."));
866
mDelAction->setWhatsThis (tr ("Removes the highlighted hard disk attachment."));
867
mVdmAction->setWhatsThis (tr ("Invokes the Virtual Media Manager to select "
868
"a hard disk to attach to the currently "
869
"highlighted slot."));
872
void VBoxVMSettingsHD::addAttachment()
874
/* Temporary disable corresponding action now to prevent calling it again
875
* before it will be disabled by current-changed processing. This can
876
* happens if the user just pressed & hold the shortcut combination. */
877
mNewAction->setEnabled (false);
881
{ /* Clear the focus */
882
FocusGuardBlock guard (mTwAts);
884
bool uniqueDiskSelected = false;
885
HDSettings *hds = HDSettings::instance();
887
{ /* Add new item with default values */
888
SlotValue slot (hds->slotsList (SlotValue(), true) [0]);
890
uniqueDiskSelected = hds->tryToChooseUniqueDisk (disk);
891
mModel->addItem (slot, disk);
892
} /* Add new item with default values */
894
/* If there are not enough unique disks */
895
if (!uniqueDiskSelected)
897
/* Ask the user for method to add new disk */
898
int confirm = vboxProblem().confirmRunNewHDWzdOrVDM (this);
899
newId = confirm == QIMessageBox::Yes ? getWithNewHDWizard() :
900
confirm == QIMessageBox::No ? getWithMediaManager() : QString::null;
902
} /* Clear the focus */
904
/* Set the right column of new index to be the current */
905
mTwAts->setCurrentIndex (mModel->index (mModel->rowCount() - 2, 1));
909
/* Compose & apply resulting disk */
911
newValue.setValue (DiskValue (newId));
912
mModel->setData (mTwAts->currentIndex(), newValue);
915
/* Validate if possible */
917
mValidator->revalidate();
921
void VBoxVMSettingsHD::delAttachment()
923
Assert (mTwAts->currentIndex().isValid());
925
/* Temporary disable corresponding action now to prevent calling it again
926
* before it will be disabled by current-changed processing. This can
927
* happens if the user just pressed & hold the shortcut combination. */
928
mDelAction->setEnabled (false);
930
/* Clear the focus */
931
FocusGuardBlock guard (mTwAts);
933
/* Storing current attributes */
934
int row = mTwAts->currentIndex().row();
935
int col = mTwAts->currentIndex().column();
937
/* Erase current index */
938
mTwAts->setCurrentIndex (QModelIndex());
940
/* Calculate new current index */
941
int newRow = row < mModel->rowCount() - 2 ? row :
942
row > 0 ? row - 1 : -1;
943
QModelIndex next = newRow == -1 ? mModel->index (0, col) :
944
mModel->index (newRow, col);
946
/* Delete current index */
947
mModel->delItem (row);
949
/* Set the new index to be the current */
950
mTwAts->setCurrentIndex (next);
951
updateActions (next);
954
mValidator->revalidate();
958
void VBoxVMSettingsHD::showMediaManager()
960
Assert (mTwAts->currentIndex().isValid());
962
/* Clear the focus */
963
FocusGuardBlock guard (mTwAts);
965
DiskValue current (mModel->data (mTwAts->currentIndex(), Qt::EditRole)
966
.value <DiskValue>());
968
QString id = getWithMediaManager (current.id);
972
/* Compose & apply resulting disk */
974
newValue.setValue (DiskValue (id));
975
mModel->setData (mTwAts->currentIndex(), newValue);
979
void VBoxVMSettingsHD::updateActions (const QModelIndex& /* aIndex */)
981
mNewAction->setEnabled (mModel->rowCount() - 1 <
982
HDSettings::instance()->slotsList().count());
983
mDelAction->setEnabled (mTwAts->currentIndex().row() != mModel->rowCount() - 1);
984
mVdmAction->setEnabled (mTwAts->currentIndex().row() != mModel->rowCount() - 1 &&
985
mTwAts->currentIndex().column() == 1);
988
void VBoxVMSettingsHD::onAddControllerCheckToggled (int aState)
991
if (mAddControllerCheck->checkState() == Qt::Unchecked)
992
if (checkAddControllers (0))
994
/* Switch check-box back to "Qt::Checked" */
995
mAddControllerCheck->blockSignals (true);
996
mAddControllerCheck->setCheckState (Qt::Checked);
997
mAddControllerCheck->blockSignals (false);
998
/* The user cancel the request so do nothing */
1002
mCbControllerType->setEnabled (aState == Qt::Checked);
1003
HDSettings::instance()->setAddCount (mAddControllerCheck->checkState() == Qt::Checked ?
1004
currentMaxPortCount() : 0,
1006
updateActions (mTwAts->currentIndex());
1009
void VBoxVMSettingsHD::onAddControllerTypeChanged (int aIndex)
1012
if (checkAddControllers (1))
1014
/* Switch check-box back to "Qt::Checked" */
1015
mCbControllerType->blockSignals (true);
1016
mCbControllerType->setCurrentIndex (mLastSelAddControllerIndex);
1017
mCbControllerType->blockSignals (false);
1018
/* The user cancel the request so do nothing */
1022
/* Save the new index for later roll back */
1023
mLastSelAddControllerIndex = aIndex;
1025
HDSettings::instance()->setAddCount (mAddControllerCheck->checkState() == Qt::Checked ?
1026
currentMaxPortCount() : 0,
1028
updateActions (mTwAts->currentIndex());
1031
bool VBoxVMSettingsHD::checkAddControllers (int aWhat)
1033
/* Search the list for at least one SATA/SCSI port in */
1034
QList <SlotValue> list (mModel->usedSlotsList());
1035
int firstAddPort = 0;
1036
for (; firstAddPort < list.size(); ++ firstAddPort)
1037
if (list [firstAddPort].bus == KStorageBus_SATA ||
1038
list [firstAddPort].bus == KStorageBus_SCSI)
1041
/* If list contains at least one SATA/SCSI port */
1042
if (firstAddPort < list.size())
1046
result = vboxProblem().confirmDetachAddControllerSlots (this);
1048
result = vboxProblem().confirmChangeAddControllerSlots (this);
1049
if (result != QIMessageBox::Ok)
1054
/* Delete additional controller items */
1055
mModel->removeAddController();
1057
/* Set column #1 of first index to be the current */
1058
mTwAts->setCurrentIndex (mModel->index (0, 1));
1061
mValidator->revalidate();
1067
void VBoxVMSettingsHD::onShowDiffsCheckToggled (int aState)
1070
HDSettings::instance()->setShowDiffs (aState == Qt::Checked);
1073
bool VBoxVMSettingsHD::eventFilter (QObject *aObject, QEvent *aEvent)
1075
if (!aObject->isWidgetType())
1076
return QWidget::eventFilter (aObject, aEvent);
1078
QWidget *widget = static_cast <QWidget*> (aObject);
1079
if (widget->inherits ("SlotEditor") ||
1080
widget->inherits ("DiskEditor"))
1082
if (aEvent->type() == QEvent::KeyPress)
1084
QKeyEvent *e = static_cast <QKeyEvent*> (aEvent);
1085
QModelIndex cur = mTwAts->currentIndex();
1091
mTwAts->setCurrentIndex (mModel->index (cur.row() - 1,
1097
if (cur.row() < mModel->rowCount() - 1)
1098
mTwAts->setCurrentIndex (mModel->index (cur.row() + 1,
1104
if (cur.column() == 0)
1105
mTwAts->setCurrentIndex (mModel->index (cur.row(), 1));
1110
if (cur.column() == 1)
1111
mTwAts->setCurrentIndex (mModel->index (cur.row(), 0));
1116
focusNextPrevChild (true);
1119
case Qt::Key_Backtab:
1121
/* Due to table on getting focus back from the child
1122
* put it instantly to this child again, make a hack
1123
* to put focus to the real previous owner. */
1124
mAddControllerCheck->setFocus();
1131
if (aEvent->type() == QEvent::WindowDeactivate)
1133
/* Store focus state if it is on temporary editor. */
1134
if (widget->hasFocus())
1135
mWasTableSelected = true;
1138
if (widget == mTwAts->viewport() &&
1139
aEvent->type() == QEvent::MouseButtonDblClick)
1141
QMouseEvent *e = static_cast <QMouseEvent*> (aEvent);
1142
QModelIndex index = mTwAts->indexAt (e->pos());
1143
if (mNewAction->isEnabled() &&
1144
(index.row() == mModel->rowCount() - 1 || !index.isValid()))
1147
if (aEvent->type() == QEvent::WindowActivate)
1149
if (mWasTableSelected)
1151
/* Restore focus state if it was on temporary editor. */
1152
mWasTableSelected = false;
1157
return QWidget::eventFilter (aObject, aEvent);
1160
void VBoxVMSettingsHD::showEvent (QShowEvent *aEvent)
1162
QWidget::showEvent (aEvent);
1168
/* Some delayed polishing */
1169
mTwAts->horizontalHeader()->resizeSection (0,
1170
style()->pixelMetric (QStyle::PM_ScrollBarExtent, 0, this) +
1171
maxNameLength() + 9 * 2 /* 2 margins */);
1173
/* Activate edit triggers only now to avoid influencing
1174
* HD Attachments table during data loading. */
1175
mTwAts->setEditTriggers (QAbstractItemView::CurrentChanged |
1176
QAbstractItemView::SelectedClicked |
1177
QAbstractItemView::EditKeyPressed);
1179
/* That little hack allows avoid one of qt4 children focusing bug */
1180
QWidget *current = QApplication::focusWidget();
1181
mTwAts->setFocus (Qt::TabFocusReason);
1183
current->setFocus (Qt::TabFocusReason);
1186
QString VBoxVMSettingsHD::getWithMediaManager (const QString &aInitialId)
1188
/* Run Media Manager */
1189
VBoxMediaManagerDlg dlg (this);
1190
dlg.setup (VBoxDefs::MediaType_HardDisk,
1191
true /* do select? */,
1192
false /* do refresh? */,
1195
HDSettings::instance()->showDiffs());
1197
return dlg.exec() == QDialog::Accepted ? dlg.selectedId() : QString::null;
1200
QString VBoxVMSettingsHD::getWithNewHDWizard()
1202
/* Run New HD Wizard */
1203
VBoxNewHDWzd dlg (this);
1205
return dlg.exec() == QDialog::Accepted ? dlg.hardDisk().GetId() : QString::null;
1208
int VBoxVMSettingsHD::maxNameLength() const
1210
QList <SlotValue> slts (HDSettings::instance()->slotsList());
1212
for (int i = 0; i < slts.size(); ++ i)
1214
int length = mTwAts->fontMetrics().width (slts [i].name);
1215
nameLength = length > nameLength ? length : nameLength;
1220
void VBoxVMSettingsHD::removeFocus()
1223
/* On the Mac checkboxes aren't focus aware. Therewith a editor widget get
1224
* not closed by clicking on the checkbox. As a result the content of the
1225
* currently used editor (QComboBox) isn't updated. So manually remove the
1226
* focus to force the closing of any open editor widget. */
1227
QWidget *focusWidget = qApp->focusWidget();
1229
focusWidget->clearFocus();
1230
#endif /* Q_WS_MAC */