VirtualBox

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

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

Renaming VBoxGlobal to UICommon for bugref:9049 as planned.

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

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