VirtualBox

source: vbox/trunk/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserAbstractModel.cpp@ 103023

Last change on this file since 103023 was 103023, checked in by vboxsync, 5 months ago

FE/Qt: bugref:10501: On cloud profile state changes we should notify Global Activity Overview tool about cloud machine list changes.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 75.9 KB
Line 
1/* $Id: UIChooserAbstractModel.cpp 103023 2024-01-24 13:28:00Z vboxsync $ */
2/** @file
3 * VBox Qt GUI - UIChooserAbstractModel class implementation.
4 */
5
6/*
7 * Copyright (C) 2012-2023 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 <QRegExp>
30#include <QRegularExpression>
31#include <QThread>
32
33/* GUI includes: */
34#include "UICommon.h"
35#include "UIChooser.h"
36#include "UIChooserAbstractModel.h"
37#include "UIChooserNode.h"
38#include "UIChooserNodeGroup.h"
39#include "UIChooserNodeGlobal.h"
40#include "UIChooserNodeMachine.h"
41#include "UICloudNetworkingStuff.h"
42#include "UIExtraDataManager.h"
43#include "UIMessageCenter.h"
44#include "UINotificationCenter.h"
45#include "UIProgressTaskReadCloudMachineList.h"
46#include "UIVirtualBoxEventHandler.h"
47#include "UIVirtualMachineItemCloud.h"
48
49/* COM includes: */
50#include "CCloudMachine.h"
51#include "CCloudProfile.h"
52#include "CCloudProvider.h"
53#include "CMachine.h"
54
55/* Type defs: */
56typedef QSet<QString> UIStringSet;
57
58
59/** QThread subclass allowing to save group settings asynchronously. */
60class UIThreadGroupSettingsSave : public QThread
61{
62 Q_OBJECT;
63
64signals:
65
66 /** Notifies about machine with certain @a uMachineId to be reloaded. */
67 void sigReload(const QUuid &uMachineId);
68
69 /** Notifies about task is complete. */
70 void sigComplete();
71
72public:
73
74 /** Returns group settings saving thread instance. */
75 static UIThreadGroupSettingsSave *instance();
76 /** Prepares group settings saving thread instance. */
77 static void prepare();
78 /** Cleanups group settings saving thread instance. */
79 static void cleanup();
80
81 /** Configures @a group settings saving thread with corresponding @a pListener.
82 * @param oldLists Brings the old settings list to be compared.
83 * @param newLists Brings the new settings list to be saved. */
84 void configure(QObject *pParent,
85 const QMap<QString, QStringList> &oldLists,
86 const QMap<QString, QStringList> &newLists);
87
88protected:
89
90 /** Constructs group settings saving thread. */
91 UIThreadGroupSettingsSave();
92 /** Destructs group settings saving thread. */
93 virtual ~UIThreadGroupSettingsSave() RT_OVERRIDE;
94
95 /** Contains a thread task to be executed. */
96 virtual void run() RT_OVERRIDE;
97
98 /** Holds the singleton instance. */
99 static UIThreadGroupSettingsSave *s_pInstance;
100
101 /** Holds the map of group settings to be compared. */
102 QMap<QString, QStringList> m_oldLists;
103 /** Holds the map of group settings to be saved. */
104 QMap<QString, QStringList> m_newLists;
105};
106
107
108/** QThread subclass allowing to save group definitions asynchronously. */
109class UIThreadGroupDefinitionsSave : public QThread
110{
111 Q_OBJECT;
112
113signals:
114
115 /** Notifies about task is complete. */
116 void sigComplete();
117
118public:
119
120 /** Returns group definitions saving thread instance. */
121 static UIThreadGroupDefinitionsSave *instance();
122 /** Prepares group definitions saving thread instance. */
123 static void prepare();
124 /** Cleanups group definitions saving thread instance. */
125 static void cleanup();
126
127 /** Configures group definitions saving thread with corresponding @a pListener.
128 * @param lists Brings definitions lists to be saved. */
129 void configure(QObject *pListener,
130 const QMap<QString, QStringList> &lists);
131
132protected:
133
134 /** Constructs group definitions saving thread. */
135 UIThreadGroupDefinitionsSave();
136 /** Destructs group definitions saving thread. */
137 virtual ~UIThreadGroupDefinitionsSave() RT_OVERRIDE;
138
139 /** Contains a thread task to be executed. */
140 virtual void run() RT_OVERRIDE;
141
142 /** Holds the singleton instance. */
143 static UIThreadGroupDefinitionsSave *s_pInstance;
144
145 /** Holds the map of group definitions to be saved. */
146 QMap<QString, QStringList> m_lists;
147};
148
149
150/*********************************************************************************************************************************
151* Class UIThreadGroupSettingsSave implementation. *
152*********************************************************************************************************************************/
153
154/* static */
155UIThreadGroupSettingsSave *UIThreadGroupSettingsSave::s_pInstance = 0;
156
157/* static */
158UIThreadGroupSettingsSave *UIThreadGroupSettingsSave::instance()
159{
160 return s_pInstance;
161}
162
163/* static */
164void UIThreadGroupSettingsSave::prepare()
165{
166 /* Make sure instance is not prepared: */
167 if (s_pInstance)
168 return;
169
170 /* Crate instance: */
171 new UIThreadGroupSettingsSave;
172}
173
174/* static */
175void UIThreadGroupSettingsSave::cleanup()
176{
177 /* Make sure instance is prepared: */
178 if (!s_pInstance)
179 return;
180
181 /* Delete instance: */
182 delete s_pInstance;
183}
184
185void UIThreadGroupSettingsSave::configure(QObject *pParent,
186 const QMap<QString, QStringList> &oldLists,
187 const QMap<QString, QStringList> &newLists)
188{
189 m_oldLists = oldLists;
190 m_newLists = newLists;
191 UIChooserAbstractModel *pChooserAbstractModel = qobject_cast<UIChooserAbstractModel*>(pParent);
192 AssertPtrReturnVoid(pChooserAbstractModel);
193 {
194 connect(this, &UIThreadGroupSettingsSave::sigComplete,
195 pChooserAbstractModel, &UIChooserAbstractModel::sltGroupSettingsSaveComplete);
196 }
197}
198
199UIThreadGroupSettingsSave::UIThreadGroupSettingsSave()
200{
201 /* Assign instance: */
202 s_pInstance = this;
203}
204
205UIThreadGroupSettingsSave::~UIThreadGroupSettingsSave()
206{
207 /* Make sure thread work is complete: */
208 wait();
209
210 /* Erase instance: */
211 s_pInstance = 0;
212}
213
214void UIThreadGroupSettingsSave::run()
215{
216 /* COM prepare: */
217 COMBase::InitializeCOM(false);
218
219 /* For every particular machine ID: */
220 foreach (const QString &strId, m_newLists.keys())
221 {
222 /* Get new group list/set: */
223 const QStringList &newGroupList = m_newLists.value(strId);
224#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
225 const UIStringSet newGroupSet(newGroupList.begin(), newGroupList.end());
226#else
227 const UIStringSet &newGroupSet = UIStringSet::fromList(newGroupList);
228#endif
229 /* Get old group list/set: */
230 const QStringList &oldGroupList = m_oldLists.value(strId);
231#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
232 const UIStringSet oldGroupSet(oldGroupList.begin(), oldGroupList.end());
233#else
234 const UIStringSet &oldGroupSet = UIStringSet::fromList(oldGroupList);
235#endif
236 /* Make sure group set changed: */
237 if (newGroupSet == oldGroupSet)
238 continue;
239
240 /* The next steps are subsequent.
241 * Every of them is mandatory in order to continue
242 * with common cleanup in case of failure.
243 * We have to simulate a try-catch block. */
244 CSession comSession;
245 CMachine comMachine;
246 do
247 {
248 /* 1. Open session: */
249 comSession = uiCommon().openSession(QUuid(strId));
250 if (comSession.isNull())
251 break;
252
253 /* 2. Get session machine: */
254 comMachine = comSession.GetMachine();
255 if (comMachine.isNull())
256 break;
257
258 /* 3. Set new groups: */
259 comMachine.SetGroups(newGroupList.toVector());
260 if (!comMachine.isOk())
261 {
262 msgCenter().cannotSetGroups(comMachine);
263 break;
264 }
265
266 /* 4. Save settings: */
267 comMachine.SaveSettings();
268 if (!comMachine.isOk())
269 {
270 msgCenter().cannotSaveMachineSettings(comMachine);
271 break;
272 }
273 } while (0);
274
275 /* Cleanup if necessary: */
276 if (comMachine.isNull() || !comMachine.isOk())
277 emit sigReload(QUuid(strId));
278 if (!comSession.isNull())
279 comSession.UnlockMachine();
280 }
281
282 /* Notify listeners about completeness: */
283 emit sigComplete();
284
285 /* COM cleanup: */
286 COMBase::CleanupCOM();
287}
288
289
290/*********************************************************************************************************************************
291* Class UIThreadGroupDefinitionsSave implementation. *
292*********************************************************************************************************************************/
293
294/* static */
295UIThreadGroupDefinitionsSave *UIThreadGroupDefinitionsSave::s_pInstance = 0;
296
297/* static */
298UIThreadGroupDefinitionsSave *UIThreadGroupDefinitionsSave::instance()
299{
300 return s_pInstance;
301}
302
303/* static */
304void UIThreadGroupDefinitionsSave::prepare()
305{
306 /* Make sure instance is not prepared: */
307 if (s_pInstance)
308 return;
309
310 /* Crate instance: */
311 new UIThreadGroupDefinitionsSave;
312}
313
314/* static */
315void UIThreadGroupDefinitionsSave::cleanup()
316{
317 /* Make sure instance is prepared: */
318 if (!s_pInstance)
319 return;
320
321 /* Delete instance: */
322 delete s_pInstance;
323}
324
325void UIThreadGroupDefinitionsSave::configure(QObject *pParent,
326 const QMap<QString, QStringList> &groups)
327{
328 m_lists = groups;
329 UIChooserAbstractModel *pChooserAbstractModel = qobject_cast<UIChooserAbstractModel*>(pParent);
330 AssertPtrReturnVoid(pChooserAbstractModel);
331 {
332 connect(this, &UIThreadGroupDefinitionsSave::sigComplete,
333 pChooserAbstractModel, &UIChooserAbstractModel::sltGroupDefinitionsSaveComplete);
334 }
335}
336
337UIThreadGroupDefinitionsSave::UIThreadGroupDefinitionsSave()
338{
339 /* Assign instance: */
340 s_pInstance = this;
341}
342
343UIThreadGroupDefinitionsSave::~UIThreadGroupDefinitionsSave()
344{
345 /* Make sure thread work is complete: */
346 wait();
347
348 /* Erase instance: */
349 s_pInstance = 0;
350}
351
352void UIThreadGroupDefinitionsSave::run()
353{
354 /* COM prepare: */
355 COMBase::InitializeCOM(false);
356
357 /* Acquire a list of known group definition keys: */
358 QStringList knownKeys = gEDataManager->knownMachineGroupDefinitionKeys();
359 /* For every group definition to be saved: */
360 foreach (const QString &strId, m_lists.keys())
361 {
362 /* Save definition only if there is a change: */
363 if (gEDataManager->machineGroupDefinitions(strId) != m_lists.value(strId))
364 gEDataManager->setMachineGroupDefinitions(strId, m_lists.value(strId));
365 /* Remove it from known keys: */
366 knownKeys.removeAll(strId);
367 }
368 /* Wipe out rest of known group definitions: */
369 foreach (const QString strId, knownKeys)
370 gEDataManager->setMachineGroupDefinitions(strId, QStringList());
371
372 /* Notify listeners about completeness: */
373 emit sigComplete();
374
375 /* COM cleanup: */
376 COMBase::CleanupCOM();
377}
378
379
380/*********************************************************************************************************************************
381* Class UIChooserAbstractModel implementation. *
382*********************************************************************************************************************************/
383
384UIChooserAbstractModel::UIChooserAbstractModel(UIChooser *pParent)
385 : QObject(pParent)
386 , m_pParent(pParent)
387 , m_pInvisibleRootNode(0)
388 , m_fKeepCloudNodesUpdated(false)
389{
390 prepare();
391}
392
393UIChooserAbstractModel::~UIChooserAbstractModel()
394{
395 cleanup();
396}
397
398void UIChooserAbstractModel::init()
399{
400 /* Create invisible root group node: */
401 m_pInvisibleRootNode = new UIChooserNodeGroup(0 /* parent */,
402 0 /* position */,
403 QUuid() /* id */,
404 QString() /* name */,
405 UIChooserNodeGroupType_Local,
406 true /* opened */);
407 if (invisibleRoot())
408 {
409 /* Link root to this model: */
410 invisibleRoot()->setModel(this);
411
412 /* Create global node: */
413 new UIChooserNodeGlobal(invisibleRoot() /* parent */,
414 0 /* position */,
415 shouldGlobalNodeBeFavorite(invisibleRoot()),
416 QString() /* tip */);
417
418 /* Reload local tree: */
419 reloadLocalTree();
420 /* Reload cloud tree: */
421 reloadCloudTree();
422 }
423}
424
425void UIChooserAbstractModel::deinit()
426{
427 /* Make sure all saving steps complete: */
428 makeSureGroupSettingsSaveIsFinished();
429 makeSureGroupDefinitionsSaveIsFinished();
430}
431
432void UIChooserAbstractModel::wipeOutEmptyGroups()
433{
434 wipeOutEmptyGroupsStartingFrom(invisibleRoot());
435}
436
437QStringList UIChooserAbstractModel::possibleGroupNodeNamesForMachineNodeToMove(const QUuid &uId)
438{
439 /* Search for all the machine nodes with passed ID: */
440 QList<UIChooserNode*> machineNodes;
441 invisibleRoot()->searchForNodes(uId.toString(),
442 UIChooserItemSearchFlag_Machine | UIChooserItemSearchFlag_ExactId,
443 machineNodes);
444
445 /* Return group nodes starting from root one: */
446 return gatherPossibleGroupNodeNames(invisibleRoot(), machineNodes);
447}
448
449QStringList UIChooserAbstractModel::possibleGroupNodeNamesForGroupNodeToMove(const QString &strFullName)
450{
451 /* Search for all the group nodes with passed full-name: */
452 QList<UIChooserNode*> groupNodes;
453 invisibleRoot()->searchForNodes(strFullName,
454 UIChooserItemSearchFlag_LocalGroup | UIChooserItemSearchFlag_FullName,
455 groupNodes);
456
457 /* Return group nodes starting from root one: */
458 return gatherPossibleGroupNodeNames(invisibleRoot(), groupNodes);
459}
460
461/* static */
462QString UIChooserAbstractModel::uniqueGroupName(UIChooserNode *pRoot)
463{
464 /* Enumerate all the group names: */
465 QStringList groupNames;
466 foreach (UIChooserNode *pNode, pRoot->nodes(UIChooserNodeType_Group))
467 groupNames << pNode->name();
468
469 /* Prepare reg-exp: */
470 const QString strMinimumName = tr("New group");
471 const QString strShortTemplate = strMinimumName;
472 const QString strFullTemplate = strShortTemplate + QString(" (\\d+)");
473 const QRegExp shortRegExp(strShortTemplate);
474 const QRegExp fullRegExp(strFullTemplate);
475
476 /* Search for the maximum index: */
477 int iMinimumPossibleNumber = 0;
478 foreach (const QString &strName, groupNames)
479 {
480 if (shortRegExp.exactMatch(strName))
481 iMinimumPossibleNumber = qMax(iMinimumPossibleNumber, 2);
482 else if (fullRegExp.exactMatch(strName))
483 iMinimumPossibleNumber = qMax(iMinimumPossibleNumber, fullRegExp.cap(1).toInt() + 1);
484 }
485
486 /* Prepare/return result: */
487 QString strResult = strMinimumName;
488 if (iMinimumPossibleNumber)
489 strResult += " " + QString::number(iMinimumPossibleNumber);
490 return strResult;
491}
492
493void UIChooserAbstractModel::performSearch(const QString &strSearchTerm, int iSearchFlags)
494{
495 /* Make sure invisible root exists: */
496 AssertPtrReturnVoid(invisibleRoot());
497
498 /* Currently we perform the search only for machines, when this to be changed make
499 * sure the disabled flags of the other item types are also managed correctly. */
500
501 /* Reset the search first to erase the disabled flag,
502 * this also returns a full list of all machine nodes: */
503 const QList<UIChooserNode*> nodes = resetSearch();
504
505 /* Stop here if no search conditions specified: */
506 if (strSearchTerm.isEmpty())
507 return;
508
509 /* Search for all the nodes matching required condition: */
510 invisibleRoot()->searchForNodes(strSearchTerm, iSearchFlags, m_searchResults);
511
512 /* Assign/reset the disabled flag for required nodes: */
513 foreach (UIChooserNode *pNode, nodes)
514 {
515 AssertPtrReturnVoid(pNode);
516 pNode->setDisabled(!m_searchResults.contains(pNode));
517 }
518}
519
520QList<UIChooserNode*> UIChooserAbstractModel::resetSearch()
521{
522 /* Prepare resulting nodes: */
523 QList<UIChooserNode*> nodes;
524
525 /* Make sure invisible root exists: */
526 AssertPtrReturn(invisibleRoot(), nodes);
527
528 /* Calling UIChooserNode::searchForNodes with an empty search term
529 * returns a list all nodes (of the whole tree) of the required type: */
530 invisibleRoot()->searchForNodes(QString(), UIChooserItemSearchFlag_Machine, nodes);
531
532 /* Reset the disabled flag of the nodes first: */
533 foreach (UIChooserNode *pNode, nodes)
534 {
535 AssertPtrReturn(pNode, nodes);
536 pNode->setDisabled(false);
537 }
538
539 /* Reset the search result related data: */
540 m_searchResults.clear();
541
542 /* Return nodes: */
543 return nodes;
544}
545
546QList<UIChooserNode*> UIChooserAbstractModel::searchResult() const
547{
548 return m_searchResults;
549}
550
551void UIChooserAbstractModel::saveGroups()
552{
553 emit sigSaveSettings();
554}
555
556bool UIChooserAbstractModel::isGroupSavingInProgress() const
557{
558 return UIThreadGroupSettingsSave::instance()
559 || UIThreadGroupDefinitionsSave::instance();
560}
561
562/* static */
563QString UIChooserAbstractModel::toOldStyleUuid(const QUuid &uId)
564{
565 return uId.toString().remove(QRegularExpression("[{}]"));
566}
567
568/* static */
569QString UIChooserAbstractModel::prefixToString(UIChooserNodeDataPrefixType enmType)
570{
571 switch (enmType)
572 {
573 /* Global nodes: */
574 case UIChooserNodeDataPrefixType_Global: return "n";
575 /* Machine nodes: */
576 case UIChooserNodeDataPrefixType_Machine: return "m";
577 /* Group nodes: */
578 case UIChooserNodeDataPrefixType_Local: return "g";
579 case UIChooserNodeDataPrefixType_Provider: return "p";
580 case UIChooserNodeDataPrefixType_Profile: return "a";
581 }
582 return QString();
583}
584
585/* static */
586QString UIChooserAbstractModel::optionToString(UIChooserNodeDataOptionType enmType)
587{
588 switch (enmType)
589 {
590 /* Global nodes: */
591 case UIChooserNodeDataOptionType_GlobalFavorite: return "f";
592 /* Group nodes: */
593 case UIChooserNodeDataOptionType_GroupOpened: return "o";
594 }
595 return QString();
596}
597
598/* static */
599QString UIChooserAbstractModel::valueToString(UIChooserNodeDataValueType enmType)
600{
601 switch (enmType)
602 {
603 /* Global nodes: */
604 case UIChooserNodeDataValueType_GlobalDefault: return "GLOBAL";
605 }
606 return QString();
607}
608
609void UIChooserAbstractModel::setKeepCloudNodesUpdated(bool fUpdate)
610{
611 /* Make sure something changed: */
612 if (m_fKeepCloudNodesUpdated == fUpdate)
613 return;
614
615 /* Holds the value: */
616 m_fKeepCloudNodesUpdated = fUpdate;
617
618 /* Update all the real cloud machine items: */
619 foreach (UIChooserNode *pNode, enumerateCloudMachineNodes())
620 {
621 AssertReturnVoid(pNode && pNode->type() == UIChooserNodeType_Machine);
622 UIChooserNodeMachine *pMachineNode = pNode->toMachineNode();
623 AssertPtrReturnVoid(pMachineNode);
624 if (pMachineNode->cacheType() != UIVirtualMachineItemType_CloudReal)
625 continue;
626 UIVirtualMachineItemCloud *pCloudMachineItem = pMachineNode->cache()->toCloud();
627 AssertPtrReturnVoid(pCloudMachineItem);
628 pCloudMachineItem->setUpdateRequiredByGlobalReason(m_fKeepCloudNodesUpdated);
629 if (m_fKeepCloudNodesUpdated)
630 pCloudMachineItem->updateInfoAsync(false /* delayed? */);
631 }
632}
633
634void UIChooserAbstractModel::insertCloudEntityKey(const UICloudEntityKey &key)
635{
636// printf("Cloud entity with key %s being updated..\n", key.toString().toUtf8().constData());
637 m_cloudEntityKeysBeingUpdated.insert(key);
638 emit sigCloudUpdateStateChanged();
639}
640
641void UIChooserAbstractModel::removeCloudEntityKey(const UICloudEntityKey &key)
642{
643// printf("Cloud entity with key %s is updated!\n", key.toString().toUtf8().constData());
644 m_cloudEntityKeysBeingUpdated.remove(key);
645 emit sigCloudUpdateStateChanged();
646}
647
648bool UIChooserAbstractModel::containsCloudEntityKey(const UICloudEntityKey &key) const
649{
650 return m_cloudEntityKeysBeingUpdated.contains(key);
651}
652
653bool UIChooserAbstractModel::isCloudProfileUpdateInProgress() const
654{
655 /* Compose RE for profile: */
656 QRegExp re("^/[^/]+/[^/]+$");
657 /* Check whether keys match profile RE: */
658 foreach (const UICloudEntityKey &key, m_cloudEntityKeysBeingUpdated)
659 {
660 const int iIndex = re.indexIn(key.toString());
661 if (iIndex != -1)
662 return true;
663 }
664 /* False by default: */
665 return false;
666}
667
668QList<UIVirtualMachineItemCloud*> UIChooserAbstractModel::cloudMachineItems() const
669{
670 QList<UIVirtualMachineItemCloud*> items;
671 foreach (UIChooserNode *pNode, enumerateCloudMachineNodes())
672 {
673 AssertReturn(pNode && pNode->type() == UIChooserNodeType_Machine, items);
674 UIChooserNodeMachine *pMachineNode = pNode->toMachineNode();
675 AssertPtrReturn(pMachineNode, items);
676 if (pMachineNode->cacheType() != UIVirtualMachineItemType_CloudReal)
677 continue;
678 UIVirtualMachineItemCloud *pCloudMachineItem = pMachineNode->cache()->toCloud();
679 AssertPtrReturn(pCloudMachineItem, items);
680 items << pCloudMachineItem;
681 }
682 return items;
683}
684
685void UIChooserAbstractModel::sltHandleCloudMachineRefreshStarted()
686{
687 /* Acquire sender: */
688 UIVirtualMachineItem *pCache = qobject_cast<UIVirtualMachineItem*>(sender());
689 AssertPtrReturnVoid(pCache);
690
691 /* Acquire sender's ID: */
692 const QUuid uId = pCache->id();
693
694 /* Search for a first machine node with passed ID: */
695 UIChooserNode *pMachineNode = searchMachineNode(invisibleRoot(), uId);
696
697 /* Insert cloud machine key into a list of keys currently being updated: */
698 const UICloudEntityKey guiCloudMachineKey = UICloudEntityKey(pMachineNode->parentNode()->parentNode()->name(),
699 pMachineNode->parentNode()->name(),
700 pMachineNode->toMachineNode()->id());
701 insertCloudEntityKey(guiCloudMachineKey);
702}
703
704void UIChooserAbstractModel::sltHandleCloudMachineRefreshFinished()
705{
706 /* Acquire sender: */
707 UIVirtualMachineItem *pCache = qobject_cast<UIVirtualMachineItem*>(sender());
708 AssertPtrReturnVoid(pCache);
709
710 /* Acquire sender's ID: */
711 const QUuid uId = pCache->id();
712
713 /* Search for a first machine node with passed ID: */
714 UIChooserNode *pMachineNode = searchMachineNode(invisibleRoot(), uId);
715
716 /* Remove cloud machine key from the list of keys currently being updated: */
717 const UICloudEntityKey guiCloudMachineKey = UICloudEntityKey(pMachineNode->parentNode()->parentNode()->name(),
718 pMachineNode->parentNode()->name(),
719 pMachineNode->toMachineNode()->id());
720 removeCloudEntityKey(guiCloudMachineKey);
721
722 /* Notify listeners: */
723 emit sigCloudMachineStateChange(uId);
724}
725
726void UIChooserAbstractModel::sltGroupSettingsSaveComplete()
727{
728 makeSureGroupSettingsSaveIsFinished();
729 emit sigGroupSavingStateChanged();
730}
731
732void UIChooserAbstractModel::sltGroupDefinitionsSaveComplete()
733{
734 makeSureGroupDefinitionsSaveIsFinished();
735 emit sigGroupSavingStateChanged();
736}
737
738void UIChooserAbstractModel::sltLocalMachineStateChanged(const QUuid &uMachineId, const KMachineState)
739{
740 /* Update machine-nodes with passed id: */
741 invisibleRoot()->updateAllNodes(uMachineId);
742}
743
744void UIChooserAbstractModel::sltLocalMachineDataChanged(const QUuid &uMachineId)
745{
746 /* Update machine-nodes with passed id: */
747 invisibleRoot()->updateAllNodes(uMachineId);
748}
749
750void UIChooserAbstractModel::sltLocalMachineRegistrationChanged(const QUuid &uMachineId, const bool fRegistered)
751{
752 /* Existing VM unregistered? */
753 if (!fRegistered)
754 {
755 /* Remove machine-items with passed id: */
756 invisibleRoot()->removeAllNodes(uMachineId);
757 /* Wipe out empty groups: */
758 wipeOutEmptyGroups();
759 }
760 /* New VM registered? */
761 else
762 {
763 /* Should we show this VM? */
764 if (gEDataManager->showMachineInVirtualBoxManagerChooser(uMachineId))
765 {
766 /* Add new machine-item: */
767 const CMachine comMachine = uiCommon().virtualBox().FindMachine(uMachineId.toString());
768 if (comMachine.isNotNull())
769 addLocalMachineIntoTheTree(comMachine, true /* make it visible */);
770 }
771 }
772}
773
774void UIChooserAbstractModel::sltLocalMachineGroupsChanged(const QUuid &uMachineId)
775{
776 /* Skip VM if restricted: */
777 if (!gEDataManager->showMachineInVirtualBoxManagerChooser(uMachineId))
778 return;
779
780 /* Search for cached group list: */
781 const QStringList oldGroupList = m_groups.value(toOldStyleUuid(uMachineId));
782 //printf("Old groups for VM with ID=%s: %s\n",
783 // uMachineId.toString().toUtf8().constData(),
784 // oldGroupList.join(", ").toUtf8().constData());
785
786 /* Search for existing registered machine: */
787 const CMachine comMachine = uiCommon().virtualBox().FindMachine(uMachineId.toString());
788 if (comMachine.isNull())
789 return;
790 /* Look for a new group list: */
791 const QStringList newGroupList = comMachine.GetGroups().toList();
792 //printf("New groups for VM with ID=%s: %s\n",
793 // uMachineId.toString().toUtf8().constData(),
794 // newGroupList.join(", ").toUtf8().constData());
795
796 /* Re-register VM if required: */
797 QSet<QString> newGroupSet(newGroupList.begin(), newGroupList.end());
798 QSet<QString> oldGroupSet(oldGroupList.begin(), oldGroupList.end());
799 if (newGroupSet != oldGroupSet)
800 {
801 sltLocalMachineRegistrationChanged(uMachineId, false);
802 sltLocalMachineRegistrationChanged(uMachineId, true);
803 }
804}
805
806void UIChooserAbstractModel::sltSessionStateChanged(const QUuid &uMachineId, const KSessionState)
807{
808 /* Update machine-nodes with passed id: */
809 invisibleRoot()->updateAllNodes(uMachineId);
810}
811
812void UIChooserAbstractModel::sltSnapshotChanged(const QUuid &uMachineId, const QUuid &)
813{
814 /* Update machine-nodes with passed id: */
815 invisibleRoot()->updateAllNodes(uMachineId);
816}
817
818void UIChooserAbstractModel::sltHandleCloudProviderUninstall(const QUuid &uProviderId)
819{
820 /* First of all, stop all cloud updates: */
821 stopCloudUpdates();
822
823 /* Search and delete corresponding cloud provider node if present: */
824 delete searchProviderNode(uProviderId);
825}
826
827void UIChooserAbstractModel::sltReloadMachine(const QUuid &uMachineId)
828{
829 /* Remove machine-items with passed id: */
830 invisibleRoot()->removeAllNodes(uMachineId);
831 /* Wipe out empty groups: */
832 wipeOutEmptyGroups();
833
834 /* Should we show this VM? */
835 if (gEDataManager->showMachineInVirtualBoxManagerChooser(uMachineId))
836 {
837 /* Add new machine-item: */
838 const CMachine comMachine = uiCommon().virtualBox().FindMachine(uMachineId.toString());
839 addLocalMachineIntoTheTree(comMachine, true /* make it visible */);
840 }
841}
842
843void UIChooserAbstractModel::sltCommitData()
844{
845 /* Finally, stop all cloud updates: */
846 stopCloudUpdates(true /* forced? */);
847}
848
849void UIChooserAbstractModel::sltDetachCOM()
850{
851 /* Delete tree: */
852 delete m_pInvisibleRootNode;
853 m_pInvisibleRootNode = 0;
854}
855
856void UIChooserAbstractModel::sltCloudMachineUnregistered(const QString &strProviderShortName,
857 const QString &strProfileName,
858 const QUuid &uId)
859{
860 /* Search for profile node: */
861 UIChooserNode *pProfileNode = searchProfileNode(strProviderShortName, strProfileName);
862 if (!pProfileNode)
863 return;
864
865 /* Remove machine-item with passed uId: */
866 pProfileNode->removeAllNodes(uId);
867
868 /* If there are no items left => add fake cloud VM node: */
869 if (pProfileNode->nodes(UIChooserNodeType_Machine).isEmpty())
870 createCloudMachineNode(pProfileNode, UIFakeCloudVirtualMachineItemState_Done);
871}
872
873void UIChooserAbstractModel::sltCloudMachinesUnregistered(const QString &strProviderShortName,
874 const QString &strProfileName,
875 const QList<QUuid> &ids)
876{
877 /* Search for profile node: */
878 UIChooserNode *pProfileNode = searchProfileNode(strProviderShortName, strProfileName);
879 if (!pProfileNode)
880 return;
881
882 /* Remove machine-items with passed id: */
883 foreach (const QUuid &uId, ids)
884 pProfileNode->removeAllNodes(uId);
885
886 /* If there are no items left => add fake cloud VM node: */
887 if (pProfileNode->nodes(UIChooserNodeType_Machine).isEmpty())
888 createCloudMachineNode(pProfileNode, UIFakeCloudVirtualMachineItemState_Done);
889}
890
891void UIChooserAbstractModel::sltCloudMachineRegistered(const QString &strProviderShortName,
892 const QString &strProfileName,
893 const CCloudMachine &comMachine)
894{
895 /* Search for profile node: */
896 UIChooserNode *pProfileNode = searchProfileNode(strProviderShortName, strProfileName);
897 if (!pProfileNode)
898 return;
899
900 /* Compose corresponding group path: */
901 const QString strGroup = QString("/%1/%2").arg(strProviderShortName, strProfileName);
902 /* Make sure there is no VM with such ID already: */
903 QUuid uId;
904 if (!cloudMachineId(comMachine, uId))
905 return;
906 if (checkIfNodeContainChildWithId(pProfileNode, uId))
907 return;
908 /* Add new machine-item: */
909 addCloudMachineIntoTheTree(strGroup, comMachine, true /* make it visible? */);
910
911 /* Delete fake node if present: */
912 delete searchFakeNode(pProfileNode);
913}
914
915void UIChooserAbstractModel::sltCloudMachinesRegistered(const QString &strProviderShortName,
916 const QString &strProfileName,
917 const QVector<CCloudMachine> &machines)
918{
919 /* Search for profile node: */
920 UIChooserNode *pProfileNode = searchProfileNode(strProviderShortName, strProfileName);
921 if (!pProfileNode)
922 return;
923
924 /* Compose corresponding group path: */
925 const QString strGroup = QString("/%1/%2").arg(strProviderShortName, strProfileName);
926 foreach (const CCloudMachine &comMachine, machines)
927 {
928 /* Make sure there is no VM with such ID already: */
929 QUuid uId;
930 if (!cloudMachineId(comMachine, uId))
931 continue;
932 if (checkIfNodeContainChildWithId(pProfileNode, uId))
933 continue;
934 /* Add new machine-item: */
935 addCloudMachineIntoTheTree(strGroup, comMachine, false /* make it visible? */);
936 }
937
938 /* Delete fake node if present: */
939 delete searchFakeNode(pProfileNode);
940}
941
942void UIChooserAbstractModel::sltHandleReadCloudMachineListTaskComplete()
943{
944 /* Parse task result: */
945 UIProgressTaskReadCloudMachineList *pSender = qobject_cast<UIProgressTaskReadCloudMachineList*>(sender());
946 AssertPtrReturnVoid(pSender);
947 const UICloudEntityKey guiCloudProfileKey = pSender->cloudProfileKey();
948 const QVector<CCloudMachine> machines = pSender->machines();
949 const QString strErrorMessage = pSender->errorMessage();
950
951 /* Delete task: */
952 delete pSender;
953
954 /* Check whether this task was expected: */
955 if (!containsCloudEntityKey(guiCloudProfileKey))
956 return;
957
958 /* Search for provider node separately, it can be removed already: */
959 UIChooserNode *pProviderNode = searchProviderNode(guiCloudProfileKey.m_strProviderShortName);
960 if (pProviderNode)
961 {
962 /* Search for profile node separately, it can be hidden at all: */
963 UIChooserNode *pProfileNode = searchProfileNode(pProviderNode, guiCloudProfileKey.m_strProfileName);
964 if (pProfileNode)
965 {
966 /* Compose old set of machine IDs: */
967 QSet<QUuid> oldIDs;
968 foreach (UIChooserNode *pNode, pProfileNode->nodes(UIChooserNodeType_Machine))
969 {
970 AssertPtrReturnVoid(pNode);
971 UIChooserNodeMachine *pNodeMachine = pNode->toMachineNode();
972 AssertPtrReturnVoid(pNodeMachine);
973 if (pNodeMachine->cacheType() != UIVirtualMachineItemType_CloudReal)
974 continue;
975 oldIDs << pNodeMachine->id();
976 }
977 /* Compose new set of machine IDs and map of machines: */
978 QSet<QUuid> newIDs;
979 QMap<QUuid, CCloudMachine> newMachines;
980 foreach (const CCloudMachine &comMachine, machines)
981 {
982 QUuid uId;
983 AssertReturnVoid(cloudMachineId(comMachine, uId));
984 newMachines[uId] = comMachine;
985 newIDs << uId;
986 }
987
988 /* Calculate set of unregistered/registered IDs: */
989 const QSet<QUuid> unregisteredIDs = oldIDs - newIDs;
990 const QSet<QUuid> registeredIDs = newIDs - oldIDs;
991 QVector<CCloudMachine> registeredMachines;
992 foreach (const QUuid &uId, registeredIDs)
993 registeredMachines << newMachines.value(uId);
994
995 /* Remove unregistered cloud VM nodes: */
996 if (!unregisteredIDs.isEmpty())
997 {
998#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
999 QList<QUuid> listUnregisteredIDs(unregisteredIDs.begin(), unregisteredIDs.end());
1000#else
1001 QList<QUuid> listUnregisteredIDs = unregisteredIDs.toList();
1002#endif
1003 sltCloudMachinesUnregistered(guiCloudProfileKey.m_strProviderShortName,
1004 guiCloudProfileKey.m_strProfileName,
1005 listUnregisteredIDs);
1006 }
1007 /* Add registered cloud VM nodes: */
1008 if (!registeredMachines.isEmpty())
1009 sltCloudMachinesRegistered(guiCloudProfileKey.m_strProviderShortName,
1010 guiCloudProfileKey.m_strProfileName,
1011 registeredMachines);
1012 /* If we changed nothing and have nothing currently: */
1013 if (unregisteredIDs.isEmpty() && newIDs.isEmpty())
1014 {
1015 /* We should update at least fake cloud machine node: */
1016 UIChooserNode *pFakeNode = searchFakeNode(pProfileNode);
1017 AssertPtrReturnVoid(pFakeNode);
1018 UIVirtualMachineItemCloud *pFakeMachineItem = pFakeNode->toMachineNode()->cache()->toCloud();
1019 AssertPtrReturnVoid(pFakeMachineItem);
1020 pFakeMachineItem->setFakeCloudItemState(UIFakeCloudVirtualMachineItemState_Done);
1021 pFakeMachineItem->setFakeCloudItemErrorMessage(strErrorMessage);
1022 if (pFakeNode->item())
1023 pFakeNode->item()->updateItem();
1024 }
1025 }
1026 }
1027
1028 /* Remove cloud entity key from the list of keys currently being updated: */
1029 removeCloudEntityKey(guiCloudProfileKey);
1030
1031 /* Notify listeners: */
1032 emit sigCloudProfileStateChange(guiCloudProfileKey.m_strProviderShortName,
1033 guiCloudProfileKey.m_strProfileName);
1034}
1035
1036void UIChooserAbstractModel::sltHandleCloudProfileManagerCumulativeChange()
1037{
1038 /* Reload cloud tree: */
1039 reloadCloudTree();
1040}
1041
1042void UIChooserAbstractModel::createReadCloudMachineListTask(const UICloudEntityKey &guiCloudProfileKey, bool fWithRefresh)
1043{
1044 /* Do not create task if already registered: */
1045 if (containsCloudEntityKey(guiCloudProfileKey))
1046 return;
1047
1048 /* Create task: */
1049 UIProgressTaskReadCloudMachineList *pTask = new UIProgressTaskReadCloudMachineList(this,
1050 guiCloudProfileKey,
1051 fWithRefresh);
1052 if (pTask)
1053 {
1054 /* It's easy to find child by name later: */
1055 pTask->setObjectName(guiCloudProfileKey.toString());
1056
1057 /* Insert cloud profile key into a list of keys currently being updated: */
1058 insertCloudEntityKey(guiCloudProfileKey);
1059
1060 /* Connect and start it finally: */
1061 connect(pTask, &UIProgressTaskReadCloudMachineList::sigProgressFinished,
1062 this, &UIChooserAbstractModel::sltHandleReadCloudMachineListTaskComplete);
1063 pTask->start();
1064 }
1065}
1066
1067void UIChooserAbstractModel::sltSaveSettings()
1068{
1069 saveGroupSettings();
1070 saveGroupDefinitions();
1071}
1072
1073void UIChooserAbstractModel::prepare()
1074{
1075 prepareConnections();
1076}
1077
1078void UIChooserAbstractModel::prepareConnections()
1079{
1080 /* UICommon connections: */
1081 connect(&uiCommon(), &UICommon::sigAskToCommitData,
1082 this, &UIChooserAbstractModel::sltCommitData);
1083 connect(&uiCommon(), &UICommon::sigAskToDetachCOM,
1084 this, &UIChooserAbstractModel::sltDetachCOM);
1085 connect(&uiCommon(), &UICommon::sigCloudMachineUnregistered,
1086 this, &UIChooserAbstractModel::sltCloudMachineUnregistered);
1087 connect(&uiCommon(), &UICommon::sigCloudMachineRegistered,
1088 this, &UIChooserAbstractModel::sltCloudMachineRegistered);
1089
1090 /* Global connections: */
1091 connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigMachineStateChange,
1092 this, &UIChooserAbstractModel::sltLocalMachineStateChanged);
1093 connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigMachineDataChange,
1094 this, &UIChooserAbstractModel::sltLocalMachineDataChanged);
1095 connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigMachineRegistered,
1096 this, &UIChooserAbstractModel::sltLocalMachineRegistrationChanged);
1097 connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigMachineGroupsChange,
1098 this, &UIChooserAbstractModel::sltLocalMachineGroupsChanged);
1099 connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigSessionStateChange,
1100 this, &UIChooserAbstractModel::sltSessionStateChanged);
1101 connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigSnapshotTake,
1102 this, &UIChooserAbstractModel::sltSnapshotChanged);
1103 connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigSnapshotDelete,
1104 this, &UIChooserAbstractModel::sltSnapshotChanged);
1105 connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigSnapshotChange,
1106 this, &UIChooserAbstractModel::sltSnapshotChanged);
1107 connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigSnapshotRestore,
1108 this, &UIChooserAbstractModel::sltSnapshotChanged);
1109 connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigCloudProviderListChanged,
1110 this, &UIChooserAbstractModel::sltHandleCloudProfileManagerCumulativeChange);
1111 connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigCloudProfileRegistered,
1112 this, &UIChooserAbstractModel::sltHandleCloudProfileManagerCumulativeChange);
1113 connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigCloudProfileChanged,
1114 this, &UIChooserAbstractModel::sltHandleCloudProfileManagerCumulativeChange);
1115 connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigCloudProviderUninstall,
1116 this, &UIChooserAbstractModel::sltHandleCloudProviderUninstall);
1117
1118 /* Settings saving connections: */
1119 connect(this, &UIChooserAbstractModel::sigSaveSettings,
1120 this, &UIChooserAbstractModel::sltSaveSettings,
1121 Qt::QueuedConnection);
1122
1123 /* Extra-data connections: */
1124 connect(gEDataManager, &UIExtraDataManager::sigCloudProfileManagerRestrictionChange,
1125 this, &UIChooserAbstractModel::sltHandleCloudProfileManagerCumulativeChange);
1126}
1127
1128void UIChooserAbstractModel::cleanupConnections()
1129{
1130 /* Group saving connections: */
1131 disconnect(this, &UIChooserAbstractModel::sigSaveSettings,
1132 this, &UIChooserAbstractModel::sltSaveSettings);
1133}
1134
1135void UIChooserAbstractModel::cleanup()
1136{
1137 cleanupConnections();
1138}
1139
1140void UIChooserAbstractModel::reloadLocalTree()
1141{
1142 LogRelFlow(("UIChooserAbstractModel: Loading local VMs...\n"));
1143
1144 /* Acquire VBox: */
1145 const CVirtualBox comVBox = uiCommon().virtualBox();
1146
1147 /* Acquire existing local machines: */
1148 const QVector<CMachine> machines = comVBox.GetMachines();
1149 /* Show error message if necessary: */
1150 if (!comVBox.isOk())
1151 UINotificationMessage::cannotAcquireVirtualBoxParameter(comVBox);
1152 else
1153 {
1154 /* Iterate through existing machines: */
1155 foreach (const CMachine &comMachine, machines)
1156 {
1157 /* Skip if we have nothing to populate (wtf happened?): */
1158 if (comMachine.isNull())
1159 continue;
1160
1161 /* Get machine ID: */
1162 const QUuid uMachineID = comMachine.GetId();
1163 /* Show error message if necessary: */
1164 if (!comMachine.isOk())
1165 {
1166 UINotificationMessage::cannotAcquireMachineParameter(comMachine);
1167 continue;
1168 }
1169
1170 /* Skip if we have nothing to show (wtf happened?): */
1171 if (uMachineID.isNull())
1172 continue;
1173
1174 /* Skip if machine is restricted from being shown: */
1175 if (!gEDataManager->showMachineInVirtualBoxManagerChooser(uMachineID))
1176 continue;
1177
1178 /* Add machine into tree: */
1179 addLocalMachineIntoTheTree(comMachine);
1180 }
1181 }
1182
1183 LogRelFlow(("UIChooserAbstractModel: Local VMs loaded.\n"));
1184}
1185
1186void UIChooserAbstractModel::reloadCloudTree()
1187{
1188 LogRelFlow(("UIChooserAbstractModel: Loading cloud providers/profiles...\n"));
1189
1190 /* Wipe out existing cloud providers first.
1191 * This is quite rude, in future we need to reimplement it more wise.. */
1192 foreach (UIChooserNode *pNode, invisibleRoot()->nodes(UIChooserNodeType_Group))
1193 {
1194 AssertPtrReturnVoid(pNode);
1195 UIChooserNodeGroup *pGroupNode = pNode->toGroupNode();
1196 AssertPtrReturnVoid(pGroupNode);
1197 if (pGroupNode->groupType() == UIChooserNodeGroupType_Provider)
1198 delete pNode;
1199 }
1200
1201 /* Acquire Cloud Profile Manager restrictions: */
1202 const QStringList restrictions = gEDataManager->cloudProfileManagerRestrictions();
1203
1204 /* Iterate through existing providers: */
1205 foreach (CCloudProvider comCloudProvider, listCloudProviders())
1206 {
1207 /* Skip if we have nothing to populate: */
1208 if (comCloudProvider.isNull())
1209 continue;
1210
1211 /* Acquire provider id: */
1212 QUuid uProviderId;
1213 if (!cloudProviderId(comCloudProvider, uProviderId))
1214 continue;
1215
1216 /* Acquire provider short name: */
1217 QString strProviderShortName;
1218 if (!cloudProviderShortName(comCloudProvider, strProviderShortName))
1219 continue;
1220
1221 /* Make sure this provider isn't restricted: */
1222 const QString strProviderPath = QString("/%1").arg(strProviderShortName);
1223 if (restrictions.contains(strProviderPath))
1224 continue;
1225
1226 /* Acquire list of profiles: */
1227 const QVector<CCloudProfile> profiles = listCloudProfiles(comCloudProvider);
1228 if (profiles.isEmpty())
1229 continue;
1230
1231 /* Add provider group node: */
1232 UIChooserNodeGroup *pProviderNode =
1233 new UIChooserNodeGroup(invisibleRoot() /* parent */,
1234 getDesiredNodePosition(invisibleRoot(),
1235 UIChooserNodeDataPrefixType_Provider,
1236 strProviderShortName),
1237 uProviderId,
1238 strProviderShortName,
1239 UIChooserNodeGroupType_Provider,
1240 shouldGroupNodeBeOpened(invisibleRoot(),
1241 UIChooserNodeDataPrefixType_Provider,
1242 strProviderShortName));
1243
1244 /* Iterate through provider's profiles: */
1245 foreach (CCloudProfile comCloudProfile, profiles)
1246 {
1247 /* Skip if we have nothing to populate: */
1248 if (comCloudProfile.isNull())
1249 continue;
1250
1251 /* Acquire profile name: */
1252 QString strProfileName;
1253 if (!cloudProfileName(comCloudProfile, strProfileName))
1254 continue;
1255
1256 /* Make sure this profile isn't restricted: */
1257 const QString strProfilePath = QString("/%1/%2").arg(strProviderShortName, strProfileName);
1258 if (restrictions.contains(strProfilePath))
1259 continue;
1260
1261 /* Add profile sub-group node: */
1262 UIChooserNodeGroup *pProfileNode =
1263 new UIChooserNodeGroup(pProviderNode /* parent */,
1264 getDesiredNodePosition(pProviderNode,
1265 UIChooserNodeDataPrefixType_Profile,
1266 strProfileName),
1267 QUuid() /* id */,
1268 strProfileName,
1269 UIChooserNodeGroupType_Profile,
1270 shouldGroupNodeBeOpened(pProviderNode,
1271 UIChooserNodeDataPrefixType_Profile,
1272 strProfileName));
1273
1274 /* Add fake cloud VM item: */
1275 createCloudMachineNode(pProfileNode, UIFakeCloudVirtualMachineItemState_Loading);
1276
1277 /* Create read cloud machine list task: */
1278 const UICloudEntityKey guiCloudProfileKey = UICloudEntityKey(strProviderShortName, strProfileName);
1279 createReadCloudMachineListTask(guiCloudProfileKey, true /* with refresh? */);
1280 }
1281 }
1282
1283 LogRelFlow(("UIChooserAbstractModel: Cloud providers/profiles loaded.\n"));
1284}
1285
1286void UIChooserAbstractModel::addLocalMachineIntoTheTree(const CMachine &comMachine,
1287 bool fMakeItVisible /* = false */)
1288{
1289 /* Make sure passed VM is not NULL: */
1290 if (comMachine.isNull())
1291 LogRelFlow(("UIChooserModel: ERROR: Passed local VM is NULL!\n"));
1292 AssertReturnVoid(!comMachine.isNull());
1293
1294 /* Which VM we are loading: */
1295 const QUuid uId = comMachine.GetId();
1296 LogRelFlow(("UIChooserModel: Loading local VM with ID={%s}...\n",
1297 toOldStyleUuid(uId).toUtf8().constData()));
1298
1299 /* Is that machine accessible? */
1300 if (comMachine.GetAccessible())
1301 {
1302 /* Acquire VM name: */
1303 const QString strName = comMachine.GetName();
1304 LogRelFlow(("UIChooserModel: Local VM {%s} is accessible.\n", strName.toUtf8().constData()));
1305 /* Which groups passed machine attached to? */
1306 const QVector<QString> groups = comMachine.GetGroups();
1307 const QStringList groupList = groups.toList();
1308 const QString strGroups = groupList.join(", ");
1309 LogRelFlow(("UIChooserModel: Local VM {%s} has groups: {%s}.\n",
1310 strName.toUtf8().constData(), strGroups.toUtf8().constData()));
1311 foreach (QString strGroup, groups)
1312 {
1313 /* Remove last '/' if any: */
1314 if (strGroup.right(1) == "/")
1315 strGroup.truncate(strGroup.size() - 1);
1316 /* Create machine-item with found group-item as parent: */
1317 LogRelFlow(("UIChooserModel: Creating node for local VM {%s} in group {%s}.\n",
1318 strName.toUtf8().constData(), strGroup.toUtf8().constData()));
1319 createLocalMachineNode(getLocalGroupNode(strGroup, invisibleRoot(), fMakeItVisible), comMachine);
1320 }
1321 /* Update group settings: */
1322 m_groups[toOldStyleUuid(uId)] = groupList;
1323 }
1324 /* Inaccessible machine: */
1325 else
1326 {
1327 /* VM is accessible: */
1328 LogRelFlow(("UIChooserModel: Local VM {%s} is inaccessible.\n",
1329 toOldStyleUuid(uId).toUtf8().constData()));
1330 /* Create machine-item with main-root group-item as parent: */
1331 createLocalMachineNode(invisibleRoot(), comMachine);
1332 }
1333}
1334
1335void UIChooserAbstractModel::addCloudMachineIntoTheTree(const QString &strGroup,
1336 const CCloudMachine &comMachine,
1337 bool fMakeItVisible /* = false */)
1338{
1339 /* Make sure passed VM is not NULL: */
1340 if (comMachine.isNull())
1341 LogRelFlow(("UIChooserModel: ERROR: Passed cloud VM is NULL!\n"));
1342 AssertReturnVoid(!comMachine.isNull());
1343
1344 /* Which VM we are loading: */
1345 const QUuid uId = comMachine.GetId();
1346 LogRelFlow(("UIChooserModel: Loading cloud VM with ID={%s}...\n",
1347 toOldStyleUuid(uId).toUtf8().constData()));
1348
1349 /* Acquire VM name: */
1350 QString strName = comMachine.GetName();
1351 if (strName.isEmpty())
1352 strName = uId.toString();
1353 LogRelFlow(("UIChooserModel: Creating node for cloud VM {%s} in group {%s}.\n",
1354 strName.toUtf8().constData(), strGroup.toUtf8().constData()));
1355 /* Create machine-item with found group-item as parent: */
1356 createCloudMachineNode(getCloudGroupNode(strGroup, invisibleRoot(), fMakeItVisible), comMachine);
1357 /* Update group settings: */
1358 const QStringList groupList(strGroup);
1359 m_groups[toOldStyleUuid(uId)] = groupList;
1360}
1361
1362UIChooserNode *UIChooserAbstractModel::getLocalGroupNode(const QString &strName, UIChooserNode *pParentNode, bool fAllGroupsOpened)
1363{
1364 /* Check passed stuff: */
1365 if (pParentNode->name() == strName)
1366 return pParentNode;
1367
1368 /* Prepare variables: */
1369 const QString strFirstSubName = strName.section('/', 0, 0);
1370 const QString strFirstSuffix = strName.section('/', 1, -1);
1371 const QString strSecondSubName = strFirstSuffix.section('/', 0, 0);
1372 const QString strSecondSuffix = strFirstSuffix.section('/', 1, -1);
1373
1374 /* Passed group name equal to first sub-name: */
1375 if (pParentNode->name() == strFirstSubName)
1376 {
1377 /* Make sure first-suffix is NOT empty: */
1378 AssertMsg(!strFirstSuffix.isEmpty(), ("Invalid group name!"));
1379 /* Trying to get group node among our children: */
1380 foreach (UIChooserNode *pNode, pParentNode->nodes(UIChooserNodeType_Group))
1381 {
1382 AssertPtrReturn(pNode, 0);
1383 UIChooserNodeGroup *pGroupNode = pNode->toGroupNode();
1384 AssertPtrReturn(pGroupNode, 0);
1385 if ( pGroupNode->groupType() == UIChooserNodeGroupType_Local
1386 && pNode->name() == strSecondSubName)
1387 {
1388 UIChooserNode *pFoundNode = getLocalGroupNode(strFirstSuffix, pNode, fAllGroupsOpened);
1389 if (UIChooserNodeGroup *pFoundGroupNode = pFoundNode->toGroupNode())
1390 if (fAllGroupsOpened && pFoundGroupNode->isClosed())
1391 pFoundGroupNode->open();
1392 return pFoundNode;
1393 }
1394 }
1395 }
1396
1397 /* Found nothing? Creating: */
1398 UIChooserNodeGroup *pNewGroupNode =
1399 new UIChooserNodeGroup(pParentNode,
1400 getDesiredNodePosition(pParentNode,
1401 UIChooserNodeDataPrefixType_Local,
1402 strSecondSubName),
1403 QUuid() /* id */,
1404 strSecondSubName,
1405 UIChooserNodeGroupType_Local,
1406 fAllGroupsOpened || shouldGroupNodeBeOpened(pParentNode,
1407 UIChooserNodeDataPrefixType_Local,
1408 strSecondSubName));
1409 return strSecondSuffix.isEmpty() ? pNewGroupNode : getLocalGroupNode(strFirstSuffix, pNewGroupNode, fAllGroupsOpened);
1410}
1411
1412UIChooserNode *UIChooserAbstractModel::getCloudGroupNode(const QString &strName, UIChooserNode *pParentNode, bool fAllGroupsOpened)
1413{
1414 /* Check passed stuff: */
1415 if (pParentNode->name() == strName)
1416 return pParentNode;
1417
1418 /* Prepare variables: */
1419 const QString strFirstSubName = strName.section('/', 0, 0);
1420 const QString strFirstSuffix = strName.section('/', 1, -1);
1421 const QString strSecondSubName = strFirstSuffix.section('/', 0, 0);
1422
1423 /* Passed group name equal to first sub-name: */
1424 if (pParentNode->name() == strFirstSubName)
1425 {
1426 /* Make sure first-suffix is NOT empty: */
1427 AssertMsg(!strFirstSuffix.isEmpty(), ("Invalid group name!"));
1428 /* Trying to get group node among our children: */
1429 foreach (UIChooserNode *pNode, pParentNode->nodes(UIChooserNodeType_Group))
1430 {
1431 AssertPtrReturn(pNode, 0);
1432 UIChooserNodeGroup *pGroupNode = pNode->toGroupNode();
1433 AssertPtrReturn(pGroupNode, 0);
1434 if ( ( pGroupNode->groupType() == UIChooserNodeGroupType_Provider
1435 || pGroupNode->groupType() == UIChooserNodeGroupType_Profile)
1436 && pNode->name() == strSecondSubName)
1437 {
1438 UIChooserNode *pFoundNode = getCloudGroupNode(strFirstSuffix, pNode, fAllGroupsOpened);
1439 if (UIChooserNodeGroup *pFoundGroupNode = pFoundNode->toGroupNode())
1440 if (fAllGroupsOpened && pFoundGroupNode->isClosed())
1441 pFoundGroupNode->open();
1442 return pFoundNode;
1443 }
1444 }
1445 }
1446
1447 /* Found nothing? Returning parent: */
1448 AssertFailedReturn(pParentNode);
1449}
1450
1451bool UIChooserAbstractModel::shouldGroupNodeBeOpened(UIChooserNode *pParentNode,
1452 UIChooserNodeDataPrefixType enmDataType,
1453 const QString &strName) const
1454{
1455 /* Read group definitions: */
1456 const QStringList definitions = gEDataManager->machineGroupDefinitions(pParentNode->fullName());
1457 /* Return 'false' if no definitions found: */
1458 if (definitions.isEmpty())
1459 return false;
1460
1461 /* Prepare required group definition reg-exp: */
1462 const QString strNodePrefix = prefixToString(enmDataType);
1463 const QString strNodeOptionOpened = optionToString(UIChooserNodeDataOptionType_GroupOpened);
1464 const QString strDefinitionTemplate = QString("%1(\\S)*=%2").arg(strNodePrefix, strName);
1465 const QRegExp definitionRegExp(strDefinitionTemplate);
1466 /* For each the group definition: */
1467 foreach (const QString &strDefinition, definitions)
1468 {
1469 /* Check if this is required definition: */
1470 if (definitionRegExp.indexIn(strDefinition) == 0)
1471 {
1472 /* Get group descriptor: */
1473 const QString strDescriptor(definitionRegExp.cap(1));
1474 if (strDescriptor.contains(strNodeOptionOpened))
1475 return true;
1476 }
1477 }
1478
1479 /* Return 'false' by default: */
1480 return false;
1481}
1482
1483bool UIChooserAbstractModel::shouldGlobalNodeBeFavorite(UIChooserNode *pParentNode) const
1484{
1485 /* Read group definitions: */
1486 const QStringList definitions = gEDataManager->machineGroupDefinitions(pParentNode->fullName());
1487 /* Return 'false' if no definitions found: */
1488 if (definitions.isEmpty())
1489 return false;
1490
1491 /* Prepare required group definition reg-exp: */
1492 const QString strNodePrefix = prefixToString(UIChooserNodeDataPrefixType_Global);
1493 const QString strNodeOptionFavorite = optionToString(UIChooserNodeDataOptionType_GlobalFavorite);
1494 const QString strNodeValueDefault = valueToString(UIChooserNodeDataValueType_GlobalDefault);
1495 const QString strDefinitionTemplate = QString("%1(\\S)*=%2").arg(strNodePrefix, strNodeValueDefault);
1496 const QRegExp definitionRegExp(strDefinitionTemplate);
1497 /* For each the group definition: */
1498 foreach (const QString &strDefinition, definitions)
1499 {
1500 /* Check if this is required definition: */
1501 if (definitionRegExp.indexIn(strDefinition) == 0)
1502 {
1503 /* Get group descriptor: */
1504 const QString strDescriptor(definitionRegExp.cap(1));
1505 if (strDescriptor.contains(strNodeOptionFavorite))
1506 return true;
1507 }
1508 }
1509
1510 /* Return 'false' by default: */
1511 return false;
1512}
1513
1514void UIChooserAbstractModel::wipeOutEmptyGroupsStartingFrom(UIChooserNode *pParent)
1515{
1516 /* Cleanup all the group children recursively first: */
1517 foreach (UIChooserNode *pNode, pParent->nodes(UIChooserNodeType_Group))
1518 wipeOutEmptyGroupsStartingFrom(pNode);
1519 /* If parent isn't root and has no nodes: */
1520 if (!pParent->isRoot() && !pParent->hasNodes())
1521 {
1522 /* Delete parent node and item: */
1523 delete pParent;
1524 }
1525}
1526
1527int UIChooserAbstractModel::getDesiredNodePosition(UIChooserNode *pParentNode,
1528 UIChooserNodeDataPrefixType enmDataType,
1529 const QString &strName)
1530{
1531 /* End of list (by default)? */
1532 int iNewNodeDesiredPosition = -1;
1533 /* Which position should be new node placed by definitions: */
1534 const int iNewNodeDefinitionPosition = getDefinedNodePosition(pParentNode, enmDataType, strName);
1535
1536 /* If some position defined: */
1537 if (iNewNodeDefinitionPosition != -1)
1538 {
1539 /* Start of list if some definition present: */
1540 iNewNodeDesiredPosition = 0;
1541 /* We have to check all the existing node positions: */
1542 UIChooserNodeType enmType = UIChooserNodeType_Any;
1543 switch (enmDataType)
1544 {
1545 case UIChooserNodeDataPrefixType_Global: enmType = UIChooserNodeType_Global; break;
1546 case UIChooserNodeDataPrefixType_Machine: enmType = UIChooserNodeType_Machine; break;
1547 case UIChooserNodeDataPrefixType_Local:
1548 case UIChooserNodeDataPrefixType_Provider:
1549 case UIChooserNodeDataPrefixType_Profile: enmType = UIChooserNodeType_Group; break;
1550 }
1551 const QList<UIChooserNode*> nodes = pParentNode->nodes(enmType);
1552 for (int i = nodes.size() - 1; i >= 0; --i)
1553 {
1554 /* Get current node: */
1555 UIChooserNode *pNode = nodes.at(i);
1556 AssertPtrReturn(pNode, iNewNodeDesiredPosition);
1557 /* Which position should be current node placed by definitions? */
1558 UIChooserNodeDataPrefixType enmNodeDataType = UIChooserNodeDataPrefixType_Global;
1559 QString strDefinitionName;
1560 switch (pNode->type())
1561 {
1562 case UIChooserNodeType_Machine:
1563 {
1564 enmNodeDataType = UIChooserNodeDataPrefixType_Machine;
1565 strDefinitionName = toOldStyleUuid(pNode->toMachineNode()->id());
1566 break;
1567 }
1568 case UIChooserNodeType_Group:
1569 {
1570 /* Cast to group node: */
1571 UIChooserNodeGroup *pGroupNode = pNode->toGroupNode();
1572 AssertPtrReturn(pGroupNode, iNewNodeDesiredPosition);
1573 switch (pGroupNode->groupType())
1574 {
1575 case UIChooserNodeGroupType_Local: enmNodeDataType = UIChooserNodeDataPrefixType_Local; break;
1576 case UIChooserNodeGroupType_Provider: enmNodeDataType = UIChooserNodeDataPrefixType_Provider; break;
1577 case UIChooserNodeGroupType_Profile: enmNodeDataType = UIChooserNodeDataPrefixType_Profile; break;
1578 default: break;
1579 }
1580 strDefinitionName = pNode->name();
1581 break;
1582 }
1583 default:
1584 break;
1585 }
1586 /* If some position defined: */
1587 const int iNodeDefinitionPosition = getDefinedNodePosition(pParentNode, enmNodeDataType, strDefinitionName);
1588 if (iNodeDefinitionPosition != -1)
1589 {
1590 AssertReturn(iNodeDefinitionPosition != iNewNodeDefinitionPosition, iNewNodeDesiredPosition);
1591 if (iNodeDefinitionPosition < iNewNodeDefinitionPosition)
1592 {
1593 iNewNodeDesiredPosition = i + 1;
1594 break;
1595 }
1596 }
1597 }
1598 }
1599
1600 /* Return desired node position: */
1601 return iNewNodeDesiredPosition;
1602}
1603
1604int UIChooserAbstractModel::getDefinedNodePosition(UIChooserNode *pParentNode, UIChooserNodeDataPrefixType enmDataType, const QString &strName)
1605{
1606 /* Read group definitions: */
1607 const QStringList definitions = gEDataManager->machineGroupDefinitions(pParentNode->fullName());
1608 /* Return 'false' if no definitions found: */
1609 if (definitions.isEmpty())
1610 return -1;
1611
1612 /* Prepare definition reg-exp: */
1613 QString strDefinitionTemplateShort;
1614 QString strDefinitionTemplateFull;
1615 const QString strNodePrefixLocal = prefixToString(UIChooserNodeDataPrefixType_Local);
1616 const QString strNodePrefixProvider = prefixToString(UIChooserNodeDataPrefixType_Provider);
1617 const QString strNodePrefixProfile = prefixToString(UIChooserNodeDataPrefixType_Profile);
1618 const QString strNodePrefixMachine = prefixToString(UIChooserNodeDataPrefixType_Machine);
1619 switch (enmDataType)
1620 {
1621 case UIChooserNodeDataPrefixType_Local:
1622 {
1623 strDefinitionTemplateShort = QString("^[%1%2%3](\\S)*=").arg(strNodePrefixLocal, strNodePrefixProvider, strNodePrefixProfile);
1624 strDefinitionTemplateFull = QString("^%1(\\S)*=%2$").arg(strNodePrefixLocal, strName);
1625 break;
1626 }
1627 case UIChooserNodeDataPrefixType_Provider:
1628 {
1629 strDefinitionTemplateShort = QString("^[%1%2%3](\\S)*=").arg(strNodePrefixLocal, strNodePrefixProvider, strNodePrefixProfile);
1630 strDefinitionTemplateFull = QString("^%1(\\S)*=%2$").arg(strNodePrefixProvider, strName);
1631 break;
1632 }
1633 case UIChooserNodeDataPrefixType_Profile:
1634 {
1635 strDefinitionTemplateShort = QString("^[%1%2%3](\\S)*=").arg(strNodePrefixLocal, strNodePrefixProvider, strNodePrefixProfile);
1636 strDefinitionTemplateFull = QString("^%1(\\S)*=%2$").arg(strNodePrefixProfile, strName);
1637 break;
1638 }
1639 case UIChooserNodeDataPrefixType_Machine:
1640 {
1641 strDefinitionTemplateShort = QString("^%1=").arg(strNodePrefixMachine);
1642 strDefinitionTemplateFull = QString("^%1=%2$").arg(strNodePrefixMachine, strName);
1643 break;
1644 }
1645 default:
1646 return -1;
1647 }
1648 QRegExp definitionRegExpShort(strDefinitionTemplateShort);
1649 QRegExp definitionRegExpFull(strDefinitionTemplateFull);
1650
1651 /* For each the definition: */
1652 int iDefinitionIndex = -1;
1653 foreach (const QString &strDefinition, definitions)
1654 {
1655 /* Check if this definition is of required type: */
1656 if (definitionRegExpShort.indexIn(strDefinition) == 0)
1657 {
1658 ++iDefinitionIndex;
1659 /* Check if this definition is exactly what we need: */
1660 if (definitionRegExpFull.indexIn(strDefinition) == 0)
1661 return iDefinitionIndex;
1662 }
1663 }
1664
1665 /* Return result: */
1666 return -1;
1667}
1668
1669void UIChooserAbstractModel::createLocalMachineNode(UIChooserNode *pParentNode, const CMachine &comMachine)
1670{
1671 new UIChooserNodeMachine(pParentNode,
1672 getDesiredNodePosition(pParentNode,
1673 UIChooserNodeDataPrefixType_Machine,
1674 toOldStyleUuid(comMachine.GetId())),
1675 comMachine);
1676}
1677
1678void UIChooserAbstractModel::createCloudMachineNode(UIChooserNode *pParentNode, UIFakeCloudVirtualMachineItemState enmState)
1679{
1680 new UIChooserNodeMachine(pParentNode,
1681 0 /* position */,
1682 enmState);
1683}
1684
1685void UIChooserAbstractModel::createCloudMachineNode(UIChooserNode *pParentNode, const CCloudMachine &comMachine)
1686{
1687 UIChooserNodeMachine *pNode = new UIChooserNodeMachine(pParentNode,
1688 getDesiredNodePosition(pParentNode,
1689 UIChooserNodeDataPrefixType_Machine,
1690 toOldStyleUuid(comMachine.GetId())),
1691 comMachine);
1692 /* Request for async node update if necessary: */
1693 if ( m_fKeepCloudNodesUpdated
1694 || !comMachine.GetAccessible())
1695 {
1696 AssertReturnVoid(pNode && pNode->cacheType() == UIVirtualMachineItemType_CloudReal);
1697 UIVirtualMachineItemCloud *pCloudMachineItem = pNode->cache()->toCloud();
1698 pCloudMachineItem->setUpdateRequiredByGlobalReason(m_fKeepCloudNodesUpdated);
1699 pCloudMachineItem->updateInfoAsync(false /* delayed? */);
1700 }
1701}
1702
1703QStringList UIChooserAbstractModel::gatherPossibleGroupNodeNames(UIChooserNode *pCurrentNode, QList<UIChooserNode*> exceptions) const
1704{
1705 /* Prepare result: */
1706 QStringList result;
1707
1708 /* Walk through all the children and make sure there are no exceptions: */
1709 bool fAddCurrent = true;
1710 foreach (UIChooserNode *pChild, pCurrentNode->nodes(UIChooserNodeType_Any))
1711 {
1712 AssertPtrReturn(pChild, result);
1713 if (exceptions.contains(pChild))
1714 fAddCurrent = false;
1715 else
1716 {
1717 if (pChild->type() == UIChooserNodeType_Group)
1718 {
1719 UIChooserNodeGroup *pChildGroup = pChild->toGroupNode();
1720 AssertPtrReturn(pChildGroup, result);
1721 if (pChildGroup->groupType() == UIChooserNodeGroupType_Local)
1722 result << gatherPossibleGroupNodeNames(pChild, exceptions);
1723 }
1724 }
1725 }
1726
1727 /* Add current item if not overridden: */
1728 if (fAddCurrent)
1729 result.prepend(pCurrentNode->fullName());
1730
1731 /* Return result: */
1732 return result;
1733}
1734
1735bool UIChooserAbstractModel::checkIfNodeContainChildWithId(UIChooserNode *pParentNode, const QUuid &uId) const
1736{
1737 /* Check parent-node type: */
1738 AssertPtrReturn(pParentNode, false);
1739 switch (pParentNode->type())
1740 {
1741 case UIChooserNodeType_Machine:
1742 {
1743 /* Check if pParentNode has the passed uId itself: */
1744 UIChooserNodeMachine *pMachineNode = pParentNode->toMachineNode();
1745 AssertPtrReturn(pMachineNode, false);
1746 if (pMachineNode->id() == uId)
1747 return true;
1748 break;
1749 }
1750 case UIChooserNodeType_Group:
1751 {
1752 /* Recursively iterate through children: */
1753 foreach (UIChooserNode *pChildNode, pParentNode->nodes())
1754 if (checkIfNodeContainChildWithId(pChildNode, uId))
1755 return true;
1756 break;
1757 }
1758 default:
1759 break;
1760 }
1761
1762 /* False by default: */
1763 return false;
1764}
1765
1766void UIChooserAbstractModel::saveGroupSettings()
1767{
1768 /* Make sure there is no group settings saving activity: */
1769 if (UIThreadGroupSettingsSave::instance())
1770 return;
1771
1772 /* Prepare full group map: */
1773 QMap<QString, QStringList> groups;
1774 gatherGroupSettings(groups, invisibleRoot());
1775
1776 /* Save information in other thread: */
1777 UIThreadGroupSettingsSave::prepare();
1778 emit sigGroupSavingStateChanged();
1779 connect(UIThreadGroupSettingsSave::instance(), &UIThreadGroupSettingsSave::sigReload,
1780 this, &UIChooserAbstractModel::sltReloadMachine);
1781 UIThreadGroupSettingsSave::instance()->configure(this, m_groups, groups);
1782 UIThreadGroupSettingsSave::instance()->start();
1783 m_groups = groups;
1784}
1785
1786void UIChooserAbstractModel::saveGroupDefinitions()
1787{
1788 /* Make sure there is no group definitions save activity: */
1789 if (UIThreadGroupDefinitionsSave::instance())
1790 return;
1791
1792 /* Prepare full group map: */
1793 QMap<QString, QStringList> groups;
1794 gatherGroupDefinitions(groups, invisibleRoot());
1795
1796 /* Save information in other thread: */
1797 UIThreadGroupDefinitionsSave::prepare();
1798 emit sigGroupSavingStateChanged();
1799 UIThreadGroupDefinitionsSave::instance()->configure(this, groups);
1800 UIThreadGroupDefinitionsSave::instance()->start();
1801}
1802
1803void UIChooserAbstractModel::gatherGroupSettings(QMap<QString, QStringList> &settings,
1804 UIChooserNode *pParentGroup)
1805{
1806 /* Iterate over all the machine-nodes: */
1807 foreach (UIChooserNode *pNode, pParentGroup->nodes(UIChooserNodeType_Machine))
1808 {
1809 /* Make sure it's really machine node: */
1810 AssertPtrReturnVoid(pNode);
1811 UIChooserNodeMachine *pMachineNode = pNode->toMachineNode();
1812 AssertPtrReturnVoid(pMachineNode);
1813 /* Make sure it's local machine node exactly and it's accessible: */
1814 if ( pMachineNode->cacheType() == UIVirtualMachineItemType_Local
1815 && pMachineNode->accessible())
1816 settings[toOldStyleUuid(pMachineNode->id())] << pParentGroup->fullName();
1817 }
1818 /* Iterate over all the group-nodes: */
1819 foreach (UIChooserNode *pNode, pParentGroup->nodes(UIChooserNodeType_Group))
1820 gatherGroupSettings(settings, pNode);
1821}
1822
1823void UIChooserAbstractModel::gatherGroupDefinitions(QMap<QString, QStringList> &definitions,
1824 UIChooserNode *pParentGroup)
1825{
1826 /* Prepare extra-data key for current group: */
1827 const QString strExtraDataKey = pParentGroup->fullName();
1828 /* Iterate over all the global-nodes: */
1829 foreach (UIChooserNode *pNode, pParentGroup->nodes(UIChooserNodeType_Global))
1830 {
1831 /* Append node definition: */
1832 AssertPtrReturnVoid(pNode);
1833 definitions[strExtraDataKey] << pNode->definition(true /* full */);
1834 }
1835 /* Iterate over all the group-nodes: */
1836 foreach (UIChooserNode *pNode, pParentGroup->nodes(UIChooserNodeType_Group))
1837 {
1838 /* Append node definition: */
1839 AssertPtrReturnVoid(pNode);
1840 definitions[strExtraDataKey] << pNode->definition(true /* full */);
1841 /* Go recursively through children: */
1842 gatherGroupDefinitions(definitions, pNode);
1843 }
1844 /* Iterate over all the machine-nodes: */
1845 foreach (UIChooserNode *pNode, pParentGroup->nodes(UIChooserNodeType_Machine))
1846 {
1847 /* Make sure it's really machine node: */
1848 AssertPtrReturnVoid(pNode);
1849 UIChooserNodeMachine *pMachineNode = pNode->toMachineNode();
1850 AssertPtrReturnVoid(pMachineNode);
1851 /* Append node definition, make sure it's local or real cloud machine node only: */
1852 if ( pMachineNode->cacheType() == UIVirtualMachineItemType_Local
1853 || pMachineNode->cacheType() == UIVirtualMachineItemType_CloudReal)
1854 definitions[strExtraDataKey] << pNode->definition(true /* full */);
1855 }
1856}
1857
1858void UIChooserAbstractModel::makeSureGroupSettingsSaveIsFinished()
1859{
1860 /* Cleanup if necessary: */
1861 if (UIThreadGroupSettingsSave::instance())
1862 UIThreadGroupSettingsSave::cleanup();
1863}
1864
1865void UIChooserAbstractModel::makeSureGroupDefinitionsSaveIsFinished()
1866{
1867 /* Cleanup if necessary: */
1868 if (UIThreadGroupDefinitionsSave::instance())
1869 UIThreadGroupDefinitionsSave::cleanup();
1870}
1871
1872UIChooserNode *UIChooserAbstractModel::searchProviderNode(const QUuid &uProviderId)
1873{
1874 /* Search for a list of nodes matching passed name: */
1875 QList<UIChooserNode*> providerNodes;
1876 invisibleRoot()->searchForNodes(uProviderId.toString(),
1877 UIChooserItemSearchFlag_CloudProvider | UIChooserItemSearchFlag_ExactId,
1878 providerNodes);
1879
1880 /* Return 1st node if any: */
1881 return providerNodes.value(0);
1882}
1883
1884UIChooserNode *UIChooserAbstractModel::searchProviderNode(const QString &strProviderShortName)
1885{
1886 /* Search for a list of nodes matching passed name: */
1887 QList<UIChooserNode*> providerNodes;
1888 invisibleRoot()->searchForNodes(strProviderShortName,
1889 UIChooserItemSearchFlag_CloudProvider | UIChooserItemSearchFlag_ExactName,
1890 providerNodes);
1891
1892 /* Return 1st node if any: */
1893 return providerNodes.value(0);
1894}
1895
1896UIChooserNode *UIChooserAbstractModel::searchProfileNode(UIChooserNode *pProviderNode, const QString &strProfileName)
1897{
1898 AssertPtrReturn(pProviderNode, 0);
1899
1900 /* Search for a list of nodes matching passed name: */
1901 QList<UIChooserNode*> profileNodes;
1902 pProviderNode->searchForNodes(strProfileName,
1903 UIChooserItemSearchFlag_CloudProfile | UIChooserItemSearchFlag_ExactName,
1904 profileNodes);
1905
1906 /* Return 1st node if any: */
1907 return profileNodes.value(0);
1908}
1909
1910UIChooserNode *UIChooserAbstractModel::searchProfileNode(const QString &strProviderShortName, const QString &strProfileName)
1911{
1912 /* Wrap method above: */
1913 return searchProfileNode(searchProviderNode(strProviderShortName), strProfileName);
1914}
1915
1916UIChooserNode *UIChooserAbstractModel::searchMachineNode(UIChooserNode *pProfileNode, const QUuid &uMachineId)
1917{
1918 AssertPtrReturn(pProfileNode, 0);
1919
1920 /* Search for a list of nodes matching passed ID: */
1921 QList<UIChooserNode*> machineNodes;
1922 pProfileNode->searchForNodes(uMachineId.toString(),
1923 UIChooserItemSearchFlag_Machine | UIChooserItemSearchFlag_ExactId,
1924 machineNodes);
1925
1926 /* Return 1st node if any: */
1927 return machineNodes.value(0);
1928}
1929
1930UIChooserNode *UIChooserAbstractModel::searchMachineNode(const QString &strProviderShortName, const QString &strProfileName, const QUuid &uMachineId)
1931{
1932 /* Wrap method above: */
1933 return searchMachineNode(searchProfileNode(strProviderShortName, strProfileName), uMachineId);
1934}
1935
1936UIChooserNode *UIChooserAbstractModel::searchFakeNode(UIChooserNode *pProfileNode)
1937{
1938 /* Wrap method above: */
1939 return searchMachineNode(pProfileNode, QUuid());
1940}
1941
1942UIChooserNode *UIChooserAbstractModel::searchFakeNode(const QString &strProviderShortName, const QString &strProfileName)
1943{
1944 /* Wrap method above: */
1945 return searchMachineNode(strProviderShortName, strProfileName, QUuid());
1946}
1947
1948QList<UIChooserNode*> UIChooserAbstractModel::enumerateCloudMachineNodes() const
1949{
1950 /* Search for a list of provider nodes: */
1951 QList<UIChooserNode*> providerNodes;
1952 invisibleRoot()->searchForNodes(QString(),
1953 UIChooserItemSearchFlag_CloudProvider,
1954 providerNodes);
1955
1956 /* Search for a list of profile nodes: */
1957 QList<UIChooserNode*> profileNodes;
1958 foreach (UIChooserNode *pProviderNode, providerNodes)
1959 pProviderNode->searchForNodes(QString(),
1960 UIChooserItemSearchFlag_CloudProfile,
1961 profileNodes);
1962
1963 /* Search for a list of machine nodes: */
1964 QList<UIChooserNode*> machineNodes;
1965 foreach (UIChooserNode *pProfileNode, profileNodes)
1966 pProfileNode->searchForNodes(QString(),
1967 UIChooserItemSearchFlag_Machine,
1968 machineNodes);
1969
1970 return machineNodes;
1971}
1972
1973void UIChooserAbstractModel::stopCloudUpdates(bool fForced /* = false */)
1974{
1975 /* Stop all cloud entity updates currently being performed: */
1976 foreach (const UICloudEntityKey &key, m_cloudEntityKeysBeingUpdated)
1977 {
1978 /* For profiles: */
1979 if (key.m_uMachineId.isNull())
1980 {
1981 /* Search task child by key: */
1982 UIProgressTaskReadCloudMachineList *pTask = findChild<UIProgressTaskReadCloudMachineList*>(key.toString());
1983 AssertPtrReturnVoid(pTask);
1984
1985 /* Wait for cloud profile refresh task to complete,
1986 * then delete the task itself manually: */
1987 if (!fForced)
1988 pTask->cancel();
1989 delete pTask;
1990 }
1991 /* For machines: */
1992 else
1993 {
1994 /* Search machine node: */
1995 UIChooserNode *pNode = searchMachineNode(key.m_strProviderShortName, key.m_strProfileName, key.m_uMachineId);
1996 AssertPtrReturnVoid(pNode);
1997 /* Acquire cloud machine item: */
1998 UIVirtualMachineItemCloud *pCloudMachineItem = pNode->toMachineNode()->cache()->toCloud();
1999 AssertPtrReturnVoid(pCloudMachineItem);
2000
2001 /* Wait for cloud machine refresh task to complete,
2002 * task itself will be deleted with the machine-node: */
2003 pCloudMachineItem->waitForAsyncInfoUpdateFinished();
2004 }
2005 }
2006
2007 /* We haven't let tasks to unregister themselves
2008 * so we have to cleanup task set ourselves: */
2009 m_cloudEntityKeysBeingUpdated.clear();
2010}
2011
2012
2013#include "UIChooserAbstractModel.moc"
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use