VirtualBox

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

Last change on this file since 48608 was 48608, checked in by vboxsync, 11 years ago

FE/Qt: language string updates.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 20.7 KB
Line 
1/* $Id: UIMedium.cpp 48608 2013-09-20 15:49:23Z vboxsync $ */
2/** @file
3 *
4 * VBox frontends: Qt GUI ("VirtualBox"):
5 * UIMedium class implementation
6 */
7
8/*
9 * Copyright (C) 2009-2013 Oracle Corporation
10 *
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.
18 */
19
20#ifdef VBOX_WITH_PRECOMPILED_HEADERS
21# include "precomp.h"
22#else /* !VBOX_WITH_PRECOMPILED_HEADERS */
23
24/* Qt includes: */
25#include <QDir>
26
27/* GUI includes: */
28#include "UIMedium.h"
29#include "VBoxGlobal.h"
30#include "UIConverter.h"
31#include "UIMessageCenter.h"
32
33/* COM includes: */
34#include "CMachine.h"
35#include "CSnapshot.h"
36
37#endif /* !VBOX_WITH_PRECOMPILED_HEADERS */
38
39QString UIMedium::m_sstrNullID = QUuid().toString().remove('{').remove('}');
40QString UIMedium::m_sstrTable = QString("<table>%1</table>");
41QString UIMedium::m_sstrRow = QString("<tr><td>%1</td></tr>");
42
43UIMedium::UIMedium()
44 : m_type(UIMediumType_Invalid)
45 , m_state(KMediumState_NotCreated)
46{
47 refresh();
48// printf("UIMedium: New NULL medium created.\n");
49}
50
51UIMedium::UIMedium(const CMedium &medium, UIMediumType type)
52 : m_medium(medium)
53 , m_type(type)
54 , m_state(KMediumState_NotCreated)
55{
56 refresh();
57// printf("UIMedium: New medium with ID={%s} created.\n", id().toAscii().constData());
58}
59
60UIMedium::UIMedium(const CMedium &medium, UIMediumType type, KMediumState state)
61 : m_medium(medium)
62 , m_type(type)
63 , m_state(state)
64{
65 refresh();
66// printf("UIMedium: New medium with ID={%s} created (with known state).\n", id().toAscii().constData());
67}
68
69UIMedium::UIMedium(const UIMedium &other)
70{
71 *this = other;
72}
73
74UIMedium& UIMedium::operator=(const UIMedium &other)
75{
76 m_medium = other.medium();
77 m_type = other.type();
78 m_state = other.state();
79 m_strLastAccessError = other.lastAccessError();
80 m_result = other.result();
81
82 m_strId = other.id();
83 m_strName = other.name();
84 m_strLocation = other.location();
85
86 m_strSize = other.size();
87 m_strLogicalSize = other.logicalSize();
88
89 m_strHardDiskFormat = other.hardDiskFormat();
90 m_strHardDiskType = other.hardDiskType();
91
92 m_strStorageDetails = other.storageDetails();
93
94 m_strUsage = other.usage();
95 m_strToolTip = other.tip();
96
97 m_fHidden = other.m_fHidden;
98 m_fAttachedToHiddenMachinesOnly = other.m_fAttachedToHiddenMachinesOnly;
99 m_fReadOnly = other.isReadOnly();
100 m_fUsedInSnapshots = other.isUsedInSnapshots();
101 m_fHostDrive = other.isHostDrive();
102
103 m_machineIds = other.machineIds();
104 m_curStateMachineIds = other.curStateMachineIds();
105
106 m_strParentID = other.parentID();
107 m_strRootID = other.rootID();
108
109 m_noDiffs = other.cache();
110
111 return *this;
112}
113
114/**
115 * Queries the medium state. Call this and then read the state field instead
116 * of calling GetState() on medium directly as it will properly handle the
117 * situation when GetState() itself fails by setting state to Inaccessible
118 * and memorizing the error info describing why GetState() failed.
119 *
120 * As the last step, this method calls #refresh() to refresh all precomposed
121 * strings.
122 *
123 * @note This method blocks for the duration of the state check. Since this
124 * check may take quite a while (e.g. for a medium located on a
125 * network share), the calling thread must not be the UI thread. You
126 * have been warned.
127 */
128void UIMedium::blockAndQueryState()
129{
130 if (m_medium.isNull())
131 return;
132
133 m_state = m_medium.RefreshState();
134
135 /* Save the result to distinguish between
136 * inaccessible and e.g. uninitialized objects: */
137 m_result = COMResult(m_medium);
138 if (!m_result.isOk())
139 {
140 m_state = KMediumState_Inaccessible;
141 m_strLastAccessError = QString();
142 }
143 else
144 m_strLastAccessError = m_medium.GetLastAccessError();
145
146 refresh();
147}
148
149/**
150 * Refreshes the precomposed strings containing such media parameters as
151 * location, size by querying the respective data from the associated
152 * media object.
153 *
154 * Note that some string such as #size() are meaningless if the media state is
155 * KMediumState_NotCreated (i.e. the medium has not yet been checked for
156 * accessibility).
157 */
158void UIMedium::refresh()
159{
160 /* Flags are 'false' by default: */
161 m_fHidden = false;
162 m_fAttachedToHiddenMachinesOnly = false;
163 m_fReadOnly = false;
164 m_fUsedInSnapshots = false;
165 m_fHostDrive = false;
166
167 /* Detect basic parameters... */
168
169 m_strId = m_medium.isNull() ? nullID() : m_medium.GetId();
170
171 m_fHostDrive = m_medium.isNull() ? false : m_medium.GetHostDrive();
172
173 if (m_medium.isNull())
174 m_strName = VBoxGlobal::tr("Empty", "medium");
175 else if (!m_fHostDrive)
176 m_strName = m_medium.GetName();
177 else if (m_medium.GetDescription().isEmpty())
178 m_strName = VBoxGlobal::tr("Host Drive '%1'", "medium").arg(QDir::toNativeSeparators(m_medium.GetLocation()));
179 else
180 m_strName = VBoxGlobal::tr("Host Drive %1 (%2)", "medium").arg(m_medium.GetDescription(), m_medium.GetName());
181
182 m_strLocation = m_medium.isNull() || m_fHostDrive ? QString("--") :
183 QDir::toNativeSeparators(m_medium.GetLocation());
184
185 QString tmp;
186 if (!m_medium.isNull())
187 tmp = m_medium.GetProperty("Special/GUI/Hints");
188 if (!tmp.isEmpty())
189 {
190 QStringList tmpList(tmp.split(','));
191 if (tmpList.contains("Hide", Qt::CaseInsensitive))
192 m_fHidden = true;
193 }
194
195 /* Initialize parent/root IDs: */
196 m_strParentID = nullID();
197 m_strRootID = m_strId;
198 if (m_type == UIMediumType_HardDisk)
199 {
200 m_strHardDiskFormat = m_medium.GetFormat();
201 m_strHardDiskType = vboxGlobal().mediumTypeString(m_medium);
202
203 QVector<KMediumVariant> mediumVariants = m_medium.GetVariant();
204 qlonglong mediumVariant = 0;
205 for (int i = 0; i < mediumVariants.size(); ++i)
206 mediumVariant |= mediumVariants[i];
207
208 m_strStorageDetails = gpConverter->toString((KMediumVariant)mediumVariant);
209 m_fReadOnly = m_medium.GetReadOnly();
210
211 /* Adjust parent/root IDs: */
212 CMedium parentMedium = m_medium.GetParent();
213 if (!parentMedium.isNull())
214 m_strParentID = parentMedium.GetId();
215 while (!parentMedium.isNull())
216 {
217 m_strRootID = parentMedium.GetId();
218 parentMedium = parentMedium.GetParent();
219 }
220 }
221 else
222 {
223 m_strHardDiskFormat = QString();
224 m_strHardDiskType = QString();
225 m_fReadOnly = false;
226 }
227
228 /* Detect sizes */
229 if (m_state != KMediumState_Inaccessible && m_state != KMediumState_NotCreated && !m_fHostDrive)
230 {
231 m_strSize = vboxGlobal().formatSize(m_medium.GetSize());
232 if (m_type == UIMediumType_HardDisk)
233 m_strLogicalSize = vboxGlobal().formatSize(m_medium.GetLogicalSize());
234 else
235 m_strLogicalSize = m_strSize;
236 }
237 else
238 {
239 m_strSize = m_strLogicalSize = QString("--");
240 }
241
242 /* Detect usage */
243 m_strUsage = QString();
244 if (!m_medium.isNull())
245 {
246 m_curStateMachineIds.clear();
247 m_machineIds = m_medium.GetMachineIds().toList();
248 if (m_machineIds.size() > 0)
249 {
250 /* We assume this flag is 'true' if at least one machine present: */
251 m_fAttachedToHiddenMachinesOnly = true;
252
253 QString strUsage;
254
255 CVirtualBox vbox = vboxGlobal().virtualBox();
256
257 foreach (const QString &strMachineID, m_machineIds)
258 {
259 CMachine machine = vbox.FindMachine(strMachineID);
260
261 /* UIMedium object can wrap newly created CMedium object which belongs to
262 * not yet registered machine, like while creating VM clone.
263 * We can skip such a machines in usage string.
264 * CVirtualBox::FindMachine() will return null machine for such case. */
265 if (machine.isNull())
266 {
267 /* We can't decide for that medium yet,
268 * assume this flag is 'false' for now: */
269 m_fAttachedToHiddenMachinesOnly = false;
270 continue;
271 }
272
273 /* Finally, we are checking if current machine overrides this flag: */
274 if (m_fAttachedToHiddenMachinesOnly && vboxGlobal().shouldWeShowMachine(machine))
275 m_fAttachedToHiddenMachinesOnly = false;
276
277 QString strName = machine.GetName();
278 QString strSnapshots;
279
280 foreach (const QString &strSnapshotID, m_medium.GetSnapshotIds(strMachineID))
281 {
282 if (strSnapshotID == strMachineID)
283 {
284 /* The medium is attached to the machine in the current
285 * state, we don't distinguish this for now by always
286 * giving the VM name in front of snapshot names. */
287 m_curStateMachineIds.push_back(strSnapshotID);
288 continue;
289 }
290
291 CSnapshot snapshot = machine.FindSnapshot(strSnapshotID);
292 if (!snapshot.isNull()) // can be NULL while takeSnaphot is in progress
293 {
294 if (!strSnapshots.isNull())
295 strSnapshots += ", ";
296 strSnapshots += snapshot.GetName();
297 }
298 }
299
300 if (!strUsage.isNull())
301 strUsage += ", ";
302
303 strUsage += strName;
304
305 if (!strSnapshots.isNull())
306 {
307 strUsage += QString(" (%2)").arg(strSnapshots);
308 m_fUsedInSnapshots = true;
309 }
310 else
311 m_fUsedInSnapshots = false;
312 }
313
314 if (!strUsage.isEmpty())
315 m_strUsage = strUsage;
316 }
317 }
318
319 /* Compose the tooltip */
320 if (!m_medium.isNull())
321 {
322 m_strToolTip = m_sstrRow.arg(QString("<p style=white-space:pre><b>%1</b></p>").arg(m_fHostDrive ? m_strName : m_strLocation));
323
324 if (m_type == UIMediumType_HardDisk)
325 {
326 m_strToolTip += m_sstrRow.arg(VBoxGlobal::tr("<p style=white-space:pre>Type (Format): %1 (%2)</p>", "medium")
327 .arg(m_strHardDiskType).arg(m_strHardDiskFormat));
328 }
329
330 m_strToolTip += m_sstrRow.arg(VBoxGlobal::tr("<p>Attached to: %1</p>", "image")
331 .arg(m_strUsage.isNull() ? VBoxGlobal::tr("<i>Not Attached</i>", "image") : m_strUsage));
332
333 switch (m_state)
334 {
335 case KMediumState_NotCreated:
336 {
337 m_strToolTip += m_sstrRow.arg(VBoxGlobal::tr("<i>Checking accessibility...</i>", "medium"));
338 break;
339 }
340 case KMediumState_Inaccessible:
341 {
342 if (m_result.isOk())
343 {
344 /* Not Accessible */
345 m_strToolTip += m_sstrRow.arg("<hr>") + m_sstrRow.arg(VBoxGlobal::highlight(m_strLastAccessError, true /* aToolTip */));
346 }
347 else
348 {
349 /* Accessibility check (eg GetState()) itself failed: */
350 m_strToolTip += m_sstrRow.arg("<hr>") + m_sstrRow.arg(VBoxGlobal::tr("Failed to check accessibility of disk image files.", "medium")) +
351 m_sstrRow.arg(UIMessageCenter::formatErrorInfo(m_result) + ".");
352 }
353 break;
354 }
355 default:
356 break;
357 }
358 }
359
360 /* Reset m_noDiffs */
361 m_noDiffs.isSet = false;
362}
363
364UIMedium UIMedium::parent() const
365{
366 /* Redirect call to VBoxGlobal: */
367 return vboxGlobal().medium(m_strParentID);
368}
369
370UIMedium UIMedium::root() const
371{
372 /* Redirect call to VBoxGlobal: */
373 return vboxGlobal().medium(m_strRootID);
374}
375
376/**
377 * Returns generated tooltip for this medium.
378 *
379 * In "don't show diffs" mode (where the attributes of the base hard disk are
380 * shown instead of the attributes of the differencing hard disk), extra
381 * information will be added to the tooltip to give the user a hint that the
382 * medium is actually a differencing hard disk.
383 *
384 * @param fNoDiffs @c true to enable user-friendly "don't show diffs" mode.
385 * @param fCheckRO @c true to perform the #readOnly() check and add a notice
386 * accordingly.
387 */
388QString UIMedium::toolTip (bool fNoDiffs /* = false */, bool fCheckRO /* = false */, bool fNullAllowed /* = false */) const
389{
390 QString strTip;
391
392 if (m_medium.isNull())
393 {
394 strTip = fNullAllowed ? m_sstrRow.arg(VBoxGlobal::tr("<b>No disk image file selected</b>", "medium")) +
395 m_sstrRow.arg(VBoxGlobal::tr("You can also change this while the machine is running.")) :
396 m_sstrRow.arg(VBoxGlobal::tr("<b>No disk image files available</b>", "medium")) +
397 m_sstrRow.arg(VBoxGlobal::tr("You can create or add disk image files in the virtual machine settings."));
398 }
399 else
400 {
401 unconst(this)->checkNoDiffs(fNoDiffs);
402
403 strTip = fNoDiffs ? m_noDiffs.toolTip : m_strToolTip;
404
405 if (fCheckRO && m_fReadOnly)
406 strTip += m_sstrRow.arg("<hr>") +
407 m_sstrRow.arg(VBoxGlobal::tr("Attaching this hard disk will be performed indirectly using "
408 "a newly created differencing hard disk.", "medium"));
409 }
410
411 return m_sstrTable.arg(strTip);
412}
413
414/**
415 * Returns an icon corresponding to the media state. Distinguishes between
416 * the Inaccessible state and the situation when querying the state itself
417 * failed.
418 *
419 * In "don't show diffs" mode (where the attributes of the base hard disk are
420 * shown instead of the attributes of the differencing hard disk), the most
421 * worst media state on the given hard disk chain will be used to select the
422 * media icon.
423 *
424 * @param fNoDiffs @c true to enable user-friendly "don't show diffs" mode.
425 * @param fCheckRO @c true to perform the #readOnly() check and change the icon
426 * accordingly.
427 */
428QPixmap UIMedium::icon(bool fNoDiffs /* = false */, bool fCheckRO /* = false */) const
429{
430 QPixmap pixmap;
431
432 if (state(fNoDiffs) == KMediumState_Inaccessible)
433 pixmap = result(fNoDiffs).isOk() ? vboxGlobal().warningIcon() : vboxGlobal().errorIcon();
434
435 if (fCheckRO && m_fReadOnly)
436 pixmap = VBoxGlobal::joinPixmaps(pixmap, QPixmap(":/hd_new_16px.png"));
437
438 return pixmap;
439}
440
441/**
442 * Returns the details of this medium as a single-line string
443 *
444 * For hard disks, the details include the location, type and the logical size
445 * of the hard disk. Note that if @a fNoDiffs is @c true, these properties are
446 * queried on the root hard disk of the given hard disk because the primary
447 * purpose of the returned string is to be human readable (so that seeing a
448 * complex diff hard disk name is usually not desirable).
449 *
450 * For other media types, the location and the actual size are returned.
451 * Arguments @a fPredictDiff and @a aNoRoot are ignored in this case.
452 *
453 * @param fNoDiffs @c true to enable user-friendly "don't show diffs" mode.
454 * @param fPredictDiff @c true to mark the hard disk as differencing if
455 * attaching it would create a differencing hard disk (not
456 * used when @a aNoRoot is true).
457 * @param fUseHTML @c true to allow for emphasizing using bold and italics.
458 *
459 * @note Use #detailsHTML() instead of passing @c true for @a fUseHTML.
460 *
461 * @note The media object may become uninitialized by a third party while this
462 * method is reading its properties. In this case, the method will return
463 * an empty string.
464 */
465QString UIMedium::details(bool fNoDiffs /* = false */,
466 bool fPredictDiff /* = false */,
467 bool fUseHTML /* = false */) const
468{
469 // @todo the below check is rough; if m_medium becomes uninitialized, any
470 // of getters called afterwards will also fail. The same relates to the
471 // root hard disk object (that will be the hard disk itself in case of
472 // non-differencing disks). However, this check was added to fix a
473 // particular use case: when the hard disk is a differencing hard disk and
474 // it happens to be discarded (and uninitialized) after this method is
475 // called but before we read all its properties (yes, it's possible!), the
476 // root object will be null and calling methods on it will assert in the
477 // debug builds. This check seems to be enough as a quick solution (fresh
478 // hard disk attachments will be re-read by a machine state change signal
479 // after the discard operation is finished, so the user will eventually see
480 // correct data), but in order to solve the problem properly we need to use
481 // exceptions everywhere (or check the result after every method call). See
482 // @bugref{2149}.
483
484 if (m_medium.isNull() || m_fHostDrive)
485 return m_strName;
486
487 if (!m_medium.isOk())
488 return QString();
489
490 QString strDetails, strText;
491
492 /* Note: root accessible only if medium enumerated: */
493 UIMedium rootMedium = root();
494 KMediumState eState = m_state;
495
496 if (m_type == UIMediumType_HardDisk)
497 {
498 if (fNoDiffs)
499 {
500 bool isDiff = (!fPredictDiff && parentID() != nullID()) || (fPredictDiff && m_fReadOnly);
501
502 strDetails = isDiff && fUseHTML ?
503 QString("<i>%1</i>, ").arg(rootMedium.m_strHardDiskType) :
504 QString("%1, ").arg(rootMedium.m_strHardDiskType);
505
506 eState = this->state(true /* fNoDiffs */);
507
508 if (rootMedium.m_state == KMediumState_NotCreated)
509 eState = KMediumState_NotCreated;
510 }
511 else
512 {
513 strDetails = QString("%1, ").arg(rootMedium.m_strHardDiskType);
514 }
515 }
516
517 // @todo prepend the details with the warning/error icon when not accessible
518
519 switch (eState)
520 {
521 case KMediumState_NotCreated:
522 strText = VBoxGlobal::tr("Checking...", "medium");
523 strDetails += fUseHTML ? QString("<i>%1</i>").arg(strText) : strText;
524 break;
525 case KMediumState_Inaccessible:
526 strText = VBoxGlobal::tr("Inaccessible", "medium");
527 strDetails += fUseHTML ? QString("<b>%1</b>").arg(strText) : strText;
528 break;
529 default:
530 strDetails += m_type == UIMediumType_HardDisk ? rootMedium.m_strLogicalSize : rootMedium.m_strSize;
531 break;
532 }
533
534 strDetails = fUseHTML ?
535 QString("%1 (<nobr>%2</nobr>)").arg(VBoxGlobal::locationForHTML(rootMedium.m_strName), strDetails) :
536 QString("%1 (%2)").arg(VBoxGlobal::locationForHTML(rootMedium.m_strName), strDetails);
537
538 return strDetails;
539}
540
541/* static */
542QString UIMedium::nullID()
543{
544 return m_sstrNullID;
545}
546
547/**
548 * Checks if m_noDiffs is filled in and does it if not.
549 *
550 * @param fNoDiffs @if false, this method immediately returns.
551 */
552void UIMedium::checkNoDiffs(bool fNoDiffs)
553{
554 if (!fNoDiffs || m_noDiffs.isSet)
555 return;
556
557 m_noDiffs.toolTip = QString();
558
559 m_noDiffs.state = m_state;
560 for (UIMedium parentMedium = parent(); !parentMedium.isNull(); parentMedium = parentMedium.parent())
561 {
562 if (parentMedium.m_state == KMediumState_Inaccessible)
563 {
564 m_noDiffs.state = parentMedium.m_state;
565
566 if (m_noDiffs.toolTip.isNull())
567 m_noDiffs.toolTip = m_sstrRow.arg(VBoxGlobal::tr("Some of the files in this hard disk chain "
568 "are inaccessible. Please use the Virtual Media "
569 "Manager in <b>Show Differencing Hard Disks</b> "
570 "mode to inspect these files.", "medium"));
571
572 if (!parentMedium.m_result.isOk())
573 {
574 m_noDiffs.result = parentMedium.m_result;
575 break;
576 }
577 }
578 }
579
580 if (parentID() != nullID() && !m_fReadOnly)
581 {
582 m_noDiffs.toolTip = root().tip() +
583 m_sstrRow.arg("<hr>") +
584 m_sstrRow.arg(VBoxGlobal::tr("This base hard disk is indirectly attached using "
585 "the following differencing hard disk:", "medium")) +
586 m_strToolTip + m_noDiffs.toolTip;
587 }
588
589 if (m_noDiffs.toolTip.isNull())
590 m_noDiffs.toolTip = m_strToolTip;
591
592 m_noDiffs.isSet = true;
593}
594
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