VirtualBox

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

Last change on this file since 68435 was 68435, checked in by vboxsync, 7 years ago

FE/Qt: Factoring out error string formatting functions from UIMessageCenter.

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