1
/* $Id: VBoxVMSettingsSF.cpp $ */
4
* VBox frontends: Qt4 GUI ("VirtualBox"):
5
* VBoxVMSettingsSF class implementation
9
* Copyright (C) 2008-2009 Oracle Corporation
11
* This file is part of VirtualBox Open Source Edition (OSE), as
12
* available from http://www.virtualbox.org. This file is free software;
13
* you can redistribute it and/or modify it under the terms of the GNU
14
* General Public License (GPL) as published by the Free Software
15
* Foundation, in version 2 as it comes in the "COPYING" file of the
16
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
17
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
21
#include <QHeaderView>
25
#include "VBoxVMSettingsSF.h"
26
#include "VBoxVMSettingsSFDetails.h"
27
#include "VBoxGlobal.h"
28
#include "VBoxProblemReporter.h"
29
#include "VBoxUtils.h"
31
class SFTreeViewItem : public QTreeWidgetItem
35
enum { SFTreeViewItemType = QTreeWidgetItem::UserType + 1 };
47
SFTreeViewItem (QTreeWidget *aParent, const QStringList &aFields, FormatType aFormat)
48
: QTreeWidgetItem (aParent, aFields, SFTreeViewItemType), mFormat (aFormat)
50
setFirstColumnSpanned (true);
51
setFlags (flags() ^ Qt::ItemIsSelectable);
55
SFTreeViewItem (SFTreeViewItem *aParent, const QStringList &aFields, FormatType aFormat)
56
: QTreeWidgetItem (aParent, aFields, SFTreeViewItemType), mFormat (aFormat)
61
bool operator< (const QTreeWidgetItem &aOther) const
63
/* Root items should always been sorted by id-field. */
64
return parent() ? text (0).toLower() < aOther.text (0).toLower() :
65
text (1).toLower() < aOther.text (1).toLower();
68
SFTreeViewItem* child (int aIndex) const
70
QTreeWidgetItem *item = QTreeWidgetItem::child (aIndex);
71
return item && item->type() == SFTreeViewItemType ? static_cast <SFTreeViewItem*> (item) : 0;
74
QString getText (int aIndex) const
76
return aIndex >= 0 && aIndex < (int)mTextList.size() ? mTextList [aIndex] : QString::null;
79
void updateText (const QStringList &aFields)
88
for (int i = 0; i < treeWidget()->columnCount(); ++ i)
94
void processColumn (int aColumn)
96
QString oneString = getText (aColumn);
97
if (oneString.isNull())
99
QFontMetrics fm = treeWidget()->fontMetrics();
100
int oldSize = fm.width (oneString);
101
int indentSize = fm.width (" ... ");
102
int itemIndent = parent() ? treeWidget()->indentation() * 2 : treeWidget()->indentation();
104
indentSize += itemIndent;
105
int cWidth = treeWidget()->columnWidth (aColumn);
114
textWidth = fm.width (oneString);
115
if (textWidth + indentSize > cWidth)
118
finish = oneString.length();
120
/* Selecting remove position */
127
position = (finish - start) / 2;
130
position = finish - 1;
134
QRegExp regExp ("([\\\\/][^\\\\^/]+[\\\\/]?$)");
135
int newFinish = regExp.indexIn (oneString);
138
position = (finish - start) / 2;
142
AssertMsgFailed (("Invalid format type\n"));
145
if (position == finish)
148
oneString.remove (position, 1);
151
while (textWidth + indentSize > cWidth);
153
if (position || mFormat == EllipsisFile) oneString.insert (position, "...");
154
int newSize = fm.width (oneString);
155
setText (aColumn, newSize < oldSize ? oneString : mTextList [aColumn]);
156
setToolTip (aColumn, text (aColumn) == getText (aColumn) ? QString::null : getText (aColumn));
158
/* Calculate item's size-hint */
159
setSizeHint (aColumn, QSize (fm.width (QString (" %1 ").arg (getText (aColumn))), 100));
163
QStringList mTextList;
166
VBoxVMSettingsSF::VBoxVMSettingsSF (int aType, QWidget *aParent)
167
: VBoxSettingsPage (aParent)
168
, mDialogType (aType)
169
, mIsListViewChanged (false)
171
/* Apply UI decorations */
172
Ui::VBoxVMSettingsSF::setupUi (this);
174
/* Prepare actions */
175
mNewAction = new QAction (this);
176
mEdtAction = new QAction (this);
177
mDelAction = new QAction (this);
179
mNewAction->setShortcut (QKeySequence ("Ins"));
180
mEdtAction->setShortcut (QKeySequence ("Ctrl+Space"));
181
mDelAction->setShortcut (QKeySequence ("Del"));
183
mNewAction->setIcon (VBoxGlobal::iconSet (":/add_shared_folder_16px.png",
184
":/add_shared_folder_disabled_16px.png"));
185
mEdtAction->setIcon (VBoxGlobal::iconSet (":/edit_shared_folder_16px.png",
186
":/edit_shared_folder_disabled_16px.png"));
187
mDelAction->setIcon (VBoxGlobal::iconSet (":/revome_shared_folder_16px.png",
188
":/revome_shared_folder_disabled_16px.png"));
190
/* Prepare toolbar */
191
mTbFolders->setUsesTextLabel (false);
192
mTbFolders->setIconSize (QSize (16, 16));
193
mTbFolders->setOrientation (Qt::Vertical);
194
mTbFolders->addAction (mNewAction);
195
mTbFolders->addAction (mEdtAction);
196
mTbFolders->addAction (mDelAction);
198
/* Setup connections */
199
mTwFolders->header()->setMovable (false);
200
connect (mNewAction, SIGNAL (triggered (bool)), this, SLOT (addTriggered()));
201
connect (mEdtAction, SIGNAL (triggered (bool)), this, SLOT (edtTriggered()));
202
connect (mDelAction, SIGNAL (triggered (bool)), this, SLOT (delTriggered()));
203
connect (mTwFolders, SIGNAL (currentItemChanged (QTreeWidgetItem *, QTreeWidgetItem *)),
204
this, SLOT (processCurrentChanged (QTreeWidgetItem *)));
205
connect (mTwFolders, SIGNAL (itemDoubleClicked (QTreeWidgetItem *, int)),
206
this, SLOT (processDoubleClick (QTreeWidgetItem *)));
207
connect (mTwFolders, SIGNAL (customContextMenuRequested (const QPoint &)),
208
this, SLOT (showContextMenu (const QPoint &)));
210
/* Create mTwFolders root items */
212
if (aType == GlobalType)
215
fields << tr (" Global Folders") /* name */ << QString::number (GlobalType) /* key */;
216
new SFTreeViewItem (mTwFolders, fields, SFTreeViewItem::EllipsisEnd);
219
if (aType & MachineType)
222
fields << tr (" Machine Folders") /* name */ << QString::number (MachineType) /* key */;
223
new SFTreeViewItem (mTwFolders, fields, SFTreeViewItem::EllipsisEnd);
225
if (aType & ConsoleType)
228
fields << tr (" Transient Folders") /* name */ << QString::number (ConsoleType) /* key */;
229
new SFTreeViewItem (mTwFolders, fields, SFTreeViewItem::EllipsisEnd);
231
mTwFolders->sortItems (0, Qt::AscendingOrder);
236
void VBoxVMSettingsSF::getFromGlobal()
238
AssertMsgFailed (("Global shared folders are not supported now!\n"));
240
SFTreeViewItem *root = searchRoot (true, GlobalType);
241
root->setHidden (false);
242
getFrom (vboxGlobal().virtualBox().GetSharedFolders(), root);
246
void VBoxVMSettingsSF::getFromMachine (const CMachine &aMachine)
249
SFTreeViewItem *root = searchRoot (true, MachineType);
250
root->setHidden (false);
251
getFrom (mMachine.GetSharedFolders(), root);
254
void VBoxVMSettingsSF::getFromConsole (const CConsole &aConsole)
257
SFTreeViewItem *root = searchRoot (true, ConsoleType);
258
root->setHidden (false);
259
getFrom (mConsole.GetSharedFolders(), root);
262
void VBoxVMSettingsSF::putBackToGlobal()
264
AssertMsgFailed (("Global shared folders are not supported now!\n"));
266
if (!mIsListViewChanged)
268
/* This function is only available for GlobalType dialog */
269
Assert (mDialogType == GlobalType);
270
/* Searching for GlobalType item's root */
271
SFTreeViewItem *root = searchRoot (true, GlobalType);
273
CSharedFolderVector vec = vboxGlobal().virtualBox().GetSharedFolders();
274
putBackTo (vec, root);
278
void VBoxVMSettingsSF::putBackToMachine()
280
if (!mIsListViewChanged)
282
/* This function is only available for MachineType dialog */
283
Assert (mDialogType & MachineType);
284
/* Searching for MachineType item's root */
285
SFTreeViewItem *root = searchRoot (true, MachineType);
287
CSharedFolderVector sfvec = mMachine.GetSharedFolders();
288
putBackTo (sfvec, root);
291
void VBoxVMSettingsSF::putBackToConsole()
293
if (!mIsListViewChanged)
295
/* This function is only available for ConsoleType dialog */
296
Assert (mDialogType & ConsoleType);
297
/* Searching for ConsoleType item's root */
298
SFTreeViewItem *root = searchRoot (true, ConsoleType);
300
CSharedFolderVector sfvec = mConsole.GetSharedFolders();
301
putBackTo (sfvec, root);
304
int VBoxVMSettingsSF::dialogType() const
309
void VBoxVMSettingsSF::getFrom (const CMachine &aMachine)
311
getFromMachine (aMachine);
314
void VBoxVMSettingsSF::putBackTo()
319
void VBoxVMSettingsSF::setOrderAfter (QWidget *aWidget)
321
setTabOrder (aWidget, mTwFolders);
324
void VBoxVMSettingsSF::retranslateUi()
326
/* Translate uic generated strings */
327
Ui::VBoxVMSettingsSF::retranslateUi (this);
329
mNewAction->setText (tr ("&Add Shared Folder"));
330
mEdtAction->setText (tr ("&Edit Shared Folder"));
331
mDelAction->setText (tr ("&Remove Shared Folder"));
333
mNewAction->setToolTip (mNewAction->text().remove ('&') +
334
QString (" (%1)").arg (mNewAction->shortcut().toString()));
335
mEdtAction->setToolTip (mEdtAction->text().remove ('&') +
336
QString (" (%1)").arg (mEdtAction->shortcut().toString()));
337
mDelAction->setToolTip (mDelAction->text().remove ('&') +
338
QString (" (%1)").arg (mDelAction->shortcut().toString()));
340
mNewAction->setWhatsThis (tr ("Adds a new shared folder definition."));
341
mEdtAction->setWhatsThis (tr ("Edits the selected shared folder definition."));
342
mDelAction->setWhatsThis (tr ("Removes the selected shared folder definition."));
344
mTrFull = tr ("Full");
345
mTrReadOnly = tr ("Read-only");
348
void VBoxVMSettingsSF::addTriggered()
350
/* Invoke Add-Box Dialog */
351
VBoxVMSettingsSFDetails dlg (VBoxVMSettingsSFDetails::AddType, mDialogType & ConsoleType, usedList (true), this);
352
if (dlg.exec() == QDialog::Accepted)
354
QString name = dlg.name();
355
QString path = dlg.path();
356
bool isPermanent = dlg.isPermanent();
357
/* Shared folder's name & path could not be empty */
358
Assert (!name.isEmpty() && !path.isEmpty());
359
/* Searching root for the new listview item */
360
SFTreeViewItem *root = searchRoot (isPermanent);
362
/* Appending a new listview item to the root */
364
fields << name /* name */ << path /* path */
365
<< (dlg.isWriteable() ? mTrFull : mTrReadOnly /* writable? */)
366
<< "edited" /* mark item as edited */;
367
SFTreeViewItem *item = new SFTreeViewItem (root, fields, SFTreeViewItem::EllipsisFile);
368
mTwFolders->sortItems (0, Qt::AscendingOrder);
369
mTwFolders->scrollToItem (item);
370
mTwFolders->setCurrentItem (item);
371
processCurrentChanged (item);
372
mTwFolders->setFocus();
375
mIsListViewChanged = true;
379
void VBoxVMSettingsSF::edtTriggered()
381
/* Check selected item */
382
QTreeWidgetItem *selectedItem = mTwFolders->selectedItems().size() == 1 ? mTwFolders->selectedItems() [0] : 0;
383
SFTreeViewItem *item = selectedItem && selectedItem->type() == SFTreeViewItem::SFTreeViewItemType ?
384
static_cast <SFTreeViewItem*> (selectedItem) : 0;
386
Assert (item->parent());
388
/* Invoke Edit-Box Dialog */
389
VBoxVMSettingsSFDetails dlg (VBoxVMSettingsSFDetails::EditType, mDialogType & ConsoleType, usedList (false), this);
390
dlg.setPath (item->getText (1));
391
dlg.setName (item->getText (0));
392
dlg.setPermanent ((SFDialogType)item->parent()->text (1).toInt() != ConsoleType);
393
dlg.setWriteable (item->getText (2) == mTrFull);
394
if (dlg.exec() == QDialog::Accepted)
396
QString name = dlg.name();
397
QString path = dlg.path();
398
bool isPermanent = dlg.isPermanent();
399
/* Shared folder's name & path could not be empty */
400
Assert (!name.isEmpty() && !path.isEmpty());
401
/* Searching new root for the selected listview item */
402
SFTreeViewItem *root = searchRoot (isPermanent);
404
/* Updating an edited listview item */
406
fields << name /* name */ << path /* path */
407
<< (dlg.isWriteable() ? mTrFull : mTrReadOnly /* writable? */)
408
<< "edited" /* mark item as edited */;
409
item->updateText (fields);
410
mTwFolders->sortItems (0, Qt::AscendingOrder);
411
if (item->parent() != root)
413
/* Move the selected item into new location */
414
item->parent()->takeChild (item->parent()->indexOfChild (item));
415
root->insertChild (root->childCount(), item);
416
mTwFolders->scrollToItem (item);
417
mTwFolders->setCurrentItem (item);
418
processCurrentChanged (item);
419
mTwFolders->setFocus();
423
mIsListViewChanged = true;
427
void VBoxVMSettingsSF::delTriggered()
429
QTreeWidgetItem *selectedItem = mTwFolders->selectedItems().size() == 1 ? mTwFolders->selectedItems() [0] : 0;
430
Assert (selectedItem);
433
mIsListViewChanged = true;
436
void VBoxVMSettingsSF::processCurrentChanged (QTreeWidgetItem *aCurrentItem)
438
if (aCurrentItem && aCurrentItem->parent() && !aCurrentItem->isSelected())
439
aCurrentItem->setSelected (true);
440
QString key = !aCurrentItem ? QString::null : aCurrentItem->parent() ?
441
aCurrentItem->parent()->text (1) : aCurrentItem->text (1);
442
bool addEnabled = aCurrentItem && isEditable (key);
443
bool removeEnabled = addEnabled && aCurrentItem->parent();
444
mNewAction->setEnabled (addEnabled);
445
mEdtAction->setEnabled (removeEnabled);
446
mDelAction->setEnabled (removeEnabled);
449
void VBoxVMSettingsSF::processDoubleClick (QTreeWidgetItem *aItem)
451
bool editEnabled = aItem && aItem->parent() && isEditable (aItem->parent()->text (1));
456
void VBoxVMSettingsSF::showContextMenu (const QPoint &aPos)
459
QTreeWidgetItem *item = mTwFolders->itemAt (aPos);
460
if (item && item->flags() & Qt::ItemIsSelectable)
462
menu.addAction (mEdtAction);
463
menu.addAction (mDelAction);
467
menu.addAction (mNewAction);
469
menu.exec (mTwFolders->viewport()->mapToGlobal (aPos));
472
void VBoxVMSettingsSF::adjustList()
474
/* Calculating required columns size & watching those columns (0 and 2) to feat 1/3 of total width. */
475
QAbstractItemView *itemView = mTwFolders;
476
QHeaderView *itemHeader = mTwFolders->header();
477
int total = mTwFolders->viewport()->width();
478
int mw0 = qMax (itemView->sizeHintForColumn (0), itemHeader->sectionSizeHint (0));
479
int mw2 = qMax (itemView->sizeHintForColumn (2), itemHeader->sectionSizeHint (2));
480
int w0 = mw0 < total / 3 ? mw0 : total / 3;
481
int w2 = mw2 < total / 3 ? mw2 : total / 3;
483
/* Giving 1st column all the available space. */
484
mTwFolders->setColumnWidth (0, w0);
485
mTwFolders->setColumnWidth (1, total - w0 - w2);
486
mTwFolders->setColumnWidth (2, w2);
489
void VBoxVMSettingsSF::adjustFields()
491
QTreeWidgetItem *mainRoot = mTwFolders->invisibleRootItem();
492
for (int i = 0; i < mainRoot->childCount(); ++ i)
494
QTreeWidgetItem *subRoot = mainRoot->child (i);
495
for (int j = 0; j < subRoot->childCount(); ++ j)
497
SFTreeViewItem *item = subRoot->child (j) &&
498
subRoot->child (j)->type() == SFTreeViewItem::SFTreeViewItemType ?
499
static_cast <SFTreeViewItem*> (subRoot->child (j)) : 0;
506
void VBoxVMSettingsSF::showEvent (QShowEvent *aEvent)
508
QWidget::showEvent (aEvent);
510
/* Connect header-resize signal just before widget is shown after all the items properly loaded and initialized. */
511
connect (mTwFolders->header(), SIGNAL (sectionResized (int, int, int)), this, SLOT (adjustFields()));
513
/* Adjusting size after all pending show events are processed. */
514
QTimer::singleShot (0, this, SLOT (adjustList()));
517
void VBoxVMSettingsSF::createSharedFolder (const QString &aName, const QString &aPath, bool aWritable, SFDialogType aType)
523
/* This feature is not supported now */
524
AssertMsgFailed (("Global shared folders are not supported now!\n"));
529
Assert (!mMachine.isNull());
530
mMachine.CreateSharedFolder (aName, aPath, aWritable);
531
if (!mMachine.isOk())
532
vboxProblem().cannotCreateSharedFolder (this, mMachine, aName, aPath);
537
Assert (!mConsole.isNull());
538
mConsole.CreateSharedFolder (aName, aPath, aWritable);
539
if (!mConsole.isOk())
540
vboxProblem().cannotCreateSharedFolder (this, mConsole, aName, aPath);
545
AssertMsgFailed (("Incorrect shared folder type\n"));
550
void VBoxVMSettingsSF::removeSharedFolder (const QString &aName, const QString &aPath, SFDialogType aType)
556
/* This feature is not supported now */
557
AssertMsgFailed (("Global shared folders are not supported now!\n"));
562
Assert (!mMachine.isNull());
563
mMachine.RemoveSharedFolder (aName);
564
if (!mMachine.isOk())
565
vboxProblem().cannotRemoveSharedFolder (this, mMachine, aName, aPath);
570
Assert (!mConsole.isNull());
571
mConsole.RemoveSharedFolder (aName);
572
if (!mConsole.isOk())
573
vboxProblem().cannotRemoveSharedFolder (this, mConsole, aName, aPath);
578
AssertMsgFailed (("Incorrect shared folder type\n"));
583
void VBoxVMSettingsSF::getFrom (const CSharedFolderVector &aVec, SFTreeViewItem *aRoot)
585
for (int i = 0; i < aVec.size(); ++ i)
587
CSharedFolder sf = aVec [i];
589
fields << sf.GetName() /* name */ << sf.GetHostPath() /* path */
590
<< (sf.GetWritable() ? mTrFull : mTrReadOnly /* writable? */)
591
<< "not edited" /* initially not edited */;
592
new SFTreeViewItem (aRoot, fields, SFTreeViewItem::EllipsisFile);
594
aRoot->setExpanded (true);
595
mTwFolders->sortItems (0, Qt::AscendingOrder);
596
mTwFolders->setCurrentItem (aRoot->childCount() ? aRoot->child (0) : aRoot);
597
processCurrentChanged (aRoot->childCount() ? aRoot->child (0) : aRoot);
600
void VBoxVMSettingsSF::putBackTo (CSharedFolderVector &aVec, SFTreeViewItem *aRoot)
602
Assert (!aRoot->text (1).isNull());
603
SFDialogType type = (SFDialogType) aRoot->text (1).toInt();
605
/* Delete all changed folders from vm */
606
for (int idx = 0; idx < aVec.size(); ++ idx)
608
CSharedFolder sf = aVec [idx];
610
/* Iterate through this root's children */
612
for (; i < aRoot->childCount(); ++ i)
614
SFTreeViewItem *item = aRoot->child (i);
615
if (item->getText (0) == sf.GetName() && item->getText (3) == "not edited")
619
if (i == aRoot->childCount())
620
removeSharedFolder (sf.GetName(), sf.GetHostPath(), type);
623
/* Save all edited tree widget items as folders */
624
for (int i = 0; i < aRoot->childCount(); ++ i)
626
SFTreeViewItem *item = aRoot->child (i);
628
if (!item->getText (0).isNull() && !item->getText (1).isNull() && item->getText (3) == "edited")
629
createSharedFolder (item->getText (0), item->getText (1), item->getText (2) == mTrFull ? true : false, type);
633
SFTreeViewItem* VBoxVMSettingsSF::searchRoot (bool aIsPermanent, SFDialogType aType)
635
QString type = aType != WrongType ? QString::number (aType) : !aIsPermanent ? QString::number (ConsoleType) :
636
mDialogType & MachineType ? QString::number (MachineType) : QString::number (GlobalType);
637
QTreeWidgetItem *mainRoot = mTwFolders->invisibleRootItem();
640
for (; i < mainRoot->childCount(); ++ i)
642
if (mainRoot->child (i)->text (1) == type)
646
Assert (i < mainRoot->childCount());
647
return i < mainRoot->childCount() && mainRoot->child (i)->type() == SFTreeViewItem::SFTreeViewItemType ?
648
static_cast <SFTreeViewItem*> (mainRoot->child (i)) : 0;
651
bool VBoxVMSettingsSF::isEditable (const QString &aKey)
653
/* mDialogType should be correct */
654
Assert (mDialogType);
656
SFDialogType type = (SFDialogType) aKey.toInt();
657
if (!type) return false;
658
return mDialogType & type;
661
SFoldersNameList VBoxVMSettingsSF::usedList (bool aIncludeSelected)
663
/* Make the used names list: */
664
SFoldersNameList list;
665
QTreeWidgetItemIterator it (mTwFolders);
668
if ((*it)->parent() && (aIncludeSelected || !(*it)->isSelected()) &&
669
(*it)->type() == SFTreeViewItem::SFTreeViewItemType)
671
SFTreeViewItem *item = static_cast <SFTreeViewItem*> (*it);
672
SFDialogType type = (SFDialogType) item->parent()->text (1).toInt();
673
list << qMakePair (item->getText (0), type);