VirtualBox

source: vbox/trunk/src/VBox/Frontends/VirtualBox/src/runtime/UIMachineWindow.cpp@ 35740

Last change on this file since 35740 was 34983, checked in by vboxsync, 13 years ago

FE/Qt4: more progress images

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 20.2 KB
Line 
1/* $Id: UIMachineWindow.cpp 34983 2010-12-13 10:14:08Z vboxsync $ */
2/** @file
3 *
4 * VBox frontends: Qt GUI ("VirtualBox"):
5 * UIMachineWindow class implementation
6 */
7
8/*
9 * Copyright (C) 2010 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/* Global includes */
21#include <QCloseEvent>
22#include <QTimer>
23
24#include <VBox/version.h>
25#ifdef VBOX_BLEEDING_EDGE
26# include <iprt/buildconfig.h>
27#endif /* VBOX_BLEEDING_EDGE */
28
29/* Local includes */
30#include "COMDefs.h"
31#include "VBoxGlobal.h"
32#include "VBoxProblemReporter.h"
33
34#include "UIActionsPool.h"
35#include "UIKeyboardHandler.h"
36#include "UIMachineLogic.h"
37#include "UIMachineView.h"
38#include "UIMachineWindow.h"
39#include "UIMachineWindowFullscreen.h"
40#include "UIMachineWindowNormal.h"
41#include "UIMachineWindowScale.h"
42#include "UIMachineWindowSeamless.h"
43#include "UIMouseHandler.h"
44#include "UISession.h"
45#include "UIVMCloseDialog.h"
46
47#ifdef Q_WS_X11
48# include <X11/Xlib.h>
49#endif
50
51UIMachineWindow* UIMachineWindow::create(UIMachineLogic *pMachineLogic, UIVisualStateType visualStateType, ulong uScreenId)
52{
53 UIMachineWindow *window = 0;
54 switch (visualStateType)
55 {
56 case UIVisualStateType_Normal:
57 window = new UIMachineWindowNormal(pMachineLogic, uScreenId);
58 break;
59 case UIVisualStateType_Fullscreen:
60 window = new UIMachineWindowFullscreen(pMachineLogic, uScreenId);
61 break;
62 case UIVisualStateType_Seamless:
63 window = new UIMachineWindowSeamless(pMachineLogic, uScreenId);
64 break;
65 case UIVisualStateType_Scale:
66 window = new UIMachineWindowScale(pMachineLogic, uScreenId);
67 break;
68 }
69 return window;
70}
71
72void UIMachineWindow::destroy(UIMachineWindow *pWhichWindow)
73{
74 delete pWhichWindow;
75}
76
77void UIMachineWindow::sltTryClose()
78{
79 /* Do not try to close if restricted: */
80 if (machineLogic()->isPreventAutoClose())
81 return;
82
83 /* First close any open modal & popup widgets.
84 * Use a single shot with timeout 0 to allow the widgets to cleanly close and test then again.
85 * If all open widgets are closed destroy ourself: */
86 QWidget *widget = QApplication::activeModalWidget() ?
87 QApplication::activeModalWidget() :
88 QApplication::activePopupWidget() ?
89 QApplication::activePopupWidget() : 0;
90 if (widget)
91 {
92 widget->close();
93 QTimer::singleShot(0, machineWindow(), SLOT(sltTryClose()));
94 }
95 else
96 machineWindow()->close();
97}
98
99UIMachineWindow::UIMachineWindow(UIMachineLogic *pMachineLogic, ulong uScreenId)
100 : m_pMachineLogic(pMachineLogic)
101 , m_pMachineWindow(0)
102 , m_uScreenId(uScreenId)
103 , m_pMachineViewContainer(0)
104 , m_pTopSpacer(0)
105 , m_pBottomSpacer(0)
106 , m_pLeftSpacer(0)
107 , m_pRightSpacer(0)
108 , m_pMachineView(0)
109{
110}
111
112UIMachineWindow::~UIMachineWindow()
113{
114 /* Close any opened modal & popup widgets: */
115 while (QWidget *pWidget = QApplication::activeModalWidget() ? QApplication::activeModalWidget() :
116 QApplication::activePopupWidget() ? QApplication::activePopupWidget() : 0)
117 {
118 /* Set modal/popup window's parent to null early,
119 * because deleteLater() is synchronous
120 * and will be called later than this destructor: */
121 pWidget->setParent(0);
122 /* Close modal/popup window early to hide it
123 * because deleteLater() is synchronous
124 * and will be called later than this destructor: */
125 pWidget->close();
126 /* Delete modal/popup window synchronously (safe): */
127 pWidget->deleteLater();
128 }
129}
130
131UISession* UIMachineWindow::uisession() const
132{
133 return machineLogic()->uisession();
134}
135
136CSession& UIMachineWindow::session() const
137{
138 return uisession()->session();
139}
140
141void UIMachineWindow::setMask(const QRegion &region)
142{
143 machineWindow()->setMask(region);
144}
145
146void UIMachineWindow::retranslateUi()
147{
148#ifdef VBOX_OSE
149 m_strWindowTitlePrefix = UIMachineLogic::tr("VirtualBox OSE");
150#else
151 m_strWindowTitlePrefix = VBOX_PRODUCT;
152#endif
153#ifdef VBOX_BLEEDING_EDGE
154 m_strWindowTitlePrefix += UIMachineLogic::tr(" EXPERIMENTAL build %1r%2 - %3")
155 .arg(RTBldCfgVersion())
156 .arg(RTBldCfgRevisionStr())
157 .arg(VBOX_BLEEDING_EDGE);
158#endif
159 updateAppearanceOf(UIVisualElement_WindowCaption);
160}
161
162#ifdef Q_WS_X11
163bool UIMachineWindow::x11Event(XEvent *pEvent)
164{
165 // TODO: Check if that is still required!
166 /* Qt bug: when the machine-view grabs the keyboard,
167 * FocusIn, FocusOut, WindowActivate and WindowDeactivate Qt events are
168 * not properly sent on top level window deactivation.
169 * The fix is to substiute the mode in FocusOut X11 event structure
170 * to NotifyNormal to cause Qt to process it as desired. */
171 if (pEvent->type == FocusOut)
172 {
173 if (pEvent->xfocus.mode == NotifyWhileGrabbed &&
174 (pEvent->xfocus.detail == NotifyAncestor ||
175 pEvent->xfocus.detail == NotifyInferior ||
176 pEvent->xfocus.detail == NotifyNonlinear))
177 {
178 pEvent->xfocus.mode = NotifyNormal;
179 }
180 }
181 return false;
182}
183#endif
184
185void UIMachineWindow::closeEvent(QCloseEvent *pEvent)
186{
187 /* Always ignore close event: */
188 pEvent->ignore();
189
190 /* Should we close application? */
191 bool fCloseApplication = false;
192
193 switch (uisession()->machineState())
194 {
195 case KMachineState_Running:
196 case KMachineState_Paused:
197 case KMachineState_Stuck:
198 case KMachineState_LiveSnapshotting:
199 case KMachineState_Teleporting: // TODO: Test this!
200 case KMachineState_TeleportingPausedVM: // TODO: Test this!
201 {
202 /* Get the machine: */
203 CMachine machine = session().GetMachine();
204
205 /* Prepare close dialog: */
206 UIVMCloseDialog dlg(machineWindow());
207
208 /* Assign close-dialog pixmap: */
209 dlg.pmIcon->setPixmap(vboxGlobal().vmGuestOSTypeIcon(machine.GetOSTypeId()));
210
211 /* Check which close actions are disallowed: */
212 QStringList restictedActionsList = machine.GetExtraData(VBoxDefs::GUI_RestrictedCloseActions).split(',');
213 bool fIsStateSavingAllowed = !restictedActionsList.contains("SaveState", Qt::CaseInsensitive);
214 bool fIsACPIShutdownAllowed = !restictedActionsList.contains("Shutdown", Qt::CaseInsensitive);
215 bool fIsPowerOffAllowed = !restictedActionsList.contains("PowerOff", Qt::CaseInsensitive);
216 bool fIsPowerOffAndRestoreAllowed = fIsPowerOffAllowed && !restictedActionsList.contains("Restore", Qt::CaseInsensitive);
217
218 /* Make Save State button visible/hidden depending on restriction: */
219 dlg.mRbSave->setVisible(fIsStateSavingAllowed);
220 dlg.mTxSave->setVisible(fIsStateSavingAllowed);
221 /* Make Save State button enabled/disabled depending on machine state: */
222 dlg.mRbSave->setEnabled(uisession()->machineState() != KMachineState_Stuck);
223
224 /* Make ACPI shutdown button visible/hidden depending on restriction: */
225 dlg.mRbShutdown->setVisible(fIsACPIShutdownAllowed);
226 dlg.mTxShutdown->setVisible(fIsACPIShutdownAllowed);
227 /* Make ACPI shutdown button enabled/disabled depending on ACPI state & machine state: */
228 bool isACPIEnabled = session().GetConsole().GetGuestEnteredACPIMode();
229 dlg.mRbShutdown->setEnabled(isACPIEnabled && uisession()->machineState() != KMachineState_Stuck);
230
231 /* Make Power Off button visible/hidden depending on restriction: */
232 dlg.mRbPowerOff->setVisible(fIsPowerOffAllowed);
233 dlg.mTxPowerOff->setVisible(fIsPowerOffAllowed);
234
235 /* Make the Restore Snapshot checkbox visible/hidden depending on snapshots count & restrictions: */
236 dlg.mCbDiscardCurState->setVisible(fIsPowerOffAndRestoreAllowed && machine.GetSnapshotCount() > 0);
237 if (!machine.GetCurrentSnapshot().isNull())
238 dlg.mCbDiscardCurState->setText(dlg.mCbDiscardCurState->text().arg(machine.GetCurrentSnapshot().GetName()));
239
240 /* Choice string tags for close-dialog: */
241 QString strSave("save");
242 QString strShutdown("shutdown");
243 QString strPowerOff("powerOff");
244 QString strDiscardCurState("discardCurState");
245
246 /* Read the last user's choice for the given VM: */
247 QStringList lastAction = machine.GetExtraData(VBoxDefs::GUI_LastCloseAction).split(',');
248
249 /* Check which button should be initially chosen: */
250 QRadioButton *pRadioButton = 0;
251
252 /* If choosing 'last choice' is possible: */
253 if (lastAction[0] == strSave && fIsStateSavingAllowed)
254 {
255 pRadioButton = dlg.mRbSave;
256 }
257 else if (lastAction[0] == strShutdown && fIsACPIShutdownAllowed && isACPIEnabled)
258 {
259 pRadioButton = dlg.mRbShutdown;
260 }
261 else if (lastAction[0] == strPowerOff && fIsPowerOffAllowed)
262 {
263 pRadioButton = dlg.mRbPowerOff;
264 if (fIsPowerOffAndRestoreAllowed)
265 dlg.mCbDiscardCurState->setChecked(lastAction.count() > 1 && lastAction[1] == strDiscardCurState);
266 }
267 /* Else 'default choice' will be used: */
268 else
269 {
270 if (fIsACPIShutdownAllowed && isACPIEnabled)
271 pRadioButton = dlg.mRbShutdown;
272 else if (fIsPowerOffAllowed)
273 pRadioButton = dlg.mRbPowerOff;
274 else if (fIsStateSavingAllowed)
275 pRadioButton = dlg.mRbSave;
276 }
277
278 /* If some radio button was chosen: */
279 if (pRadioButton)
280 {
281 /* Check and focus it: */
282 pRadioButton->setChecked(true);
283 pRadioButton->setFocus();
284 }
285 /* If no one of radio buttons was chosen: */
286 else
287 {
288 /* Just break and leave: */
289 break;
290 }
291
292 /* This flag will keep the status of every further logical operation: */
293 bool success = true;
294
295 /* Pause before showing dialog if necessary: */
296 bool fWasPaused = uisession()->isPaused() || uisession()->machineState() == KMachineState_Stuck;
297 if (!fWasPaused)
298 success = uisession()->pause();
299
300 if (success)
301 {
302 /* Preventing auto-closure: */
303 machineLogic()->setPreventAutoClose(true);
304
305 /* If close dialog accepted: */
306 if (dlg.exec() == QDialog::Accepted)
307 {
308 /* Get current console: */
309 CConsole console = session().GetConsole();
310
311 success = false;
312
313 if (dlg.mRbSave->isChecked())
314 {
315 CProgress progress = console.SaveState();
316
317 if (!console.isOk())
318 vboxProblem().cannotSaveMachineState(console);
319 else
320 {
321 /* Show the "VM saving" progress dialog: */
322 vboxProblem().showModalProgressDialog(progress, machine.GetName(), ":/progress_state_save_90px.png", 0, true);
323 if (progress.GetResultCode() != 0)
324 vboxProblem().cannotSaveMachineState(progress);
325 else
326 success = true;
327 }
328
329 if (success)
330 fCloseApplication = true;
331 }
332 else if (dlg.mRbShutdown->isChecked())
333 {
334 /* Unpause the VM to let it grab the ACPI shutdown event: */
335 uisession()->unpause();
336 /* Prevent the subsequent unpause request: */
337 fWasPaused = true;
338 /* Signal ACPI shutdown (if there is no ACPI device, the operation will fail): */
339 console.PowerButton();
340 if (!console.isOk())
341 vboxProblem().cannotACPIShutdownMachine(console);
342 else
343 success = true;
344 }
345 else if (dlg.mRbPowerOff->isChecked())
346 {
347 CProgress progress = console.PowerDown();
348
349 if (!console.isOk())
350 vboxProblem().cannotStopMachine(console);
351 else
352 {
353 /* Show the power down progress dialog: */
354 vboxProblem().showModalProgressDialog(progress, machine.GetName(), ":/progress_poweroff_90px.png", 0, true);
355 if (progress.GetResultCode() != 0)
356 vboxProblem().cannotStopMachine(progress);
357 else
358 success = true;
359 }
360
361 if (success)
362 {
363 /* Discard the current state if requested: */
364 if (dlg.mCbDiscardCurState->isChecked() && dlg.mCbDiscardCurState->isVisibleTo(&dlg))
365 {
366 CSnapshot snapshot = machine.GetCurrentSnapshot();
367 CProgress progress = console.RestoreSnapshot(snapshot);
368 if (!console.isOk())
369 vboxProblem().cannotRestoreSnapshot(console, snapshot.GetName());
370 else
371 {
372 /* Show the progress dialog: */
373 vboxProblem().showModalProgressDialog(progress, machine.GetName(), ":/progress_snapshot_discard_90px.png", 0, true);
374 if (progress.GetResultCode() != 0)
375 vboxProblem().cannotRestoreSnapshot(progress, snapshot.GetName());
376 }
377 }
378 }
379
380 if (success)
381 fCloseApplication = true;
382 }
383
384 if (success)
385 {
386 /* Read the last user's choice for the given VM: */
387 QStringList prevAction = machine.GetExtraData(VBoxDefs::GUI_LastCloseAction).split(',');
388 /* Memorize the last user's choice for the given VM: */
389 QString lastAction = strPowerOff;
390 if (dlg.mRbSave->isChecked())
391 lastAction = strSave;
392 else if ((dlg.mRbShutdown->isChecked()) ||
393 (dlg.mRbPowerOff->isChecked() && prevAction[0] == strShutdown && !isACPIEnabled))
394 lastAction = strShutdown;
395 else if (dlg.mRbPowerOff->isChecked())
396 lastAction = strPowerOff;
397 else
398 AssertFailed();
399 if (dlg.mCbDiscardCurState->isChecked())
400 (lastAction += ",") += strDiscardCurState;
401 machine.SetExtraData(VBoxDefs::GUI_LastCloseAction, lastAction);
402 }
403 }
404
405 /* Restore the running state if needed: */
406 if (success && !fCloseApplication && !fWasPaused && uisession()->machineState() == KMachineState_Paused)
407 uisession()->unpause();
408
409 /* Allowing auto-closure: */
410 machineLogic()->setPreventAutoClose(false);
411 }
412
413 break;
414 }
415
416 default:
417 break;
418 }
419
420 if (fCloseApplication)
421 {
422 /* VM has been powered off or saved. We must *safely* close the VM window(s): */
423 QTimer::singleShot(0, uisession(), SLOT(sltCloseVirtualSession()));
424 }
425}
426
427void UIMachineWindow::prepareWindowIcon()
428{
429#if !(defined (Q_WS_WIN) || defined (Q_WS_MAC))
430 /* The default application icon (will be changed to VM-specific icon little bit later):
431 * 1. On Win32, it's built-in to the executable;
432 * 2. On Mac OS X the icon referenced in info.plist is used. */
433 machineWindow()->setWindowIcon(QIcon(":/VirtualBox_48px.png"));
434#endif
435
436#ifndef Q_WS_MAC
437 /* Set the VM-specific application icon except Mac OS X: */
438 machineWindow()->setWindowIcon(vboxGlobal().vmGuestOSTypeIcon(session().GetMachine().GetOSTypeId()));
439#endif
440}
441
442void UIMachineWindow::prepareConsoleConnections()
443{
444 /* Machine state-change updater: */
445 QObject::connect(uisession(), SIGNAL(sigMachineStateChange()), machineWindow(), SLOT(sltMachineStateChanged()));
446}
447
448void UIMachineWindow::prepareMachineViewContainer()
449{
450 /* Create view container.
451 * After it will be passed to parent widget of some mode,
452 * there will be no need to delete it, so no need to cleanup: */
453 m_pMachineViewContainer = new QGridLayout();
454 m_pMachineViewContainer->setMargin(0);
455 m_pMachineViewContainer->setSpacing(0);
456
457 /* Create and add shifting spacers.
458 * After they will be inserted into layout, it will get the parentness
459 * of those spacers, so there will be no need to cleanup them. */
460 m_pTopSpacer = new QSpacerItem(0, 0, QSizePolicy::Fixed, QSizePolicy::Expanding);
461 m_pBottomSpacer = new QSpacerItem(0, 0, QSizePolicy::Fixed, QSizePolicy::Expanding);
462 m_pLeftSpacer = new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Fixed);
463 m_pRightSpacer = new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Fixed);
464
465 m_pMachineViewContainer->addItem(m_pTopSpacer, 0, 1);
466 m_pMachineViewContainer->addItem(m_pBottomSpacer, 2, 1);
467 m_pMachineViewContainer->addItem(m_pLeftSpacer, 1, 0);
468 m_pMachineViewContainer->addItem(m_pRightSpacer, 1, 2);
469}
470
471void UIMachineWindow::prepareHandlers()
472{
473 /* Register keyboard-handler: */
474 machineLogic()->keyboardHandler()->prepareListener(m_uScreenId, this);
475
476 /* Register mouse-handler: */
477 machineLogic()->mouseHandler()->prepareListener(m_uScreenId, this);
478}
479
480void UIMachineWindow::cleanupHandlers()
481{
482 /* Unregister mouse-handler: */
483 machineLogic()->mouseHandler()->cleanupListener(m_uScreenId);
484
485 /* Unregister keyboard-handler: */
486 machineLogic()->keyboardHandler()->cleanupListener(m_uScreenId);
487}
488
489void UIMachineWindow::updateAppearanceOf(int iElement)
490{
491 CMachine machine = session().GetMachine();
492
493 if (iElement & UIVisualElement_WindowCaption)
494 {
495 /* Get machine state: */
496 KMachineState state = uisession()->machineState();
497 /* Prepare full name: */
498 QString strSnapshotName;
499 if (machine.GetSnapshotCount() > 0)
500 {
501 CSnapshot snapshot = machine.GetCurrentSnapshot();
502 strSnapshotName = " (" + snapshot.GetName() + ")";
503 }
504 QString strMachineName = machine.GetName() + strSnapshotName;
505 if (state != KMachineState_Null)
506 strMachineName += " [" + vboxGlobal().toString(state) + "]";
507 /* Unusual on the Mac. */
508#ifndef Q_WS_MAC
509 strMachineName += " - " + m_strWindowTitlePrefix;
510#endif /* Q_WS_MAC */
511 if (machine.GetMonitorCount() > 1)
512 strMachineName += QString(" : %1").arg(m_uScreenId + 1);
513 machineWindow()->setWindowTitle(strMachineName);
514 }
515}
516
517#ifdef VBOX_WITH_DEBUGGER_GUI
518void UIMachineWindow::updateDbgWindows()
519{
520 /* The debugger windows are bind to the main VM window. */
521 if (m_uScreenId == 0)
522 machineLogic()->dbgAdjustRelativePos();
523}
524#endif /* VBOX_WITH_DEBUGGER_GUI */
525
526void UIMachineWindow::sltMachineStateChanged()
527{
528 updateAppearanceOf(UIVisualElement_WindowCaption);
529}
530
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use