1 | /* $Id: UIChooserModel.cpp 106143 2024-09-24 19:53:49Z vboxsync $ */
|
---|
2 | /** @file
|
---|
3 | * VBox Qt GUI - UIChooserModel class implementation.
|
---|
4 | */
|
---|
5 |
|
---|
6 | /*
|
---|
7 | * Copyright (C) 2012-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 <QDrag>
|
---|
30 | #include <QGraphicsScene>
|
---|
31 | #include <QGraphicsSceneContextMenuEvent>
|
---|
32 | #include <QGraphicsView>
|
---|
33 | #include <QScrollBar>
|
---|
34 | #include <QTimer>
|
---|
35 |
|
---|
36 | /* GUI includes: */
|
---|
37 | #include "QIMessageBox.h"
|
---|
38 | #include "UIActionPoolManager.h"
|
---|
39 | #include "UIChooser.h"
|
---|
40 | #include "UIChooserHandlerMouse.h"
|
---|
41 | #include "UIChooserHandlerKeyboard.h"
|
---|
42 | #include "UIChooserItemGroup.h"
|
---|
43 | #include "UIChooserItemGlobal.h"
|
---|
44 | #include "UIChooserItemMachine.h"
|
---|
45 | #include "UIChooserModel.h"
|
---|
46 | #include "UIChooserNode.h"
|
---|
47 | #include "UIChooserNodeGroup.h"
|
---|
48 | #include "UIChooserNodeGlobal.h"
|
---|
49 | #include "UIChooserNodeMachine.h"
|
---|
50 | #include "UIChooserView.h"
|
---|
51 | #include "UICloudNetworkingStuff.h"
|
---|
52 | #include "UIExtraDataManager.h"
|
---|
53 | #include "UIMessageCenter.h"
|
---|
54 | #include "UIModalWindowManager.h"
|
---|
55 | #include "UINotificationCenter.h"
|
---|
56 | #include "UIVirtualBoxManagerWidget.h"
|
---|
57 | #include "UIVirtualMachineItemCloud.h"
|
---|
58 | #include "UIVirtualMachineItemLocal.h"
|
---|
59 |
|
---|
60 | /* COM includes: */
|
---|
61 | #include "CExtPack.h"
|
---|
62 | #include "CExtPackManager.h"
|
---|
63 |
|
---|
64 | /* Type defs: */
|
---|
65 | typedef QSet<QString> UIStringSet;
|
---|
66 |
|
---|
67 |
|
---|
68 | UIChooserModel::UIChooserModel(UIChooser *pParent, UIActionPool *pActionPool)
|
---|
69 | : UIChooserAbstractModel(pParent)
|
---|
70 | , m_pActionPool(pActionPool)
|
---|
71 | , m_pScene(0)
|
---|
72 | , m_pMouseHandler(0)
|
---|
73 | , m_pKeyboardHandler(0)
|
---|
74 | , m_fSelectionSaveAllowed(false)
|
---|
75 | , m_iCurrentSearchResultIndex(-1)
|
---|
76 | , m_iScrollingTokenSize(30)
|
---|
77 | , m_fIsScrollingInProgress(false)
|
---|
78 | , m_iGlobalItemHeightHint(0)
|
---|
79 | , m_pTimerCloudProfileUpdate(0)
|
---|
80 | {
|
---|
81 | prepare();
|
---|
82 | }
|
---|
83 |
|
---|
84 | UIChooserModel::~UIChooserModel()
|
---|
85 | {
|
---|
86 | cleanup();
|
---|
87 | }
|
---|
88 |
|
---|
89 | void UIChooserModel::init()
|
---|
90 | {
|
---|
91 | /* Call to base-class: */
|
---|
92 | UIChooserAbstractModel::init();
|
---|
93 |
|
---|
94 | /* Build tree for main root: */
|
---|
95 | buildTreeForMainRoot();
|
---|
96 | /* Load settings: */
|
---|
97 | loadSettings();
|
---|
98 | }
|
---|
99 |
|
---|
100 | UIActionPool *UIChooserModel::actionPool() const
|
---|
101 | {
|
---|
102 | return m_pActionPool;
|
---|
103 | }
|
---|
104 |
|
---|
105 | QGraphicsScene *UIChooserModel::scene() const
|
---|
106 | {
|
---|
107 | return m_pScene;
|
---|
108 | }
|
---|
109 |
|
---|
110 | UIChooserView *UIChooserModel::view() const
|
---|
111 | {
|
---|
112 | return scene() && !scene()->views().isEmpty() ? qobject_cast<UIChooserView*>(scene()->views().first()) : 0;
|
---|
113 | }
|
---|
114 |
|
---|
115 | QPaintDevice *UIChooserModel::paintDevice() const
|
---|
116 | {
|
---|
117 | return scene() && !scene()->views().isEmpty() ? scene()->views().first() : 0;
|
---|
118 | }
|
---|
119 |
|
---|
120 | QGraphicsItem *UIChooserModel::itemAt(const QPointF &position, const QTransform &deviceTransform /* = QTransform() */) const
|
---|
121 | {
|
---|
122 | return scene() ? scene()->itemAt(position, deviceTransform) : 0;
|
---|
123 | }
|
---|
124 |
|
---|
125 | void UIChooserModel::handleToolButtonClick(UIChooserItem *pItem)
|
---|
126 | {
|
---|
127 | emit sigToolMenuRequested(pItem->mapToScene(QPointF(pItem->size().width(), 0)).toPoint(),
|
---|
128 | pItem->type() == UIChooserNodeType_Machine ? pItem->toMachineItem()->cache() : 0);
|
---|
129 | }
|
---|
130 |
|
---|
131 | void UIChooserModel::handlePinButtonClick(UIChooserItem *pItem)
|
---|
132 | {
|
---|
133 | switch (pItem->type())
|
---|
134 | {
|
---|
135 | case UIChooserNodeType_Global:
|
---|
136 | pItem->setFavorite(!pItem->isFavorite());
|
---|
137 | break;
|
---|
138 | default:
|
---|
139 | break;
|
---|
140 | }
|
---|
141 | }
|
---|
142 |
|
---|
143 | void UIChooserModel::setSelectedItems(const QList<UIChooserItem*> &items)
|
---|
144 | {
|
---|
145 | /* Is there something changed? */
|
---|
146 | if (m_selectedItems == items)
|
---|
147 | return;
|
---|
148 |
|
---|
149 | /* Remember old selected-item list: */
|
---|
150 | const QList<UIChooserItem*> oldCurrentItems = m_selectedItems;
|
---|
151 |
|
---|
152 | /* Clear current selected-item list: */
|
---|
153 | m_selectedItems.clear();
|
---|
154 |
|
---|
155 | /* Iterate over all the passed items: */
|
---|
156 | foreach (UIChooserItem *pItem, items)
|
---|
157 | {
|
---|
158 | /* Add item to current selected-item list if navigation list contains it: */
|
---|
159 | if (pItem && navigationItems().contains(pItem))
|
---|
160 | m_selectedItems << pItem;
|
---|
161 | else
|
---|
162 | AssertMsgFailed(("Passed item is not in navigation list!"));
|
---|
163 | }
|
---|
164 |
|
---|
165 | /* Make sure selection list is never empty if current-item present: */
|
---|
166 | if (m_selectedItems.isEmpty() && currentItem() && navigationItems().contains(currentItem()))
|
---|
167 | m_selectedItems << currentItem();
|
---|
168 |
|
---|
169 | /* Is there something really changed? */
|
---|
170 | if (oldCurrentItems == m_selectedItems)
|
---|
171 | return;
|
---|
172 |
|
---|
173 | /* Update all the old items (they are no longer selected): */
|
---|
174 | foreach (UIChooserItem *pItem, oldCurrentItems)
|
---|
175 | {
|
---|
176 | pItem->setSelected(false);
|
---|
177 | pItem->update();
|
---|
178 | }
|
---|
179 | /* Update all the new items (they are selected now): */
|
---|
180 | foreach (UIChooserItem *pItem, m_selectedItems)
|
---|
181 | {
|
---|
182 | pItem->setSelected(true);
|
---|
183 | pItem->update();
|
---|
184 | }
|
---|
185 |
|
---|
186 | /* Should the selection changes be saved? */
|
---|
187 | if (m_fSelectionSaveAllowed)
|
---|
188 | {
|
---|
189 | /* Acquire first selected item: */
|
---|
190 | UIChooserItem *pFirstSelectedItem = m_selectedItems.value(0);
|
---|
191 | /* If this item is of machine type: */
|
---|
192 | if ( pFirstSelectedItem
|
---|
193 | && pFirstSelectedItem->type() == UIChooserNodeType_Machine)
|
---|
194 | {
|
---|
195 | /* Cast to machine item: */
|
---|
196 | UIChooserItemMachine *pMachineItem = pFirstSelectedItem->toMachineItem();
|
---|
197 | /* If this machine item is of cloud type =>
|
---|
198 | * Choose the parent (profile) group item as the last one selected: */
|
---|
199 | if ( pMachineItem
|
---|
200 | && ( pMachineItem->cacheType() == UIVirtualMachineItemType_CloudFake
|
---|
201 | || pMachineItem->cacheType() == UIVirtualMachineItemType_CloudReal))
|
---|
202 | pFirstSelectedItem = pMachineItem->parentItem();
|
---|
203 | }
|
---|
204 | /* Save last selected-item: */
|
---|
205 | gEDataManager->setSelectorWindowLastItemChosen(pFirstSelectedItem ? pFirstSelectedItem->definition() : QString());
|
---|
206 | }
|
---|
207 |
|
---|
208 | /* Notify about selection changes: */
|
---|
209 | emit sigSelectionChanged();
|
---|
210 | }
|
---|
211 |
|
---|
212 | void UIChooserModel::setSelectedItem(UIChooserItem *pItem)
|
---|
213 | {
|
---|
214 | /* Call for wrapper above: */
|
---|
215 | QList<UIChooserItem*> items;
|
---|
216 | if (pItem)
|
---|
217 | items << pItem;
|
---|
218 | setSelectedItems(items);
|
---|
219 |
|
---|
220 | /* Make selected-item current one as well: */
|
---|
221 | setCurrentItem(firstSelectedItem());
|
---|
222 | }
|
---|
223 |
|
---|
224 | void UIChooserModel::setSelectedItem(const QString &strDefinition)
|
---|
225 | {
|
---|
226 | /* Search an item by definition: */
|
---|
227 | UIChooserItem *pItem = searchItemByDefinition(strDefinition);
|
---|
228 |
|
---|
229 | /* Make sure found item is in navigation list: */
|
---|
230 | if (!pItem || !navigationItems().contains(pItem))
|
---|
231 | return;
|
---|
232 |
|
---|
233 | /* Call for wrapper above: */
|
---|
234 | setSelectedItem(pItem);
|
---|
235 | }
|
---|
236 |
|
---|
237 | void UIChooserModel::clearSelectedItems()
|
---|
238 | {
|
---|
239 | /* Call for wrapper above: */
|
---|
240 | setSelectedItem(0);
|
---|
241 | }
|
---|
242 |
|
---|
243 | const QList<UIChooserItem*> &UIChooserModel::selectedItems() const
|
---|
244 | {
|
---|
245 | return m_selectedItems;
|
---|
246 | }
|
---|
247 |
|
---|
248 | void UIChooserModel::addToSelectedItems(UIChooserItem *pItem)
|
---|
249 | {
|
---|
250 | /* Prepare updated list: */
|
---|
251 | QList<UIChooserItem*> list(selectedItems());
|
---|
252 | list << pItem;
|
---|
253 | /* Call for wrapper above: */
|
---|
254 | setSelectedItems(list);
|
---|
255 | }
|
---|
256 |
|
---|
257 | void UIChooserModel::removeFromSelectedItems(UIChooserItem *pItem)
|
---|
258 | {
|
---|
259 | /* Prepare updated list: */
|
---|
260 | QList<UIChooserItem*> list(selectedItems());
|
---|
261 | list.removeAll(pItem);
|
---|
262 | /* Call for wrapper above: */
|
---|
263 | setSelectedItems(list);
|
---|
264 | }
|
---|
265 |
|
---|
266 | UIChooserItem *UIChooserModel::firstSelectedItem() const
|
---|
267 | {
|
---|
268 | /* Return first of selected-items, if any: */
|
---|
269 | return selectedItems().value(0);
|
---|
270 | }
|
---|
271 |
|
---|
272 | UIVirtualMachineItem *UIChooserModel::firstSelectedMachineItem() const
|
---|
273 | {
|
---|
274 | /* Return first machine-item of the selected-item: */
|
---|
275 | return firstSelectedItem()
|
---|
276 | && firstSelectedItem()->firstMachineItem()
|
---|
277 | && firstSelectedItem()->firstMachineItem()->toMachineItem()
|
---|
278 | ? firstSelectedItem()->firstMachineItem()->toMachineItem()->cache()
|
---|
279 | : 0;
|
---|
280 | }
|
---|
281 |
|
---|
282 | QList<UIVirtualMachineItem*> UIChooserModel::selectedMachineItems() const
|
---|
283 | {
|
---|
284 | /* Gather list of selected unique machine-items: */
|
---|
285 | QList<UIChooserItemMachine*> currentMachineItemList;
|
---|
286 | UIChooserItemMachine::enumerateMachineItems(selectedItems(), currentMachineItemList,
|
---|
287 | UIChooserItemMachineEnumerationFlag_Unique);
|
---|
288 |
|
---|
289 | /* Reintegrate machine-items into valid format: */
|
---|
290 | QList<UIVirtualMachineItem*> currentMachineList;
|
---|
291 | foreach (UIChooserItemMachine *pItem, currentMachineItemList)
|
---|
292 | currentMachineList << pItem->cache();
|
---|
293 | return currentMachineList;
|
---|
294 | }
|
---|
295 |
|
---|
296 | bool UIChooserModel::isGroupItemSelected() const
|
---|
297 | {
|
---|
298 | return firstSelectedItem() && firstSelectedItem()->type() == UIChooserNodeType_Group;
|
---|
299 | }
|
---|
300 |
|
---|
301 | bool UIChooserModel::isGlobalItemSelected() const
|
---|
302 | {
|
---|
303 | return firstSelectedItem() && firstSelectedItem()->type() == UIChooserNodeType_Global;
|
---|
304 | }
|
---|
305 |
|
---|
306 | bool UIChooserModel::isMachineItemSelected() const
|
---|
307 | {
|
---|
308 | return firstSelectedItem() && firstSelectedItem()->type() == UIChooserNodeType_Machine;
|
---|
309 | }
|
---|
310 |
|
---|
311 | bool UIChooserModel::isLocalMachineItemSelected() const
|
---|
312 | {
|
---|
313 | return isMachineItemSelected()
|
---|
314 | && firstSelectedItem()->toMachineItem()->cacheType() == UIVirtualMachineItemType_Local;
|
---|
315 | }
|
---|
316 |
|
---|
317 | bool UIChooserModel::isCloudMachineItemSelected() const
|
---|
318 | {
|
---|
319 | return isMachineItemSelected()
|
---|
320 | && firstSelectedItem()->toMachineItem()->cacheType() == UIVirtualMachineItemType_CloudReal;
|
---|
321 | }
|
---|
322 |
|
---|
323 | bool UIChooserModel::isSingleGroupSelected() const
|
---|
324 | {
|
---|
325 | return selectedItems().size() == 1
|
---|
326 | && firstSelectedItem()->type() == UIChooserNodeType_Group;
|
---|
327 | }
|
---|
328 |
|
---|
329 | bool UIChooserModel::isSingleLocalGroupSelected() const
|
---|
330 | {
|
---|
331 | return isSingleGroupSelected()
|
---|
332 | && firstSelectedItem()->toGroupItem()->groupType() == UIChooserNodeGroupType_Local;
|
---|
333 | }
|
---|
334 |
|
---|
335 | bool UIChooserModel::isSingleCloudProviderGroupSelected() const
|
---|
336 | {
|
---|
337 | return isSingleGroupSelected()
|
---|
338 | && firstSelectedItem()->toGroupItem()->groupType() == UIChooserNodeGroupType_Provider;
|
---|
339 | }
|
---|
340 |
|
---|
341 | bool UIChooserModel::isSingleCloudProfileGroupSelected() const
|
---|
342 | {
|
---|
343 | return isSingleGroupSelected()
|
---|
344 | && firstSelectedItem()->toGroupItem()->groupType() == UIChooserNodeGroupType_Profile;
|
---|
345 | }
|
---|
346 |
|
---|
347 | bool UIChooserModel::isAllItemsOfOneGroupSelected() const
|
---|
348 | {
|
---|
349 | /* Make sure at least one item selected: */
|
---|
350 | if (selectedItems().isEmpty())
|
---|
351 | return false;
|
---|
352 |
|
---|
353 | /* Determine the parent group of the first item: */
|
---|
354 | UIChooserItem *pFirstParent = firstSelectedItem()->parentItem();
|
---|
355 |
|
---|
356 | /* Make sure this parent is not main root-item: */
|
---|
357 | if (pFirstParent == root())
|
---|
358 | return false;
|
---|
359 |
|
---|
360 | /* Enumerate selected-item set: */
|
---|
361 | QSet<UIChooserItem*> currentItemSet;
|
---|
362 | foreach (UIChooserItem *pCurrentItem, selectedItems())
|
---|
363 | currentItemSet << pCurrentItem;
|
---|
364 |
|
---|
365 | /* Enumerate first parent children set: */
|
---|
366 | QSet<UIChooserItem*> firstParentItemSet;
|
---|
367 | foreach (UIChooserItem *pFirstParentItem, pFirstParent->items())
|
---|
368 | firstParentItemSet << pFirstParentItem;
|
---|
369 |
|
---|
370 | /* Check if both sets contains the same: */
|
---|
371 | return currentItemSet == firstParentItemSet;
|
---|
372 | }
|
---|
373 |
|
---|
374 | QString UIChooserModel::fullGroupName() const
|
---|
375 | {
|
---|
376 | return isSingleGroupSelected() ? firstSelectedItem()->fullName() : firstSelectedItem()->parentItem()->fullName();
|
---|
377 | }
|
---|
378 |
|
---|
379 | UIChooserItem *UIChooserModel::findClosestUnselectedItem() const
|
---|
380 | {
|
---|
381 | /* Take the current-item (if any) as a starting point
|
---|
382 | * and find the closest non-selected-item. */
|
---|
383 | UIChooserItem *pItem = currentItem();
|
---|
384 | if (!pItem)
|
---|
385 | pItem = firstSelectedItem();
|
---|
386 | if (pItem)
|
---|
387 | {
|
---|
388 | int idxBefore = navigationItems().indexOf(pItem) - 1;
|
---|
389 | int idxAfter = idxBefore + 2;
|
---|
390 | while (idxBefore >= 0 || idxAfter < navigationItems().size())
|
---|
391 | {
|
---|
392 | if (idxAfter < navigationItems().size())
|
---|
393 | {
|
---|
394 | pItem = navigationItems().at(idxAfter);
|
---|
395 | if ( !selectedItems().contains(pItem)
|
---|
396 | && ( pItem->type() == UIChooserNodeType_Machine
|
---|
397 | || pItem->type() == UIChooserNodeType_Global))
|
---|
398 | return pItem;
|
---|
399 | ++idxAfter;
|
---|
400 | }
|
---|
401 | if (idxBefore >= 0)
|
---|
402 | {
|
---|
403 | pItem = navigationItems().at(idxBefore);
|
---|
404 | if ( !selectedItems().contains(pItem)
|
---|
405 | && ( pItem->type() == UIChooserNodeType_Machine
|
---|
406 | || pItem->type() == UIChooserNodeType_Global))
|
---|
407 | return pItem;
|
---|
408 | --idxBefore;
|
---|
409 | }
|
---|
410 | }
|
---|
411 | }
|
---|
412 | return 0;
|
---|
413 | }
|
---|
414 |
|
---|
415 | void UIChooserModel::makeSureNoItemWithCertainIdSelected(const QUuid &uId)
|
---|
416 | {
|
---|
417 | /* Look for all nodes with passed uId: */
|
---|
418 | QList<UIChooserNode*> matchedNodes;
|
---|
419 | invisibleRoot()->searchForNodes(uId.toString(),
|
---|
420 | UIChooserItemSearchFlag_Machine |
|
---|
421 | UIChooserItemSearchFlag_ExactId,
|
---|
422 | matchedNodes);
|
---|
423 |
|
---|
424 | /* Compose a set of items with passed uId: */
|
---|
425 | QSet<UIChooserItem*> matchedItems;
|
---|
426 | foreach (UIChooserNode *pNode, matchedNodes)
|
---|
427 | if (pNode && pNode->item())
|
---|
428 | matchedItems << pNode->item();
|
---|
429 |
|
---|
430 | /* If we have at least one of those items currently selected: */
|
---|
431 | const QList<UIChooserItem*> selectedItemsList = selectedItems();
|
---|
432 | const QSet<UIChooserItem*> selectedItemsSet(selectedItemsList.begin(), selectedItemsList.end());
|
---|
433 | if (selectedItemsSet.intersects(matchedItems))
|
---|
434 | setSelectedItem(findClosestUnselectedItem());
|
---|
435 |
|
---|
436 | /* If global item is currently chosen, selection should be invalidated: */
|
---|
437 | if (firstSelectedItem() && firstSelectedItem()->type() == UIChooserNodeType_Global)
|
---|
438 | emit sigSelectionInvalidated();
|
---|
439 | }
|
---|
440 |
|
---|
441 | void UIChooserModel::makeSureAtLeastOneItemSelected()
|
---|
442 | {
|
---|
443 | /* If we have no item selected but
|
---|
444 | * at least one in the navigation list (global item): */
|
---|
445 | if (!firstSelectedItem() && !navigationItems().isEmpty())
|
---|
446 | {
|
---|
447 | /* We are choosing it, selection should be invalidated: */
|
---|
448 | setSelectedItem(navigationItems().first());
|
---|
449 | emit sigSelectionInvalidated();
|
---|
450 | }
|
---|
451 | }
|
---|
452 |
|
---|
453 | void UIChooserModel::setCurrentItem(UIChooserItem *pItem)
|
---|
454 | {
|
---|
455 | /* Make sure real focus unset: */
|
---|
456 | clearRealFocus();
|
---|
457 |
|
---|
458 | /* Is there something changed? */
|
---|
459 | if (m_pCurrentItem == pItem)
|
---|
460 | return;
|
---|
461 |
|
---|
462 | /* Remember old current-item: */
|
---|
463 | UIChooserItem *pOldCurrentItem = m_pCurrentItem;
|
---|
464 |
|
---|
465 | /* Set new current-item: */
|
---|
466 | m_pCurrentItem = pItem;
|
---|
467 |
|
---|
468 | /* Disconnect old current-item (if any): */
|
---|
469 | if (pOldCurrentItem)
|
---|
470 | disconnect(pOldCurrentItem, &UIChooserItem::destroyed, this, &UIChooserModel::sltCurrentItemDestroyed);
|
---|
471 | /* Connect new current-item (if any): */
|
---|
472 | if (m_pCurrentItem)
|
---|
473 | connect(m_pCurrentItem.data(), &UIChooserItem::destroyed, this, &UIChooserModel::sltCurrentItemDestroyed);
|
---|
474 |
|
---|
475 | /* If dialog is visible and item exists => make it visible as well: */
|
---|
476 | if (view() && view()->window() && root())
|
---|
477 | if (view()->window()->isVisible() && pItem)
|
---|
478 | root()->toGroupItem()->makeSureItemIsVisible(pItem);
|
---|
479 |
|
---|
480 | /* Make sure selection list is never empty if current-item present: */
|
---|
481 | if (!firstSelectedItem() && m_pCurrentItem)
|
---|
482 | setSelectedItem(m_pCurrentItem);
|
---|
483 | }
|
---|
484 |
|
---|
485 | UIChooserItem *UIChooserModel::currentItem() const
|
---|
486 | {
|
---|
487 | return m_pCurrentItem;
|
---|
488 | }
|
---|
489 |
|
---|
490 | const QList<UIChooserItem*> &UIChooserModel::navigationItems() const
|
---|
491 | {
|
---|
492 | return m_navigationItems;
|
---|
493 | }
|
---|
494 |
|
---|
495 | void UIChooserModel::removeFromNavigationItems(UIChooserItem *pItem)
|
---|
496 | {
|
---|
497 | AssertMsg(pItem, ("Passed item is invalid!"));
|
---|
498 | m_navigationItems.removeAll(pItem);
|
---|
499 | }
|
---|
500 |
|
---|
501 | void UIChooserModel::updateNavigationItemList()
|
---|
502 | {
|
---|
503 | m_navigationItems.clear();
|
---|
504 | m_navigationItems = createNavigationItemList(root());
|
---|
505 | }
|
---|
506 |
|
---|
507 | UIChooserItem *UIChooserModel::searchItemByDefinition(const QString &strDefinition) const
|
---|
508 | {
|
---|
509 | /* Null if empty definition passed: */
|
---|
510 | if (strDefinition.isEmpty())
|
---|
511 | return 0;
|
---|
512 |
|
---|
513 | /* Parse definition: */
|
---|
514 | UIChooserItem *pItem = 0;
|
---|
515 | const QString strItemType = strDefinition.section('=', 0, 0);
|
---|
516 | const QString strItemDescriptor = strDefinition.section('=', 1, -1);
|
---|
517 | /* Its a local group-item definition? */
|
---|
518 | if (strItemType == prefixToString(UIChooserNodeDataPrefixType_Local))
|
---|
519 | {
|
---|
520 | /* Search for group-item with passed descriptor (name): */
|
---|
521 | pItem = root()->searchForItem(strItemDescriptor,
|
---|
522 | UIChooserItemSearchFlag_LocalGroup |
|
---|
523 | UIChooserItemSearchFlag_FullName);
|
---|
524 | }
|
---|
525 | /* Its a provider group-item definition? */
|
---|
526 | else if (strItemType == prefixToString(UIChooserNodeDataPrefixType_Provider))
|
---|
527 | {
|
---|
528 | /* Search for group-item with passed descriptor (name): */
|
---|
529 | pItem = root()->searchForItem(strItemDescriptor,
|
---|
530 | UIChooserItemSearchFlag_CloudProvider |
|
---|
531 | UIChooserItemSearchFlag_FullName);
|
---|
532 | }
|
---|
533 | /* Its a profile group-item definition? */
|
---|
534 | else if (strItemType == prefixToString(UIChooserNodeDataPrefixType_Profile))
|
---|
535 | {
|
---|
536 | /* Search for group-item with passed descriptor (name): */
|
---|
537 | pItem = root()->searchForItem(strItemDescriptor,
|
---|
538 | UIChooserItemSearchFlag_CloudProfile |
|
---|
539 | UIChooserItemSearchFlag_FullName);
|
---|
540 | }
|
---|
541 | /* Its a global-item definition? */
|
---|
542 | else if (strItemType == prefixToString(UIChooserNodeDataPrefixType_Global))
|
---|
543 | {
|
---|
544 | /* Search for global-item with required name: */
|
---|
545 | pItem = root()->searchForItem(strItemDescriptor,
|
---|
546 | UIChooserItemSearchFlag_Global |
|
---|
547 | UIChooserItemSearchFlag_ExactName);
|
---|
548 | }
|
---|
549 | /* Its a machine-item definition? */
|
---|
550 | else if (strItemType == prefixToString(UIChooserNodeDataPrefixType_Machine))
|
---|
551 | {
|
---|
552 | /* Search for machine-item with required ID: */
|
---|
553 | pItem = root()->searchForItem(strItemDescriptor,
|
---|
554 | UIChooserItemSearchFlag_Machine |
|
---|
555 | UIChooserItemSearchFlag_ExactId);
|
---|
556 | }
|
---|
557 |
|
---|
558 | /* Return result: */
|
---|
559 | return pItem;
|
---|
560 | }
|
---|
561 |
|
---|
562 | void UIChooserModel::performSearch(const QString &strSearchTerm, int iSearchFlags)
|
---|
563 | {
|
---|
564 | /* Call to base-class: */
|
---|
565 | UIChooserAbstractModel::performSearch(strSearchTerm, iSearchFlags);
|
---|
566 |
|
---|
567 | /* Select 1st found item: */
|
---|
568 | selectSearchResult(true);
|
---|
569 | }
|
---|
570 |
|
---|
571 | QList<UIChooserNode*> UIChooserModel::resetSearch()
|
---|
572 | {
|
---|
573 | /* Reset search result index: */
|
---|
574 | m_iCurrentSearchResultIndex = -1;
|
---|
575 |
|
---|
576 | /* Call to base-class: */
|
---|
577 | return UIChooserAbstractModel::resetSearch();
|
---|
578 | }
|
---|
579 |
|
---|
580 | void UIChooserModel::selectSearchResult(bool fIsNext)
|
---|
581 | {
|
---|
582 | /* If nothing was found: */
|
---|
583 | if (searchResult().isEmpty())
|
---|
584 | {
|
---|
585 | /* Reset search result index: */
|
---|
586 | m_iCurrentSearchResultIndex = -1;
|
---|
587 | }
|
---|
588 | /* If something was found: */
|
---|
589 | else
|
---|
590 | {
|
---|
591 | /* Advance index forward: */
|
---|
592 | if (fIsNext)
|
---|
593 | {
|
---|
594 | if (++m_iCurrentSearchResultIndex >= searchResult().size())
|
---|
595 | m_iCurrentSearchResultIndex = 0;
|
---|
596 | }
|
---|
597 | /* Advance index backward: */
|
---|
598 | else
|
---|
599 | {
|
---|
600 | if (--m_iCurrentSearchResultIndex < 0)
|
---|
601 | m_iCurrentSearchResultIndex = searchResult().size() - 1;
|
---|
602 | }
|
---|
603 |
|
---|
604 | /* If found item exists: */
|
---|
605 | if (searchResult().at(m_iCurrentSearchResultIndex))
|
---|
606 | {
|
---|
607 | /* Select curresponding found item, make sure it's visible, scroll if necessary: */
|
---|
608 | UIChooserItem *pItem = searchResult().at(m_iCurrentSearchResultIndex)->item();
|
---|
609 | if (pItem)
|
---|
610 | {
|
---|
611 | pItem->makeSureItsVisible();
|
---|
612 | setSelectedItem(pItem);
|
---|
613 | }
|
---|
614 | }
|
---|
615 | }
|
---|
616 |
|
---|
617 | /* Update the search widget's match count(s): */
|
---|
618 | if (view())
|
---|
619 | view()->setSearchResultsCount(searchResult().size(), m_iCurrentSearchResultIndex);
|
---|
620 | }
|
---|
621 |
|
---|
622 | void UIChooserModel::setSearchWidgetVisible(bool fVisible)
|
---|
623 | {
|
---|
624 | if (view())
|
---|
625 | view()->setSearchWidgetVisible(fVisible);
|
---|
626 | }
|
---|
627 |
|
---|
628 | UIChooserItem *UIChooserModel::root() const
|
---|
629 | {
|
---|
630 | return m_pRoot.data();
|
---|
631 | }
|
---|
632 |
|
---|
633 | void UIChooserModel::startEditingSelectedGroupItemName()
|
---|
634 | {
|
---|
635 | /* Only for single selected local group: */
|
---|
636 | if (!isSingleLocalGroupSelected())
|
---|
637 | return;
|
---|
638 |
|
---|
639 | /* Start editing first selected item name: */
|
---|
640 | firstSelectedItem()->startEditing();
|
---|
641 | }
|
---|
642 |
|
---|
643 | void UIChooserModel::disbandSelectedGroupItem()
|
---|
644 | {
|
---|
645 | /* Only for single selected local group: */
|
---|
646 | if (!isSingleLocalGroupSelected())
|
---|
647 | return;
|
---|
648 |
|
---|
649 | /* Check if we have collisions between disbandable group children and their potential siblings: */
|
---|
650 | UIChooserItem *pCurrentItem = currentItem();
|
---|
651 | UIChooserNode *pCurrentNode = pCurrentItem->node();
|
---|
652 | UIChooserItem *pParentItem = pCurrentItem->parentItem();
|
---|
653 | UIChooserNode *pParentNode = pParentItem->node();
|
---|
654 | QList<UIChooserNode*> childrenToBeRenamed;
|
---|
655 | foreach (UIChooserNode *pChildNode, pCurrentNode->nodes())
|
---|
656 | {
|
---|
657 | /* Acquire disbandable group child name to check for collision with group siblings: */
|
---|
658 | const QString strChildName = pChildNode->name();
|
---|
659 | UIChooserNode *pCollisionSibling = 0;
|
---|
660 | /* And then compare this child name with all the sibling names: */
|
---|
661 | foreach (UIChooserNode *pSiblingNode, pParentNode->nodes())
|
---|
662 | {
|
---|
663 | /* There can't be a collision between local child and cloud provider sibling: */
|
---|
664 | if ( pSiblingNode->type() == UIChooserNodeType_Group
|
---|
665 | && pSiblingNode->toGroupNode()->groupType() == UIChooserNodeGroupType_Provider)
|
---|
666 | continue;
|
---|
667 | /* If sibling isn't disbandable group itself and has name similar to one of group children: */
|
---|
668 | if (pSiblingNode != pCurrentNode && pSiblingNode->name() == strChildName)
|
---|
669 | {
|
---|
670 | /* We have a collision sibling: */
|
---|
671 | pCollisionSibling = pSiblingNode;
|
---|
672 | break;
|
---|
673 | }
|
---|
674 | }
|
---|
675 | /* If there is a collision sibling: */
|
---|
676 | if (pCollisionSibling)
|
---|
677 | {
|
---|
678 | switch (pChildNode->type())
|
---|
679 | {
|
---|
680 | /* We can't resolve collision automatically for VMs: */
|
---|
681 | case UIChooserNodeType_Machine:
|
---|
682 | {
|
---|
683 | UINotificationMessage::cannotResolveCollisionAutomatically(strChildName, pParentNode->name());
|
---|
684 | return;
|
---|
685 | }
|
---|
686 | /* But we can do it for VM groups: */
|
---|
687 | case UIChooserNodeType_Group:
|
---|
688 | {
|
---|
689 | if (!msgCenter().confirmAutomaticCollisionResolve(strChildName, pParentNode->name()))
|
---|
690 | return;
|
---|
691 | childrenToBeRenamed << pChildNode;
|
---|
692 | break;
|
---|
693 | }
|
---|
694 | default:
|
---|
695 | break;
|
---|
696 | }
|
---|
697 | }
|
---|
698 | }
|
---|
699 |
|
---|
700 | /* Copy all the children into our parent: */
|
---|
701 | QList<UIChooserItem*> ungroupedItems;
|
---|
702 | foreach (UIChooserNode *pNode, pCurrentNode->nodes())
|
---|
703 | {
|
---|
704 | switch (pNode->type())
|
---|
705 | {
|
---|
706 | case UIChooserNodeType_Group:
|
---|
707 | {
|
---|
708 | UIChooserNodeGroup *pGroupNode = new UIChooserNodeGroup(pParentNode,
|
---|
709 | pParentNode->nodes().size(),
|
---|
710 | pNode->toGroupNode());
|
---|
711 | UIChooserItemGroup *pGroupItem = new UIChooserItemGroup(pParentItem, pGroupNode);
|
---|
712 | if (childrenToBeRenamed.contains(pNode))
|
---|
713 | pGroupNode->setName(uniqueGroupName(pParentNode));
|
---|
714 | ungroupedItems << pGroupItem;
|
---|
715 | break;
|
---|
716 | }
|
---|
717 | case UIChooserNodeType_Machine:
|
---|
718 | {
|
---|
719 | UIChooserNodeMachine *pMachineNode = new UIChooserNodeMachine(pParentNode,
|
---|
720 | pParentNode->nodes().size(),
|
---|
721 | pNode->toMachineNode());
|
---|
722 | UIChooserItemMachine *pMachineItem = new UIChooserItemMachine(pParentItem, pMachineNode);
|
---|
723 | ungroupedItems << pMachineItem;
|
---|
724 | break;
|
---|
725 | }
|
---|
726 | default:
|
---|
727 | break;
|
---|
728 | }
|
---|
729 | }
|
---|
730 |
|
---|
731 | /* Delete current group: */
|
---|
732 | delete pCurrentNode;
|
---|
733 |
|
---|
734 | /* And update model: */
|
---|
735 | updateTreeForMainRoot();
|
---|
736 |
|
---|
737 | /* Choose ungrouped items if present: */
|
---|
738 | if (!ungroupedItems.isEmpty())
|
---|
739 | {
|
---|
740 | setSelectedItems(ungroupedItems);
|
---|
741 | setCurrentItem(firstSelectedItem());
|
---|
742 | }
|
---|
743 | makeSureAtLeastOneItemSelected();
|
---|
744 |
|
---|
745 | /* Save groups finally: */
|
---|
746 | saveGroups();
|
---|
747 | }
|
---|
748 |
|
---|
749 | void UIChooserModel::removeSelectedMachineItems()
|
---|
750 | {
|
---|
751 | /* Enumerate all the selected machine-items: */
|
---|
752 | QList<UIChooserItemMachine*> selectedMachineItemList;
|
---|
753 | UIChooserItemMachine::enumerateMachineItems(selectedItems(), selectedMachineItemList);
|
---|
754 | /* Enumerate all the existing machine-items: */
|
---|
755 | QList<UIChooserItemMachine*> existingMachineItemList;
|
---|
756 | UIChooserItemMachine::enumerateMachineItems(root()->items(), existingMachineItemList);
|
---|
757 |
|
---|
758 | /* Prepare arrays: */
|
---|
759 | QMap<QUuid, bool> verdicts;
|
---|
760 | QList<UIChooserItemMachine*> localMachineItemsToRemove;
|
---|
761 | QList<CMachine> localMachinesToUnregister;
|
---|
762 | QList<UIChooserItemMachine*> cloudMachineItemsToUnregister;
|
---|
763 |
|
---|
764 | /* For each selected machine-item: */
|
---|
765 | foreach (UIChooserItemMachine *pMachineItem, selectedMachineItemList)
|
---|
766 | {
|
---|
767 | /* Get machine-item id: */
|
---|
768 | AssertPtrReturnVoid(pMachineItem);
|
---|
769 | const QUuid uId = pMachineItem->id();
|
---|
770 |
|
---|
771 | /* We already decided for that machine? */
|
---|
772 | if (verdicts.contains(uId))
|
---|
773 | {
|
---|
774 | /* To remove similar machine items? */
|
---|
775 | if (!verdicts.value(uId))
|
---|
776 | localMachineItemsToRemove << pMachineItem;
|
---|
777 | continue;
|
---|
778 | }
|
---|
779 |
|
---|
780 | /* Selected copy count: */
|
---|
781 | int iSelectedCopyCount = 0;
|
---|
782 | foreach (UIChooserItemMachine *pSelectedItem, selectedMachineItemList)
|
---|
783 | {
|
---|
784 | AssertPtrReturnVoid(pSelectedItem);
|
---|
785 | if (pSelectedItem->id() == uId)
|
---|
786 | ++iSelectedCopyCount;
|
---|
787 | }
|
---|
788 | /* Existing copy count: */
|
---|
789 | int iExistingCopyCount = 0;
|
---|
790 | foreach (UIChooserItemMachine *pExistingItem, existingMachineItemList)
|
---|
791 | {
|
---|
792 | AssertPtrReturnVoid(pExistingItem);
|
---|
793 | if (pExistingItem->id() == uId)
|
---|
794 | ++iExistingCopyCount;
|
---|
795 | }
|
---|
796 | /* If selected copy count equal to existing copy count,
|
---|
797 | * we will propose ro unregister machine fully else
|
---|
798 | * we will just propose to remove selected-items: */
|
---|
799 | const bool fVerdict = iSelectedCopyCount == iExistingCopyCount;
|
---|
800 | verdicts.insert(uId, fVerdict);
|
---|
801 | if (fVerdict)
|
---|
802 | {
|
---|
803 | if (pMachineItem->cacheType() == UIVirtualMachineItemType_Local)
|
---|
804 | localMachinesToUnregister.append(pMachineItem->cache()->toLocal()->machine());
|
---|
805 | else if (pMachineItem->cacheType() == UIVirtualMachineItemType_CloudReal)
|
---|
806 | cloudMachineItemsToUnregister.append(pMachineItem);
|
---|
807 | }
|
---|
808 | else
|
---|
809 | localMachineItemsToRemove << pMachineItem;
|
---|
810 | }
|
---|
811 |
|
---|
812 | /* If we have something to remove: */
|
---|
813 | if (!localMachineItemsToRemove.isEmpty())
|
---|
814 | removeLocalMachineItems(localMachineItemsToRemove);
|
---|
815 | /* If we have something local to unregister: */
|
---|
816 | if (!localMachinesToUnregister.isEmpty())
|
---|
817 | unregisterLocalMachines(localMachinesToUnregister);
|
---|
818 | /* If we have something cloud to unregister: */
|
---|
819 | if (!cloudMachineItemsToUnregister.isEmpty())
|
---|
820 | unregisterCloudMachineItems(cloudMachineItemsToUnregister);
|
---|
821 | }
|
---|
822 |
|
---|
823 | void UIChooserModel::moveSelectedMachineItemsToGroupItem(const QString &strName /* = QString() */)
|
---|
824 | {
|
---|
825 | /* Prepare target group pointers: */
|
---|
826 | UIChooserNodeGroup *pTargetGroupNode = 0;
|
---|
827 | UIChooserItemGroup *pTargetGroupItem = 0;
|
---|
828 | if (strName.isNull())
|
---|
829 | {
|
---|
830 | /* Create new group node in the current root: */
|
---|
831 | pTargetGroupNode = new UIChooserNodeGroup(invisibleRoot(),
|
---|
832 | invisibleRoot()->nodes().size() /* position */,
|
---|
833 | QUuid() /* id */,
|
---|
834 | uniqueGroupName(invisibleRoot()),
|
---|
835 | UIChooserNodeGroupType_Local,
|
---|
836 | true /* opened */);
|
---|
837 | pTargetGroupItem = new UIChooserItemGroup(root(), pTargetGroupNode);
|
---|
838 | }
|
---|
839 | else
|
---|
840 | {
|
---|
841 | /* Search for existing group with certain name: */
|
---|
842 | UIChooserItem *pTargetItem = root()->searchForItem(strName,
|
---|
843 | UIChooserItemSearchFlag_LocalGroup |
|
---|
844 | UIChooserItemSearchFlag_FullName);
|
---|
845 | AssertPtrReturnVoid(pTargetItem);
|
---|
846 | pTargetGroupItem = pTargetItem->toGroupItem();
|
---|
847 | UIChooserNode *pTargetNode = pTargetItem->node();
|
---|
848 | AssertPtrReturnVoid(pTargetNode);
|
---|
849 | pTargetGroupNode = pTargetNode->toGroupNode();
|
---|
850 | }
|
---|
851 | AssertPtrReturnVoid(pTargetGroupNode);
|
---|
852 | AssertPtrReturnVoid(pTargetGroupItem);
|
---|
853 |
|
---|
854 | /* For each of currently selected-items: */
|
---|
855 | QStringList busyGroupNames;
|
---|
856 | QStringList busyMachineNames;
|
---|
857 | QList<UIChooserItem*> copiedItems;
|
---|
858 | foreach (UIChooserItem *pItem, selectedItems())
|
---|
859 | {
|
---|
860 | /* For each of known types: */
|
---|
861 | switch (pItem->type())
|
---|
862 | {
|
---|
863 | case UIChooserNodeType_Group:
|
---|
864 | {
|
---|
865 | /* Avoid name collisions: */
|
---|
866 | if (busyGroupNames.contains(pItem->name()))
|
---|
867 | break;
|
---|
868 | /* Add name to busy: */
|
---|
869 | busyGroupNames << pItem->name();
|
---|
870 | /* Copy or move group-item: */
|
---|
871 | UIChooserNodeGroup *pNewGroupSubNode = new UIChooserNodeGroup(pTargetGroupNode,
|
---|
872 | pTargetGroupNode->nodes().size(),
|
---|
873 | pItem->node()->toGroupNode());
|
---|
874 | copiedItems << new UIChooserItemGroup(pTargetGroupItem, pNewGroupSubNode);
|
---|
875 | delete pItem->node();
|
---|
876 | break;
|
---|
877 | }
|
---|
878 | case UIChooserNodeType_Machine:
|
---|
879 | {
|
---|
880 | /* Avoid name collisions: */
|
---|
881 | if (busyMachineNames.contains(pItem->name()))
|
---|
882 | break;
|
---|
883 | /* Add name to busy: */
|
---|
884 | busyMachineNames << pItem->name();
|
---|
885 | /* Copy or move machine-item: */
|
---|
886 | UIChooserNodeMachine *pNewMachineSubNode = new UIChooserNodeMachine(pTargetGroupNode,
|
---|
887 | pTargetGroupNode->nodes().size(),
|
---|
888 | pItem->node()->toMachineNode());
|
---|
889 | copiedItems << new UIChooserItemMachine(pTargetGroupItem, pNewMachineSubNode);
|
---|
890 | delete pItem->node();
|
---|
891 | break;
|
---|
892 | }
|
---|
893 | }
|
---|
894 | }
|
---|
895 |
|
---|
896 | /* Update model: */
|
---|
897 | wipeOutEmptyGroups();
|
---|
898 | updateTreeForMainRoot();
|
---|
899 |
|
---|
900 | /* Check if we can select copied items: */
|
---|
901 | QList<UIChooserItem*> itemsToSelect;
|
---|
902 | foreach (UIChooserItem *pCopiedItem, copiedItems)
|
---|
903 | if (navigationItems().contains(pCopiedItem))
|
---|
904 | itemsToSelect << pCopiedItem;
|
---|
905 | if (!itemsToSelect.isEmpty())
|
---|
906 | {
|
---|
907 | setSelectedItems(itemsToSelect);
|
---|
908 | setCurrentItem(firstSelectedItem());
|
---|
909 | }
|
---|
910 | else
|
---|
911 | {
|
---|
912 | /* Otherwise check if we can select one of our parents: */
|
---|
913 | UIChooserItem *pItemToSelect = pTargetGroupItem;
|
---|
914 | while ( !navigationItems().contains(pItemToSelect)
|
---|
915 | && pItemToSelect->parentItem() != root())
|
---|
916 | pItemToSelect = pItemToSelect->parentItem();
|
---|
917 | if (navigationItems().contains(pItemToSelect))
|
---|
918 | setSelectedItem(pItemToSelect);
|
---|
919 | }
|
---|
920 |
|
---|
921 | /* Save groups finally: */
|
---|
922 | saveGroups();
|
---|
923 | }
|
---|
924 |
|
---|
925 | void UIChooserModel::startOrShowSelectedItems()
|
---|
926 | {
|
---|
927 | emit sigStartOrShowRequest();
|
---|
928 | }
|
---|
929 |
|
---|
930 | void UIChooserModel::refreshSelectedMachineItems()
|
---|
931 | {
|
---|
932 | /* Gather list of current unique inaccessible machine-items: */
|
---|
933 | QList<UIChooserItemMachine*> inaccessibleMachineItemList;
|
---|
934 | UIChooserItemMachine::enumerateMachineItems(selectedItems(), inaccessibleMachineItemList,
|
---|
935 | UIChooserItemMachineEnumerationFlag_Unique |
|
---|
936 | UIChooserItemMachineEnumerationFlag_Inaccessible);
|
---|
937 |
|
---|
938 | /* Prepare item to be selected: */
|
---|
939 | UIChooserItem *pSelectedItem = 0;
|
---|
940 |
|
---|
941 | /* For each machine-item: */
|
---|
942 | foreach (UIChooserItemMachine *pItem, inaccessibleMachineItemList)
|
---|
943 | {
|
---|
944 | AssertPtrReturnVoid(pItem);
|
---|
945 | switch (pItem->cacheType())
|
---|
946 | {
|
---|
947 | case UIVirtualMachineItemType_Local:
|
---|
948 | {
|
---|
949 | /* Recache: */
|
---|
950 | pItem->recache();
|
---|
951 |
|
---|
952 | /* Became accessible? */
|
---|
953 | if (pItem->accessible())
|
---|
954 | {
|
---|
955 | /* Acquire machine ID: */
|
---|
956 | const QUuid uId = pItem->id();
|
---|
957 | /* Reload this machine: */
|
---|
958 | sltReloadMachine(uId);
|
---|
959 | /* Select first of reloaded items: */
|
---|
960 | if (!pSelectedItem)
|
---|
961 | pSelectedItem = root()->searchForItem(uId.toString(),
|
---|
962 | UIChooserItemSearchFlag_Machine |
|
---|
963 | UIChooserItemSearchFlag_ExactId);
|
---|
964 | }
|
---|
965 |
|
---|
966 | break;
|
---|
967 | }
|
---|
968 | case UIVirtualMachineItemType_CloudFake:
|
---|
969 | {
|
---|
970 | /* Compose cloud entity key: */
|
---|
971 | UIChooserItem *pParent = pItem->parentItem();
|
---|
972 | AssertPtrReturnVoid(pParent);
|
---|
973 | UIChooserItem *pParentOfParent = pParent->parentItem();
|
---|
974 | AssertPtrReturnVoid(pParentOfParent);
|
---|
975 |
|
---|
976 | /* Create read cloud machine list task: */
|
---|
977 | const UICloudEntityKey guiCloudProfileKey = UICloudEntityKey(pParentOfParent->name(), pParent->name());
|
---|
978 | createReadCloudMachineListTask(guiCloudProfileKey, true /* with refresh? */);
|
---|
979 |
|
---|
980 | break;
|
---|
981 | }
|
---|
982 | case UIVirtualMachineItemType_CloudReal:
|
---|
983 | {
|
---|
984 | /* Much more simple than for local items, we are not reloading them, just refreshing: */
|
---|
985 | pItem->cache()->toCloud()->updateInfoAsync(false /* delayed */);
|
---|
986 |
|
---|
987 | break;
|
---|
988 | }
|
---|
989 | default:
|
---|
990 | break;
|
---|
991 | }
|
---|
992 | }
|
---|
993 |
|
---|
994 | /* Some item to be selected? */
|
---|
995 | if (pSelectedItem)
|
---|
996 | {
|
---|
997 | pSelectedItem->makeSureItsVisible();
|
---|
998 | setSelectedItem(pSelectedItem);
|
---|
999 | }
|
---|
1000 | }
|
---|
1001 |
|
---|
1002 | void UIChooserModel::sortSelectedGroupItem()
|
---|
1003 | {
|
---|
1004 | /* For single selected group, sort first selected item children: */
|
---|
1005 | if (isSingleGroupSelected())
|
---|
1006 | firstSelectedItem()->node()->sortNodes();
|
---|
1007 | /* Otherwise, sort first selected item neighbors: */
|
---|
1008 | else
|
---|
1009 | firstSelectedItem()->parentItem()->node()->sortNodes();
|
---|
1010 |
|
---|
1011 | /* Rebuild tree for main root: */
|
---|
1012 | buildTreeForMainRoot(true /* preserve selection */);
|
---|
1013 | }
|
---|
1014 |
|
---|
1015 | void UIChooserModel::setCurrentMachineItem(const QUuid &uId)
|
---|
1016 | {
|
---|
1017 | /* Look whether we have such item at all: */
|
---|
1018 | UIChooserItem *pItem = root()->searchForItem(uId.toString(),
|
---|
1019 | UIChooserItemSearchFlag_Machine |
|
---|
1020 | UIChooserItemSearchFlag_ExactId);
|
---|
1021 |
|
---|
1022 | /* Select item if exists: */
|
---|
1023 | if (pItem)
|
---|
1024 | setSelectedItem(pItem);
|
---|
1025 | }
|
---|
1026 |
|
---|
1027 | void UIChooserModel::setCurrentGlobalItem()
|
---|
1028 | {
|
---|
1029 | /* Look whether we have such item at all: */
|
---|
1030 | UIChooserItem *pItem = root()->searchForItem(QString(),
|
---|
1031 | UIChooserItemSearchFlag_Global);
|
---|
1032 |
|
---|
1033 | /* Select item if exists: */
|
---|
1034 | if (pItem)
|
---|
1035 | setSelectedItem(pItem);
|
---|
1036 | }
|
---|
1037 |
|
---|
1038 | void UIChooserModel::setCurrentDragObject(QDrag *pDragObject)
|
---|
1039 | {
|
---|
1040 | /* Make sure real focus unset: */
|
---|
1041 | clearRealFocus();
|
---|
1042 |
|
---|
1043 | /* Remember new drag-object: */
|
---|
1044 | m_pCurrentDragObject = pDragObject;
|
---|
1045 | connect(m_pCurrentDragObject.data(), &QDrag::destroyed,
|
---|
1046 | this, &UIChooserModel::sltCurrentDragObjectDestroyed);
|
---|
1047 | }
|
---|
1048 |
|
---|
1049 | void UIChooserModel::lookFor(const QString &strLookupText)
|
---|
1050 | {
|
---|
1051 | if (view())
|
---|
1052 | {
|
---|
1053 | view()->setSearchWidgetVisible(true);
|
---|
1054 | view()->appendToSearchString(strLookupText);
|
---|
1055 | }
|
---|
1056 | }
|
---|
1057 |
|
---|
1058 | void UIChooserModel::updateLayout()
|
---|
1059 | {
|
---|
1060 | /* Sanity check. This method can be called when invisible root is
|
---|
1061 | * temporary deleted. We should ignore request in such case. */
|
---|
1062 | if (!view() || !root())
|
---|
1063 | return;
|
---|
1064 |
|
---|
1065 | /* Initialize variables: */
|
---|
1066 | const QSize viewportSize = view()->size();
|
---|
1067 | const int iViewportWidth = viewportSize.width();
|
---|
1068 | const int iViewportHeight = root()->minimumSizeHint().toSize().height();
|
---|
1069 |
|
---|
1070 | /* Move root: */
|
---|
1071 | root()->setPos(0, 0);
|
---|
1072 | /* Resize root: */
|
---|
1073 | root()->resize(iViewportWidth, iViewportHeight);
|
---|
1074 | /* Layout root content: */
|
---|
1075 | root()->updateLayout();
|
---|
1076 | }
|
---|
1077 |
|
---|
1078 | void UIChooserModel::setGlobalItemHeightHint(int iHint)
|
---|
1079 | {
|
---|
1080 | /* Save and apply global item height hint: */
|
---|
1081 | m_iGlobalItemHeightHint = iHint;
|
---|
1082 | applyGlobalItemHeightHint();
|
---|
1083 | }
|
---|
1084 |
|
---|
1085 | void UIChooserModel::sltHandleViewResized()
|
---|
1086 | {
|
---|
1087 | /* Relayout: */
|
---|
1088 | updateLayout();
|
---|
1089 |
|
---|
1090 | /* Make current item visible asynchronously: */
|
---|
1091 | QMetaObject::invokeMethod(this, "sltMakeSureCurrentItemVisible", Qt::QueuedConnection);
|
---|
1092 | }
|
---|
1093 |
|
---|
1094 | bool UIChooserModel::eventFilter(QObject *pWatched, QEvent *pEvent)
|
---|
1095 | {
|
---|
1096 | /* Process only scene events: */
|
---|
1097 | if (pWatched != scene())
|
---|
1098 | return QObject::eventFilter(pWatched, pEvent);
|
---|
1099 |
|
---|
1100 | /* Process only item focused by model: */
|
---|
1101 | if (scene()->focusItem())
|
---|
1102 | return QObject::eventFilter(pWatched, pEvent);
|
---|
1103 |
|
---|
1104 | /* Checking event-type: */
|
---|
1105 | switch (pEvent->type())
|
---|
1106 | {
|
---|
1107 | /* Keyboard handler: */
|
---|
1108 | case QEvent::KeyPress:
|
---|
1109 | return m_pKeyboardHandler->handle(static_cast<QKeyEvent*>(pEvent), UIKeyboardEventType_Press);
|
---|
1110 | case QEvent::KeyRelease:
|
---|
1111 | return m_pKeyboardHandler->handle(static_cast<QKeyEvent*>(pEvent), UIKeyboardEventType_Release);
|
---|
1112 | /* Mouse handler: */
|
---|
1113 | case QEvent::GraphicsSceneMousePress:
|
---|
1114 | return m_pMouseHandler->handle(static_cast<QGraphicsSceneMouseEvent*>(pEvent), UIMouseEventType_Press);
|
---|
1115 | case QEvent::GraphicsSceneMouseRelease:
|
---|
1116 | return m_pMouseHandler->handle(static_cast<QGraphicsSceneMouseEvent*>(pEvent), UIMouseEventType_Release);
|
---|
1117 | case QEvent::GraphicsSceneMouseDoubleClick:
|
---|
1118 | return m_pMouseHandler->handle(static_cast<QGraphicsSceneMouseEvent*>(pEvent), UIMouseEventType_DoubleClick);
|
---|
1119 | /* Context-menu handler: */
|
---|
1120 | case QEvent::GraphicsSceneContextMenu:
|
---|
1121 | return processContextMenuEvent(static_cast<QGraphicsSceneContextMenuEvent*>(pEvent));
|
---|
1122 | /* Drag&drop scroll-event (drag-move) handler: */
|
---|
1123 | case QEvent::GraphicsSceneDragMove:
|
---|
1124 | return processDragMoveEvent(static_cast<QGraphicsSceneDragDropEvent*>(pEvent));
|
---|
1125 | /* Drag&drop scroll-event (drag-leave) handler: */
|
---|
1126 | case QEvent::GraphicsSceneDragLeave:
|
---|
1127 | return processDragLeaveEvent(static_cast<QGraphicsSceneDragDropEvent*>(pEvent));
|
---|
1128 | default: break; /* Shut up MSC */
|
---|
1129 | }
|
---|
1130 |
|
---|
1131 | /* Call to base-class: */
|
---|
1132 | return QObject::eventFilter(pWatched, pEvent);
|
---|
1133 | }
|
---|
1134 |
|
---|
1135 | void UIChooserModel::sltLocalMachineRegistrationChanged(const QUuid &uMachineId, const bool fRegistered)
|
---|
1136 | {
|
---|
1137 | /* Existing VM unregistered => make sure no item with passed uMachineId is selected: */
|
---|
1138 | if (!fRegistered)
|
---|
1139 | makeSureNoItemWithCertainIdSelected(uMachineId);
|
---|
1140 |
|
---|
1141 | /* Call to base-class: */
|
---|
1142 | UIChooserAbstractModel::sltLocalMachineRegistrationChanged(uMachineId, fRegistered);
|
---|
1143 |
|
---|
1144 | /* Existing VM unregistered? */
|
---|
1145 | if (!fRegistered)
|
---|
1146 | {
|
---|
1147 | /* Update tree for main root: */
|
---|
1148 | updateTreeForMainRoot();
|
---|
1149 | }
|
---|
1150 | /* New VM registered? */
|
---|
1151 | else
|
---|
1152 | {
|
---|
1153 | /* Should we show this VM? */
|
---|
1154 | if (gEDataManager->showMachineInVirtualBoxManagerChooser(uMachineId))
|
---|
1155 | {
|
---|
1156 | /* Rebuild tree for main root: */
|
---|
1157 | buildTreeForMainRoot(true /* preserve selection */);
|
---|
1158 | /* Search for newly added item: */
|
---|
1159 | UIChooserItem *pNewItem = root()->searchForItem(uMachineId.toString(),
|
---|
1160 | UIChooserItemSearchFlag_Machine |
|
---|
1161 | UIChooserItemSearchFlag_ExactId);
|
---|
1162 | /* Select newly added item if any: */
|
---|
1163 | if (pNewItem)
|
---|
1164 | setSelectedItem(pNewItem);
|
---|
1165 | }
|
---|
1166 | }
|
---|
1167 | }
|
---|
1168 |
|
---|
1169 | void UIChooserModel::sltHandleCloudProviderUninstall(const QUuid &uProviderId)
|
---|
1170 | {
|
---|
1171 | /* Call to base-class: */
|
---|
1172 | UIChooserAbstractModel::sltHandleCloudProviderUninstall(uProviderId);
|
---|
1173 |
|
---|
1174 | /* Notify about selection invalidated: */
|
---|
1175 | emit sigSelectionInvalidated();
|
---|
1176 | }
|
---|
1177 |
|
---|
1178 | void UIChooserModel::sltReloadMachine(const QUuid &uMachineId)
|
---|
1179 | {
|
---|
1180 | /* Call to base-class: */
|
---|
1181 | UIChooserAbstractModel::sltReloadMachine(uMachineId);
|
---|
1182 |
|
---|
1183 | /* Should we show this VM? */
|
---|
1184 | if (gEDataManager->showMachineInVirtualBoxManagerChooser(uMachineId))
|
---|
1185 | {
|
---|
1186 | /* Rebuild tree for main root: */
|
---|
1187 | buildTreeForMainRoot(false /* preserve selection */);
|
---|
1188 | /* Select newly added item: */
|
---|
1189 | setSelectedItem(root()->searchForItem(uMachineId.toString(),
|
---|
1190 | UIChooserItemSearchFlag_Machine |
|
---|
1191 | UIChooserItemSearchFlag_ExactId));
|
---|
1192 | }
|
---|
1193 | makeSureAtLeastOneItemSelected();
|
---|
1194 |
|
---|
1195 | /* Notify listeners about selection change: */
|
---|
1196 | emit sigSelectionChanged();
|
---|
1197 | }
|
---|
1198 |
|
---|
1199 | void UIChooserModel::sltDetachCOM()
|
---|
1200 | {
|
---|
1201 | /* Clean tree for main root: */
|
---|
1202 | clearTreeForMainRoot();
|
---|
1203 | emit sigSelectionInvalidated();
|
---|
1204 |
|
---|
1205 | /* Call to base-class: */
|
---|
1206 | UIChooserAbstractModel::sltDetachCOM();
|
---|
1207 | }
|
---|
1208 |
|
---|
1209 | void UIChooserModel::sltCloudMachineUnregistered(const QString &strProviderShortName,
|
---|
1210 | const QString &strProfileName,
|
---|
1211 | const QUuid &uId)
|
---|
1212 | {
|
---|
1213 | /* Make sure no item with passed uId is selected: */
|
---|
1214 | makeSureNoItemWithCertainIdSelected(uId);
|
---|
1215 |
|
---|
1216 | /* Call to base-class: */
|
---|
1217 | UIChooserAbstractModel::sltCloudMachineUnregistered(strProviderShortName, strProfileName, uId);
|
---|
1218 |
|
---|
1219 | /* Rebuild tree for main root: */
|
---|
1220 | buildTreeForMainRoot(true /* preserve selection */);
|
---|
1221 | }
|
---|
1222 |
|
---|
1223 | void UIChooserModel::sltCloudMachinesUnregistered(const QString &strProviderShortName,
|
---|
1224 | const QString &strProfileName,
|
---|
1225 | const QList<QUuid> &ids)
|
---|
1226 | {
|
---|
1227 | /* Make sure no item with one of passed ids is selected: */
|
---|
1228 | foreach (const QUuid &uId, ids)
|
---|
1229 | makeSureNoItemWithCertainIdSelected(uId);
|
---|
1230 |
|
---|
1231 | /* Call to base-class: */
|
---|
1232 | UIChooserAbstractModel::sltCloudMachinesUnregistered(strProviderShortName, strProfileName, ids);
|
---|
1233 |
|
---|
1234 | /* Rebuild tree for main root: */
|
---|
1235 | buildTreeForMainRoot(true /* preserve selection */);
|
---|
1236 | }
|
---|
1237 |
|
---|
1238 | void UIChooserModel::sltCloudMachineRegistered(const QString &strProviderShortName,
|
---|
1239 | const QString &strProfileName,
|
---|
1240 | const CCloudMachine &comMachine)
|
---|
1241 | {
|
---|
1242 | /* Call to base-class: */
|
---|
1243 | UIChooserAbstractModel::sltCloudMachineRegistered(strProviderShortName, strProfileName, comMachine);
|
---|
1244 |
|
---|
1245 | /* Rebuild tree for main root: */
|
---|
1246 | buildTreeForMainRoot(false /* preserve selection */);
|
---|
1247 |
|
---|
1248 | /* Select newly added item: */
|
---|
1249 | QUuid uMachineId;
|
---|
1250 | if (cloudMachineId(comMachine, uMachineId))
|
---|
1251 | setSelectedItem(root()->searchForItem(uMachineId.toString(),
|
---|
1252 | UIChooserItemSearchFlag_Machine |
|
---|
1253 | UIChooserItemSearchFlag_ExactId));
|
---|
1254 | }
|
---|
1255 |
|
---|
1256 | void UIChooserModel::sltCloudMachinesRegistered(const QString &strProviderShortName,
|
---|
1257 | const QString &strProfileName,
|
---|
1258 | const QVector<CCloudMachine> &machines)
|
---|
1259 | {
|
---|
1260 | /* Call to base-class: */
|
---|
1261 | UIChooserAbstractModel::sltCloudMachinesRegistered(strProviderShortName, strProfileName, machines);
|
---|
1262 |
|
---|
1263 | /* Rebuild tree for main root: */
|
---|
1264 | buildTreeForMainRoot(true /* preserve selection */);
|
---|
1265 | }
|
---|
1266 |
|
---|
1267 | void UIChooserModel::sltHandleReadCloudMachineListTaskComplete()
|
---|
1268 | {
|
---|
1269 | /* Call to base-class: */
|
---|
1270 | UIChooserAbstractModel::sltHandleReadCloudMachineListTaskComplete();
|
---|
1271 |
|
---|
1272 | /* Postpone update since we have just updated: */
|
---|
1273 | if (m_pTimerCloudProfileUpdate)
|
---|
1274 | m_pTimerCloudProfileUpdate->start(10000);
|
---|
1275 | }
|
---|
1276 |
|
---|
1277 | void UIChooserModel::sltHandleCloudProfileManagerCumulativeChange()
|
---|
1278 | {
|
---|
1279 | /* Call to base-class: */
|
---|
1280 | UIChooserAbstractModel::sltHandleCloudProfileManagerCumulativeChange();
|
---|
1281 |
|
---|
1282 | /* Build tree for main root: */
|
---|
1283 | buildTreeForMainRoot(true /* preserve selection */);
|
---|
1284 | }
|
---|
1285 |
|
---|
1286 | void UIChooserModel::sltMakeSureCurrentItemVisible()
|
---|
1287 | {
|
---|
1288 | root()->toGroupItem()->makeSureItemIsVisible(currentItem());
|
---|
1289 | }
|
---|
1290 |
|
---|
1291 | void UIChooserModel::sltCurrentItemDestroyed()
|
---|
1292 | {
|
---|
1293 | AssertMsgFailed(("Current-item destroyed!"));
|
---|
1294 | }
|
---|
1295 |
|
---|
1296 | void UIChooserModel::sltStartScrolling()
|
---|
1297 | {
|
---|
1298 | /* Make sure view exists: */
|
---|
1299 | AssertPtrReturnVoid(view());
|
---|
1300 |
|
---|
1301 | /* Should we scroll? */
|
---|
1302 | if (!m_fIsScrollingInProgress)
|
---|
1303 | return;
|
---|
1304 |
|
---|
1305 | /* Reset scrolling progress: */
|
---|
1306 | m_fIsScrollingInProgress = false;
|
---|
1307 |
|
---|
1308 | /* Convert mouse position to view co-ordinates: */
|
---|
1309 | const QPoint mousePos = view()->mapFromGlobal(QCursor::pos());
|
---|
1310 | /* Mouse position is at the top of view? */
|
---|
1311 | if (mousePos.y() < m_iScrollingTokenSize && mousePos.y() > 0)
|
---|
1312 | {
|
---|
1313 | int iValue = mousePos.y();
|
---|
1314 | if (!iValue)
|
---|
1315 | iValue = 1;
|
---|
1316 | const int iDelta = m_iScrollingTokenSize / iValue;
|
---|
1317 | /* Backward scrolling: */
|
---|
1318 | root()->toGroupItem()->scrollBy(- 2 * iDelta);
|
---|
1319 | m_fIsScrollingInProgress = true;
|
---|
1320 | QTimer::singleShot(10, this, SLOT(sltStartScrolling()));
|
---|
1321 | }
|
---|
1322 | /* Mouse position is at the bottom of view? */
|
---|
1323 | else if (mousePos.y() > view()->height() - m_iScrollingTokenSize && mousePos.y() < view()->height())
|
---|
1324 | {
|
---|
1325 | int iValue = view()->height() - mousePos.y();
|
---|
1326 | if (!iValue)
|
---|
1327 | iValue = 1;
|
---|
1328 | const int iDelta = m_iScrollingTokenSize / iValue;
|
---|
1329 | /* Forward scrolling: */
|
---|
1330 | root()->toGroupItem()->scrollBy(2 * iDelta);
|
---|
1331 | m_fIsScrollingInProgress = true;
|
---|
1332 | QTimer::singleShot(10, this, SLOT(sltStartScrolling()));
|
---|
1333 | }
|
---|
1334 | }
|
---|
1335 |
|
---|
1336 | void UIChooserModel::sltCurrentDragObjectDestroyed()
|
---|
1337 | {
|
---|
1338 | root()->resetDragToken();
|
---|
1339 | }
|
---|
1340 |
|
---|
1341 | void UIChooserModel::sltHandleCloudMachineRemoved(const QString &strProviderShortName,
|
---|
1342 | const QString &strProfileName,
|
---|
1343 | const QString &strName)
|
---|
1344 | {
|
---|
1345 | Q_UNUSED(strName);
|
---|
1346 |
|
---|
1347 | /* Update profile to make sure it has no stale instances: */
|
---|
1348 | const UICloudEntityKey cloudEntityKeyForProfile = UICloudEntityKey(strProviderShortName, strProfileName);
|
---|
1349 | createReadCloudMachineListTask(cloudEntityKeyForProfile, false /* with refresh? */);
|
---|
1350 | }
|
---|
1351 |
|
---|
1352 | void UIChooserModel::sltUpdateSelectedCloudProfiles()
|
---|
1353 | {
|
---|
1354 | /* Postpone update since we are already working on it: */
|
---|
1355 | if (m_pTimerCloudProfileUpdate)
|
---|
1356 | m_pTimerCloudProfileUpdate->start(10000);
|
---|
1357 |
|
---|
1358 | /* Compose a list of items to update: */
|
---|
1359 | QList<UIChooserItem*> itemsToUpdate;
|
---|
1360 |
|
---|
1361 | /* For the case if requested - just update everything: */
|
---|
1362 | if (isKeepCloudNodesUpdated())
|
---|
1363 | {
|
---|
1364 | /* Search for a list of provider nodes having items: */
|
---|
1365 | QList<UIChooserNode*> providerNodes;
|
---|
1366 | invisibleRoot()->searchForNodes(QString(),
|
---|
1367 | UIChooserItemSearchFlag_CloudProvider,
|
---|
1368 | providerNodes);
|
---|
1369 | foreach (UIChooserNode *pNode, providerNodes)
|
---|
1370 | if (UIChooserItem *pItem = pNode->item())
|
---|
1371 | itemsToUpdate << pItem;
|
---|
1372 |
|
---|
1373 | /* Have at least something as a fallback: */
|
---|
1374 | if (itemsToUpdate.isEmpty())
|
---|
1375 | itemsToUpdate << selectedItems();
|
---|
1376 | }
|
---|
1377 | /* Otherwise update selected items only: */
|
---|
1378 | else
|
---|
1379 | itemsToUpdate << selectedItems();
|
---|
1380 |
|
---|
1381 | /* For every selected item: */
|
---|
1382 | QSet<UICloudEntityKey> selectedCloudProfileKeys;
|
---|
1383 | foreach (UIChooserItem *pSelectedItem, itemsToUpdate)
|
---|
1384 | {
|
---|
1385 | /* Enumerate cloud profile keys to update: */
|
---|
1386 | switch (pSelectedItem->type())
|
---|
1387 | {
|
---|
1388 | case UIChooserNodeType_Group:
|
---|
1389 | {
|
---|
1390 | UIChooserItemGroup *pGroupItem = pSelectedItem->toGroupItem();
|
---|
1391 | AssertPtrReturnVoid(pGroupItem);
|
---|
1392 | switch (pGroupItem->groupType())
|
---|
1393 | {
|
---|
1394 | case UIChooserNodeGroupType_Provider:
|
---|
1395 | {
|
---|
1396 | const QString strProviderShortName = pSelectedItem->name();
|
---|
1397 | foreach (UIChooserItem *pChildItem, pSelectedItem->items(UIChooserNodeType_Group))
|
---|
1398 | {
|
---|
1399 | const QString strProfileName = pChildItem->name();
|
---|
1400 | const UICloudEntityKey guiCloudProfileKey = UICloudEntityKey(strProviderShortName, strProfileName);
|
---|
1401 | if (!selectedCloudProfileKeys.contains(guiCloudProfileKey))
|
---|
1402 | selectedCloudProfileKeys.insert(guiCloudProfileKey);
|
---|
1403 | }
|
---|
1404 | break;
|
---|
1405 | }
|
---|
1406 | case UIChooserNodeGroupType_Profile:
|
---|
1407 | {
|
---|
1408 | const QString strProviderShortName = pSelectedItem->parentItem()->name();
|
---|
1409 | const QString strProfileName = pSelectedItem->name();
|
---|
1410 | const UICloudEntityKey guiCloudProfileKey = UICloudEntityKey(strProviderShortName, strProfileName);
|
---|
1411 | if (!selectedCloudProfileKeys.contains(guiCloudProfileKey))
|
---|
1412 | selectedCloudProfileKeys.insert(guiCloudProfileKey);
|
---|
1413 | break;
|
---|
1414 | }
|
---|
1415 | default:
|
---|
1416 | break;
|
---|
1417 | }
|
---|
1418 | break;
|
---|
1419 | }
|
---|
1420 | case UIChooserNodeType_Machine:
|
---|
1421 | {
|
---|
1422 | UIChooserItemMachine *pMachineItem = pSelectedItem->toMachineItem();
|
---|
1423 | AssertPtrReturnVoid(pMachineItem);
|
---|
1424 | if ( pMachineItem->cacheType() == UIVirtualMachineItemType_CloudFake
|
---|
1425 | || pMachineItem->cacheType() == UIVirtualMachineItemType_CloudReal)
|
---|
1426 | {
|
---|
1427 | const QString strProviderShortName = pMachineItem->parentItem()->parentItem()->name();
|
---|
1428 | const QString strProfileName = pMachineItem->parentItem()->name();
|
---|
1429 | const UICloudEntityKey guiCloudProfileKey = UICloudEntityKey(strProviderShortName, strProfileName);
|
---|
1430 | if (!selectedCloudProfileKeys.contains(guiCloudProfileKey))
|
---|
1431 | selectedCloudProfileKeys.insert(guiCloudProfileKey);
|
---|
1432 | }
|
---|
1433 | break;
|
---|
1434 | }
|
---|
1435 | }
|
---|
1436 | }
|
---|
1437 |
|
---|
1438 | /* Restart List Cloud Machines task for selected profile keys: */
|
---|
1439 | foreach (const UICloudEntityKey &guiCloudProfileKey, selectedCloudProfileKeys)
|
---|
1440 | createReadCloudMachineListTask(guiCloudProfileKey, false /* with refresh? */);
|
---|
1441 | }
|
---|
1442 |
|
---|
1443 | void UIChooserModel::prepare()
|
---|
1444 | {
|
---|
1445 | prepareScene();
|
---|
1446 | prepareContextMenu();
|
---|
1447 | prepareHandlers();
|
---|
1448 | prepareCloudUpdateTimer();
|
---|
1449 | prepareConnections();
|
---|
1450 | }
|
---|
1451 |
|
---|
1452 | void UIChooserModel::prepareScene()
|
---|
1453 | {
|
---|
1454 | m_pScene = new QGraphicsScene(this);
|
---|
1455 | if (m_pScene)
|
---|
1456 | m_pScene->installEventFilter(this);
|
---|
1457 | }
|
---|
1458 |
|
---|
1459 | void UIChooserModel::prepareContextMenu()
|
---|
1460 | {
|
---|
1461 | /* Context menu for global(s): */
|
---|
1462 | m_localMenus[UIChooserNodeType_Global] = new QMenu;
|
---|
1463 | if (QMenu *pMenuGlobal = m_localMenus.value(UIChooserNodeType_Global))
|
---|
1464 | {
|
---|
1465 | #ifdef VBOX_WS_MAC
|
---|
1466 | pMenuGlobal->addAction(actionPool()->action(UIActionIndex_M_Application_S_About));
|
---|
1467 | pMenuGlobal->addSeparator();
|
---|
1468 | pMenuGlobal->addAction(actionPool()->action(UIActionIndex_M_Application_S_Preferences));
|
---|
1469 | pMenuGlobal->addSeparator();
|
---|
1470 | pMenuGlobal->addAction(actionPool()->action(UIActionIndexMN_M_File_S_ImportAppliance));
|
---|
1471 | pMenuGlobal->addAction(actionPool()->action(UIActionIndexMN_M_File_S_ExportAppliance));
|
---|
1472 | # ifdef VBOX_GUI_WITH_EXTRADATA_MANAGER_UI
|
---|
1473 | pMenuGlobal->addAction(actionPool()->action(UIActionIndexMN_M_File_S_ShowExtraDataManager));
|
---|
1474 | pMenuGlobal->addSeparator();
|
---|
1475 | # endif
|
---|
1476 | pMenuGlobal->addAction(actionPool()->action(UIActionIndexMN_M_File_M_Tools));
|
---|
1477 |
|
---|
1478 | #else /* !VBOX_WS_MAC */
|
---|
1479 |
|
---|
1480 | pMenuGlobal->addAction(actionPool()->action(UIActionIndex_M_Application_S_Preferences));
|
---|
1481 | pMenuGlobal->addSeparator();
|
---|
1482 | pMenuGlobal->addAction(actionPool()->action(UIActionIndexMN_M_File_S_ImportAppliance));
|
---|
1483 | pMenuGlobal->addAction(actionPool()->action(UIActionIndexMN_M_File_S_ExportAppliance));
|
---|
1484 | pMenuGlobal->addSeparator();
|
---|
1485 | # ifdef VBOX_GUI_WITH_EXTRADATA_MANAGER_UI
|
---|
1486 | pMenuGlobal->addAction(actionPool()->action(UIActionIndexMN_M_File_S_ShowExtraDataManager));
|
---|
1487 | pMenuGlobal->addSeparator();
|
---|
1488 | # endif
|
---|
1489 | pMenuGlobal->addAction(actionPool()->action(UIActionIndexMN_M_File_M_Tools));
|
---|
1490 | pMenuGlobal->addSeparator();
|
---|
1491 | # ifdef VBOX_GUI_WITH_NETWORK_MANAGER
|
---|
1492 | if (gEDataManager->applicationUpdateEnabled())
|
---|
1493 | pMenuGlobal->addAction(actionPool()->action(UIActionIndex_M_Application_S_CheckForUpdates));
|
---|
1494 | # endif
|
---|
1495 | #endif /* !VBOX_WS_MAC */
|
---|
1496 | }
|
---|
1497 |
|
---|
1498 | /* Context menu for local group(s): */
|
---|
1499 | m_localMenus[UIChooserNodeType_Group] = new QMenu;
|
---|
1500 | if (QMenu *pMenuGroup = m_localMenus.value(UIChooserNodeType_Group))
|
---|
1501 | {
|
---|
1502 | pMenuGroup->addAction(actionPool()->action(UIActionIndexMN_M_Group_S_New));
|
---|
1503 | pMenuGroup->addAction(actionPool()->action(UIActionIndexMN_M_Group_S_Add));
|
---|
1504 | pMenuGroup->addSeparator();
|
---|
1505 | pMenuGroup->addAction(actionPool()->action(UIActionIndexMN_M_Group_S_Rename));
|
---|
1506 | pMenuGroup->addAction(actionPool()->action(UIActionIndexMN_M_Group_S_Remove));
|
---|
1507 | pMenuGroup->addMenu(actionPool()->action(UIActionIndexMN_M_Group_M_MoveToGroup)->menu());
|
---|
1508 | pMenuGroup->addSeparator();
|
---|
1509 | pMenuGroup->addAction(actionPool()->action(UIActionIndexMN_M_Group_M_StartOrShow));
|
---|
1510 | pMenuGroup->addAction(actionPool()->action(UIActionIndexMN_M_Group_T_Pause));
|
---|
1511 | pMenuGroup->addAction(actionPool()->action(UIActionIndexMN_M_Group_S_Reset));
|
---|
1512 | // pMenuGroup->addAction(actionPool()->action(UIActionIndexMN_M_Group_S_Detach));
|
---|
1513 | pMenuGroup->addMenu(actionPool()->action(UIActionIndexMN_M_Group_M_Stop)->menu());
|
---|
1514 | pMenuGroup->addSeparator();
|
---|
1515 | pMenuGroup->addAction(actionPool()->action(UIActionIndexMN_M_Group_S_Discard));
|
---|
1516 | pMenuGroup->addAction(actionPool()->action(UIActionIndexMN_M_Group_S_Refresh));
|
---|
1517 | pMenuGroup->addSeparator();
|
---|
1518 | pMenuGroup->addAction(actionPool()->action(UIActionIndexMN_M_Group_S_ShowInFileManager));
|
---|
1519 | pMenuGroup->addAction(actionPool()->action(UIActionIndexMN_M_Group_S_CreateShortcut));
|
---|
1520 | pMenuGroup->addSeparator();
|
---|
1521 | pMenuGroup->addAction(actionPool()->action(UIActionIndexMN_M_Group_S_Sort));
|
---|
1522 | pMenuGroup->addAction(actionPool()->action(UIActionIndexMN_M_Group_T_Search));
|
---|
1523 | }
|
---|
1524 |
|
---|
1525 | /* Context menu for local machine(s): */
|
---|
1526 | m_localMenus[UIChooserNodeType_Machine] = new QMenu;
|
---|
1527 | if (QMenu *pMenuMachine = m_localMenus.value(UIChooserNodeType_Machine))
|
---|
1528 | {
|
---|
1529 | pMenuMachine->addAction(actionPool()->action(UIActionIndexMN_M_Machine_S_Settings));
|
---|
1530 | pMenuMachine->addAction(actionPool()->action(UIActionIndexMN_M_Machine_S_Clone));
|
---|
1531 | pMenuMachine->addAction(actionPool()->action(UIActionIndexMN_M_Machine_S_Move));
|
---|
1532 | pMenuMachine->addAction(actionPool()->action(UIActionIndexMN_M_Machine_S_ExportToOCI));
|
---|
1533 | pMenuMachine->addAction(actionPool()->action(UIActionIndexMN_M_Machine_S_Remove));
|
---|
1534 | pMenuMachine->addMenu(actionPool()->action(UIActionIndexMN_M_Machine_M_MoveToGroup)->menu());
|
---|
1535 | pMenuMachine->addSeparator();
|
---|
1536 | pMenuMachine->addAction(actionPool()->action(UIActionIndexMN_M_Machine_M_StartOrShow));
|
---|
1537 | pMenuMachine->addAction(actionPool()->action(UIActionIndexMN_M_Machine_T_Pause));
|
---|
1538 | pMenuMachine->addAction(actionPool()->action(UIActionIndexMN_M_Machine_S_Reset));
|
---|
1539 | // pMenuMachine->addAction(actionPool()->action(UIActionIndexMN_M_Machine_S_Detach));
|
---|
1540 | pMenuMachine->addMenu(actionPool()->action(UIActionIndexMN_M_Machine_M_Stop)->menu());
|
---|
1541 | pMenuMachine->addSeparator();
|
---|
1542 | pMenuMachine->addAction(actionPool()->action(UIActionIndexMN_M_Machine_S_Discard));
|
---|
1543 | pMenuMachine->addAction(actionPool()->action(UIActionIndexMN_M_Machine_S_Refresh));
|
---|
1544 | pMenuMachine->addSeparator();
|
---|
1545 | pMenuMachine->addAction(actionPool()->action(UIActionIndexMN_M_Machine_S_ShowInFileManager));
|
---|
1546 | pMenuMachine->addAction(actionPool()->action(UIActionIndexMN_M_Machine_S_CreateShortcut));
|
---|
1547 | pMenuMachine->addSeparator();
|
---|
1548 | pMenuMachine->addAction(actionPool()->action(UIActionIndexMN_M_Machine_S_SortParent));
|
---|
1549 | pMenuMachine->addAction(actionPool()->action(UIActionIndexMN_M_Machine_T_Search));
|
---|
1550 | }
|
---|
1551 |
|
---|
1552 | /* Context menu for cloud group(s): */
|
---|
1553 | m_cloudMenus[UIChooserNodeType_Group] = new QMenu;
|
---|
1554 | if (QMenu *pMenuGroup = m_cloudMenus.value(UIChooserNodeType_Group))
|
---|
1555 | {
|
---|
1556 | pMenuGroup->addAction(actionPool()->action(UIActionIndexMN_M_Group_S_New));
|
---|
1557 | pMenuGroup->addAction(actionPool()->action(UIActionIndexMN_M_Group_S_Add));
|
---|
1558 | pMenuGroup->addSeparator();
|
---|
1559 | pMenuGroup->addAction(actionPool()->action(UIActionIndexMN_M_Group_M_StartOrShow));
|
---|
1560 | pMenuGroup->addAction(actionPool()->action(UIActionIndexMN_M_Group_S_Reset));
|
---|
1561 | pMenuGroup->addMenu(actionPool()->action(UIActionIndexMN_M_Group_M_Console)->menu());
|
---|
1562 | pMenuGroup->addMenu(actionPool()->action(UIActionIndexMN_M_Group_M_Stop)->menu());
|
---|
1563 | pMenuGroup->addSeparator();
|
---|
1564 | pMenuGroup->addAction(actionPool()->action(UIActionIndexMN_M_Group_S_Refresh));
|
---|
1565 | pMenuGroup->addSeparator();
|
---|
1566 | pMenuGroup->addAction(actionPool()->action(UIActionIndexMN_M_Group_S_Sort));
|
---|
1567 | pMenuGroup->addAction(actionPool()->action(UIActionIndexMN_M_Group_T_Search));
|
---|
1568 | }
|
---|
1569 |
|
---|
1570 | /* Context menu for cloud machine(s): */
|
---|
1571 | m_cloudMenus[UIChooserNodeType_Machine] = new QMenu;
|
---|
1572 | if (QMenu *pMenuMachine = m_cloudMenus.value(UIChooserNodeType_Machine))
|
---|
1573 | {
|
---|
1574 | pMenuMachine->addAction(actionPool()->action(UIActionIndexMN_M_Machine_S_Settings));
|
---|
1575 | pMenuMachine->addAction(actionPool()->action(UIActionIndexMN_M_Machine_S_Clone));
|
---|
1576 | pMenuMachine->addAction(actionPool()->action(UIActionIndexMN_M_Machine_S_Remove));
|
---|
1577 | pMenuMachine->addSeparator();
|
---|
1578 | pMenuMachine->addAction(actionPool()->action(UIActionIndexMN_M_Machine_M_StartOrShow));
|
---|
1579 | pMenuMachine->addAction(actionPool()->action(UIActionIndexMN_M_Machine_S_Reset));
|
---|
1580 | pMenuMachine->addMenu(actionPool()->action(UIActionIndexMN_M_Machine_M_Console)->menu());
|
---|
1581 | pMenuMachine->addMenu(actionPool()->action(UIActionIndexMN_M_Machine_M_Stop)->menu());
|
---|
1582 | pMenuMachine->addSeparator();
|
---|
1583 | pMenuMachine->addAction(actionPool()->action(UIActionIndexMN_M_Machine_S_Refresh));
|
---|
1584 | pMenuMachine->addSeparator();
|
---|
1585 | pMenuMachine->addAction(actionPool()->action(UIActionIndexMN_M_Machine_S_SortParent));
|
---|
1586 | pMenuMachine->addAction(actionPool()->action(UIActionIndexMN_M_Machine_T_Search));
|
---|
1587 | }
|
---|
1588 | }
|
---|
1589 |
|
---|
1590 | void UIChooserModel::prepareHandlers()
|
---|
1591 | {
|
---|
1592 | m_pMouseHandler = new UIChooserHandlerMouse(this);
|
---|
1593 | m_pKeyboardHandler = new UIChooserHandlerKeyboard(this);
|
---|
1594 | }
|
---|
1595 |
|
---|
1596 | void UIChooserModel::prepareCloudUpdateTimer()
|
---|
1597 | {
|
---|
1598 | m_pTimerCloudProfileUpdate = new QTimer;
|
---|
1599 | if (m_pTimerCloudProfileUpdate)
|
---|
1600 | m_pTimerCloudProfileUpdate->start(10000);
|
---|
1601 | }
|
---|
1602 |
|
---|
1603 | void UIChooserModel::prepareConnections()
|
---|
1604 | {
|
---|
1605 | connect(this, &UIChooserModel::sigSelectionChanged,
|
---|
1606 | this, &UIChooserModel::sltUpdateSelectedCloudProfiles);
|
---|
1607 | connect(m_pTimerCloudProfileUpdate, &QTimer::timeout,
|
---|
1608 | this, &UIChooserModel::sltUpdateSelectedCloudProfiles);
|
---|
1609 | }
|
---|
1610 |
|
---|
1611 | void UIChooserModel::loadSettings()
|
---|
1612 | {
|
---|
1613 | /* Load last selected-item (choose first if unable to load): */
|
---|
1614 | setSelectedItem(gEDataManager->selectorWindowLastItemChosen());
|
---|
1615 | makeSureAtLeastOneItemSelected();
|
---|
1616 | }
|
---|
1617 |
|
---|
1618 | void UIChooserModel::cleanupConnections()
|
---|
1619 | {
|
---|
1620 | disconnect(this, &UIChooserModel::sigSelectionChanged,
|
---|
1621 | this, &UIChooserModel::sltUpdateSelectedCloudProfiles);
|
---|
1622 | disconnect(m_pTimerCloudProfileUpdate, &QTimer::timeout,
|
---|
1623 | this, &UIChooserModel::sltUpdateSelectedCloudProfiles);
|
---|
1624 | }
|
---|
1625 |
|
---|
1626 | void UIChooserModel::cleanupCloudUpdateTimer()
|
---|
1627 | {
|
---|
1628 | delete m_pTimerCloudProfileUpdate;
|
---|
1629 | m_pTimerCloudProfileUpdate = 0;
|
---|
1630 | }
|
---|
1631 |
|
---|
1632 | void UIChooserModel::cleanupHandlers()
|
---|
1633 | {
|
---|
1634 | delete m_pKeyboardHandler;
|
---|
1635 | m_pKeyboardHandler = 0;
|
---|
1636 | delete m_pMouseHandler;
|
---|
1637 | m_pMouseHandler = 0;
|
---|
1638 | }
|
---|
1639 |
|
---|
1640 | void UIChooserModel::cleanupContextMenu()
|
---|
1641 | {
|
---|
1642 | qDeleteAll(m_localMenus);
|
---|
1643 | m_localMenus.clear();
|
---|
1644 | qDeleteAll(m_cloudMenus);
|
---|
1645 | m_cloudMenus.clear();
|
---|
1646 | }
|
---|
1647 |
|
---|
1648 | void UIChooserModel::cleanupScene()
|
---|
1649 | {
|
---|
1650 | delete m_pScene;
|
---|
1651 | m_pScene = 0;
|
---|
1652 | }
|
---|
1653 |
|
---|
1654 | void UIChooserModel::cleanup()
|
---|
1655 | {
|
---|
1656 | cleanupConnections();
|
---|
1657 | cleanupCloudUpdateTimer();
|
---|
1658 | cleanupHandlers();
|
---|
1659 | cleanupContextMenu();
|
---|
1660 | cleanupScene();
|
---|
1661 | }
|
---|
1662 |
|
---|
1663 | bool UIChooserModel::processContextMenuEvent(QGraphicsSceneContextMenuEvent *pEvent)
|
---|
1664 | {
|
---|
1665 | /* Whats the reason? */
|
---|
1666 | switch (pEvent->reason())
|
---|
1667 | {
|
---|
1668 | case QGraphicsSceneContextMenuEvent::Mouse:
|
---|
1669 | {
|
---|
1670 | /* Look for an item under cursor: */
|
---|
1671 | if (QGraphicsItem *pItem = itemAt(pEvent->scenePos()))
|
---|
1672 | {
|
---|
1673 | switch (pItem->type())
|
---|
1674 | {
|
---|
1675 | case UIChooserNodeType_Global:
|
---|
1676 | {
|
---|
1677 | /* Global context menu for all global item cases: */
|
---|
1678 | m_localMenus.value(UIChooserNodeType_Global)->exec(pEvent->screenPos());
|
---|
1679 | break;
|
---|
1680 | }
|
---|
1681 | case UIChooserNodeType_Group:
|
---|
1682 | {
|
---|
1683 | /* Get group-item: */
|
---|
1684 | UIChooserItemGroup *pGroupItem = qgraphicsitem_cast<UIChooserItemGroup*>(pItem);
|
---|
1685 | /* Don't show context menu for root-item: */
|
---|
1686 | if (pGroupItem->isRoot())
|
---|
1687 | break;
|
---|
1688 | /* Make sure we have group-item selected exclusively: */
|
---|
1689 | if (selectedItems().contains(pGroupItem) && selectedItems().size() == 1)
|
---|
1690 | {
|
---|
1691 | /* Group context menu in that case: */
|
---|
1692 | if (pGroupItem->groupType() == UIChooserNodeGroupType_Local)
|
---|
1693 | m_localMenus.value(UIChooserNodeType_Group)->exec(pEvent->screenPos());
|
---|
1694 | else if ( pGroupItem->groupType() == UIChooserNodeGroupType_Provider
|
---|
1695 | || pGroupItem->groupType() == UIChooserNodeGroupType_Profile)
|
---|
1696 | m_cloudMenus.value(UIChooserNodeType_Group)->exec(pEvent->screenPos());
|
---|
1697 | break;
|
---|
1698 | }
|
---|
1699 | /* Otherwise we have to find a first child machine-item: */
|
---|
1700 | else
|
---|
1701 | pItem = qobject_cast<UIChooserItem*>(pGroupItem)->firstMachineItem();
|
---|
1702 | }
|
---|
1703 | RT_FALL_THRU();
|
---|
1704 | case UIChooserNodeType_Machine:
|
---|
1705 | {
|
---|
1706 | /* Get machine-item: */
|
---|
1707 | UIChooserItemMachine *pMachineItem = qgraphicsitem_cast<UIChooserItemMachine*>(pItem);
|
---|
1708 | /* Machine context menu for other Group/Machine cases: */
|
---|
1709 | if (pMachineItem->cacheType() == UIVirtualMachineItemType_Local)
|
---|
1710 | m_localMenus.value(UIChooserNodeType_Machine)->exec(pEvent->screenPos());
|
---|
1711 | else if (pMachineItem->cacheType() == UIVirtualMachineItemType_CloudReal)
|
---|
1712 | m_cloudMenus.value(UIChooserNodeType_Machine)->exec(pEvent->screenPos());
|
---|
1713 | break;
|
---|
1714 | }
|
---|
1715 | default:
|
---|
1716 | break;
|
---|
1717 | }
|
---|
1718 | }
|
---|
1719 | /* Filter out by default: */
|
---|
1720 | return true;
|
---|
1721 | }
|
---|
1722 | case QGraphicsSceneContextMenuEvent::Keyboard:
|
---|
1723 | {
|
---|
1724 | /* Get first selected-item: */
|
---|
1725 | if (UIChooserItem *pItem = firstSelectedItem())
|
---|
1726 | {
|
---|
1727 | switch (pItem->type())
|
---|
1728 | {
|
---|
1729 | case UIChooserNodeType_Global:
|
---|
1730 | {
|
---|
1731 | /* Global context menu for all global item cases: */
|
---|
1732 | m_localMenus.value(UIChooserNodeType_Global)->exec(pEvent->screenPos());
|
---|
1733 | break;
|
---|
1734 | }
|
---|
1735 | case UIChooserNodeType_Group:
|
---|
1736 | {
|
---|
1737 | /* Get group-item: */
|
---|
1738 | UIChooserItemGroup *pGroupItem = qgraphicsitem_cast<UIChooserItemGroup*>(pItem);
|
---|
1739 | /* Make sure we have group-item selected exclusively: */
|
---|
1740 | if (selectedItems().contains(pGroupItem) && selectedItems().size() == 1)
|
---|
1741 | {
|
---|
1742 | /* Group context menu in that case: */
|
---|
1743 | if (pGroupItem->groupType() == UIChooserNodeGroupType_Local)
|
---|
1744 | m_localMenus.value(UIChooserNodeType_Group)->exec(pEvent->screenPos());
|
---|
1745 | else if ( pGroupItem->groupType() == UIChooserNodeGroupType_Provider
|
---|
1746 | || pGroupItem->groupType() == UIChooserNodeGroupType_Profile)
|
---|
1747 | m_cloudMenus.value(UIChooserNodeType_Group)->exec(pEvent->screenPos());
|
---|
1748 | break;
|
---|
1749 | }
|
---|
1750 | /* Otherwise we have to find a first child machine-item: */
|
---|
1751 | else
|
---|
1752 | pItem = qobject_cast<UIChooserItem*>(pGroupItem)->firstMachineItem();
|
---|
1753 | }
|
---|
1754 | RT_FALL_THRU();
|
---|
1755 | case UIChooserNodeType_Machine:
|
---|
1756 | {
|
---|
1757 | /* Get machine-item: */
|
---|
1758 | UIChooserItemMachine *pMachineItem = qgraphicsitem_cast<UIChooserItemMachine*>(pItem);
|
---|
1759 | /* Machine context menu for other Group/Machine cases: */
|
---|
1760 | if (pMachineItem->cacheType() == UIVirtualMachineItemType_Local)
|
---|
1761 | m_localMenus.value(UIChooserNodeType_Machine)->exec(pEvent->screenPos());
|
---|
1762 | else if (pMachineItem->cacheType() == UIVirtualMachineItemType_CloudReal)
|
---|
1763 | m_cloudMenus.value(UIChooserNodeType_Machine)->exec(pEvent->screenPos());
|
---|
1764 | break;
|
---|
1765 | }
|
---|
1766 | default:
|
---|
1767 | break;
|
---|
1768 | }
|
---|
1769 | }
|
---|
1770 | /* Filter out by default: */
|
---|
1771 | return true;
|
---|
1772 | }
|
---|
1773 | default:
|
---|
1774 | break;
|
---|
1775 | }
|
---|
1776 | /* Pass others context menu events: */
|
---|
1777 | return false;
|
---|
1778 | }
|
---|
1779 |
|
---|
1780 | void UIChooserModel::clearRealFocus()
|
---|
1781 | {
|
---|
1782 | /* Set the real focus to null: */
|
---|
1783 | scene()->setFocusItem(0);
|
---|
1784 | }
|
---|
1785 |
|
---|
1786 | QList<UIChooserItem*> UIChooserModel::createNavigationItemList(UIChooserItem *pItem)
|
---|
1787 | {
|
---|
1788 | /* Prepare navigation list: */
|
---|
1789 | QList<UIChooserItem*> navigationItems;
|
---|
1790 |
|
---|
1791 | /* Iterate over all the global-items: */
|
---|
1792 | foreach (UIChooserItem *pGlobalItem, pItem->items(UIChooserNodeType_Global))
|
---|
1793 | navigationItems << pGlobalItem;
|
---|
1794 | /* Iterate over all the group-items: */
|
---|
1795 | foreach (UIChooserItem *pGroupItem, pItem->items(UIChooserNodeType_Group))
|
---|
1796 | {
|
---|
1797 | navigationItems << pGroupItem;
|
---|
1798 | if (pGroupItem->toGroupItem()->isOpened())
|
---|
1799 | navigationItems << createNavigationItemList(pGroupItem);
|
---|
1800 | }
|
---|
1801 | /* Iterate over all the machine-items: */
|
---|
1802 | foreach (UIChooserItem *pMachineItem, pItem->items(UIChooserNodeType_Machine))
|
---|
1803 | navigationItems << pMachineItem;
|
---|
1804 |
|
---|
1805 | /* Return navigation list: */
|
---|
1806 | return navigationItems;
|
---|
1807 | }
|
---|
1808 |
|
---|
1809 | void UIChooserModel::clearTreeForMainRoot()
|
---|
1810 | {
|
---|
1811 | /* Forbid to save selection changes: */
|
---|
1812 | m_fSelectionSaveAllowed = false;
|
---|
1813 |
|
---|
1814 | /* Cleanup tree if exists: */
|
---|
1815 | delete m_pRoot;
|
---|
1816 | m_pRoot = 0;
|
---|
1817 | }
|
---|
1818 |
|
---|
1819 | void UIChooserModel::buildTreeForMainRoot(bool fPreserveSelection /* = false */)
|
---|
1820 | {
|
---|
1821 | /* This isn't safe if dragging is started and needs to be fixed properly,
|
---|
1822 | * but for now we will just ignore build request: */
|
---|
1823 | /// @todo Make sure D&D is safe on tree rebuild
|
---|
1824 | if (m_pCurrentDragObject)
|
---|
1825 | return;
|
---|
1826 |
|
---|
1827 | /* Remember scrolling location: */
|
---|
1828 | const int iScrollLocation = m_pRoot ? m_pRoot->toGroupItem()->scrollingValue() : 0;
|
---|
1829 |
|
---|
1830 | /* Remember all selected items if requested: */
|
---|
1831 | QStringList selectedItemDefinitions;
|
---|
1832 | if (fPreserveSelection && !selectedItems().isEmpty())
|
---|
1833 | {
|
---|
1834 | foreach (UIChooserItem *pSelectedItem, selectedItems())
|
---|
1835 | selectedItemDefinitions << pSelectedItem->definition();
|
---|
1836 | }
|
---|
1837 |
|
---|
1838 | /* Clean tree for main root: */
|
---|
1839 | clearTreeForMainRoot();
|
---|
1840 |
|
---|
1841 | /* Build whole tree for invisible root item: */
|
---|
1842 | m_pRoot = new UIChooserItemGroup(scene(), invisibleRoot()->toGroupNode());
|
---|
1843 |
|
---|
1844 | /* Install root as event-filter for scene view,
|
---|
1845 | * we need QEvent::Scroll events from it: */
|
---|
1846 | root()->installEventFilterHelper(view());
|
---|
1847 |
|
---|
1848 | /* Update tree for main root: */
|
---|
1849 | updateTreeForMainRoot();
|
---|
1850 |
|
---|
1851 | /* Apply current global item height hint: */
|
---|
1852 | applyGlobalItemHeightHint();
|
---|
1853 |
|
---|
1854 | /* Restore all selected items if requested: */
|
---|
1855 | if (fPreserveSelection)
|
---|
1856 | {
|
---|
1857 | QList<UIChooserItem*> selectedItems;
|
---|
1858 | foreach (const QString &strSelectedItemDefinition, selectedItemDefinitions)
|
---|
1859 | {
|
---|
1860 | UIChooserItem *pSelectedItem = searchItemByDefinition(strSelectedItemDefinition);
|
---|
1861 | if (pSelectedItem)
|
---|
1862 | selectedItems << pSelectedItem;
|
---|
1863 | }
|
---|
1864 | setSelectedItems(selectedItems);
|
---|
1865 | setCurrentItem(firstSelectedItem());
|
---|
1866 | makeSureAtLeastOneItemSelected();
|
---|
1867 | }
|
---|
1868 |
|
---|
1869 | /* Restore scrolling location: */
|
---|
1870 | m_pRoot->toGroupItem()->setScrollingValue(iScrollLocation);
|
---|
1871 |
|
---|
1872 | /* Repeat search if search widget is visible: */
|
---|
1873 | if (view() && view()->isSearchWidgetVisible())
|
---|
1874 | view()->redoSearch();
|
---|
1875 |
|
---|
1876 | /* Allow to save selection changes: */
|
---|
1877 | m_fSelectionSaveAllowed = true;
|
---|
1878 | }
|
---|
1879 |
|
---|
1880 | void UIChooserModel::updateTreeForMainRoot()
|
---|
1881 | {
|
---|
1882 | updateNavigationItemList();
|
---|
1883 | updateLayout();
|
---|
1884 | }
|
---|
1885 |
|
---|
1886 | void UIChooserModel::removeLocalMachineItems(const QList<UIChooserItemMachine*> &machineItems)
|
---|
1887 | {
|
---|
1888 | /* Confirm machine-items removal: */
|
---|
1889 | QStringList names;
|
---|
1890 | foreach (UIChooserItemMachine *pItem, machineItems)
|
---|
1891 | names << pItem->name();
|
---|
1892 | if (!msgCenter().confirmMachineItemRemoval(names))
|
---|
1893 | return;
|
---|
1894 |
|
---|
1895 | /* Find and select closest unselected item: */
|
---|
1896 | setSelectedItem(findClosestUnselectedItem());
|
---|
1897 |
|
---|
1898 | /* Remove nodes of all the passed items: */
|
---|
1899 | foreach (UIChooserItemMachine *pItem, machineItems)
|
---|
1900 | delete pItem->node();
|
---|
1901 |
|
---|
1902 | /* And update model: */
|
---|
1903 | wipeOutEmptyGroups();
|
---|
1904 | updateTreeForMainRoot();
|
---|
1905 |
|
---|
1906 | /* Save groups finally: */
|
---|
1907 | saveGroups();
|
---|
1908 | }
|
---|
1909 |
|
---|
1910 | void UIChooserModel::unregisterLocalMachines(const QList<CMachine> &machines)
|
---|
1911 | {
|
---|
1912 | /* Confirm machine removal: */
|
---|
1913 | const int iResultCode = msgCenter().confirmMachineRemoval(machines);
|
---|
1914 | if (iResultCode == AlertButton_Cancel)
|
---|
1915 | return;
|
---|
1916 |
|
---|
1917 | /* For every selected machine: */
|
---|
1918 | foreach (CMachine comMachine, machines)
|
---|
1919 | {
|
---|
1920 | if (iResultCode == AlertButton_Choice1)
|
---|
1921 | {
|
---|
1922 | /* Unregister machine first: */
|
---|
1923 | CMediumVector media = comMachine.Unregister(KCleanupMode_DetachAllReturnHardDisksAndVMRemovable);
|
---|
1924 | if (!comMachine.isOk())
|
---|
1925 | {
|
---|
1926 | UINotificationMessage::cannotRemoveMachine(comMachine);
|
---|
1927 | continue;
|
---|
1928 | }
|
---|
1929 | /* Removing machine: */
|
---|
1930 | UINotificationProgressMachineMediaRemove *pNotification = new UINotificationProgressMachineMediaRemove(comMachine, media);
|
---|
1931 | gpNotificationCenter->append(pNotification);
|
---|
1932 | }
|
---|
1933 | else if (iResultCode == AlertButton_Choice2 || iResultCode == AlertButton_Ok)
|
---|
1934 | {
|
---|
1935 | /* Unregister machine first: */
|
---|
1936 | CMediumVector media = comMachine.Unregister(KCleanupMode_DetachAllReturnHardDisksAndVMRemovable);
|
---|
1937 | if (!comMachine.isOk())
|
---|
1938 | {
|
---|
1939 | UINotificationMessage::cannotRemoveMachine(comMachine);
|
---|
1940 | continue;
|
---|
1941 | }
|
---|
1942 | /* Finally close all media, deliberately ignoring errors: */
|
---|
1943 | foreach (CMedium comMedium, media)
|
---|
1944 | {
|
---|
1945 | if (!comMedium.isNull())
|
---|
1946 | comMedium.Close();
|
---|
1947 | }
|
---|
1948 | }
|
---|
1949 | }
|
---|
1950 | }
|
---|
1951 |
|
---|
1952 | void UIChooserModel::unregisterCloudMachineItems(const QList<UIChooserItemMachine*> &machineItems)
|
---|
1953 | {
|
---|
1954 | /* Compose a list of machines: */
|
---|
1955 | QList<CCloudMachine> machines;
|
---|
1956 | foreach (UIChooserItemMachine *pMachineItem, machineItems)
|
---|
1957 | machines << pMachineItem->cache()->toCloud()->machine();
|
---|
1958 |
|
---|
1959 | /* Stop cloud profile update prematurely: */
|
---|
1960 | if (m_pTimerCloudProfileUpdate)
|
---|
1961 | m_pTimerCloudProfileUpdate->stop();
|
---|
1962 |
|
---|
1963 | /* Confirm machine removal: */
|
---|
1964 | const int iResultCode = msgCenter().confirmCloudMachineRemoval(machines);
|
---|
1965 | if (iResultCode == AlertButton_Cancel)
|
---|
1966 | {
|
---|
1967 | /* Resume cloud profile update if cancelled: */
|
---|
1968 | if (m_pTimerCloudProfileUpdate)
|
---|
1969 | m_pTimerCloudProfileUpdate->start(10000);
|
---|
1970 | return;
|
---|
1971 | }
|
---|
1972 |
|
---|
1973 | /* For every selected machine-item: */
|
---|
1974 | foreach (UIChooserItemMachine *pMachineItem, machineItems)
|
---|
1975 | {
|
---|
1976 | /* Compose cloud entity keys for profile and machine: */
|
---|
1977 | const QString strProviderShortName = pMachineItem->parentItem()->parentItem()->name();
|
---|
1978 | const QString strProfileName = pMachineItem->parentItem()->name();
|
---|
1979 | const QUuid uMachineId = pMachineItem->id();
|
---|
1980 | const UICloudEntityKey cloudEntityKeyForMachine = UICloudEntityKey(strProviderShortName, strProfileName, uMachineId);
|
---|
1981 |
|
---|
1982 | /* Stop refreshing machine being deleted: */
|
---|
1983 | if (containsCloudEntityKey(cloudEntityKeyForMachine))
|
---|
1984 | pMachineItem->cache()->toCloud()->waitForAsyncInfoUpdateFinished();
|
---|
1985 |
|
---|
1986 | /* Acquire cloud machine: */
|
---|
1987 | CCloudMachine comMachine = pMachineItem->cache()->toCloud()->machine();
|
---|
1988 |
|
---|
1989 | /* Removing cloud machine: */
|
---|
1990 | UINotificationProgressCloudMachineRemove *pNotification =
|
---|
1991 | new UINotificationProgressCloudMachineRemove(comMachine,
|
---|
1992 | iResultCode == AlertButton_Choice1,
|
---|
1993 | strProviderShortName,
|
---|
1994 | strProfileName);
|
---|
1995 | connect(pNotification, &UINotificationProgressCloudMachineRemove::sigCloudMachineRemoved,
|
---|
1996 | this, &UIChooserModel::sltHandleCloudMachineRemoved);
|
---|
1997 | gpNotificationCenter->append(pNotification);
|
---|
1998 | }
|
---|
1999 |
|
---|
2000 | /* Resume cloud profile update after all: */
|
---|
2001 | if (m_pTimerCloudProfileUpdate)
|
---|
2002 | m_pTimerCloudProfileUpdate->start(10000);
|
---|
2003 | }
|
---|
2004 |
|
---|
2005 | bool UIChooserModel::processDragMoveEvent(QGraphicsSceneDragDropEvent *pEvent)
|
---|
2006 | {
|
---|
2007 | /* Make sure view exists: */
|
---|
2008 | AssertPtrReturn(view(), false);
|
---|
2009 |
|
---|
2010 | /* Do we scrolling already? */
|
---|
2011 | if (m_fIsScrollingInProgress)
|
---|
2012 | return false;
|
---|
2013 |
|
---|
2014 | /* Check scroll-area: */
|
---|
2015 | const QPoint eventPoint = view()->mapFromGlobal(pEvent->screenPos());
|
---|
2016 | if ( (eventPoint.y() < m_iScrollingTokenSize)
|
---|
2017 | || (eventPoint.y() > view()->height() - m_iScrollingTokenSize))
|
---|
2018 | {
|
---|
2019 | /* Set scrolling in progress: */
|
---|
2020 | m_fIsScrollingInProgress = true;
|
---|
2021 | /* Start scrolling: */
|
---|
2022 | QTimer::singleShot(200, this, SLOT(sltStartScrolling()));
|
---|
2023 | }
|
---|
2024 |
|
---|
2025 | /* Pass event: */
|
---|
2026 | return false;
|
---|
2027 | }
|
---|
2028 |
|
---|
2029 | bool UIChooserModel::processDragLeaveEvent(QGraphicsSceneDragDropEvent *pEvent)
|
---|
2030 | {
|
---|
2031 | /* Event object is not required here: */
|
---|
2032 | Q_UNUSED(pEvent);
|
---|
2033 |
|
---|
2034 | /* Make sure to stop scrolling as drag-leave event happened: */
|
---|
2035 | if (m_fIsScrollingInProgress)
|
---|
2036 | m_fIsScrollingInProgress = false;
|
---|
2037 |
|
---|
2038 | /* Pass event: */
|
---|
2039 | return false;
|
---|
2040 | }
|
---|
2041 |
|
---|
2042 | void UIChooserModel::applyGlobalItemHeightHint()
|
---|
2043 | {
|
---|
2044 | /* Make sure there is something to apply: */
|
---|
2045 | if (m_iGlobalItemHeightHint == 0)
|
---|
2046 | return;
|
---|
2047 |
|
---|
2048 | /* Walk thrugh all the items of navigation list: */
|
---|
2049 | foreach (UIChooserItem *pItem, navigationItems())
|
---|
2050 | {
|
---|
2051 | /* And for each global item: */
|
---|
2052 | if (pItem->type() == UIChooserNodeType_Global)
|
---|
2053 | {
|
---|
2054 | /* Apply the height hint we have: */
|
---|
2055 | UIChooserItemGlobal *pGlobalItem = pItem->toGlobalItem();
|
---|
2056 | if (pGlobalItem)
|
---|
2057 | pGlobalItem->setHeightHint(m_iGlobalItemHeightHint);
|
---|
2058 | }
|
---|
2059 | }
|
---|
2060 | }
|
---|