VirtualBox

source: vbox/trunk/src/VBox/Frontends/VirtualBox/src/medium/UIMedium.cpp@ 73926

Last change on this file since 73926 was 73926, checked in by vboxsync, 6 years ago

FE/Qt: bugref:8472. Refactoring the term 'mediums' to media

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 22.6 KB
Line 
1/* $Id: UIMedium.cpp 73926 2018-08-28 10:02:14Z vboxsync $ */
2/** @file
3 * VBox Qt GUI - UIMedium class implementation.
4 */
5
6/*
7 * Copyright (C) 2009-2017 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#ifdef VBOX_WITH_PRECOMPILED_HEADERS
19# include <precomp.h>
20#else /* !VBOX_WITH_PRECOMPILED_HEADERS */
21
22/* Qt includes: */
23# include <QApplication>
24# include <QDir>
25
26/* GUI includes: */
27# include "UIMedium.h"
28# include "VBoxGlobal.h"
29# include "UIConverter.h"
30# include "UIErrorString.h"
31# include "UIExtraDataManager.h"
32# include "UIIconPool.h"
33
34/* COM includes: */
35# include "CMachine.h"
36# include "CSnapshot.h"
37
38#endif /* !VBOX_WITH_PRECOMPILED_HEADERS */
39
40QString UIMedium::m_sstrNullID = QUuid().toString().remove('{').remove('}');
41QString UIMedium::m_sstrTable = QString("<table>%1</table>");
42QString UIMedium::m_sstrRow = QString("<tr><td>%1</td></tr>");
43
44UIMedium::UIMedium()
45 : m_type(UIMediumType_Invalid)
46 , m_medium(CMedium())
47 , m_state(KMediumState_NotCreated)
48 , m_enmMediumType(KMediumType_Max)
49 , m_enmMediumVariant(KMediumVariant_Max)
50{
51 refresh();
52}
53
54UIMedium::UIMedium(const CMedium &medium, UIMediumType type)
55 : m_type(type)
56 , m_medium(medium)
57 , m_state(KMediumState_NotCreated)
58 , m_enmMediumType(KMediumType_Max)
59 , m_enmMediumVariant(KMediumVariant_Max)
60{
61 refresh();
62}
63
64UIMedium::UIMedium(const CMedium &medium, UIMediumType type, KMediumState state)
65 : m_type(type)
66 , m_medium(medium)
67 , m_state(state)
68 , m_enmMediumType(KMediumType_Max)
69 , m_enmMediumVariant(KMediumVariant_Max)
70{
71 refresh();
72}
73
74UIMedium::UIMedium(const UIMedium &other)
75{
76 *this = other;
77}
78
79UIMedium& UIMedium::operator=(const UIMedium &other)
80{
81 m_type = other.type();
82
83 m_medium = other.medium();
84
85 m_state = other.state();
86 m_result = other.result();
87 m_strLastAccessError = other.lastAccessError();
88
89 m_strId = other.id();
90 m_strRootId = other.rootID();
91 m_strParentId = other.parentID();
92
93 m_strKey = other.key();
94
95 m_strName = other.name();
96 m_strLocation = other.location();
97 m_strDescription = other.description();
98
99 m_uSize = other.sizeInBytes();
100 m_uLogicalSize = other.logicalSizeInBytes();
101 m_strSize = other.size();
102 m_strLogicalSize = other.logicalSize();
103
104 m_enmMediumType = other.mediumType();
105 m_enmMediumVariant = other.mediumVariant();
106
107 m_strHardDiskType = other.hardDiskType();
108 m_strHardDiskFormat = other.hardDiskFormat();
109 m_fHasChildren = other.hasChildren();
110 m_strStorageDetails = other.storageDetails();
111 m_strEncryptionPasswordID = other.encryptionPasswordID();
112
113 m_strUsage = other.usage();
114 m_strToolTip = other.tip();
115 m_machineIds = other.machineIds();
116 m_curStateMachineIds = other.curStateMachineIds();
117
118 m_noDiffs = other.cache();
119
120 m_fHidden = other.m_fHidden;
121 m_fUsedByHiddenMachinesOnly = other.m_fUsedByHiddenMachinesOnly;
122 m_fReadOnly = other.isReadOnly();
123 m_fUsedInSnapshots = other.isUsedInSnapshots();
124 m_fHostDrive = other.isHostDrive();
125 m_fEncrypted = other.isEncrypted();
126
127 return *this;
128}
129
130void UIMedium::blockAndQueryState()
131{
132 /* Ignore for NULL medium: */
133 if (m_medium.isNull())
134 return;
135
136 /* Acquire actual medium state: */
137 m_state = m_medium.RefreshState();
138
139 /* Save the result to distinguish between
140 * inaccessible and e.g. uninitialized objects: */
141 m_result = COMResult(m_medium);
142 if (!m_result.isOk())
143 {
144 m_state = KMediumState_Inaccessible;
145 m_strLastAccessError = QString();
146 }
147 else
148 m_strLastAccessError = m_medium.GetLastAccessError();
149
150 /* Refresh finally: */
151 refresh();
152}
153
154void UIMedium::refresh()
155{
156 /* Reset ID parameters: */
157 m_strId = nullID();
158 m_strRootId = nullID();
159 m_strParentId = nullID();
160
161 /* Reset cache parameters: */
162 //m_strKey = nullID();
163
164 /* Reset name/location/description/size parameters: */
165 m_strName = VBoxGlobal::tr("Empty", "medium");
166 m_strLocation = m_strSize = m_strLogicalSize = QString("--");
167 m_strDescription = QString();
168 m_uSize = m_uLogicalSize = 0;
169
170 /* Reset medium type & variant parameter: */
171 m_enmMediumType = KMediumType_Max;
172 m_enmMediumVariant = KMediumVariant_Max;
173
174 /* Reset hard drive related parameters: */
175 m_strHardDiskType = QString();
176 m_strHardDiskFormat = QString();
177 m_fHasChildren = false;
178 m_strStorageDetails = QString();
179 m_strEncryptionPasswordID = QString();
180
181 /* Reset data parameters: */
182 m_strUsage = QString();
183 m_strToolTip = QString();
184 m_machineIds.clear();
185 m_curStateMachineIds.clear();
186
187 /* Reset m_noDiffs: */
188 m_noDiffs.isSet = false;
189
190 /* Reset flags: */
191 m_fHidden = false;
192 m_fUsedByHiddenMachinesOnly = false;
193 m_fReadOnly = false;
194 m_fUsedInSnapshots = false;
195 m_fHostDrive = false;
196 m_fEncrypted = false;
197
198 /* For non NULL medium: */
199 if (!m_medium.isNull())
200 {
201 /* Refresh medium ID: */
202 m_strId = normalizedID(m_medium.GetId());
203 /* Refresh root medium ID: */
204 m_strRootId = m_strId;
205
206 /* Init medium key if necessary: */
207 if (m_strKey.isNull())
208 m_strKey = m_strId;
209
210 /* Check whether this is host-drive medium: */
211 m_fHostDrive = m_medium.GetHostDrive();
212
213 /* Refresh medium description: */
214 m_strDescription = m_medium.GetDescription();
215
216 /* Refresh medium name: */
217 if (!m_fHostDrive)
218 m_strName = m_medium.GetName();
219 else if (m_strDescription.isEmpty())
220 m_strName = VBoxGlobal::tr("Host Drive '%1'", "medium").arg(QDir::toNativeSeparators(m_medium.GetLocation()));
221 else
222 m_strName = VBoxGlobal::tr("Host Drive %1 (%2)", "medium").arg(m_strDescription, m_medium.GetName());
223 /* Refresh medium location: */
224 if (!m_fHostDrive)
225 m_strLocation = QDir::toNativeSeparators(m_medium.GetLocation());
226
227 /* Refresh medium size and logical size: */
228 if (!m_fHostDrive)
229 {
230 /* Only for created and accessible media: */
231 if (m_state != KMediumState_Inaccessible && m_state != KMediumState_NotCreated)
232 {
233 m_uSize = m_medium.GetSize();
234 m_strSize = vboxGlobal().formatSize(m_uSize);
235 if (m_type == UIMediumType_HardDisk)
236 {
237 m_uLogicalSize = m_medium.GetLogicalSize();
238 m_strLogicalSize = vboxGlobal().formatSize(m_uLogicalSize);
239 }
240 else
241 {
242 m_uLogicalSize = m_uSize;
243 m_strLogicalSize = m_strSize;
244 }
245 }
246 }
247
248 /* Refresh medium type & variant: */
249 m_enmMediumType = m_medium.GetType();
250 qlonglong iMediumVariant = 0;
251 foreach (const KMediumVariant &enmVariant, m_medium.GetVariant())
252 iMediumVariant |= enmVariant;
253 m_enmMediumVariant = (KMediumVariant)iMediumVariant;
254
255 /* For hard drive medium: */
256 if (m_type == UIMediumType_HardDisk)
257 {
258 /* Refresh hard drive disk type: */
259 m_strHardDiskType = mediumTypeToString(m_medium);
260 /* Refresh hard drive format: */
261 m_strHardDiskFormat = m_medium.GetFormat();
262
263 /* Refresh hard drive parental status: */
264 m_fHasChildren = m_medium.GetChildren().size();
265
266 /* Refresh hard drive storage details: */
267 m_strStorageDetails = gpConverter->toString(m_enmMediumVariant);
268
269 /* Check whether this is read-only hard drive: */
270 m_fReadOnly = m_medium.GetReadOnly();
271
272 /* Refresh parent hard drive ID: */
273 CMedium parentMedium = m_medium.GetParent();
274 if (!parentMedium.isNull())
275 m_strParentId = normalizedID(parentMedium.GetId());
276
277 /* Only for created and accessible media: */
278 if (m_state != KMediumState_Inaccessible && m_state != KMediumState_NotCreated)
279 {
280 /* Refresh root hard drive ID: */
281 while (!parentMedium.isNull())
282 {
283 m_strRootId = normalizedID(parentMedium.GetId());
284 parentMedium = parentMedium.GetParent();
285 }
286
287 /* Refresh encryption attributes: */
288 if (m_strRootId != m_strId)
289 {
290 m_strEncryptionPasswordID = root().encryptionPasswordID();
291 m_fEncrypted = root().isEncrypted();
292 }
293 else
294 {
295 QString strCipher;
296 CMedium medium(m_medium);
297 const QString strEncryptionPasswordID = medium.GetEncryptionSettings(strCipher);
298 if (medium.isOk())
299 {
300 m_strEncryptionPasswordID = strEncryptionPasswordID;
301 m_fEncrypted = true;
302 }
303 }
304 }
305 }
306
307 /* Check whether this is hidden medium: */
308 QString strHints = m_medium.GetProperty("Special/GUI/Hints");
309 if (!strHints.isEmpty())
310 {
311 QStringList hints(strHints.split(','));
312 if (hints.contains("Hide", Qt::CaseInsensitive))
313 m_fHidden = true;
314 }
315
316 /* Refresh usage data: */
317 m_curStateMachineIds.clear();
318 m_machineIds = m_medium.GetMachineIds().toList();
319 if (m_machineIds.size() > 0)
320 {
321 /* Get CVirtualBox object: */
322 CVirtualBox vbox = vboxGlobal().virtualBox();
323
324 /* By default we assuming that this medium is attached
325 * to 'hidden' machines only, if at least one machine present: */
326 m_fUsedByHiddenMachinesOnly = true;
327
328 /* Prepare machine usage: */
329 QString strMachineUsage;
330 /* Walk through all the machines this medium attached to: */
331 foreach (const QString &strMachineID, m_machineIds)
332 {
333 /* Look for the corresponding machine: */
334 CMachine machine = vbox.FindMachine(strMachineID);
335
336 /* UIMedium object can wrap newly created CMedium object
337 * which belongs to not yet registered machine, like while creating VM clone.
338 * We can skip such a machines in usage string. */
339 if (machine.isNull())
340 {
341 /* Since we can't precisely check 'hidden' status for that machine in such case,
342 * we have to assume that medium attached not only to 'hidden' machines: */
343 m_fUsedByHiddenMachinesOnly = false;
344 continue;
345 }
346
347 /* Finally we can precisely check if current machine is 'hidden': */
348 if (gEDataManager->showMachineInSelectorChooser(strMachineID))
349 m_fUsedByHiddenMachinesOnly = false;
350
351 /* Prepare snapshot usage: */
352 QString strSnapshotUsage;
353 /* Walk through all the snapshots this medium attached to: */
354 foreach (const QString &strSnapshotID, m_medium.GetSnapshotIds(strMachineID))
355 {
356 if (strSnapshotID == strMachineID)
357 {
358 /* The medium is attached to the machine in the current
359 * state, we don't distinguish this for now by always
360 * giving the VM name in front of snapshot names. */
361 m_curStateMachineIds.push_back(strSnapshotID);
362 continue;
363 }
364
365 /* Look for the corresponding snapshot: */
366 CSnapshot snapshot = machine.FindSnapshot(strSnapshotID);
367
368 /* Snapshot can be NULL while takeSnaphot is in progress: */
369 if (snapshot.isNull())
370 continue;
371
372 /* Refresh snapshot usage flag: */
373 m_fUsedInSnapshots = true;
374
375 /* Append snapshot usage: */
376 if (!strSnapshotUsage.isNull())
377 strSnapshotUsage += ", ";
378 strSnapshotUsage += snapshot.GetName();
379 }
380
381 /* Append machine usage: */
382 if (!strMachineUsage.isNull())
383 strMachineUsage += ", ";
384 strMachineUsage += machine.GetName();
385
386 /* Append snapshot usage: */
387 if (!strSnapshotUsage.isNull())
388 strMachineUsage += QString(" (%2)").arg(strSnapshotUsage);
389 }
390
391 /* Append machine usage: */
392 if (!strMachineUsage.isEmpty())
393 m_strUsage += strMachineUsage;
394 }
395
396 /* Refresh tool-tip: */
397 m_strToolTip = m_sstrRow.arg(QString("<p style=white-space:pre><b>%1</b></p>").arg(m_fHostDrive ? m_strName : m_strLocation));
398 if (m_type == UIMediumType_HardDisk)
399 {
400 m_strToolTip += m_sstrRow.arg(VBoxGlobal::tr("<p style=white-space:pre>Type (Format): %1 (%2)</p>", "medium")
401 .arg(m_strHardDiskType).arg(m_strHardDiskFormat));
402 }
403 m_strToolTip += m_sstrRow.arg(VBoxGlobal::tr("<p>Attached to: %1</p>", "image")
404 .arg(m_strUsage.isNull() ? VBoxGlobal::tr("<i>Not Attached</i>", "image") : m_strUsage));
405 switch (m_state)
406 {
407 case KMediumState_NotCreated:
408 {
409 m_strToolTip += m_sstrRow.arg(VBoxGlobal::tr("<i>Checking accessibility...</i>", "medium"));
410 break;
411 }
412 case KMediumState_Inaccessible:
413 {
414 if (m_result.isOk())
415 {
416 /* Not Accessible: */
417 m_strToolTip += m_sstrRow.arg("<hr>") + m_sstrRow.arg(VBoxGlobal::highlight(m_strLastAccessError, true /* aToolTip */));
418 }
419 else
420 {
421 /* Accessibility check (eg GetState()) itself failed: */
422 m_strToolTip += m_sstrRow.arg("<hr>") + m_sstrRow.arg(VBoxGlobal::tr("Failed to check accessibility of disk image files.", "medium")) +
423 m_sstrRow.arg(UIErrorString::formatErrorInfo(m_result) + ".");
424 }
425 break;
426 }
427 default:
428 break;
429 }
430 }
431}
432
433void UIMedium::updateParentID()
434{
435 m_strParentId = nullID();
436 if (m_type == UIMediumType_HardDisk)
437 {
438 CMedium parentMedium = m_medium.GetParent();
439 if (!parentMedium.isNull())
440 m_strParentId = normalizedID(parentMedium.GetId());
441 }
442}
443
444QString UIMedium::toolTip(bool fNoDiffs /* = false */, bool fCheckRO /* = false */, bool fNullAllowed /* = false */) const
445{
446 QString strTip;
447
448 if (m_medium.isNull())
449 {
450 strTip = fNullAllowed ? m_sstrRow.arg(VBoxGlobal::tr("<b>No disk image file selected</b>", "medium")) +
451 m_sstrRow.arg(VBoxGlobal::tr("You can also change this while the machine is running.")) :
452 m_sstrRow.arg(VBoxGlobal::tr("<b>No disk image files available</b>", "medium")) +
453 m_sstrRow.arg(VBoxGlobal::tr("You can create or add disk image files in the virtual machine settings."));
454 }
455 else
456 {
457 unconst(this)->checkNoDiffs(fNoDiffs);
458
459 strTip = fNoDiffs ? m_noDiffs.toolTip : m_strToolTip;
460
461 if (fCheckRO && m_fReadOnly)
462 strTip += m_sstrRow.arg("<hr>") +
463 m_sstrRow.arg(VBoxGlobal::tr("Attaching this hard disk will be performed indirectly using "
464 "a newly created differencing hard disk.", "medium"));
465 }
466
467 return m_sstrTable.arg(strTip);
468}
469
470QPixmap UIMedium::icon(bool fNoDiffs /* = false */, bool fCheckRO /* = false */) const
471{
472 QPixmap pixmap;
473
474 if (state(fNoDiffs) == KMediumState_Inaccessible)
475 pixmap = result(fNoDiffs).isOk() ? vboxGlobal().warningIcon() : vboxGlobal().errorIcon();
476
477 if (fCheckRO && m_fReadOnly)
478 {
479 QIcon icon = UIIconPool::iconSet(":/hd_new_16px.png");
480 pixmap = VBoxGlobal::joinPixmaps(pixmap, icon.pixmap(icon.availableSizes().value(0, QSize(16, 16))));
481 }
482
483 return pixmap;
484}
485
486QString UIMedium::details(bool fNoDiffs /* = false */,
487 bool fPredictDiff /* = false */,
488 bool fUseHTML /* = false */) const
489{
490 /// @todo the below check is rough; if m_medium becomes uninitialized, any
491 // of getters called afterwards will also fail. The same relates to the
492 // root hard drive object (that will be the hard drive itself in case of
493 // non-differencing disks). However, this check was added to fix a
494 // particular use case: when the hard drive is a differencing hard drive and
495 // it happens to be discarded (and uninitialized) after this method is
496 // called but before we read all its properties (yes, it's possible!), the
497 // root object will be null and calling methods on it will assert in the
498 // debug builds. This check seems to be enough as a quick solution (fresh
499 // hard drive attachments will be re-read by a machine state change signal
500 // after the discard operation is finished, so the user will eventually see
501 // correct data), but in order to solve the problem properly we need to use
502 // exceptions everywhere (or check the result after every method call). See
503 // @bugref{2149}.
504
505 if (m_medium.isNull() || m_fHostDrive)
506 return m_strName;
507
508 if (!m_medium.isOk())
509 return QString();
510
511 QString strDetails, strText;
512
513 /* Note: root accessible only if medium enumerated: */
514 UIMedium rootMedium = root();
515 KMediumState eState = m_state;
516
517 if (m_type == UIMediumType_HardDisk)
518 {
519 if (fNoDiffs)
520 {
521 bool isDiff = (!fPredictDiff && parentID() != nullID()) || (fPredictDiff && m_fReadOnly);
522
523 strDetails = isDiff && fUseHTML ?
524 QString("<i>%1</i>, ").arg(rootMedium.m_strHardDiskType) :
525 QString("%1, ").arg(rootMedium.m_strHardDiskType);
526
527 eState = this->state(true /* fNoDiffs */);
528
529 if (rootMedium.m_state == KMediumState_NotCreated)
530 eState = KMediumState_NotCreated;
531 }
532 else
533 {
534 strDetails = QString("%1, ").arg(rootMedium.m_strHardDiskType);
535 }
536
537 /* Add encryption status: */
538 if (m_fEncrypted)
539 strDetails += QString("%1, ").arg(VBoxGlobal::tr("Encrypted", "medium"));
540 }
541
542 /// @todo prepend the details with the warning/error icon when not accessible
543
544 switch (eState)
545 {
546 case KMediumState_NotCreated:
547 strText = VBoxGlobal::tr("Checking...", "medium");
548 strDetails += fUseHTML ? QString("<i>%1</i>").arg(strText) : strText;
549 break;
550 case KMediumState_Inaccessible:
551 strText = VBoxGlobal::tr("Inaccessible", "medium");
552 strDetails += fUseHTML ? QString("<b>%1</b>").arg(strText) : strText;
553 break;
554 default:
555 strDetails += m_type == UIMediumType_HardDisk ? rootMedium.m_strLogicalSize : rootMedium.m_strSize;
556 break;
557 }
558
559 strDetails = fUseHTML ?
560 QString("%1 (<nobr>%2</nobr>)").arg(QFileInfo(rootMedium.m_strName).fileName(), strDetails) :
561 QString("%1 (%2)").arg(QFileInfo(rootMedium.m_strName).fileName(), strDetails);
562
563 return strDetails;
564}
565
566/* static */
567QString UIMedium::nullID()
568{
569 return m_sstrNullID;
570}
571
572/* static */
573QString UIMedium::normalizedID(const QString &strID)
574{
575 /* Handle wrong UUID (null/empty or invalid format): */
576 if (QUuid(strID).toString().remove('{').remove('}') != strID)
577 return nullID();
578 return strID;
579}
580
581/* static */
582bool UIMedium::isMediumAttachedToHiddenMachinesOnly(const UIMedium &medium)
583{
584 /* Iterate till the root: */
585 UIMedium mediumIterator = medium;
586 do
587 {
588 /* Ignore medium if its hidden
589 * or attached to hidden machines only: */
590 if (mediumIterator.isHidden())
591 return true;
592 /* Move iterator to parent: */
593 mediumIterator = mediumIterator.parent();
594 }
595 while (!mediumIterator.isNull());
596 /* False by default: */
597 return false;
598}
599
600UIMedium UIMedium::root() const
601{
602 /* Redirect call to VBoxGlobal: */
603 return vboxGlobal().medium(m_strRootId);
604}
605
606UIMedium UIMedium::parent() const
607{
608 /* Redirect call to VBoxGlobal: */
609 return vboxGlobal().medium(m_strParentId);
610}
611
612void UIMedium::checkNoDiffs(bool fNoDiffs)
613{
614 if (!fNoDiffs || m_noDiffs.isSet)
615 return;
616
617 m_noDiffs.toolTip = QString();
618
619 m_noDiffs.state = m_state;
620 for (UIMedium parentMedium = parent(); !parentMedium.isNull(); parentMedium = parentMedium.parent())
621 {
622 if (parentMedium.m_state == KMediumState_Inaccessible)
623 {
624 m_noDiffs.state = parentMedium.m_state;
625
626 if (m_noDiffs.toolTip.isNull())
627 m_noDiffs.toolTip = m_sstrRow.arg(VBoxGlobal::tr("Some of the files in this hard disk chain "
628 "are inaccessible. Please use the Virtual Medium "
629 "Manager to inspect these files.", "medium"));
630
631 if (!parentMedium.m_result.isOk())
632 {
633 m_noDiffs.result = parentMedium.m_result;
634 break;
635 }
636 }
637 }
638
639 if (parentID() != nullID() && !m_fReadOnly)
640 {
641 m_noDiffs.toolTip = root().tip() +
642 m_sstrRow.arg("<hr>") +
643 m_sstrRow.arg(VBoxGlobal::tr("This base hard disk is indirectly attached using "
644 "the following differencing hard disk:", "medium")) +
645 m_strToolTip + m_noDiffs.toolTip;
646 }
647
648 if (m_noDiffs.toolTip.isNull())
649 m_noDiffs.toolTip = m_strToolTip;
650
651 m_noDiffs.isSet = true;
652}
653
654/* static */
655QString UIMedium::mediumTypeToString(const CMedium &comMedium)
656{
657 if (!comMedium.GetParent().isNull())
658 {
659 Assert(comMedium.GetType() == KMediumType_Normal);
660 return QApplication::translate("VBoxGlobal", "Differencing", "MediumType");
661 }
662 return gpConverter->toString(comMedium.GetType());
663}
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