VirtualBox

source: vbox/trunk/src/VBox/Frontends/VirtualBox/src/selector/VBoxSelectorWnd.cpp@ 35740

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

FE/Qt4: allow free configurable key shortcuts in the selector and machine window

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 61.2 KB
Line 
1/** @file
2 *
3 * VBox frontends: Qt GUI ("VirtualBox"):
4 * VBoxSelectorWnd class implementation
5 */
6
7/*
8 * Copyright (C) 2006-2010 Oracle Corporation
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 */
18
19#ifdef VBOX_WITH_PRECOMPILED_HEADERS
20# include "precomp.h"
21#else /* !VBOX_WITH_PRECOMPILED_HEADERS */
22#include "QISplitter.h"
23#include "UIBar.h"
24#include "UIDownloaderUserManual.h"
25#include "UIExportApplianceWzd.h"
26#include "UIIconPool.h"
27#include "UIImportApplianceWzd.h"
28#include "UINewVMWzd.h"
29#include "UIVMDesktop.h"
30#include "UIVMListView.h"
31#include "UIVirtualBoxEventHandler.h"
32#include "VBoxGlobal.h"
33#include "VBoxMediaManagerDlg.h"
34#include "VBoxProblemReporter.h"
35#include "VBoxSelectorWnd.h"
36#include "UISettingsDialogSpecific.h"
37#include "UIToolBar.h"
38#include "VBoxVMLogViewer.h"
39#include "QIFileDialog.h"
40#include "UISelectorShortcuts.h"
41#include "UIDesktopServices.h"
42#include "UIGlobalSettingsExtension.h" /* extension pack installation */
43
44#ifdef VBOX_GUI_WITH_SYSTRAY
45# include "VBoxTrayIcon.h"
46# include "UIExtraDataEventHandler.h"
47#endif /* VBOX_GUI_WITH_SYSTRAY */
48
49#ifdef Q_WS_MAC
50# include "VBoxUtils.h"
51# include "UIWindowMenuManager.h"
52# include "UIImageTools.h"
53#endif
54
55/* Global includes */
56#include <QDesktopWidget>
57#include <QMenuBar>
58#include <QResizeEvent>
59#include <QDesktopServices>
60
61#include <iprt/buildconfig.h>
62#include <VBox/version.h>
63#ifdef Q_WS_X11
64# include <iprt/env.h>
65#endif
66#endif /* !VBOX_WITH_PRECOMPILED_HEADERS */
67
68// VBoxSelectorWnd class
69////////////////////////////////////////////////////////////////////////////////
70
71/** \class VBoxSelectorWnd
72 *
73 * The VBoxSelectorWnd class is a VM selector window, one of two main VBox
74 * GUI windows.
75 *
76 * This window appears when the user starts the VirtualBox executable.
77 * It allows to view the list of configured VMs, their settings
78 * and the current state, create, reconfigure, delete and start VMs.
79 */
80
81/**
82 * Constructs the VM selector window.
83 *
84 * @param aSelf pointer to a variable where to store |this| right after
85 * this object's constructor is called (necessary to avoid
86 * recursion in VBoxGlobal::selectorWnd())
87 */
88VBoxSelectorWnd::
89VBoxSelectorWnd(VBoxSelectorWnd **aSelf, QWidget* aParent,
90 Qt::WindowFlags aFlags /* = Qt::Window */)
91 : QIWithRetranslateUI2<QMainWindow>(aParent, aFlags)
92 , mDoneInaccessibleWarningOnce(false)
93{
94 VBoxGlobalSettings settings = vboxGlobal().settings();
95
96 if (aSelf)
97 *aSelf = this;
98
99 statusBar()->setContextMenuPolicy(Qt::CustomContextMenu);
100 connect(statusBar(), SIGNAL(customContextMenuRequested(const QPoint&)),
101 this, SLOT(showViewContextMenu(const QPoint&)));
102
103#ifdef Q_WS_MAC
104 qApp->installEventFilter(this);
105#endif /* Q_WS_MAC */
106
107#if !(defined (Q_WS_WIN) || defined (Q_WS_MAC))
108 /* The application icon. On Win32, it's built-in to the executable. On Mac
109 * OS X the icon referenced in info.plist is used. */
110 setWindowIcon(QIcon(":/VirtualBox_48px.png"));
111#endif
112
113 /* actions */
114
115 mFileMediaMgrAction = new QAction(this);
116 mFileMediaMgrAction->setIcon(UIIconPool::iconSet(":/diskimage_16px.png"));
117
118 mFileApplianceImportAction = new QAction(this);
119 mFileApplianceImportAction->setIcon(UIIconPool::iconSet(":/import_16px.png"));
120
121 mFileApplianceExportAction = new QAction(this);
122 mFileApplianceExportAction->setIcon(UIIconPool::iconSet(":/export_16px.png"));
123
124 mFileSettingsAction = new QAction(this);
125 mFileSettingsAction->setMenuRole(QAction::PreferencesRole);
126 mFileSettingsAction->setIcon(UIIconPool::iconSet(":/global_settings_16px.png"));
127 mFileExitAction = new QAction(this);
128 mFileExitAction->setMenuRole(QAction::QuitRole);
129 mFileExitAction->setIcon(UIIconPool::iconSet(":/exit_16px.png"));
130
131 mVmNewAction = new QAction(this);
132 mVmNewAction->setIcon(UIIconPool::iconSetFull(
133 QSize(32, 32), QSize(16, 16),
134 ":/vm_new_32px.png", ":/new_16px.png"));
135 mVmAddAction = new QAction(this);
136 mVmAddAction->setIcon(UIIconPool::iconSet(
137 ":/vm_add_16px.png"));
138 mVmConfigAction = new QAction(this);
139 mVmConfigAction->setIcon(UIIconPool::iconSetFull(
140 QSize(32, 32), QSize(16, 16),
141 ":/vm_settings_32px.png", ":/settings_16px.png",
142 ":/vm_settings_disabled_32px.png", ":/settings_dis_16px.png"));
143 mVmDeleteAction = new QAction(this);
144 mVmDeleteAction->setIcon(UIIconPool::iconSetFull(
145 QSize(32, 32), QSize(16, 16),
146 ":/vm_delete_32px.png", ":/delete_16px.png",
147 ":/vm_delete_disabled_32px.png", ":/delete_dis_16px.png"));
148 mVmStartAction = new QAction(this);
149 mVmStartAction->setIcon(UIIconPool::iconSetFull(
150 QSize(32, 32), QSize(16, 16),
151 ":/vm_start_32px.png", ":/start_16px.png",
152 ":/vm_start_disabled_32px.png", ":/start_dis_16px.png"));
153 mVmDiscardAction = new QAction(this);
154 mVmDiscardAction->setIcon(UIIconPool::iconSetFull(
155 QSize(32, 32), QSize(16, 16),
156 ":/vm_discard_32px.png", ":/discard_16px.png",
157 ":/vm_discard_disabled_32px.png", ":/discard_dis_16px.png"));
158 mVmPauseAction = new QAction(this);
159 mVmPauseAction->setCheckable(true);
160 mVmPauseAction->setIcon(UIIconPool::iconSetFull(
161 QSize(32, 32), QSize(16, 16),
162 ":/vm_pause_32px.png", ":/pause_16px.png",
163 ":/vm_pause_disabled_32px.png", ":/pause_disabled_16px.png"));
164 mVmRefreshAction = new QAction(this);
165 mVmRefreshAction->setIcon(UIIconPool::iconSetFull(
166 QSize(32, 32), QSize(16, 16),
167 ":/refresh_32px.png", ":/refresh_16px.png",
168 ":/refresh_disabled_32px.png", ":/refresh_disabled_16px.png"));
169 mVmShowLogsAction = new QAction(this);
170 mVmShowLogsAction->setIcon(UIIconPool::iconSetFull(
171 QSize(32, 32), QSize(16, 16),
172 ":/vm_show_logs_32px.png", ":/show_logs_16px.png",
173 ":/vm_show_logs_disabled_32px.png", ":/show_logs_disabled_16px.png"));
174 mVmOpenInFileManagerAction = new QAction(this);
175 mVmOpenInFileManagerAction->setIcon(UIIconPool::iconSet(
176 ":/vm_open_filemanager_16px.png", ":/vm_open_filemanager_disabled_16px.png"));
177 mVmCreateShortcut = new QAction(this);
178 mVmCreateShortcut->setIcon(UIIconPool::iconSet(
179 ":/vm_create_shortcut_16px.png", ":/vm_create_shortcut_disabled_16px.png"));
180
181 mHelpActions.setup(this);
182
183 /* VM list toolbar */
184 mVMToolBar = new UIToolBar(this);
185 mVMToolBar->setContextMenuPolicy(Qt::CustomContextMenu);
186#ifndef Q_WS_MAC
187 connect(mVMToolBar, SIGNAL(customContextMenuRequested(const QPoint&)),
188 this, SLOT(showViewContextMenu(const QPoint&)));
189#else /* !Q_WS_MAC */
190 /* A simple connect doesn't work on the Mac, also we want receive right
191 * click notifications on the title bar. So register our own handler. */
192 ::darwinRegisterForUnifiedToolbarContextMenuEvents(this);
193#endif /* Q_WS_MAC */
194
195 /* VM list view */
196 mVMModel = new UIVMItemModel();
197 mVMListView = new UIVMListView(mVMModel);
198 mVMListView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
199 connect(mVMListView, SIGNAL(sigUrlsDropped(QList<QUrl>)),
200 this, SLOT(sltOpenUrls(QList<QUrl>)), Qt::QueuedConnection);
201
202 /* Make non-possible to activate list elements by single click,
203 * this hack should disable the current possibility to do it if present */
204 if (mVMListView->style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, 0, mVMListView))
205 mVMListView->setStyleSheet("activate-on-singleclick : 0");
206
207 m_pSplitter = new QISplitter(this);
208 m_pSplitter->setHandleType(QISplitter::Native);
209
210#define BIG_TOOLBAR
211#if MAC_LEOPARD_STYLE
212 addToolBar(mVMToolBar);
213 /* Central widget @ horizontal layout */
214 setCentralWidget(m_pSplitter);
215 m_pSplitter->addWidget(mVMListView);
216#else /* MAC_LEOPARD_STYLE */
217// mVMToolBar->setContentsMargins(5, 5, 0, 0);
218// addToolBar(mVMToolBar);
219// m_pSplitter->addWidget(mVMListView);
220 QWidget *pLeftWidget = new QWidget(this);
221 QVBoxLayout *pLeftVLayout = new QVBoxLayout(pLeftWidget);
222 pLeftVLayout->setContentsMargins(0, 0, 0, 0);
223 pLeftVLayout->setSpacing(0);
224# ifdef BIG_TOOLBAR
225 m_pBar = new UIMainBar(this);
226 m_pBar->setContentWidget(mVMToolBar);
227 pLeftVLayout->addWidget(m_pBar);
228 pLeftVLayout->addWidget(m_pSplitter);
229 setCentralWidget(pLeftWidget);
230 m_pSplitter->addWidget(mVMListView);
231# else /* BIG_TOOLBAR */
232 pLeftVLayout->addWidget(mVMToolBar);
233 pLeftVLayout->addWidget(mVMListView);
234 setCentralWidget(m_pSplitter);
235 m_pSplitter->addWidget(pLeftWidget);
236# endif /* BIG_TOOLBAR */
237#endif /* MAC_LEOPARD_STYLE */
238
239 /* add actions to the toolbar */
240
241 mVMToolBar->setIconSize(QSize(32, 32));
242 mVMToolBar->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
243// mVMToolBar->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
244
245 mVMToolBar->addAction(mVmNewAction);
246 mVMToolBar->addAction(mVmConfigAction);
247#if 0 /* delete action is really rare */
248 mVMToolBar->addAction(mVmDeleteAction);
249#endif
250 mVMToolBar->addAction(mVmStartAction);
251 mVMToolBar->addAction(mVmDiscardAction);
252
253 /* VM tab widget containing details and snapshots tabs */
254 QWidget *w = new QWidget();
255 QVBoxLayout *pVBox = new QVBoxLayout(w);
256 pVBox->setContentsMargins(0, 0, 0, 0);
257
258 m_pVMDesktop = new UIVMDesktop(mVMToolBar, mVmRefreshAction, this);
259 pVBox->addWidget(m_pVMDesktop);
260
261 m_pSplitter->addWidget(w);
262
263 /* Set the initial distribution. The right site is bigger. */
264 m_pSplitter->setStretchFactor(0, 2);
265 m_pSplitter->setStretchFactor(1, 3);
266
267 /* Configure menubar */
268 menuBar()->setContextMenuPolicy(Qt::CustomContextMenu);
269 connect(menuBar(), SIGNAL(customContextMenuRequested(const QPoint&)),
270 this, SLOT(showViewContextMenu(const QPoint&)));
271
272 /* add actions to menubar */
273 mFileMenu = menuBar()->addMenu(QString::null);
274 mFileMenu->addAction(mFileMediaMgrAction);
275 mFileMenu->addAction(mFileApplianceImportAction);
276 mFileMenu->addAction(mFileApplianceExportAction);
277#ifndef Q_WS_MAC
278 mFileMenu->addSeparator();
279#endif /* Q_WS_MAC */
280 mFileMenu->addAction(mFileSettingsAction);
281#ifndef Q_WS_MAC
282 mFileMenu->addSeparator();
283#endif /* Q_WS_MAC */
284 mFileMenu->addAction(mFileExitAction);
285
286 mVMMenu = menuBar()->addMenu(QString::null);
287 mVMMenu->addAction(mVmNewAction);
288 mVMMenu->addAction(mVmAddAction);
289 mVMMenu->addAction(mVmConfigAction);
290 mVMMenu->addAction(mVmDeleteAction);
291 mVMMenu->addSeparator();
292 mVMMenu->addAction(mVmStartAction);
293 mVMMenu->addAction(mVmDiscardAction);
294 mVMMenu->addAction(mVmPauseAction);
295 mVMMenu->addSeparator();
296 mVMMenu->addAction(mVmRefreshAction);
297 mVMMenu->addAction(mVmShowLogsAction);
298 mVMMenu->addSeparator();
299 mVMMenu->addAction(mVmOpenInFileManagerAction);
300 mVMMenu->addAction(mVmCreateShortcut);
301
302#ifdef Q_WS_MAC
303 menuBar()->addMenu(UIWindowMenuManager::instance(this)->createMenu(this));
304#endif /* Q_WS_MAC */
305
306 mVMCtxtMenu = new QMenu(this);
307 mVMCtxtMenu->addAction(mVmConfigAction);
308 mVMCtxtMenu->addAction(mVmDeleteAction);
309 mVMCtxtMenu->addSeparator();
310 mVMCtxtMenu->addAction(mVmStartAction);
311 mVMCtxtMenu->addAction(mVmDiscardAction);
312 mVMCtxtMenu->addAction(mVmPauseAction);
313 mVMCtxtMenu->addSeparator();
314 mVMCtxtMenu->addAction(mVmRefreshAction);
315 mVMCtxtMenu->addAction(mVmShowLogsAction);
316 mVMCtxtMenu->addSeparator();
317 mVMCtxtMenu->addAction(mVmOpenInFileManagerAction);
318 mVMCtxtMenu->addAction(mVmCreateShortcut);
319
320 /* Make sure every status bar hint from the context menu is cleared when
321 * the menu is closed. */
322 connect(mVMCtxtMenu, SIGNAL(aboutToHide()),
323 statusBar(), SLOT(clearMessage()));
324
325 mHelpMenu = menuBar()->addMenu(QString::null);
326 mHelpActions.addTo(mHelpMenu);
327
328#ifdef VBOX_GUI_WITH_SYSTRAY
329 mTrayIcon = new VBoxTrayIcon(this, mVMModel);
330 Assert(mTrayIcon);
331 connect(mTrayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
332 this, SLOT(trayIconActivated(QSystemTrayIcon::ActivationReason)));
333#endif
334
335 retranslateUi();
336
337 CVirtualBox vbox = vboxGlobal().virtualBox();
338 /* Restore the position of the window */
339 {
340 QString winPos = vbox.GetExtraData(VBoxDefs::GUI_LastWindowPosition);
341
342 bool ok = false, max = false;
343 int x = 0, y = 0, w = 0, h = 0;
344 x = winPos.section(',', 0, 0).toInt(&ok);
345 if (ok)
346 y = winPos.section(',', 1, 1).toInt(&ok);
347 if (ok)
348 w = winPos.section(',', 2, 2).toInt(&ok);
349 if (ok)
350 h = winPos.section(',', 3, 3).toInt(&ok);
351 if (ok)
352 max = winPos.section(',', 4, 4) == VBoxDefs::GUI_LastWindowState_Max;
353
354 QRect ar = ok ? QApplication::desktop()->availableGeometry(QPoint(x, y)) :
355 QApplication::desktop()->availableGeometry(this);
356
357 if (ok /* previous parameters were read correctly */
358 && (y > 0) && (y < ar.bottom()) /* check vertical bounds */
359 && (x + w > ar.left()) && (x < ar.right()) /* & horizontal bounds */)
360 {
361 mNormalGeo.moveTo(x, y);
362 mNormalGeo.setSize(QSize(w, h)
363 .expandedTo(minimumSizeHint())
364 .boundedTo(ar.size()));
365#if defined(Q_WS_MAC) && (QT_VERSION >= 0x040700)
366 move(mNormalGeo.topLeft());
367 resize(mNormalGeo.size());
368 mNormalGeo = normalGeometry();
369#else /* defined(Q_WS_MAC) && (QT_VERSION >= 0x040700) */
370 setGeometry(mNormalGeo);
371#endif /* !(defined(Q_WS_MAC) && (QT_VERSION >= 0x040700)) */
372 if (max) /* maximize if needed */
373 showMaximized();
374 }
375 else
376 {
377 mNormalGeo.setSize(QSize(770, 550).expandedTo(minimumSizeHint())
378 .boundedTo(ar.size()));
379 mNormalGeo.moveCenter(ar.center());
380 setGeometry(mNormalGeo);
381 }
382 }
383#if MAC_LEOPARD_STYLE
384 /* Enable unified toolbars on Mac OS X. Available on Qt >= 4.3. We do this
385 * after setting the window pos/size, cause Qt sometime includes the
386 * toolbar height in the content height. */
387 mVMToolBar->setMacToolbar();
388#endif /* MAC_LEOPARD_STYLE */
389
390 /* Update the list */
391 refreshVMList();
392
393 /* Reset to the first item */
394 mVMListView->selectItemByRow(0);
395
396 /* restore the position of vm selector */
397 {
398 QString prevVMId = vbox.GetExtraData(VBoxDefs::GUI_LastVMSelected);
399
400 mVMListView->selectItemById(prevVMId);
401 }
402
403 /* Read the splitter handle position */
404 {
405 QList<int> sizes = vbox.GetExtraDataIntList(VBoxDefs::GUI_SplitterSizes);
406 if (sizes.size() == 2)
407 m_pSplitter->setSizes(sizes);
408 }
409
410 /* Restore toolbar and statusbar visibility */
411 {
412 QString strToolbar = vbox.GetExtraData(VBoxDefs::GUI_Toolbar);
413 QString strStatusbar = vbox.GetExtraData(VBoxDefs::GUI_Statusbar);
414
415#ifdef Q_WS_MAC
416 mVMToolBar->setVisible(strToolbar.isEmpty() || strToolbar == "true");
417#else /* Q_WS_MAC */
418 m_pBar->setVisible(strToolbar.isEmpty() || strToolbar == "true");
419#endif /* !Q_WS_MAC */
420 statusBar()->setVisible(strStatusbar.isEmpty() || strStatusbar == "true");
421 }
422
423 /* refresh the details et all (necessary for the case when the stored
424 * selection is still the first list item) */
425 vmListViewCurrentChanged();
426
427 /* signals and slots connections */
428 connect(mFileMediaMgrAction, SIGNAL(triggered()), this, SLOT(fileMediaMgr()));
429 connect(mFileApplianceImportAction, SIGNAL(triggered()), this, SLOT(fileImportAppliance()));
430 connect(mFileApplianceExportAction, SIGNAL(triggered()), this, SLOT(fileExportAppliance()));
431 connect(mFileSettingsAction, SIGNAL(triggered()), this, SLOT(fileSettings()));
432 connect(mFileExitAction, SIGNAL(triggered()), this, SLOT(fileExit()));
433 connect(mVmNewAction, SIGNAL(triggered()), this, SLOT(vmNew()));
434 connect(mVmAddAction, SIGNAL(triggered()), this, SLOT(vmAdd()));
435
436 connect(mVmConfigAction, SIGNAL(triggered()), this, SLOT(vmSettings()));
437 connect(mVmDeleteAction, SIGNAL(triggered()), this, SLOT(vmDelete()));
438 connect(mVmStartAction, SIGNAL(triggered()), this, SLOT(vmStart()));
439 connect(mVmDiscardAction, SIGNAL(triggered()), this, SLOT(vmDiscard()));
440 connect(mVmPauseAction, SIGNAL(toggled(bool)), this, SLOT(vmPause(bool)));
441 connect(mVmRefreshAction, SIGNAL(triggered()), this, SLOT(vmRefresh()));
442 connect(mVmShowLogsAction, SIGNAL(triggered()), this, SLOT(vmShowLogs()));
443 connect(mVmOpenInFileManagerAction, SIGNAL(triggered()), this, SLOT(vmOpenInFileManager()));
444 connect(mVmCreateShortcut, SIGNAL(triggered()), this, SLOT(vmCreateShortcut()));
445
446 connect(mVMListView, SIGNAL(currentChanged()),
447 this, SLOT(vmListViewCurrentChanged()));
448 connect(mVMListView, SIGNAL(activated()),
449 this, SLOT(vmStart()));
450 connect(mVMListView, SIGNAL(customContextMenuRequested(const QPoint &)),
451 this, SLOT(showContextMenu(const QPoint &)));
452
453 connect(m_pVMDesktop, SIGNAL(linkClicked(const QString &)),
454 this, SLOT(vmSettings(const QString &)));
455
456 /* listen to media enumeration signals */
457 connect(&vboxGlobal(), SIGNAL(mediumEnumStarted()),
458 this, SLOT(mediumEnumStarted()));
459 connect(&vboxGlobal(), SIGNAL(mediumEnumFinished(const VBoxMediaList &)),
460 this, SLOT(mediumEnumFinished(const VBoxMediaList &)));
461
462 /* connect VirtualBox events */
463 connect(gVBoxEvents, SIGNAL(sigMachineStateChange(QString, KMachineState)),
464 this, SLOT(machineStateChanged(QString, KMachineState)));
465 connect(gVBoxEvents, SIGNAL(sigMachineDataChange(QString)),
466 this, SLOT(machineDataChanged(QString)));
467 connect(gVBoxEvents, SIGNAL(sigMachineRegistered(QString, bool)),
468 this, SLOT(machineRegistered(QString, bool)));
469 connect(gVBoxEvents, SIGNAL(sigSessionStateChange(QString, KSessionState)),
470 this, SLOT(sessionStateChanged(QString, KSessionState)));
471 connect(gVBoxEvents, SIGNAL(sigSnapshotChange(QString, QString)),
472 this, SLOT(snapshotChanged(QString, QString)));
473#ifdef VBOX_GUI_WITH_SYSTRAY
474 connect(gEDataEvents, SIGNAL(sigMainWindowCountChange(int)),
475 this, SLOT(mainWindowCountChanged(int)));
476 connect(gEDataEvents, SIGNAL(sigCanShowTrayIcon(bool)),
477 this, SLOT(trayIconCanShow(bool)));
478 connect(gEDataEvents, SIGNAL(sigTrayIconChange(bool)),
479 this, SLOT(trayIconChanged(bool)));
480 connect(&vboxGlobal(), SIGNAL(sigTrayIconShow(bool)),
481 this, SLOT(trayIconShow(bool)));
482#endif
483
484 /* Listen to potential downloaders signals: */
485 connect(&vboxProblem(), SIGNAL(sigDownloaderUserManualCreated()), this, SLOT(sltDownloaderUserManualEmbed()));
486
487 /* bring the VM list to the focus */
488 mVMListView->setFocus();
489
490#ifdef Q_WS_MAC
491 UIWindowMenuManager::instance()->addWindow(this);
492 /* Beta label? */
493 if (vboxGlobal().isBeta())
494 {
495 QPixmap betaLabel = ::betaLabelSleeve(QSize(107, 16));
496 ::darwinLabelWindow(this, &betaLabel, false);
497 }
498#endif /* Q_WS_MAC */
499}
500
501VBoxSelectorWnd::~VBoxSelectorWnd()
502{
503 CVirtualBox vbox = vboxGlobal().virtualBox();
504
505 /* Destroy our event handlers */
506 UIVirtualBoxEventHandler::destroy();
507
508 /* Save the position of the window */
509 {
510#if defined(Q_WS_MAC) && (QT_VERSION >= 0x040700)
511 QRect frameGeo = frameGeometry();
512 QRect save(frameGeo.x(), frameGeo.y(), mNormalGeo.width(), mNormalGeo.height());
513#else /* defined(Q_WS_MAC) && (QT_VERSION >= 0x040700) */
514 QRect save(mNormalGeo);
515#endif /* !(defined(Q_WS_MAC) && (QT_VERSION >= 0x040700)) */
516 QString winPos = QString("%1,%2,%3,%4")
517 .arg(save.x()).arg(save.y())
518 .arg(save.width()).arg(save.height());
519#ifdef Q_WS_MAC
520 UIWindowMenuManager::destroy();
521 ::darwinUnregisterForUnifiedToolbarContextMenuEvents(this);
522 if (::darwinIsWindowMaximized(this))
523#else /* Q_WS_MAC */
524 if (isMaximized())
525#endif /* !Q_WS_MAC */
526 winPos += QString(",%1").arg(VBoxDefs::GUI_LastWindowState_Max);
527
528 vbox.SetExtraData(VBoxDefs::GUI_LastWindowPosition, winPos);
529 }
530
531 /* Save VM selector position */
532 {
533 UIVMItem *item = mVMListView->selectedItem();
534 QString curVMId = item ?
535 QString(item->id()) :
536 QString::null;
537 vbox.SetExtraData(VBoxDefs::GUI_LastVMSelected, curVMId);
538 vbox.SetExtraDataStringList(VBoxDefs::GUI_SelectorVMPositions, mVMModel->idList());
539 }
540
541 /* Save the splitter handle position */
542 {
543 vbox.SetExtraDataIntList(VBoxDefs::GUI_SplitterSizes, m_pSplitter->sizes());
544 }
545
546#ifdef VBOX_GUI_WITH_SYSTRAY
547 /* Delete systray menu object */
548 delete mTrayIcon;
549 mTrayIcon = NULL;
550#endif
551
552 /* Delete the items from our model */
553 mVMModel->clear();
554}
555
556//
557// Public slots
558/////////////////////////////////////////////////////////////////////////////
559
560void VBoxSelectorWnd::fileMediaMgr()
561{
562 VBoxMediaManagerDlg::showModeless(this);
563}
564
565void VBoxSelectorWnd::fileImportAppliance(const QString &strFile /* = "" */)
566{
567#ifdef Q_WS_MAC
568 QString strTmpFile = ::darwinResolveAlias(strFile);
569#else /* Q_WS_MAC */
570 QString strTmpFile = strFile;
571#endif /* !Q_WS_MAC */
572 UIImportApplianceWzd wzd(strTmpFile, this);
573
574 if ( strFile.isEmpty()
575 || wzd.isValid())
576 wzd.exec();
577}
578
579void VBoxSelectorWnd::fileExportAppliance()
580{
581 QString name;
582
583 UIVMItem *item = mVMListView->selectedItem();
584 if (item)
585 name = item->name();
586
587 UIExportApplianceWzd wzd(this, name);
588
589 wzd.exec();
590}
591
592void VBoxSelectorWnd::fileSettings()
593{
594 VBoxGlobalSettings settings = vboxGlobal().settings();
595 CSystemProperties props = vboxGlobal().virtualBox().GetSystemProperties();
596
597 UISettingsDialog *dlg = new UIGLSettingsDlg(this);
598 dlg->getFrom();
599
600 if (dlg->exec() == QDialog::Accepted)
601 dlg->putBackTo();
602
603 delete dlg;
604}
605
606void VBoxSelectorWnd::fileExit()
607{
608 /* We have to check if there are any open windows beside this mainwindow
609 * (e.g. VDM) and if so close them. Note that the default behavior is
610 * different to Qt3 where a *mainWidget* exists & if this going to close
611 * all other windows are closed automatically. We do the same below. */
612 foreach(QWidget *widget, QApplication::topLevelWidgets())
613 {
614 if (widget->isVisible() &&
615 widget != this)
616 widget->close();
617 }
618 /* We close this widget last. */
619 close();
620}
621
622void VBoxSelectorWnd::vmNew()
623{
624 UINewVMWzd wzd(this);
625 if (wzd.exec() == QDialog::Accepted)
626 {
627 CMachine m = wzd.machine();
628
629 /* wait until the list is updated by OnMachineRegistered() */
630 QModelIndex index;
631 while(!index.isValid())
632 {
633 qApp->processEvents();
634 index = mVMModel->indexById(m.GetId());
635 }
636 mVMListView->setCurrentIndex(index);
637 }
638}
639
640void VBoxSelectorWnd::vmAdd(const QString &strFile /* = "" */)
641{
642#ifdef Q_WS_MAC
643 QString strTmpFile = ::darwinResolveAlias(strFile);
644#else /* Q_WS_MAC */
645 QString strTmpFile = strFile;
646#endif /* !Q_WS_MAC */
647 /* Initialize variables: */
648 CVirtualBox vbox = vboxGlobal().virtualBox();
649 if (strTmpFile.isEmpty())
650 {
651 QString strBaseFolder = vbox.GetSystemProperties().GetDefaultMachineFolder();
652 QString strTitle = tr("Select a virtual machine file");
653 QStringList extensions;
654 for (int i = 0; i < VBoxDefs::VBoxFileExts.size(); ++i)
655 extensions << QString("*.%1").arg(VBoxDefs::VBoxFileExts[i]);
656 QString strFilter = tr("Virtual machine files (%1)").arg(extensions.join(" "));
657 /* Create open file dialog: */
658 QStringList fileNames = QIFileDialog::getOpenFileNames(strBaseFolder, strFilter, this, strTitle, 0, true, true);
659 if (!fileNames.isEmpty())
660 strTmpFile = fileNames.at(0);
661 }
662 if (!strTmpFile.isEmpty())
663 {
664 /* Open corresponding machine: */
665 CMachine newMachine = vbox.OpenMachine(strTmpFile);
666 /* First we should test what machine was opened: */
667 if (vbox.isOk() && !newMachine.isNull())
668 {
669 /* Second we should check what such machine was NOT already registered.
670 * Actually current Main implementation will even prevent such machine opening
671 * but we will perform such a check anyway: */
672 CMachine oldMachine = vbox.FindMachine(newMachine.GetId());
673 if (oldMachine.isNull())
674 {
675 /* Register that machine: */
676 vbox.RegisterMachine(newMachine);
677
678 /* Wait until the list is updated by OnMachineRegistered(): */
679 QModelIndex index;
680 while(!index.isValid())
681 {
682 qApp->processEvents();
683 index = mVMModel->indexById(newMachine.GetId());
684 }
685 /* And choose the new machine item: */
686 mVMListView->setCurrentIndex(index);
687 }
688 else
689 vboxProblem().cannotReregisterMachine(this, strTmpFile, oldMachine.GetName());
690 }
691 else
692 vboxProblem().cannotOpenMachine(this, strTmpFile, vbox);
693 }
694}
695
696/**
697 * Opens the VM settings dialog.
698 */
699void VBoxSelectorWnd::vmSettings(const QString &aCategory /* = QString::null */,
700 const QString &aControl /* = QString::null */,
701 const QString &aUuid /* = QString::null */)
702{
703 if (!aCategory.isEmpty() && aCategory [0] != '#')
704 {
705 /* Assume it's a href from the Details HTML */
706 vboxGlobal().openURL(aCategory);
707 return;
708 }
709 QString strCategory = aCategory;
710 QString strControl = aControl;
711 /* Maybe the control is coded into the URL by %% */
712 if (aControl == QString::null)
713 {
714 QStringList parts = aCategory.split("%%");
715 if (parts.size() == 2)
716 {
717 strCategory = parts.at(0);
718 strControl = parts.at(1);
719 }
720 }
721
722 UIVMItem *item = aUuid.isNull() ? mVMListView->selectedItem() :
723 mVMModel->itemById(aUuid);
724
725 AssertMsgReturnVoid(item, ("Item must be always selected here"));
726
727 // open a direct session to modify VM settings
728 QString id = item->id();
729 CSession session = vboxGlobal().openSession(id);
730 if (session.isNull())
731 return;
732
733 CMachine m = session.GetMachine();
734 AssertMsgReturn(!m.isNull(), ("Machine must not be null"), (void) 0);
735
736 /* Don't show the inaccessible warning if the user open the vm settings. */
737 mDoneInaccessibleWarningOnce = true;
738
739 UISettingsDialog *dlg = new UIVMSettingsDlg(this, m, strCategory, strControl);
740 dlg->getFrom();
741
742 if (dlg->exec() == QDialog::Accepted)
743 {
744 QString oldName = m.GetName();
745 dlg->putBackTo();
746
747 m.SaveSettings();
748 if (!m.isOk())
749 vboxProblem().cannotSaveMachineSettings(m);
750
751 /* To check use the result in future
752 * vboxProblem().cannotApplyMachineSettings(m, res); */
753 }
754
755 delete dlg;
756
757 mVMListView->setFocus();
758
759 session.UnlockMachine();
760}
761
762void VBoxSelectorWnd::vmDelete(const QString &aUuid /* = QString::null */)
763{
764 UIVMItem *item = aUuid.isNull() ? mVMListView->selectedItem() : mVMModel->itemById(aUuid);
765
766 AssertMsgReturnVoid(item, ("Item must be always selected here"));
767
768 CMachine machine = item->machine();
769 int rc = vboxProblem().confirmMachineDeletion(machine);
770 if (rc != QIMessageBox::Cancel)
771 {
772 if (rc == QIMessageBox::Yes)
773 {
774 /* Unregister and cleanup machine's data & hard-disks: */
775 CMediumVector mediums = machine.Unregister(KCleanupMode_DetachAllReturnHardDisksOnly);
776 if (machine.isOk())
777 {
778 /* Delete machine hard-disks: */
779 CProgress progress = machine.Delete(mediums);
780 if (machine.isOk())
781 {
782 vboxProblem().showModalProgressDialog(progress, item->name(), ":/progress_delete_90px.png", 0, true);
783 if (progress.GetResultCode() != 0)
784 vboxProblem().cannotDeleteMachine(machine);
785 }
786 }
787 if (!machine.isOk())
788 vboxProblem().cannotDeleteMachine(machine);
789 }
790 else
791 {
792 /* Just unregister machine: */
793 machine.Unregister(KCleanupMode_DetachAllReturnNone);
794 if (!machine.isOk())
795 vboxProblem().cannotDeleteMachine(machine);
796 }
797 }
798}
799
800void VBoxSelectorWnd::vmStart(const QString &aUuid /* = QString::null */)
801{
802 QUuid uuid(aUuid);
803 UIVMItem *item = uuid.isNull() ? mVMListView->selectedItem() :
804 mVMModel->itemById(aUuid);
805
806 AssertMsgReturnVoid(item, ("Item must be always selected here"));
807
808 /* Are we called from the mVMListView's activated() signal? */
809 if (uuid.isNull())
810 {
811 /* We always get here when mVMListView emits the activated() signal,
812 * so we must explicitly check if the action is enabled or not. */
813 if (!mVmStartAction->isEnabled())
814 return;
815 }
816
817 AssertMsg(!vboxGlobal().isVMConsoleProcess(),
818 ("Must NOT be a VM console process"));
819
820 CMachine machine = item->machine();
821 vboxGlobal().launchMachine(machine);
822}
823
824void VBoxSelectorWnd::vmDiscard(const QString &aUuid /* = QString::null */)
825{
826 UIVMItem *item = aUuid.isNull() ? mVMListView->selectedItem() :
827 mVMModel->itemById(aUuid);
828
829 AssertMsgReturnVoid(item, ("Item must be always selected here"));
830
831 if (!vboxProblem().confirmDiscardSavedState(item->machine()))
832 return;
833
834 /* open a session to modify VM settings */
835 QString id = item->id();
836 CSession session;
837 CVirtualBox vbox = vboxGlobal().virtualBox();
838 session.createInstance(CLSID_Session);
839 if (session.isNull())
840 {
841 vboxProblem().cannotOpenSession(session);
842 return;
843 }
844
845 CMachine foundMachine = vbox.FindMachine(id);
846 if (!foundMachine.isNull())
847 foundMachine.LockMachine(session, KLockType_Write);
848 if (!vbox.isOk())
849 {
850 vboxProblem().cannotOpenSession(vbox, item->machine());
851 return;
852 }
853
854 CConsole console = session.GetConsole();
855 console.DiscardSavedState(true /* fDeleteFile */);
856 if (!console.isOk())
857 vboxProblem().cannotDiscardSavedState(console);
858
859 session.UnlockMachine();
860}
861
862void VBoxSelectorWnd::vmPause(bool aPause, const QString &aUuid /* = QString::null */)
863{
864 UIVMItem *item = aUuid.isNull() ? mVMListView->selectedItem() :
865 mVMModel->itemById(aUuid);
866
867 AssertMsgReturnVoid(item, ("Item must be always selected here"));
868
869 CSession session = vboxGlobal().openExistingSession(item->id());
870 if (session.isNull())
871 return;
872
873 CConsole console = session.GetConsole();
874 if (console.isNull())
875 return;
876
877 if (aPause)
878 console.Pause();
879 else
880 console.Resume();
881
882 bool ok = console.isOk();
883 if (!ok)
884 {
885 if (aPause)
886 vboxProblem().cannotPauseMachine(console);
887 else
888 vboxProblem().cannotResumeMachine(console);
889 }
890
891 session.UnlockMachine();
892}
893
894void VBoxSelectorWnd::vmRefresh(const QString &aUuid /* = QString::null */)
895{
896 UIVMItem *item = aUuid.isNull() ? mVMListView->selectedItem() :
897 mVMModel->itemById(aUuid);
898
899 AssertMsgReturnVoid(item, ("Item must be always selected here"));
900
901 refreshVMItem(item->id(),
902 true /* aDetails */,
903 true /* aSnapshot */,
904 true /* aDescription */);
905}
906
907void VBoxSelectorWnd::vmShowLogs(const QString &aUuid /* = QString::null */)
908{
909 UIVMItem *item = aUuid.isNull() ? mVMListView->selectedItem() :
910 mVMModel->itemById(aUuid);
911
912 AssertMsgReturnVoid(item, ("Item must be always selected here"));
913
914 CMachine machine = item->machine();
915 VBoxVMLogViewer::createLogViewer(this, machine);
916}
917
918void VBoxSelectorWnd::vmOpenInFileManager(const QString &aUuid /* = QString::null */)
919{
920 UIVMItem *item = aUuid.isNull() ? mVMListView->selectedItem() :
921 mVMModel->itemById(aUuid);
922
923 AssertMsgReturnVoid(item, ("Item must be always selected here"));
924
925 CMachine machine = item->machine();
926 UIDesktopServices::openInFileManager(machine.GetSettingsFilePath());
927}
928
929void VBoxSelectorWnd::vmCreateShortcut(const QString &aUuid /* = QString::null */)
930{
931 UIVMItem *item = aUuid.isNull() ? mVMListView->selectedItem() :
932 mVMModel->itemById(aUuid);
933
934 AssertMsgReturnVoid(item, ("Item must be always selected here"));
935
936 CMachine machine = item->machine();
937 UIDesktopServices::createMachineShortcut(machine.GetSettingsFilePath(), QDesktopServices::storageLocation(QDesktopServices::DesktopLocation), machine.GetName(), machine.GetId());
938}
939
940void VBoxSelectorWnd::refreshVMList()
941{
942 CVirtualBox vbox = vboxGlobal().virtualBox();
943 CMachineVector vec = vbox.GetMachines();
944 for (CMachineVector::ConstIterator m = vec.begin();
945 m != vec.end(); ++m)
946 mVMModel->addItem(*m);
947 /* Apply the saved sort order. */
948 mVMModel->sortByIdList(vbox.GetExtraDataStringList(VBoxDefs::GUI_SelectorVMPositions));
949 /* Update details page. */
950 vmListViewCurrentChanged();
951
952#ifdef VBOX_GUI_WITH_SYSTRAY
953 if (vboxGlobal().isTrayMenu())
954 mTrayIcon->refresh();
955#endif
956}
957
958void VBoxSelectorWnd::refreshVMItem(const QString &aID,
959 bool aDetails,
960 bool aSnapshots,
961 bool aDescription)
962{
963 UIVMItem *item = mVMModel->itemById(aID);
964 if (item)
965 {
966 mVMModel->refreshItem(item);
967 if (item && item->id() == aID)
968 vmListViewCurrentChanged(aDetails, aSnapshots, aDescription);
969 }
970}
971
972void VBoxSelectorWnd::showContextMenu(const QPoint &aPoint)
973{
974 /* Send a context menu request */
975 const QModelIndex &index = mVMListView->indexAt(aPoint);
976 if (index.isValid())
977 if (mVMListView->model()->data(index,
978 UIVMItemModel::UIVMItemPtrRole).value <UIVMItem*>())
979 mVMCtxtMenu->exec(mVMListView->mapToGlobal(aPoint));
980}
981
982
983void VBoxSelectorWnd::sltOpenUrls(QList<QUrl> list /* = QList<QUrl>() */)
984{
985 /* Make sure any pending D&D events are consumed. */
986 qApp->processEvents();
987 if (list.isEmpty())
988 {
989 list = vboxGlobal().argUrlList();
990 vboxGlobal().argUrlList().clear();
991 }
992 /* Check if we are can handle the dropped urls. */
993 for (int i = 0; i < list.size(); ++i)
994 {
995#ifdef Q_WS_MAC
996 QString strFile = ::darwinResolveAlias(list.at(i).toLocalFile());
997#else /* Q_WS_MAC */
998 QString strFile = list.at(i).toLocalFile();
999#endif /* !Q_WS_MAC */
1000 if ( !strFile.isEmpty()
1001 && QFile::exists(strFile))
1002 {
1003 if (VBoxGlobal::hasAllowedExtension(strFile, VBoxDefs::VBoxFileExts))
1004 {
1005 /* VBox config files. */
1006 CVirtualBox vbox = vboxGlobal().virtualBox();
1007 CMachine machine = vbox.FindMachine(strFile);
1008 if (!machine.isNull())
1009 {
1010 CVirtualBox vbox = vboxGlobal().virtualBox();
1011 CMachine machine = vbox.FindMachine(strFile);
1012 if (!machine.isNull())
1013 vboxGlobal().launchMachine(machine);
1014 }else
1015 vmAdd(strFile);
1016 }
1017 else if (VBoxGlobal::hasAllowedExtension(strFile, VBoxDefs::OVFFileExts))
1018 {
1019 /* OVF/OVA. Only one file at the time. */
1020 fileImportAppliance(strFile);
1021 break;
1022 }
1023 else if (VBoxGlobal::hasAllowedExtension(strFile, VBoxDefs::VBoxExtPackFileExts))
1024 {
1025 UIGlobalSettingsExtension::doInstallation(strFile, this, NULL);
1026 }
1027 }
1028 }
1029}
1030
1031#ifdef VBOX_GUI_WITH_SYSTRAY
1032
1033void VBoxSelectorWnd::trayIconActivated(QSystemTrayIcon::ActivationReason aReason)
1034{
1035 switch (aReason)
1036 {
1037 case QSystemTrayIcon::Context:
1038
1039 mTrayIcon->refresh();
1040 break;
1041
1042 case QSystemTrayIcon::Trigger:
1043 break;
1044
1045 case QSystemTrayIcon::DoubleClick:
1046
1047 vboxGlobal().trayIconShowSelector();
1048 break;
1049
1050 case QSystemTrayIcon::MiddleClick:
1051 break;
1052
1053 default:
1054 break;
1055 }
1056}
1057
1058void VBoxSelectorWnd::showWindow()
1059{
1060 showNormal();
1061 raise();
1062 activateWindow();
1063}
1064
1065#endif // VBOX_GUI_WITH_SYSTRAY
1066
1067// Protected members
1068/////////////////////////////////////////////////////////////////////////////
1069
1070bool VBoxSelectorWnd::event(QEvent *e)
1071{
1072 switch (e->type())
1073 {
1074 /* By handling every Resize and Move we keep track of the normal
1075 * (non-minimized and non-maximized) window geometry. Shame on Qt
1076 * that it doesn't provide this geometry in its public APIs. */
1077
1078 case QEvent::Resize:
1079 {
1080 QResizeEvent *re = (QResizeEvent *) e;
1081 if ((windowState() & (Qt::WindowMaximized | Qt::WindowMinimized |
1082 Qt::WindowFullScreen)) == 0)
1083 mNormalGeo.setSize(re->size());
1084 break;
1085 }
1086 case QEvent::Move:
1087 {
1088 if ((windowState() & (Qt::WindowMaximized | Qt::WindowMinimized |
1089 Qt::WindowFullScreen)) == 0)
1090 mNormalGeo.moveTo(geometry().x(), geometry().y());
1091 break;
1092 }
1093 case QEvent::WindowDeactivate:
1094 {
1095 /* Make sure every status bar hint is cleared when the window lost
1096 * focus. */
1097 statusBar()->clearMessage();
1098 break;
1099 }
1100#ifdef Q_WS_MAC
1101 case QEvent::ContextMenu:
1102 {
1103 /* This is the unified context menu event. Lets show the context
1104 * menu. */
1105 QContextMenuEvent *pCE = static_cast<QContextMenuEvent*>(e);
1106 showViewContextMenu(pCE->globalPos());
1107 /* Accept it to interrupt the chain. */
1108 pCE->accept();
1109 return false;
1110 break;
1111 }
1112 case QEvent::ToolBarChange:
1113 {
1114 CVirtualBox vbox = vboxGlobal().virtualBox();
1115 /* We have to invert the isVisible check one time, cause this event
1116 * is sent *before* the real toggle is done. Really intuitive
1117 * Trolls. */
1118 vbox.SetExtraData(VBoxDefs::GUI_Toolbar, !::darwinIsToolbarVisible(mVMToolBar) ? "true" : "false");
1119 break;
1120 }
1121#endif /* Q_WS_MAC */
1122 default:
1123 break;
1124 }
1125
1126 return QMainWindow::event(e);
1127}
1128
1129void VBoxSelectorWnd::closeEvent(QCloseEvent *aEvent)
1130{
1131#ifdef VBOX_GUI_WITH_SYSTRAY
1132 /* Needed for breaking out of the while() loop in main(). */
1133 if (vboxGlobal().isTrayMenu())
1134 vboxGlobal().setTrayMenu(false);
1135#endif
1136
1137 emit closing();
1138 QMainWindow::closeEvent(aEvent);
1139}
1140
1141#ifdef Q_WS_MAC
1142bool VBoxSelectorWnd::eventFilter(QObject *pObject, QEvent *pEvent)
1143{
1144 if (!isActiveWindow())
1145 return QIWithRetranslateUI2<QMainWindow>::eventFilter(pObject, pEvent);
1146
1147 if (qobject_cast<QWidget*>(pObject) &&
1148 qobject_cast<QWidget*>(pObject)->window() != this)
1149 return QIWithRetranslateUI2<QMainWindow>::eventFilter(pObject, pEvent);
1150
1151 switch (pEvent->type())
1152 {
1153 case QEvent::FileOpen:
1154 {
1155 sltOpenUrls(QList<QUrl>() << static_cast<QFileOpenEvent*>(pEvent)->file());
1156 pEvent->accept();
1157 return true;
1158 break;
1159 }
1160# if (QT_VERSION < 0x040402)
1161 case QEvent::KeyPress:
1162 {
1163 /* Bug in Qt below 4.4.2. The key events are send to the current
1164 * window even if a menu is shown & has the focus. See
1165 * http://trolltech.com/developer/task-tracker/index_html?method=entry&id=214681. */
1166 if (::darwinIsMenuOpen())
1167 return true;
1168 break;
1169 }
1170# endif
1171 default:
1172 break;
1173 }
1174 return QIWithRetranslateUI2<QMainWindow>::eventFilter(pObject, pEvent);
1175}
1176#endif /* Q_WS_MAC */
1177
1178/**
1179 * Sets the strings of the subwidgets using the current
1180 * language.
1181 */
1182void VBoxSelectorWnd::retranslateUi()
1183{
1184#ifdef VBOX_OSE
1185 QString title(tr("VirtualBox OSE"));
1186#else
1187 QString title(VBOX_PRODUCT);
1188#endif
1189 title += " " + tr("Manager", "Note: main window title which is pretended by the product name.");
1190
1191#ifdef VBOX_BLEEDING_EDGE
1192 title += QString(" EXPERIMENTAL build ")
1193 + QString(RTBldCfgVersion())
1194 + QString(" r")
1195 + QString(RTBldCfgRevisionStr())
1196 + QString(" - "VBOX_BLEEDING_EDGE);
1197#endif
1198
1199 setWindowTitle(title);
1200
1201 /* ensure the details and screenshot view are updated */
1202 vmListViewCurrentChanged();
1203
1204 mFileMediaMgrAction->setText(tr("&Virtual Media Manager..."));
1205 mFileMediaMgrAction->setShortcut(gSS->keySequence(UISelectorShortcuts::VirtualMediaManagerShortcut));
1206 mFileMediaMgrAction->setStatusTip(tr("Display the Virtual Media Manager dialog"));
1207
1208 mFileApplianceImportAction->setText(tr("&Import Appliance..."));
1209 mFileApplianceImportAction->setShortcut(gSS->keySequence(UISelectorShortcuts::ImportApplianceShortcut));
1210 mFileApplianceImportAction->setStatusTip(tr("Import an appliance into VirtualBox"));
1211
1212 mFileApplianceExportAction->setText(tr("&Export Appliance..."));
1213 mFileApplianceExportAction->setShortcut(gSS->keySequence(UISelectorShortcuts::ExportApplianceShortcut));
1214 mFileApplianceExportAction->setStatusTip(tr("Export one or more VirtualBox virtual machines as an appliance"));
1215
1216 mFileSettingsAction->setText(tr("&Preferences...", "global settings"));
1217 mFileSettingsAction->setShortcut(gSS->keySequence(UISelectorShortcuts::PreferencesShortcut));
1218 mFileSettingsAction->setStatusTip(tr("Display the global settings dialog"));
1219
1220 mFileExitAction->setText(tr("E&xit"));
1221 mFileExitAction->setShortcut(gSS->keySequence(UISelectorShortcuts::ExitShortcut));
1222 mFileExitAction->setStatusTip(tr("Close application"));
1223
1224 mVmNewAction->setText(tr("&New..."));
1225 mVmNewAction->setShortcut(gSS->keySequence(UISelectorShortcuts::NewVMShortcut));
1226 mVmNewAction->setStatusTip(tr("Create a new virtual machine"));
1227 mVmNewAction->setToolTip(mVmNewAction->text().remove('&').remove('.') +
1228 (mVmNewAction->shortcut().toString().isEmpty() ? "" : QString(" (%1)").arg(mVmNewAction->shortcut().toString())));
1229
1230 mVmAddAction->setText(tr("&Add..."));
1231 mVmAddAction->setShortcut(gSS->keySequence(UISelectorShortcuts::AddVMShortcut));
1232 mVmAddAction->setStatusTip(tr("Add an existing virtual machine"));
1233
1234 mVmConfigAction->setText(tr("&Settings..."));
1235 mVmConfigAction->setShortcut(gSS->keySequence(UISelectorShortcuts::SettingsVMShortcut));
1236 mVmConfigAction->setStatusTip(tr("Configure the selected virtual machine"));
1237 mVmConfigAction->setToolTip(mVmConfigAction->text().remove('&').remove('.') +
1238 (mVmConfigAction->shortcut().toString().isEmpty() ? "" : QString(" (%1)").arg(mVmConfigAction->shortcut().toString())));
1239
1240 mVmDeleteAction->setText(tr("&Remove"));
1241 mVmDeleteAction->setShortcut(gSS->keySequence(UISelectorShortcuts::RemoveVMShortcut));
1242 mVmDeleteAction->setStatusTip(tr("Remove the selected virtual machine"));
1243
1244 /* Note: mVmStartAction text is set up in vmListViewCurrentChanged() */
1245
1246 mVmDiscardAction->setText(tr("D&iscard"));
1247 mVmDiscardAction->setShortcut(gSS->keySequence(UISelectorShortcuts::DiscardVMShortcut));
1248 mVmDiscardAction->setStatusTip(
1249 tr("Discard the saved state of the selected virtual machine"));
1250 mVmDiscardAction->setToolTip(mVmDiscardAction->text().remove('&').remove('.') +
1251 (mVmDiscardAction->shortcut().toString().isEmpty() ? "" : QString(" (%1)").arg(mVmDiscardAction->shortcut().toString())));
1252
1253 mVmPauseAction->setText(tr("&Pause"));
1254 mVmPauseAction->setStatusTip(
1255 tr("Suspend the execution of the virtual machine"));
1256
1257 mVmRefreshAction->setText(tr("Re&fresh"));
1258 mVmRefreshAction->setShortcut(gSS->keySequence(UISelectorShortcuts::RefreshVMShortcut));
1259 mVmRefreshAction->setStatusTip(
1260 tr("Refresh the accessibility state of the selected virtual machine"));
1261
1262 mVmShowLogsAction->setText(tr("Show &Log..."));
1263 mVmShowLogsAction->setIconText(tr("Log", "icon text"));
1264 mVmShowLogsAction->setShortcut(gSS->keySequence(UISelectorShortcuts::ShowVMLogShortcut));
1265 mVmShowLogsAction->setStatusTip(
1266 tr("Show the log files of the selected virtual machine"));
1267
1268#if defined(Q_WS_MAC)
1269 mVmOpenInFileManagerAction->setText(tr("Show in Finder"));
1270 mVmOpenInFileManagerAction->setStatusTip(tr("Show the VirtualBox Machine Definition file in Finder."));
1271 mVmCreateShortcut->setText(tr("Create Alias on Desktop"));
1272 mVmCreateShortcut->setStatusTip(tr("Creates an Alias file to the VirtualBox Machine Definition file on your Desktop."));
1273#elif defined(Q_WS_WIN)
1274 mVmOpenInFileManagerAction->setText(tr("Show in Explorer"));
1275 mVmOpenInFileManagerAction->setStatusTip(tr("Show the VirtualBox Machine Definition file in Explorer."));
1276 mVmCreateShortcut->setText(tr("Create Shortcut on Desktop"));
1277 mVmCreateShortcut->setStatusTip(tr("Creates an Shortcut file to the VirtualBox Machine Definition file on your Desktop."));
1278#else
1279 mVmOpenInFileManagerAction->setText(tr("Show in File Manager"));
1280 mVmOpenInFileManagerAction->setStatusTip(tr("Show the VirtualBox Machine Definition file in the File Manager"));
1281 mVmCreateShortcut->setText(tr("Create Shortcut on Desktop"));
1282 mVmCreateShortcut->setStatusTip(tr("Creates an Shortcut file to the VirtualBox Machine Definition file on your Desktop."));
1283#endif
1284 mVmOpenInFileManagerAction->setShortcut(gSS->keySequence(UISelectorShortcuts::ShowVMInFileManagerShortcut));
1285 mVmCreateShortcut->setShortcut(gSS->keySequence(UISelectorShortcuts::CreateVMAliasShortcut));
1286
1287 mHelpActions.retranslateUi();
1288
1289#ifdef Q_WS_MAC
1290 mFileMenu->setTitle(tr("&File", "Mac OS X version"));
1291#else /* Q_WS_MAC */
1292 mFileMenu->setTitle(tr("&File", "Non Mac OS X version"));
1293#endif /* !Q_WS_MAC */
1294 mVMMenu->setTitle(tr("&Machine"));
1295 mHelpMenu->setTitle(tr("&Help"));
1296
1297#ifdef VBOX_GUI_WITH_SYSTRAY
1298 if (vboxGlobal().isTrayMenu())
1299 {
1300 mTrayIcon->retranslateUi();
1301 mTrayIcon->refresh();
1302 }
1303#endif
1304
1305#ifdef QT_MAC_USE_COCOA
1306 /* There is a bug in Qt Cocoa which result in showing a "more arrow" when
1307 the necessary size of the toolbar is increased. Also for some languages
1308 the with doesn't match if the text increase. So manually adjust the size
1309 after changing the text. */
1310 mVMToolBar->updateLayout();
1311#endif /* QT_MAC_USE_COCOA */
1312}
1313
1314
1315// Private members
1316/////////////////////////////////////////////////////////////////////////////
1317
1318//
1319// Private slots
1320/////////////////////////////////////////////////////////////////////////////
1321
1322void VBoxSelectorWnd::vmListViewCurrentChanged(bool aRefreshDetails,
1323 bool aRefreshSnapshots,
1324 bool aRefreshDescription)
1325{
1326 UIVMItem *item = mVMListView->selectedItem();
1327
1328 if (item && item->accessible())
1329 {
1330 CMachine m = item->machine();
1331
1332 KMachineState state = item->machineState();
1333 bool running = item->sessionState() != KSessionState_Unlocked;
1334 bool modifyEnabled = !running && state != KMachineState_Saved;
1335
1336 if ( aRefreshDetails
1337 || aRefreshDescription)
1338 m_pVMDesktop->updateDetails(item, m);
1339 if (aRefreshSnapshots)
1340 m_pVMDesktop->updateSnapshots(item, m);
1341// if (aRefreshDescription)
1342// m_pVMDesktop->updateDescription(item, m);
1343
1344 /* enable/disable modify actions */
1345 mVmConfigAction->setEnabled(modifyEnabled);
1346 mVmDeleteAction->setEnabled(!running);
1347 mVmDiscardAction->setEnabled(state == KMachineState_Saved && !running);
1348 mVmPauseAction->setEnabled( state == KMachineState_Running
1349 || state == KMachineState_Teleporting
1350 || state == KMachineState_LiveSnapshotting
1351 || state == KMachineState_Paused
1352 || state == KMachineState_TeleportingPausedVM /** @todo Live Migration: does this make sense? */
1353 );
1354
1355 /* change the Start button text accordingly */
1356 if ( state == KMachineState_PoweredOff
1357 || state == KMachineState_Saved
1358 || state == KMachineState_Teleported
1359 || state == KMachineState_Aborted
1360 )
1361 {
1362 mVmStartAction->setText(tr("S&tart"));
1363 mVmStartAction->setShortcut(gSS->keySequence(UISelectorShortcuts::StartVMShortcut));
1364 mVmStartAction->setToolTip(mVmStartAction->text().remove('&').remove('.') +
1365 (mVmStartAction->shortcut().toString().isEmpty() ? "" : QString(" (%1)").arg(mVmStartAction->shortcut().toString())));
1366#ifdef QT_MAC_USE_COCOA
1367 /* There is a bug in Qt Cocoa which result in showing a "more arrow" when
1368 the necessary size of the toolbar is increased. Also for some languages
1369 the with doesn't match if the text increase. So manually adjust the size
1370 after changing the text. */
1371 mVMToolBar->updateLayout();
1372#endif /* QT_MAC_USE_COCOA */
1373 mVmStartAction->setStatusTip(
1374 tr("Start the selected virtual machine"));
1375
1376 mVmStartAction->setEnabled(!running);
1377 }
1378 else
1379 {
1380 mVmStartAction->setText(tr("S&how"));
1381 mVmStartAction->setShortcut(gSS->keySequence(UISelectorShortcuts::StartVMShortcut));
1382 mVmStartAction->setToolTip(mVmStartAction->text().remove('&').remove('.') +
1383 (mVmStartAction->shortcut().toString().isEmpty() ? "" : QString(" (%1)").arg(mVmStartAction->shortcut().toString())));
1384#ifdef QT_MAC_USE_COCOA
1385 /* There is a bug in Qt Cocoa which result in showing a "more arrow" when
1386 the necessary size of the toolbar is increased. Also for some languages
1387 the with doesn't match if the text increase. So manually adjust the size
1388 after changing the text. */
1389 mVMToolBar->updateLayout();
1390#endif /* QT_MAC_USE_COCOA */
1391 mVmStartAction->setStatusTip(
1392 tr("Switch to the window of the selected virtual machine"));
1393
1394 mVmStartAction->setEnabled(item->canSwitchTo());
1395 }
1396
1397 /* change the Pause/Resume button text accordingly */
1398 if ( state == KMachineState_Paused
1399 || state == KMachineState_TeleportingPausedVM /*?*/
1400 )
1401 {
1402 mVmPauseAction->setText(tr("R&esume"));
1403 mVmPauseAction->setShortcut(gSS->keySequence(UISelectorShortcuts::PauseVMShortcut));
1404 mVmPauseAction->setStatusTip(
1405 tr("Resume the execution of the virtual machine"));
1406 mVmPauseAction->blockSignals(true);
1407 mVmPauseAction->setChecked(true);
1408 mVmPauseAction->blockSignals(false);
1409 }
1410 else
1411 {
1412 mVmPauseAction->setText(tr("&Pause"));
1413 mVmPauseAction->setShortcut(gSS->keySequence(UISelectorShortcuts::PauseVMShortcut));
1414 mVmPauseAction->setStatusTip(
1415 tr("Suspend the execution of the virtual machine"));
1416 mVmPauseAction->blockSignals(true);
1417 mVmPauseAction->setChecked(false);
1418 mVmPauseAction->blockSignals(false);
1419 }
1420
1421 /* disable Refresh for accessible machines */
1422 mVmRefreshAction->setEnabled(false);
1423
1424 /* enable the show log item for the selected vm */
1425 mVmShowLogsAction->setEnabled(true);
1426 /* Enable the shell interaction features. */
1427 mVmOpenInFileManagerAction->setEnabled(true);
1428#ifdef Q_WS_MAC
1429 /* On Mac OS X this are real alias files, which don't work with the old
1430 * legacy xml files. On the other OS's some kind of start up script is
1431 * used. */
1432 mVmCreateShortcut->setEnabled(item->settingsFile().endsWith(".vbox", Qt::CaseInsensitive));
1433#else /* Q_WS_MAC */
1434 mVmCreateShortcut->setEnabled(true);
1435#endif /* Q_WS_MAC */
1436 }
1437 else
1438 {
1439 /* Note that the machine becomes inaccessible (or if the last VM gets
1440 * deleted), we have to update all fields, ignoring input
1441 * arguments. */
1442
1443 if (item)
1444 {
1445 /* the VM is inaccessible */
1446 m_pVMDesktop->updateDetailsErrorText(
1447 VBoxProblemReporter::formatErrorInfo(item->accessError()));
1448 mVmRefreshAction->setEnabled(true);
1449 }
1450 else
1451 {
1452 /* default HTML support in Qt is terrible so just try to get
1453 * something really simple */
1454 m_pVMDesktop->updateDetailsText(
1455 tr("<h3>"
1456 "Welcome to VirtualBox!</h3>"
1457 "<p>The left part of this window is "
1458 "a list of all virtual machines on your computer. "
1459 "The list is empty now because you haven't created any virtual "
1460 "machines yet."
1461 "<img src=:/welcome.png align=right/></p>"
1462 "<p>In order to create a new virtual machine, press the "
1463 "<b>New</b> button in the main tool bar located "
1464 "at the top of the window.</p>"
1465 "<p>You can press the <b>%1</b> key to get instant help, "
1466 "or visit "
1467 "<a href=http://www.virtualbox.org>www.virtualbox.org</a> "
1468 "for the latest information and news.</p>").arg(QKeySequence(QKeySequence::HelpContents).toString(QKeySequence::NativeText)));
1469 mVmRefreshAction->setEnabled(false);
1470 }
1471
1472 /* empty and disable other tabs */
1473 m_pVMDesktop->updateSnapshots(0, CMachine());
1474// m_pVMDesktop->updateDescription(0, CMachine());
1475
1476 /* disable modify actions */
1477 mVmConfigAction->setEnabled(false);
1478 mVmDeleteAction->setEnabled(item != NULL);
1479 mVmDiscardAction->setEnabled(false);
1480 mVmPauseAction->setEnabled(false);
1481
1482 /* change the Start button text accordingly */
1483 mVmStartAction->setText(tr("S&tart"));
1484 mVmStartAction->setStatusTip(
1485 tr("Start the selected virtual machine"));
1486 mVmStartAction->setEnabled(false);
1487
1488 /* disable the show log item for the selected vm */
1489 mVmShowLogsAction->setEnabled(false);
1490 /* Disable the shell interaction features. */
1491 mVmOpenInFileManagerAction->setEnabled(false);
1492 mVmCreateShortcut->setEnabled(false);
1493 }
1494}
1495
1496void VBoxSelectorWnd::mediumEnumStarted()
1497{
1498 /* refresh the current details to pick up hard disk sizes */
1499 vmListViewCurrentChanged(true /* aRefreshDetails */);
1500}
1501
1502void VBoxSelectorWnd::mediumEnumFinished(const VBoxMediaList &list)
1503{
1504 /* refresh the current details to pick up hard disk sizes */
1505 vmListViewCurrentChanged(true /* aRefreshDetails */);
1506
1507 /* we warn about inaccessible media only once (after media emumeration
1508 * started from main() at startup), to avoid annoying the user */
1509 if ( mDoneInaccessibleWarningOnce
1510#ifdef VBOX_GUI_WITH_SYSTRAY
1511 || vboxGlobal().isTrayMenu()
1512#endif
1513 )
1514 return;
1515
1516 mDoneInaccessibleWarningOnce = true;
1517
1518 do
1519 {
1520 /* ignore the signal if a modal widget is currently active (we won't be
1521 * able to properly show the modeless VDI manager window in this case) */
1522 if (QApplication::activeModalWidget())
1523 break;
1524
1525 /* ignore the signal if a VBoxMediaManagerDlg window is active */
1526 if (qApp->activeWindow() &&
1527 !strcmp(qApp->activeWindow()->metaObject()->className(), "VBoxMediaManagerDlg"))
1528 break;
1529
1530 /* look for at least one inaccessible media */
1531 VBoxMediaList::const_iterator it;
1532 for (it = list.begin(); it != list.end(); ++ it)
1533 if ((*it).state() == KMediumState_Inaccessible)
1534 break;
1535
1536 if (it != list.end() && vboxProblem().remindAboutInaccessibleMedia())
1537 {
1538 /* Show the VDM dialog but don't refresh once more after a
1539 * just-finished refresh */
1540 VBoxMediaManagerDlg::showModeless(this, false /* aRefresh */);
1541 }
1542 }
1543 while (0);
1544}
1545
1546void VBoxSelectorWnd::machineStateChanged(QString strId, KMachineState /* state */)
1547{
1548#ifdef VBOX_GUI_WITH_SYSTRAY
1549 if (vboxGlobal().isTrayMenu())
1550 {
1551 /* Check if there are some machines alive - else quit, since
1552 * we're not needed as a systray menu anymore. */
1553 if (vboxGlobal().mainWindowCount() == 0)
1554 {
1555 fileExit();
1556 return;
1557 }
1558 }
1559#endif
1560
1561 refreshVMItem(strId,
1562 false /* aDetails */,
1563 false /* aSnapshots */,
1564 false /* aDescription */);
1565
1566 /* simulate a state change signal */
1567// m_pVMDesktop->updateDescriptionState();
1568}
1569
1570void VBoxSelectorWnd::machineDataChanged(QString strId)
1571{
1572 refreshVMItem(strId,
1573 true /* aDetails */,
1574 false /* aSnapshots */,
1575 true /* aDescription */);
1576}
1577
1578void VBoxSelectorWnd::machineRegistered(QString strId, bool fRegistered)
1579{
1580 if (fRegistered)
1581 {
1582 CVirtualBox vbox = vboxGlobal().virtualBox();
1583 CMachine m = vbox.FindMachine(strId);
1584 if (!m.isNull())
1585 {
1586 mVMModel->addItem(m);
1587 /* Make sure the description, ... pages are properly updated.
1588 * Actually we haven't call the next method, but unfortunately Qt
1589 * seems buggy if the new item is on the same position as the
1590 * previous one. So go on the safe side and call this by our self. */
1591 vmListViewCurrentChanged();
1592 }
1593 /* m.isNull() is ok (theoretically, the machine could have been
1594 * already deregistered by some other client at this point) */
1595 }
1596 else
1597 {
1598 UIVMItem *item = mVMModel->itemById(strId);
1599 if (item)
1600 {
1601 int row = mVMModel->rowById(item->id());
1602 mVMModel->removeItem(item);
1603 mVMListView->ensureSomeRowSelected(row);
1604 }
1605
1606 /* item = 0 is ok (if we originated this event then the item
1607 * has been already removed) */
1608 }
1609}
1610
1611void VBoxSelectorWnd::sessionStateChanged(QString strId, KSessionState /* state */)
1612{
1613 refreshVMItem(strId,
1614 true /* aDetails */,
1615 false /* aSnapshots */,
1616 false /* aDescription */);
1617
1618 /* simulate a state change signal */
1619// m_pVMDesktop->updateDescriptionState();
1620}
1621
1622void VBoxSelectorWnd::snapshotChanged(QString strId, QString /* strSnapshotId */)
1623{
1624 refreshVMItem(strId,
1625 false /* aDetails */,
1626 true /* aSnapshot */,
1627 false /* aDescription */);
1628}
1629
1630#ifdef VBOX_GUI_WITH_SYSTRAY
1631
1632void VBoxSelectorWnd::mainWindowCountChanged(int count)
1633{
1634 if (vboxGlobal().isTrayMenu() && count <= 1)
1635 fileExit();
1636}
1637
1638void VBoxSelectorWnd::trayIconCanShow(bool fEnabled)
1639{
1640 emit trayIconChanged(VBoxChangeTrayIconEvent(vboxGlobal().settings().trayIconEnabled()));
1641}
1642
1643void VBoxSelectorWnd::trayIconShow(bool fEnabled)
1644{
1645 if (vboxGlobal().isTrayMenu() && mTrayIcon)
1646 mTrayIcon->trayIconShow(fEnabled);
1647}
1648
1649void VBoxSelectorWnd::trayIconChanged(bool fEnabled)
1650{
1651 /* Not used yet. */
1652}
1653
1654#endif /* VBOX_GUI_WITH_SYSTRAY */
1655
1656void VBoxSelectorWnd::sltDownloaderUserManualEmbed()
1657{
1658 /* If there is User Manual downloader created => show the process bar: */
1659 if (UIDownloaderUserManual *pDl = UIDownloaderUserManual::current())
1660 statusBar()->addWidget(pDl->processWidget(this), 0);
1661}
1662
1663void VBoxSelectorWnd::showViewContextMenu(const QPoint &pos)
1664{
1665 CVirtualBox vbox = vboxGlobal().virtualBox();
1666 QString strToolbar = vbox.GetExtraData(VBoxDefs::GUI_Toolbar);
1667 QString strStatusbar = vbox.GetExtraData(VBoxDefs::GUI_Statusbar);
1668 bool fToolbar = strToolbar.isEmpty() || strToolbar == "true";
1669 bool fStatusbar = strStatusbar.isEmpty() || strStatusbar == "true";
1670
1671 QList<QAction*> actions;
1672 QAction *pShowToolBar = new QAction(tr("Show Toolbar"), 0);
1673 pShowToolBar->setCheckable(true);
1674 pShowToolBar->setChecked(fToolbar);
1675 actions << pShowToolBar;
1676 QAction *pShowStatusBar = new QAction(tr("Show Statusbar"), 0);
1677 pShowStatusBar->setCheckable(true);
1678 pShowStatusBar->setChecked(fStatusbar);
1679 actions << pShowStatusBar;
1680
1681 QPoint gpos = pos;
1682 QWidget *pSender = static_cast<QWidget*>(sender());
1683 if (pSender)
1684 gpos = pSender->mapToGlobal(pos);
1685 QAction *pResult = QMenu::exec(actions, gpos);
1686 if (pResult == pShowToolBar)
1687 {
1688 if (pResult->isChecked())
1689 {
1690#ifdef Q_WS_MAC
1691 mVMToolBar->show();
1692#else /* Q_WS_MAC */
1693 m_pBar->show();
1694#endif /* !Q_WS_MAC */
1695 vbox.SetExtraData(VBoxDefs::GUI_Toolbar, "true");
1696 }
1697 else
1698 {
1699#ifdef Q_WS_MAC
1700 mVMToolBar->hide();
1701#else /* Q_WS_MAC */
1702 m_pBar->hide();
1703#endif /* !Q_WS_MAC */
1704 vbox.SetExtraData(VBoxDefs::GUI_Toolbar, "false");
1705 }
1706 }else if (pResult == pShowStatusBar)
1707 {
1708 if (pResult->isChecked())
1709 {
1710 statusBar()->show();
1711 vbox.SetExtraData(VBoxDefs::GUI_Statusbar, "true");
1712 }
1713 else
1714 {
1715 statusBar()->hide();
1716 vbox.SetExtraData(VBoxDefs::GUI_Statusbar, "false");
1717 }
1718 }
1719}
1720
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use