VirtualBox

source: vbox/trunk/src/VBox/Frontends/VirtualBox/src/platform/darwin/UIWindowMenuManager.cpp

Last change on this file was 106061, checked in by vboxsync, 3 weeks ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 11.0 KB
Line 
1/* $Id: UIWindowMenuManager.cpp 106061 2024-09-16 14:03:52Z vboxsync $ */
2/** @file
3 * VBox Qt GUI - UIWindowMenuManager class implementation.
4 */
5
6/*
7 * Copyright (C) 2010-2024 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28/* Qt includes: */
29#include <QActionGroup>
30#include <QApplication>
31#include <QMenu>
32
33/* GUI includes: */
34#include "UITranslationEventListener.h"
35#include "UIWindowMenuManager.h"
36
37/* Other VBox includes: */
38#include <iprt/assert.h>
39
40
41/** QObject extension
42 * used as Mac OS X 'Window' menu helper. */
43class UIMenuHelper : public QObject
44{
45 Q_OBJECT;
46
47public:
48
49 /** Constructs menu-helper on the basis of passed @a windows. */
50 UIMenuHelper(const QList<QWidget*> &windows);
51 /** Destructs menu-helper. */
52 virtual ~UIMenuHelper() RT_OVERRIDE;
53
54 /** Returns 'Window' menu. */
55 QMenu *menu() const { return m_pWindowMenu; }
56
57 /** Adds @a pWindow into 'Window' menu. */
58 QAction *addWindow(QWidget *pWindow);
59 /** Removes @a pWindow from 'Window' menu. */
60 void removeWindow(QWidget *pWindow);
61
62 /** Handles translation event. */
63 virtual void retranslateUi();
64
65 /** Updates toggle action states according to passed @a pActiveWindow. */
66 void updateStatus(QWidget *pActiveWindow);
67
68private slots:
69
70 /** Handles request to minimize active-window. */
71 void sltMinimizeActiveWindow();
72
73 /** Handles request to raise sender window. */
74 void sltRaiseSender();
75
76private:
77
78 /** Holds the 'Window' menu instance. */
79 QMenu *m_pWindowMenu;
80 /** Holds the action group instance. */
81 QActionGroup *m_pGroup;
82 /** Holds the 'Minimize' action instance. */
83 QAction *m_pMinimizeAction;
84 /** Holds the hash of the registered menu-helper instances. */
85 QHash<QWidget*, QAction*> m_windows;
86};
87
88
89/*********************************************************************************************************************************
90* Class UIMenuHelper implementation. *
91*********************************************************************************************************************************/
92
93UIMenuHelper::UIMenuHelper(const QList<QWidget*> &windows)
94{
95 /* Prepare 'Window' menu: */
96 m_pWindowMenu = new QMenu;
97
98 /* Prepare action group: */
99 m_pGroup = new QActionGroup(this);
100 m_pGroup->setExclusive(true);
101
102 /* Prepare 'Minimize' action: */
103 m_pMinimizeAction = new QAction(this);
104 m_pWindowMenu->addAction(m_pMinimizeAction);
105 connect(m_pMinimizeAction, SIGNAL(triggered(bool)),
106 this, SLOT(sltMinimizeActiveWindow()));
107
108 /* Make sure all already available windows are
109 * properly registered within this menu: */
110 for (int i = 0; i < windows.size(); ++i)
111 addWindow(windows.at(i));
112
113 /* Apply language settings: */
114 retranslateUi();
115}
116
117UIMenuHelper::~UIMenuHelper()
118{
119 /* Cleanup 'Window' menu: */
120 delete m_pWindowMenu;
121
122 /* Cleanup actions: */
123 qDeleteAll(m_windows);
124}
125
126QAction *UIMenuHelper::addWindow(QWidget *pWindow)
127{
128 QAction *pAction = 0;
129 if ( pWindow
130 && !m_windows.contains(pWindow))
131 {
132 if (m_windows.size() < 2)
133 m_pWindowMenu->addSeparator();
134
135 /* The main window always first: */
136 pAction = new QAction(this);
137 pAction->setText(pWindow->windowTitle());
138 pAction->setMenuRole(QAction::NoRole);
139 pAction->setData(QVariant::fromValue(pWindow));
140 pAction->setCheckable(true);
141
142 /* The first registered one is always
143 * considered as the main window: */
144 if (m_windows.size() == 0)
145 pAction->setShortcut(QKeySequence("Ctrl+0"));
146 m_pGroup->addAction(pAction);
147 connect(pAction, SIGNAL(triggered(bool)),
148 this, SLOT(sltRaiseSender()));
149 m_pWindowMenu->addAction(pAction);
150 m_windows[pWindow] = pAction;
151 }
152 return pAction;
153}
154
155void UIMenuHelper::removeWindow(QWidget *pWindow)
156{
157 if (m_windows.contains(pWindow))
158 {
159 delete m_windows.value(pWindow);
160 m_windows.remove(pWindow);
161 }
162}
163
164void UIMenuHelper::retranslateUi()
165{
166 /* Translate menu: */
167 m_pWindowMenu->setTitle(QApplication::translate("UIActionPool", "&Window"));
168
169 /* Translate menu 'Minimize' action: */
170 m_pMinimizeAction->setText(QApplication::translate("UIActionPool", "&Minimize"));
171 m_pMinimizeAction->setShortcut(QKeySequence("Ctrl+M"));
172
173 /* Translate other menu-actions: */
174 foreach (QAction *pAction, m_windows.values())
175 {
176 /* Get corresponding window from action's data: */
177 QWidget *pWindow = pAction->data().value<QWidget*>();
178 /* Use the window's title as the action's text: */
179 pAction->setText(pWindow->windowTitle());
180 }
181}
182
183void UIMenuHelper::updateStatus(QWidget *pActiveWindow)
184{
185 /* 'Minimize' action is enabled if there is active-window: */
186 m_pMinimizeAction->setEnabled(pActiveWindow != 0);
187
188 /* If there is active-window: */
189 if (pActiveWindow)
190 {
191 /* Toggle corresponding action on: */
192 if (m_windows.contains(pActiveWindow))
193 m_windows.value(pActiveWindow)->setChecked(true);
194 }
195 /* If there is no active-window: */
196 else
197 {
198 /* Make sure corresponding action toggled off: */
199 if (QAction *pChecked = m_pGroup->checkedAction())
200 pChecked->setChecked(false);
201 }
202}
203
204void UIMenuHelper::sltMinimizeActiveWindow()
205{
206 if (QWidget *pActiveWindow = qApp->activeWindow())
207 pActiveWindow->showMinimized();
208}
209
210void UIMenuHelper::sltRaiseSender()
211{
212 AssertReturnVoid(sender());
213 if (QAction *pAction = qobject_cast<QAction*>(sender()))
214 {
215 if (QWidget *pWidget = pAction->data().value<QWidget*>())
216 {
217 pWidget->show();
218 pWidget->raise();
219 pWidget->activateWindow();
220 }
221 }
222}
223
224
225/*********************************************************************************************************************************
226* Class UIWindowMenuManager implementation. *
227*********************************************************************************************************************************/
228
229/* static */
230UIWindowMenuManager* UIWindowMenuManager::s_pInstance = 0;
231
232/* static */
233void UIWindowMenuManager::create()
234{
235 /* Make sure 'Window' menu Manager is not created: */
236 AssertReturnVoid(!s_pInstance);
237
238 /* Create 'Window' menu Manager: */
239 new UIWindowMenuManager;
240}
241
242/* static */
243void UIWindowMenuManager::destroy()
244{
245 /* Make sure 'Window' menu Manager is created: */
246 AssertPtrReturnVoid(s_pInstance);
247
248 /* Delete 'Window' menu Manager: */
249 delete s_pInstance;
250}
251
252QMenu *UIWindowMenuManager::createMenu(QWidget *pWindow)
253{
254 /* Create helper: */
255 UIMenuHelper *pHelper = new UIMenuHelper(m_windows);
256 /* Register it: */
257 m_helpers[pWindow] = pHelper;
258
259 /* Return menu of created helper: */
260 return pHelper->menu();
261}
262
263void UIWindowMenuManager::destroyMenu(QWidget *pWindow)
264{
265 /* If window is registered: */
266 if (m_helpers.contains(pWindow))
267 {
268 /* Delete helper: */
269 delete m_helpers.value(pWindow);
270 /* Unregister it: */
271 m_helpers.remove(pWindow);
272 }
273}
274
275void UIWindowMenuManager::addWindow(QWidget *pWindow)
276{
277 /* Register window: */
278 m_windows.append(pWindow);
279 /* Add window to all menus we have: */
280 foreach (UIMenuHelper *pHelper, m_helpers.values())
281 pHelper->addWindow(pWindow);
282}
283
284void UIWindowMenuManager::removeWindow(QWidget *pWindow)
285{
286 /* Remove window from all menus we have: */
287 foreach (UIMenuHelper *pHelper, m_helpers.values())
288 pHelper->removeWindow(pWindow);
289 /* Unregister window: */
290 m_windows.removeAll(pWindow);
291}
292
293void UIWindowMenuManager::sltRetranslateUI()
294{
295 /* Translate all the helpers: */
296 foreach (UIMenuHelper *pHelper, m_helpers.values())
297 pHelper->retranslateUi();
298}
299
300UIWindowMenuManager::UIWindowMenuManager()
301{
302 /* Assign instance: */
303 s_pInstance = this;
304
305 /* Install global event-filter: */
306 qApp->installEventFilter(this);
307 connect(&translationEventListener(), &UITranslationEventListener::sigRetranslateUI,
308 this, &UIWindowMenuManager::sltRetranslateUI);
309}
310
311UIWindowMenuManager::~UIWindowMenuManager()
312{
313 /* Cleanup all helpers: */
314 qDeleteAll(m_helpers);
315
316 /* Unassign instance: */
317 s_pInstance = 0;
318}
319
320bool UIWindowMenuManager::eventFilter(QObject *pObject, QEvent *pEvent)
321{
322 /* Acquire event type: */
323 const QEvent::Type type = pEvent->type();
324
325#ifdef VBOX_OSE /// @todo Do we still need it?
326 // WORKAROUND:
327 // Stupid Qt: Qt doesn't check if a window is minimized when a command is
328 // executed. This leads to strange behaviour. The minimized window is
329 // partly restored, but not usable. As a workaround we raise the parent
330 // window before we let execute the command.
331 // Note: fixed in our local Qt build since 4.7.0.
332 if (pObject && type == QEvent::Show)
333 {
334 QWidget *pWidget = qobject_cast<QWidget*>(pObject);
335 if ( pWidget
336 && pWidget->parentWidget()
337 && pWidget->parentWidget()->isMinimized())
338 {
339 pWidget->parentWidget()->show();
340 pWidget->parentWidget()->raise();
341 pWidget->parentWidget()->activateWindow();
342 }
343 }
344#endif /* VBOX_OSE */
345
346 /* We need to track several events which leads to different window
347 * activation and change the menu items in that case. */
348 if ( type == QEvent::ActivationChange
349 || type == QEvent::WindowActivate
350 || type == QEvent::WindowDeactivate
351 || type == QEvent::WindowStateChange
352 || type == QEvent::Show
353 || type == QEvent::Close
354 || type == QEvent::Hide)
355 {
356 QWidget *pActiveWindow = qApp->activeWindow();
357 foreach (UIMenuHelper *pHelper, m_helpers.values())
358 pHelper->updateStatus(pActiveWindow);
359 }
360
361 /* Besides our own retranslation, we should also retranslate
362 * everything on any registered widget title change event: */
363 if (pObject && type == QEvent::WindowTitleChange)
364 {
365 QWidget *pWidget = qobject_cast<QWidget*>(pObject);
366 if (pWidget && m_helpers.contains(pWidget))
367 sltRetranslateUI();
368 }
369
370 /* Call to base-class: */
371 return QObject::eventFilter(pObject, pEvent);
372}
373
374
375#include "UIWindowMenuManager.moc"
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