VirtualBox

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

Last change on this file since 103538 was 103538, checked in by vboxsync, 2 months ago

FE/Qt: Moving out logging stuff from UIDefs.h to separate UILoggingDefs.h; This breaks dependency of UIDefs/UICommon headers from VBox/log.h

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 76.3 KB
Line 
1/* $Id: UIChooserAbstractModel.cpp 103538 2024-02-22 17:06:26Z 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 <QRegularExpression>
30#include <QThread>
31
32/* GUI includes: */
33#include "UICommon.h"
34#include "UIChooser.h"
35#include "UIChooserAbstractModel.h"
36#include "UIChooserNode.h"
37#include "UIChooserNodeGroup.h"
38#include "UIChooserNodeGlobal.h"
39#include "UIChooserNodeMachine.h"
40#include "UICloudNetworkingStuff.h"
41#include "UIExtraDataManager.h"
42#include "UILoggingDefs.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 QRegularExpression shortRegExp(strShortTemplate);
474 const QRegularExpression fullRegExp(strFullTemplate);
475
476 /* Search for the maximum index: */
477 int iMinimumPossibleNumber = 0;
478 foreach (const QString &strName, groupNames)
479 {
480 const QRegularExpressionMatch mtShort = shortRegExp.match(strName);
481 const QRegularExpressionMatch mtFull = fullRegExp.match(strName);
482 if (mtShort.hasMatch())
483 iMinimumPossibleNumber = qMax(iMinimumPossibleNumber, 2);
484 else if (mtFull.hasMatch())
485 iMinimumPossibleNumber = qMax(iMinimumPossibleNumber, mtFull.captured(1).toInt() + 1);
486 }
487
488 /* Prepare/return result: */
489 QString strResult = strMinimumName;
490 if (iMinimumPossibleNumber)
491 strResult += " " + QString::number(iMinimumPossibleNumber);
492 return strResult;
493}
494
495void UIChooserAbstractModel::performSearch(const QString &strSearchTerm, int iSearchFlags)
496{
497 /* Make sure invisible root exists: */
498 AssertPtrReturnVoid(invisibleRoot());
499
500 /* Currently we perform the search only for machines, when this to be changed make
501 * sure the disabled flags of the other item types are also managed correctly. */
502
503 /* Reset the search first to erase the disabled flag,
504 * this also returns a full list of all machine nodes: */
505 const QList<UIChooserNode*> nodes = resetSearch();
506
507 /* Stop here if no search conditions specified: */
508 if (strSearchTerm.isEmpty())
509 return;
510
511 /* Search for all the nodes matching required condition: */
512 invisibleRoot()->searchForNodes(strSearchTerm, iSearchFlags, m_searchResults);
513
514 /* Assign/reset the disabled flag for required nodes: */
515 foreach (UIChooserNode *pNode, nodes)
516 {
517 AssertPtrReturnVoid(pNode);
518 pNode->setDisabled(!m_searchResults.contains(pNode));
519 }
520}
521
522QList<UIChooserNode*> UIChooserAbstractModel::resetSearch()
523{
524 /* Prepare resulting nodes: */
525 QList<UIChooserNode*> nodes;
526
527 /* Make sure invisible root exists: */
528 AssertPtrReturn(invisibleRoot(), nodes);
529
530 /* Calling UIChooserNode::searchForNodes with an empty search term
531 * returns a list all nodes (of the whole tree) of the required type: */
532 invisibleRoot()->searchForNodes(QString(), UIChooserItemSearchFlag_Machine, nodes);
533
534 /* Reset the disabled flag of the nodes first: */
535 foreach (UIChooserNode *pNode, nodes)
536 {
537 AssertPtrReturn(pNode, nodes);
538 pNode->setDisabled(false);
539 }
540
541 /* Reset the search result related data: */
542 m_searchResults.clear();
543
544 /* Return nodes: */
545 return nodes;
546}
547
548QList<UIChooserNode*> UIChooserAbstractModel::searchResult() const
549{
550 return m_searchResults;
551}
552
553void UIChooserAbstractModel::saveGroups()
554{
555 emit sigSaveSettings();
556}
557
558bool UIChooserAbstractModel::isGroupSavingInProgress() const
559{
560 return UIThreadGroupSettingsSave::instance()
561 || UIThreadGroupDefinitionsSave::instance();
562}
563
564/* static */
565QString UIChooserAbstractModel::toOldStyleUuid(const QUuid &uId)
566{
567 return uId.toString().remove(QRegularExpression("[{}]"));
568}
569
570/* static */
571QString UIChooserAbstractModel::prefixToString(UIChooserNodeDataPrefixType enmType)
572{
573 switch (enmType)
574 {
575 /* Global nodes: */
576 case UIChooserNodeDataPrefixType_Global: return "n";
577 /* Machine nodes: */
578 case UIChooserNodeDataPrefixType_Machine: return "m";
579 /* Group nodes: */
580 case UIChooserNodeDataPrefixType_Local: return "g";
581 case UIChooserNodeDataPrefixType_Provider: return "p";
582 case UIChooserNodeDataPrefixType_Profile: return "a";
583 }
584 return QString();
585}
586
587/* static */
588QString UIChooserAbstractModel::optionToString(UIChooserNodeDataOptionType enmType)
589{
590 switch (enmType)
591 {
592 /* Global nodes: */
593 case UIChooserNodeDataOptionType_GlobalFavorite: return "f";
594 /* Group nodes: */
595 case UIChooserNodeDataOptionType_GroupOpened: return "o";
596 }
597 return QString();
598}
599
600/* static */
601QString UIChooserAbstractModel::valueToString(UIChooserNodeDataValueType enmType)
602{
603 switch (enmType)
604 {
605 /* Global nodes: */
606 case UIChooserNodeDataValueType_GlobalDefault: return "GLOBAL";
607 }
608 return QString();
609}
610
611void UIChooserAbstractModel::setKeepCloudNodesUpdated(bool fUpdate)
612{
613 /* Make sure something changed: */
614 if (m_fKeepCloudNodesUpdated == fUpdate)
615 return;
616
617 /* Holds the value: */
618 m_fKeepCloudNodesUpdated = fUpdate;
619
620 /* Update all the real cloud machine items: */
621 foreach (UIChooserNode *pNode, enumerateCloudMachineNodes())
622 {
623 AssertReturnVoid(pNode && pNode->type() == UIChooserNodeType_Machine);
624 UIChooserNodeMachine *pMachineNode = pNode->toMachineNode();
625 AssertPtrReturnVoid(pMachineNode);
626 if (pMachineNode->cacheType() != UIVirtualMachineItemType_CloudReal)
627 continue;
628 UIVirtualMachineItemCloud *pCloudMachineItem = pMachineNode->cache()->toCloud();
629 AssertPtrReturnVoid(pCloudMachineItem);
630 pCloudMachineItem->setUpdateRequiredByGlobalReason(m_fKeepCloudNodesUpdated);
631 if (m_fKeepCloudNodesUpdated)
632 pCloudMachineItem->updateInfoAsync(false /* delayed? */);
633 }
634}
635
636void UIChooserAbstractModel::insertCloudEntityKey(const UICloudEntityKey &key)
637{
638// printf("Cloud entity with key %s being updated..\n", key.toString().toUtf8().constData());
639 m_cloudEntityKeysBeingUpdated.insert(key);
640 emit sigCloudUpdateStateChanged();
641}
642
643void UIChooserAbstractModel::removeCloudEntityKey(const UICloudEntityKey &key)
644{
645// printf("Cloud entity with key %s is updated!\n", key.toString().toUtf8().constData());
646 m_cloudEntityKeysBeingUpdated.remove(key);
647 emit sigCloudUpdateStateChanged();
648}
649
650bool UIChooserAbstractModel::containsCloudEntityKey(const UICloudEntityKey &key) const
651{
652 return m_cloudEntityKeysBeingUpdated.contains(key);
653}
654
655bool UIChooserAbstractModel::isCloudProfileUpdateInProgress() const
656{
657 /* Compose RE for profile: */
658 const QRegularExpression re("^/[^/]+/[^/]+$");
659 /* Check whether keys match profile RE: */
660 foreach (const UICloudEntityKey &key, m_cloudEntityKeysBeingUpdated)
661 {
662 const QRegularExpressionMatch mt = re.match(key.toString());
663 if (mt.hasMatch())
664 return true;
665 }
666 /* False by default: */
667 return false;
668}
669
670QList<UIVirtualMachineItemCloud*> UIChooserAbstractModel::cloudMachineItems() const
671{
672 QList<UIVirtualMachineItemCloud*> items;
673 foreach (UIChooserNode *pNode, enumerateCloudMachineNodes())
674 {
675 AssertReturn(pNode && pNode->type() == UIChooserNodeType_Machine, items);
676 UIChooserNodeMachine *pMachineNode = pNode->toMachineNode();
677 AssertPtrReturn(pMachineNode, items);
678 if (pMachineNode->cacheType() != UIVirtualMachineItemType_CloudReal)
679 continue;
680 UIVirtualMachineItemCloud *pCloudMachineItem = pMachineNode->cache()->toCloud();
681 AssertPtrReturn(pCloudMachineItem, items);
682 items << pCloudMachineItem;
683 }
684 return items;
685}
686
687void UIChooserAbstractModel::sltHandleCloudMachineRefreshStarted()
688{
689 /* Acquire sender: */
690 UIVirtualMachineItem *pCache = qobject_cast<UIVirtualMachineItem*>(sender());
691 AssertPtrReturnVoid(pCache);
692
693 /* Acquire sender's ID: */
694 const QUuid uId = pCache->id();
695
696 /* Search for a first machine node with passed ID: */
697 UIChooserNode *pMachineNode = searchMachineNode(invisibleRoot(), uId);
698
699 /* Insert cloud machine key into a list of keys currently being updated: */
700 const UICloudEntityKey guiCloudMachineKey = UICloudEntityKey(pMachineNode->parentNode()->parentNode()->name(),
701 pMachineNode->parentNode()->name(),
702 pMachineNode->toMachineNode()->id());
703 insertCloudEntityKey(guiCloudMachineKey);
704}
705
706void UIChooserAbstractModel::sltHandleCloudMachineRefreshFinished()
707{
708 /* Acquire sender: */
709 UIVirtualMachineItem *pCache = qobject_cast<UIVirtualMachineItem*>(sender());
710 AssertPtrReturnVoid(pCache);
711
712 /* Acquire sender's ID: */
713 const QUuid uId = pCache->id();
714
715 /* Search for a first machine node with passed ID: */
716 UIChooserNode *pMachineNode = searchMachineNode(invisibleRoot(), uId);
717
718 /* Remove cloud machine key from the list of keys currently being updated: */
719 const UICloudEntityKey guiCloudMachineKey = UICloudEntityKey(pMachineNode->parentNode()->parentNode()->name(),
720 pMachineNode->parentNode()->name(),
721 pMachineNode->toMachineNode()->id());
722 removeCloudEntityKey(guiCloudMachineKey);
723
724 /* Notify listeners: */
725 emit sigCloudMachineStateChange(uId);
726}
727
728void UIChooserAbstractModel::sltGroupSettingsSaveComplete()
729{
730 makeSureGroupSettingsSaveIsFinished();
731 emit sigGroupSavingStateChanged();
732}
733
734void UIChooserAbstractModel::sltGroupDefinitionsSaveComplete()
735{
736 makeSureGroupDefinitionsSaveIsFinished();
737 emit sigGroupSavingStateChanged();
738}
739
740void UIChooserAbstractModel::sltLocalMachineStateChanged(const QUuid &uMachineId, const KMachineState)
741{
742 /* Update machine-nodes with passed id: */
743 invisibleRoot()->updateAllNodes(uMachineId);
744}
745
746void UIChooserAbstractModel::sltLocalMachineDataChanged(const QUuid &uMachineId)
747{
748 /* Update machine-nodes with passed id: */
749 invisibleRoot()->updateAllNodes(uMachineId);
750}
751
752void UIChooserAbstractModel::sltLocalMachineRegistrationChanged(const QUuid &uMachineId, const bool fRegistered)
753{
754 /* Existing VM unregistered? */
755 if (!fRegistered)
756 {
757 /* Remove machine-items with passed id: */
758 invisibleRoot()->removeAllNodes(uMachineId);
759 /* Wipe out empty groups: */
760 wipeOutEmptyGroups();
761 }
762 /* New VM registered? */
763 else
764 {
765 /* Should we show this VM? */
766 if (gEDataManager->showMachineInVirtualBoxManagerChooser(uMachineId))
767 {
768 /* Add new machine-item: */
769 const CMachine comMachine = uiCommon().virtualBox().FindMachine(uMachineId.toString());
770 if (comMachine.isNotNull())
771 addLocalMachineIntoTheTree(comMachine, true /* make it visible */);
772 }
773 }
774}
775
776void UIChooserAbstractModel::sltLocalMachineGroupsChanged(const QUuid &uMachineId)
777{
778 /* Skip VM if restricted: */
779 if (!gEDataManager->showMachineInVirtualBoxManagerChooser(uMachineId))
780 return;
781
782 /* Search for cached group list: */
783 const QStringList oldGroupList = m_groups.value(toOldStyleUuid(uMachineId));
784 //printf("Old groups for VM with ID=%s: %s\n",
785 // uMachineId.toString().toUtf8().constData(),
786 // oldGroupList.join(", ").toUtf8().constData());
787
788 /* Search for existing registered machine: */
789 const CMachine comMachine = uiCommon().virtualBox().FindMachine(uMachineId.toString());
790 if (comMachine.isNull())
791 return;
792 /* Look for a new group list: */
793 const QStringList newGroupList = comMachine.GetGroups().toList();
794 //printf("New groups for VM with ID=%s: %s\n",
795 // uMachineId.toString().toUtf8().constData(),
796 // newGroupList.join(", ").toUtf8().constData());
797
798 /* Re-register VM if required: */
799 QSet<QString> newGroupSet(newGroupList.begin(), newGroupList.end());
800 QSet<QString> oldGroupSet(oldGroupList.begin(), oldGroupList.end());
801 if (newGroupSet != oldGroupSet)
802 {
803 sltLocalMachineRegistrationChanged(uMachineId, false);
804 sltLocalMachineRegistrationChanged(uMachineId, true);
805 }
806}
807
808void UIChooserAbstractModel::sltSessionStateChanged(const QUuid &uMachineId, const KSessionState)
809{
810 /* Update machine-nodes with passed id: */
811 invisibleRoot()->updateAllNodes(uMachineId);
812}
813
814void UIChooserAbstractModel::sltSnapshotChanged(const QUuid &uMachineId, const QUuid &)
815{
816 /* Update machine-nodes with passed id: */
817 invisibleRoot()->updateAllNodes(uMachineId);
818}
819
820void UIChooserAbstractModel::sltHandleCloudProviderUninstall(const QUuid &uProviderId)
821{
822 /* First of all, stop all cloud updates: */
823 stopCloudUpdates();
824
825 /* Search and delete corresponding cloud provider node if present: */
826 delete searchProviderNode(uProviderId);
827}
828
829void UIChooserAbstractModel::sltReloadMachine(const QUuid &uMachineId)
830{
831 /* Remove machine-items with passed id: */
832 invisibleRoot()->removeAllNodes(uMachineId);
833 /* Wipe out empty groups: */
834 wipeOutEmptyGroups();
835
836 /* Should we show this VM? */
837 if (gEDataManager->showMachineInVirtualBoxManagerChooser(uMachineId))
838 {
839 /* Add new machine-item: */
840 const CMachine comMachine = uiCommon().virtualBox().FindMachine(uMachineId.toString());
841 addLocalMachineIntoTheTree(comMachine, true /* make it visible */);
842 }
843}
844
845void UIChooserAbstractModel::sltCommitData()
846{
847 /* Finally, stop all cloud updates: */
848 stopCloudUpdates(true /* forced? */);
849}
850
851void UIChooserAbstractModel::sltDetachCOM()
852{
853 /* Delete tree: */
854 delete m_pInvisibleRootNode;
855 m_pInvisibleRootNode = 0;
856}
857
858void UIChooserAbstractModel::sltCloudMachineUnregistered(const QString &strProviderShortName,
859 const QString &strProfileName,
860 const QUuid &uId)
861{
862 /* Search for profile node: */
863 UIChooserNode *pProfileNode = searchProfileNode(strProviderShortName, strProfileName);
864 if (!pProfileNode)
865 return;
866
867 /* Remove machine-item with passed uId: */
868 pProfileNode->removeAllNodes(uId);
869
870 /* If there are no items left => add fake cloud VM node: */
871 if (pProfileNode->nodes(UIChooserNodeType_Machine).isEmpty())
872 createCloudMachineNode(pProfileNode, UIFakeCloudVirtualMachineItemState_Done);
873}
874
875void UIChooserAbstractModel::sltCloudMachinesUnregistered(const QString &strProviderShortName,
876 const QString &strProfileName,
877 const QList<QUuid> &ids)
878{
879 /* Search for profile node: */
880 UIChooserNode *pProfileNode = searchProfileNode(strProviderShortName, strProfileName);
881 if (!pProfileNode)
882 return;
883
884 /* Remove machine-items with passed id: */
885 foreach (const QUuid &uId, ids)
886 pProfileNode->removeAllNodes(uId);
887
888 /* If there are no items left => add fake cloud VM node: */
889 if (pProfileNode->nodes(UIChooserNodeType_Machine).isEmpty())
890 createCloudMachineNode(pProfileNode, UIFakeCloudVirtualMachineItemState_Done);
891}
892
893void UIChooserAbstractModel::sltCloudMachineRegistered(const QString &strProviderShortName,
894 const QString &strProfileName,
895 const CCloudMachine &comMachine)
896{
897 /* Search for profile node: */
898 UIChooserNode *pProfileNode = searchProfileNode(strProviderShortName, strProfileName);
899 if (!pProfileNode)
900 return;
901
902 /* Compose corresponding group path: */
903 const QString strGroup = QString("/%1/%2").arg(strProviderShortName, strProfileName);
904 /* Make sure there is no VM with such ID already: */
905 QUuid uId;
906 if (!cloudMachineId(comMachine, uId))
907 return;
908 if (checkIfNodeContainChildWithId(pProfileNode, uId))
909 return;
910 /* Add new machine-item: */
911 addCloudMachineIntoTheTree(strGroup, comMachine, true /* make it visible? */);
912
913 /* Delete fake node if present: */
914 delete searchFakeNode(pProfileNode);
915}
916
917void UIChooserAbstractModel::sltCloudMachinesRegistered(const QString &strProviderShortName,
918 const QString &strProfileName,
919 const QVector<CCloudMachine> &machines)
920{
921 /* Search for profile node: */
922 UIChooserNode *pProfileNode = searchProfileNode(strProviderShortName, strProfileName);
923 if (!pProfileNode)
924 return;
925
926 /* Compose corresponding group path: */
927 const QString strGroup = QString("/%1/%2").arg(strProviderShortName, strProfileName);
928 foreach (const CCloudMachine &comMachine, machines)
929 {
930 /* Make sure there is no VM with such ID already: */
931 QUuid uId;
932 if (!cloudMachineId(comMachine, uId))
933 continue;
934 if (checkIfNodeContainChildWithId(pProfileNode, uId))
935 continue;
936 /* Add new machine-item: */
937 addCloudMachineIntoTheTree(strGroup, comMachine, false /* make it visible? */);
938 }
939
940 /* Delete fake node if present: */
941 delete searchFakeNode(pProfileNode);
942}
943
944void UIChooserAbstractModel::sltHandleReadCloudMachineListTaskComplete()
945{
946 /* Parse task result: */
947 UIProgressTaskReadCloudMachineList *pSender = qobject_cast<UIProgressTaskReadCloudMachineList*>(sender());
948 AssertPtrReturnVoid(pSender);
949 const UICloudEntityKey guiCloudProfileKey = pSender->cloudProfileKey();
950 const QVector<CCloudMachine> machines = pSender->machines();
951 const QString strErrorMessage = pSender->errorMessage();
952
953 /* Delete task: */
954 delete pSender;
955
956 /* Check whether this task was expected: */
957 if (!containsCloudEntityKey(guiCloudProfileKey))
958 return;
959
960 /* Search for provider node separately, it can be removed already: */
961 UIChooserNode *pProviderNode = searchProviderNode(guiCloudProfileKey.m_strProviderShortName);
962 if (pProviderNode)
963 {
964 /* Search for profile node separately, it can be hidden at all: */
965 UIChooserNode *pProfileNode = searchProfileNode(pProviderNode, guiCloudProfileKey.m_strProfileName);
966 if (pProfileNode)
967 {
968 /* Compose old set of machine IDs: */
969 QSet<QUuid> oldIDs;
970 foreach (UIChooserNode *pNode, pProfileNode->nodes(UIChooserNodeType_Machine))
971 {
972 AssertPtrReturnVoid(pNode);
973 UIChooserNodeMachine *pNodeMachine = pNode->toMachineNode();
974 AssertPtrReturnVoid(pNodeMachine);
975 if (pNodeMachine->cacheType() != UIVirtualMachineItemType_CloudReal)
976 continue;
977 oldIDs << pNodeMachine->id();
978 }
979 /* Compose new set of machine IDs and map of machines: */
980 QSet<QUuid> newIDs;
981 QMap<QUuid, CCloudMachine> newMachines;
982 foreach (const CCloudMachine &comMachine, machines)
983 {
984 QUuid uId;
985 AssertReturnVoid(cloudMachineId(comMachine, uId));
986 newMachines[uId] = comMachine;
987 newIDs << uId;
988 }
989
990 /* Calculate set of unregistered/registered IDs: */
991 const QSet<QUuid> unregisteredIDs = oldIDs - newIDs;
992 const QSet<QUuid> registeredIDs = newIDs - oldIDs;
993 QVector<CCloudMachine> registeredMachines;
994 foreach (const QUuid &uId, registeredIDs)
995 registeredMachines << newMachines.value(uId);
996
997 /* Remove unregistered cloud VM nodes: */
998 if (!unregisteredIDs.isEmpty())
999 {
1000#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
1001 QList<QUuid> listUnregisteredIDs(unregisteredIDs.begin(), unregisteredIDs.end());
1002#else
1003 QList<QUuid> listUnregisteredIDs = unregisteredIDs.toList();
1004#endif
1005 sltCloudMachinesUnregistered(guiCloudProfileKey.m_strProviderShortName,
1006 guiCloudProfileKey.m_strProfileName,
1007 listUnregisteredIDs);
1008 }
1009 /* Add registered cloud VM nodes: */
1010 if (!registeredMachines.isEmpty())
1011 sltCloudMachinesRegistered(guiCloudProfileKey.m_strProviderShortName,
1012 guiCloudProfileKey.m_strProfileName,
1013 registeredMachines);
1014 /* If we changed nothing and have nothing currently: */
1015 if (unregisteredIDs.isEmpty() && newIDs.isEmpty())
1016 {
1017 /* We should update at least fake cloud machine node: */
1018 UIChooserNode *pFakeNode = searchFakeNode(pProfileNode);
1019 AssertPtrReturnVoid(pFakeNode);
1020 UIVirtualMachineItemCloud *pFakeMachineItem = pFakeNode->toMachineNode()->cache()->toCloud();
1021 AssertPtrReturnVoid(pFakeMachineItem);
1022 pFakeMachineItem->setFakeCloudItemState(UIFakeCloudVirtualMachineItemState_Done);
1023 pFakeMachineItem->setFakeCloudItemErrorMessage(strErrorMessage);
1024 if (pFakeNode->item())
1025 pFakeNode->item()->updateItem();
1026 }
1027 }
1028 }
1029
1030 /* Remove cloud entity key from the list of keys currently being updated: */
1031 removeCloudEntityKey(guiCloudProfileKey);
1032
1033 /* Notify listeners: */
1034 emit sigCloudProfileStateChange(guiCloudProfileKey.m_strProviderShortName,
1035 guiCloudProfileKey.m_strProfileName);
1036}
1037
1038void UIChooserAbstractModel::sltHandleCloudProfileManagerCumulativeChange()
1039{
1040 /* Reload cloud tree: */
1041 reloadCloudTree();
1042}
1043
1044void UIChooserAbstractModel::createReadCloudMachineListTask(const UICloudEntityKey &guiCloudProfileKey, bool fWithRefresh)
1045{
1046 /* Do not create task if already registered: */
1047 if (containsCloudEntityKey(guiCloudProfileKey))
1048 return;
1049
1050 /* Create task: */
1051 UIProgressTaskReadCloudMachineList *pTask = new UIProgressTaskReadCloudMachineList(this,
1052 guiCloudProfileKey,
1053 fWithRefresh);
1054 if (pTask)
1055 {
1056 /* It's easy to find child by name later: */
1057 pTask->setObjectName(guiCloudProfileKey.toString());
1058
1059 /* Insert cloud profile key into a list of keys currently being updated: */
1060 insertCloudEntityKey(guiCloudProfileKey);
1061
1062 /* Connect and start it finally: */
1063 connect(pTask, &UIProgressTaskReadCloudMachineList::sigProgressFinished,
1064 this, &UIChooserAbstractModel::sltHandleReadCloudMachineListTaskComplete);
1065 pTask->start();
1066 }
1067}
1068
1069void UIChooserAbstractModel::sltSaveSettings()
1070{
1071 saveGroupSettings();
1072 saveGroupDefinitions();
1073}
1074
1075void UIChooserAbstractModel::prepare()
1076{
1077 prepareConnections();
1078}
1079
1080void UIChooserAbstractModel::prepareConnections()
1081{
1082 /* UICommon connections: */
1083 connect(&uiCommon(), &UICommon::sigAskToCommitData,
1084 this, &UIChooserAbstractModel::sltCommitData);
1085 connect(&uiCommon(), &UICommon::sigAskToDetachCOM,
1086 this, &UIChooserAbstractModel::sltDetachCOM);
1087 connect(&uiCommon(), &UICommon::sigCloudMachineUnregistered,
1088 this, &UIChooserAbstractModel::sltCloudMachineUnregistered);
1089 connect(&uiCommon(), &UICommon::sigCloudMachineRegistered,
1090 this, &UIChooserAbstractModel::sltCloudMachineRegistered);
1091
1092 /* Global connections: */
1093 connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigMachineStateChange,
1094 this, &UIChooserAbstractModel::sltLocalMachineStateChanged);
1095 connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigMachineDataChange,
1096 this, &UIChooserAbstractModel::sltLocalMachineDataChanged);
1097 connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigMachineRegistered,
1098 this, &UIChooserAbstractModel::sltLocalMachineRegistrationChanged);
1099 connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigMachineGroupsChange,
1100 this, &UIChooserAbstractModel::sltLocalMachineGroupsChanged);
1101 connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigSessionStateChange,
1102 this, &UIChooserAbstractModel::sltSessionStateChanged);
1103 connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigSnapshotTake,
1104 this, &UIChooserAbstractModel::sltSnapshotChanged);
1105 connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigSnapshotDelete,
1106 this, &UIChooserAbstractModel::sltSnapshotChanged);
1107 connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigSnapshotChange,
1108 this, &UIChooserAbstractModel::sltSnapshotChanged);
1109 connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigSnapshotRestore,
1110 this, &UIChooserAbstractModel::sltSnapshotChanged);
1111 connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigCloudProviderListChanged,
1112 this, &UIChooserAbstractModel::sltHandleCloudProfileManagerCumulativeChange);
1113 connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigCloudProfileRegistered,
1114 this, &UIChooserAbstractModel::sltHandleCloudProfileManagerCumulativeChange);
1115 connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigCloudProfileChanged,
1116 this, &UIChooserAbstractModel::sltHandleCloudProfileManagerCumulativeChange);
1117 connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigCloudProviderUninstall,
1118 this, &UIChooserAbstractModel::sltHandleCloudProviderUninstall);
1119
1120 /* Settings saving connections: */
1121 connect(this, &UIChooserAbstractModel::sigSaveSettings,
1122 this, &UIChooserAbstractModel::sltSaveSettings,
1123 Qt::QueuedConnection);
1124
1125 /* Extra-data connections: */
1126 connect(gEDataManager, &UIExtraDataManager::sigCloudProfileManagerRestrictionChange,
1127 this, &UIChooserAbstractModel::sltHandleCloudProfileManagerCumulativeChange);
1128}
1129
1130void UIChooserAbstractModel::cleanupConnections()
1131{
1132 /* Group saving connections: */
1133 disconnect(this, &UIChooserAbstractModel::sigSaveSettings,
1134 this, &UIChooserAbstractModel::sltSaveSettings);
1135}
1136
1137void UIChooserAbstractModel::cleanup()
1138{
1139 cleanupConnections();
1140}
1141
1142void UIChooserAbstractModel::reloadLocalTree()
1143{
1144 LogRelFlow(("UIChooserAbstractModel: Loading local VMs...\n"));
1145
1146 /* Acquire VBox: */
1147 const CVirtualBox comVBox = uiCommon().virtualBox();
1148
1149 /* Acquire existing local machines: */
1150 const QVector<CMachine> machines = comVBox.GetMachines();
1151 /* Show error message if necessary: */
1152 if (!comVBox.isOk())
1153 UINotificationMessage::cannotAcquireVirtualBoxParameter(comVBox);
1154 else
1155 {
1156 /* Iterate through existing machines: */
1157 foreach (const CMachine &comMachine, machines)
1158 {
1159 /* Skip if we have nothing to populate (wtf happened?): */
1160 if (comMachine.isNull())
1161 continue;
1162
1163 /* Get machine ID: */
1164 const QUuid uMachineID = comMachine.GetId();
1165 /* Show error message if necessary: */
1166 if (!comMachine.isOk())
1167 {
1168 UINotificationMessage::cannotAcquireMachineParameter(comMachine);
1169 continue;
1170 }
1171
1172 /* Skip if we have nothing to show (wtf happened?): */
1173 if (uMachineID.isNull())
1174 continue;
1175
1176 /* Skip if machine is restricted from being shown: */
1177 if (!gEDataManager->showMachineInVirtualBoxManagerChooser(uMachineID))
1178 continue;
1179
1180 /* Add machine into tree: */
1181 addLocalMachineIntoTheTree(comMachine);
1182 }
1183 }
1184
1185 LogRelFlow(("UIChooserAbstractModel: Local VMs loaded.\n"));
1186}
1187
1188void UIChooserAbstractModel::reloadCloudTree()
1189{
1190 LogRelFlow(("UIChooserAbstractModel: Loading cloud providers/profiles...\n"));
1191
1192 /* Wipe out existing cloud providers first.
1193 * This is quite rude, in future we need to reimplement it more wise.. */
1194 foreach (UIChooserNode *pNode, invisibleRoot()->nodes(UIChooserNodeType_Group))
1195 {
1196 AssertPtrReturnVoid(pNode);
1197 UIChooserNodeGroup *pGroupNode = pNode->toGroupNode();
1198 AssertPtrReturnVoid(pGroupNode);
1199 if (pGroupNode->groupType() == UIChooserNodeGroupType_Provider)
1200 delete pNode;
1201 }
1202
1203 /* Acquire Cloud Profile Manager restrictions: */
1204 const QStringList restrictions = gEDataManager->cloudProfileManagerRestrictions();
1205
1206 /* Iterate through existing providers: */
1207 foreach (CCloudProvider comCloudProvider, listCloudProviders())
1208 {
1209 /* Skip if we have nothing to populate: */
1210 if (comCloudProvider.isNull())
1211 continue;
1212
1213 /* Acquire provider id: */
1214 QUuid uProviderId;
1215 if (!cloudProviderId(comCloudProvider, uProviderId))
1216 continue;
1217
1218 /* Acquire provider short name: */
1219 QString strProviderShortName;
1220 if (!cloudProviderShortName(comCloudProvider, strProviderShortName))
1221 continue;
1222
1223 /* Make sure this provider isn't restricted: */
1224 const QString strProviderPath = QString("/%1").arg(strProviderShortName);
1225 if (restrictions.contains(strProviderPath))
1226 continue;
1227
1228 /* Acquire list of profiles: */
1229 const QVector<CCloudProfile> profiles = listCloudProfiles(comCloudProvider);
1230 if (profiles.isEmpty())
1231 continue;
1232
1233 /* Add provider group node: */
1234 UIChooserNodeGroup *pProviderNode =
1235 new UIChooserNodeGroup(invisibleRoot() /* parent */,
1236 getDesiredNodePosition(invisibleRoot(),
1237 UIChooserNodeDataPrefixType_Provider,
1238 strProviderShortName),
1239 uProviderId,
1240 strProviderShortName,
1241 UIChooserNodeGroupType_Provider,
1242 shouldGroupNodeBeOpened(invisibleRoot(),
1243 UIChooserNodeDataPrefixType_Provider,
1244 strProviderShortName));
1245
1246 /* Iterate through provider's profiles: */
1247 foreach (CCloudProfile comCloudProfile, profiles)
1248 {
1249 /* Skip if we have nothing to populate: */
1250 if (comCloudProfile.isNull())
1251 continue;
1252
1253 /* Acquire profile name: */
1254 QString strProfileName;
1255 if (!cloudProfileName(comCloudProfile, strProfileName))
1256 continue;
1257
1258 /* Make sure this profile isn't restricted: */
1259 const QString strProfilePath = QString("/%1/%2").arg(strProviderShortName, strProfileName);
1260 if (restrictions.contains(strProfilePath))
1261 continue;
1262
1263 /* Add profile sub-group node: */
1264 UIChooserNodeGroup *pProfileNode =
1265 new UIChooserNodeGroup(pProviderNode /* parent */,
1266 getDesiredNodePosition(pProviderNode,
1267 UIChooserNodeDataPrefixType_Profile,
1268 strProfileName),
1269 QUuid() /* id */,
1270 strProfileName,
1271 UIChooserNodeGroupType_Profile,
1272 shouldGroupNodeBeOpened(pProviderNode,
1273 UIChooserNodeDataPrefixType_Profile,
1274 strProfileName));
1275
1276 /* Add fake cloud VM item: */
1277 createCloudMachineNode(pProfileNode, UIFakeCloudVirtualMachineItemState_Loading);
1278
1279 /* Create read cloud machine list task: */
1280 const UICloudEntityKey guiCloudProfileKey = UICloudEntityKey(strProviderShortName, strProfileName);
1281 createReadCloudMachineListTask(guiCloudProfileKey, true /* with refresh? */);
1282 }
1283 }
1284
1285 LogRelFlow(("UIChooserAbstractModel: Cloud providers/profiles loaded.\n"));
1286}
1287
1288void UIChooserAbstractModel::addLocalMachineIntoTheTree(const CMachine &comMachine,
1289 bool fMakeItVisible /* = false */)
1290{
1291 /* Make sure passed VM is not NULL: */
1292 if (comMachine.isNull())
1293 LogRelFlow(("UIChooserModel: ERROR: Passed local VM is NULL!\n"));
1294 AssertReturnVoid(!comMachine.isNull());
1295
1296 /* Which VM we are loading: */
1297 const QUuid uId = comMachine.GetId();
1298 LogRelFlow(("UIChooserModel: Loading local VM with ID={%s}...\n",
1299 toOldStyleUuid(uId).toUtf8().constData()));
1300
1301 /* Is that machine accessible? */
1302 if (comMachine.GetAccessible())
1303 {
1304 /* Acquire VM name: */
1305 const QString strName = comMachine.GetName();
1306 LogRelFlow(("UIChooserModel: Local VM {%s} is accessible.\n", strName.toUtf8().constData()));
1307 /* Which groups passed machine attached to? */
1308 const QVector<QString> groups = comMachine.GetGroups();
1309 const QStringList groupList = groups.toList();
1310 const QString strGroups = groupList.join(", ");
1311 LogRelFlow(("UIChooserModel: Local VM {%s} has groups: {%s}.\n",
1312 strName.toUtf8().constData(), strGroups.toUtf8().constData()));
1313 foreach (QString strGroup, groups)
1314 {
1315 /* Remove last '/' if any: */
1316 if (strGroup.right(1) == "/")
1317 strGroup.truncate(strGroup.size() - 1);
1318 /* Create machine-item with found group-item as parent: */
1319 LogRelFlow(("UIChooserModel: Creating node for local VM {%s} in group {%s}.\n",
1320 strName.toUtf8().constData(), strGroup.toUtf8().constData()));
1321 createLocalMachineNode(getLocalGroupNode(strGroup, invisibleRoot(), fMakeItVisible), comMachine);
1322 }
1323 /* Update group settings: */
1324 m_groups[toOldStyleUuid(uId)] = groupList;
1325 }
1326 /* Inaccessible machine: */
1327 else
1328 {
1329 /* VM is accessible: */
1330 LogRelFlow(("UIChooserModel: Local VM {%s} is inaccessible.\n",
1331 toOldStyleUuid(uId).toUtf8().constData()));
1332 /* Create machine-item with main-root group-item as parent: */
1333 createLocalMachineNode(invisibleRoot(), comMachine);
1334 }
1335}
1336
1337void UIChooserAbstractModel::addCloudMachineIntoTheTree(const QString &strGroup,
1338 const CCloudMachine &comMachine,
1339 bool fMakeItVisible /* = false */)
1340{
1341 /* Make sure passed VM is not NULL: */
1342 if (comMachine.isNull())
1343 LogRelFlow(("UIChooserModel: ERROR: Passed cloud VM is NULL!\n"));
1344 AssertReturnVoid(!comMachine.isNull());
1345
1346 /* Which VM we are loading: */
1347 const QUuid uId = comMachine.GetId();
1348 LogRelFlow(("UIChooserModel: Loading cloud VM with ID={%s}...\n",
1349 toOldStyleUuid(uId).toUtf8().constData()));
1350
1351 /* Acquire VM name: */
1352 QString strName = comMachine.GetName();
1353 if (strName.isEmpty())
1354 strName = uId.toString();
1355 LogRelFlow(("UIChooserModel: Creating node for cloud VM {%s} in group {%s}.\n",
1356 strName.toUtf8().constData(), strGroup.toUtf8().constData()));
1357 /* Create machine-item with found group-item as parent: */
1358 createCloudMachineNode(getCloudGroupNode(strGroup, invisibleRoot(), fMakeItVisible), comMachine);
1359 /* Update group settings: */
1360 const QStringList groupList(strGroup);
1361 m_groups[toOldStyleUuid(uId)] = groupList;
1362}
1363
1364UIChooserNode *UIChooserAbstractModel::getLocalGroupNode(const QString &strName, UIChooserNode *pParentNode, bool fAllGroupsOpened)
1365{
1366 /* Check passed stuff: */
1367 if (pParentNode->name() == strName)
1368 return pParentNode;
1369
1370 /* Prepare variables: */
1371 const QString strFirstSubName = strName.section('/', 0, 0);
1372 const QString strFirstSuffix = strName.section('/', 1, -1);
1373 const QString strSecondSubName = strFirstSuffix.section('/', 0, 0);
1374 const QString strSecondSuffix = strFirstSuffix.section('/', 1, -1);
1375
1376 /* Passed group name equal to first sub-name: */
1377 if (pParentNode->name() == strFirstSubName)
1378 {
1379 /* Make sure first-suffix is NOT empty: */
1380 AssertMsg(!strFirstSuffix.isEmpty(), ("Invalid group name!"));
1381 /* Trying to get group node among our children: */
1382 foreach (UIChooserNode *pNode, pParentNode->nodes(UIChooserNodeType_Group))
1383 {
1384 AssertPtrReturn(pNode, 0);
1385 UIChooserNodeGroup *pGroupNode = pNode->toGroupNode();
1386 AssertPtrReturn(pGroupNode, 0);
1387 if ( pGroupNode->groupType() == UIChooserNodeGroupType_Local
1388 && pNode->name() == strSecondSubName)
1389 {
1390 UIChooserNode *pFoundNode = getLocalGroupNode(strFirstSuffix, pNode, fAllGroupsOpened);
1391 if (UIChooserNodeGroup *pFoundGroupNode = pFoundNode->toGroupNode())
1392 if (fAllGroupsOpened && pFoundGroupNode->isClosed())
1393 pFoundGroupNode->open();
1394 return pFoundNode;
1395 }
1396 }
1397 }
1398
1399 /* Found nothing? Creating: */
1400 UIChooserNodeGroup *pNewGroupNode =
1401 new UIChooserNodeGroup(pParentNode,
1402 getDesiredNodePosition(pParentNode,
1403 UIChooserNodeDataPrefixType_Local,
1404 strSecondSubName),
1405 QUuid() /* id */,
1406 strSecondSubName,
1407 UIChooserNodeGroupType_Local,
1408 fAllGroupsOpened || shouldGroupNodeBeOpened(pParentNode,
1409 UIChooserNodeDataPrefixType_Local,
1410 strSecondSubName));
1411 return strSecondSuffix.isEmpty() ? pNewGroupNode : getLocalGroupNode(strFirstSuffix, pNewGroupNode, fAllGroupsOpened);
1412}
1413
1414UIChooserNode *UIChooserAbstractModel::getCloudGroupNode(const QString &strName, UIChooserNode *pParentNode, bool fAllGroupsOpened)
1415{
1416 /* Check passed stuff: */
1417 if (pParentNode->name() == strName)
1418 return pParentNode;
1419
1420 /* Prepare variables: */
1421 const QString strFirstSubName = strName.section('/', 0, 0);
1422 const QString strFirstSuffix = strName.section('/', 1, -1);
1423 const QString strSecondSubName = strFirstSuffix.section('/', 0, 0);
1424
1425 /* Passed group name equal to first sub-name: */
1426 if (pParentNode->name() == strFirstSubName)
1427 {
1428 /* Make sure first-suffix is NOT empty: */
1429 AssertMsg(!strFirstSuffix.isEmpty(), ("Invalid group name!"));
1430 /* Trying to get group node among our children: */
1431 foreach (UIChooserNode *pNode, pParentNode->nodes(UIChooserNodeType_Group))
1432 {
1433 AssertPtrReturn(pNode, 0);
1434 UIChooserNodeGroup *pGroupNode = pNode->toGroupNode();
1435 AssertPtrReturn(pGroupNode, 0);
1436 if ( ( pGroupNode->groupType() == UIChooserNodeGroupType_Provider
1437 || pGroupNode->groupType() == UIChooserNodeGroupType_Profile)
1438 && pNode->name() == strSecondSubName)
1439 {
1440 UIChooserNode *pFoundNode = getCloudGroupNode(strFirstSuffix, pNode, fAllGroupsOpened);
1441 if (UIChooserNodeGroup *pFoundGroupNode = pFoundNode->toGroupNode())
1442 if (fAllGroupsOpened && pFoundGroupNode->isClosed())
1443 pFoundGroupNode->open();
1444 return pFoundNode;
1445 }
1446 }
1447 }
1448
1449 /* Found nothing? Returning parent: */
1450 AssertFailedReturn(pParentNode);
1451}
1452
1453bool UIChooserAbstractModel::shouldGroupNodeBeOpened(UIChooserNode *pParentNode,
1454 UIChooserNodeDataPrefixType enmDataType,
1455 const QString &strName) const
1456{
1457 /* Read group definitions: */
1458 const QStringList definitions = gEDataManager->machineGroupDefinitions(pParentNode->fullName());
1459 /* Return 'false' if no definitions found: */
1460 if (definitions.isEmpty())
1461 return false;
1462
1463 /* Prepare required group definition reg-exp: */
1464 const QString strNodePrefix = prefixToString(enmDataType);
1465 const QString strNodeOptionOpened = optionToString(UIChooserNodeDataOptionType_GroupOpened);
1466 const QString strDefinitionTemplate = QString("%1(\\S)*=%2").arg(strNodePrefix, strName);
1467 const QRegularExpression re(strDefinitionTemplate);
1468 /* For each the group definition: */
1469 foreach (const QString &strDefinition, definitions)
1470 {
1471 /* Check if this is required definition: */
1472 const QRegularExpressionMatch mt = re.match(strDefinition);
1473 if (mt.capturedStart() == 0)
1474 {
1475 /* Get group descriptor: */
1476 const QString strDescriptor = mt.captured(1);
1477 if (strDescriptor.contains(strNodeOptionOpened))
1478 return true;
1479 }
1480 }
1481
1482 /* Return 'false' by default: */
1483 return false;
1484}
1485
1486bool UIChooserAbstractModel::shouldGlobalNodeBeFavorite(UIChooserNode *pParentNode) const
1487{
1488 /* Read group definitions: */
1489 const QStringList definitions = gEDataManager->machineGroupDefinitions(pParentNode->fullName());
1490 /* Return 'false' if no definitions found: */
1491 if (definitions.isEmpty())
1492 return false;
1493
1494 /* Prepare required group definition reg-exp: */
1495 const QString strNodePrefix = prefixToString(UIChooserNodeDataPrefixType_Global);
1496 const QString strNodeOptionFavorite = optionToString(UIChooserNodeDataOptionType_GlobalFavorite);
1497 const QString strNodeValueDefault = valueToString(UIChooserNodeDataValueType_GlobalDefault);
1498 const QString strDefinitionTemplate = QString("%1(\\S)*=%2").arg(strNodePrefix, strNodeValueDefault);
1499 const QRegularExpression re(strDefinitionTemplate);
1500 /* For each the group definition: */
1501 foreach (const QString &strDefinition, definitions)
1502 {
1503 /* Check if this is required definition: */
1504 const QRegularExpressionMatch mt = re.match(strDefinition);
1505 if (mt.capturedStart() == 0)
1506 {
1507 /* Get group descriptor: */
1508 const QString strDescriptor = mt.captured(1);
1509 if (strDescriptor.contains(strNodeOptionFavorite))
1510 return true;
1511 }
1512 }
1513
1514 /* Return 'false' by default: */
1515 return false;
1516}
1517
1518void UIChooserAbstractModel::wipeOutEmptyGroupsStartingFrom(UIChooserNode *pParent)
1519{
1520 /* Cleanup all the group children recursively first: */
1521 foreach (UIChooserNode *pNode, pParent->nodes(UIChooserNodeType_Group))
1522 wipeOutEmptyGroupsStartingFrom(pNode);
1523 /* If parent isn't root and has no nodes: */
1524 if (!pParent->isRoot() && !pParent->hasNodes())
1525 {
1526 /* Delete parent node and item: */
1527 delete pParent;
1528 }
1529}
1530
1531int UIChooserAbstractModel::getDesiredNodePosition(UIChooserNode *pParentNode,
1532 UIChooserNodeDataPrefixType enmDataType,
1533 const QString &strName)
1534{
1535 /* End of list (by default)? */
1536 int iNewNodeDesiredPosition = -1;
1537 /* Which position should be new node placed by definitions: */
1538 const int iNewNodeDefinitionPosition = getDefinedNodePosition(pParentNode, enmDataType, strName);
1539
1540 /* If some position defined: */
1541 if (iNewNodeDefinitionPosition != -1)
1542 {
1543 /* Start of list if some definition present: */
1544 iNewNodeDesiredPosition = 0;
1545 /* We have to check all the existing node positions: */
1546 UIChooserNodeType enmType = UIChooserNodeType_Any;
1547 switch (enmDataType)
1548 {
1549 case UIChooserNodeDataPrefixType_Global: enmType = UIChooserNodeType_Global; break;
1550 case UIChooserNodeDataPrefixType_Machine: enmType = UIChooserNodeType_Machine; break;
1551 case UIChooserNodeDataPrefixType_Local:
1552 case UIChooserNodeDataPrefixType_Provider:
1553 case UIChooserNodeDataPrefixType_Profile: enmType = UIChooserNodeType_Group; break;
1554 }
1555 const QList<UIChooserNode*> nodes = pParentNode->nodes(enmType);
1556 for (int i = nodes.size() - 1; i >= 0; --i)
1557 {
1558 /* Get current node: */
1559 UIChooserNode *pNode = nodes.at(i);
1560 AssertPtrReturn(pNode, iNewNodeDesiredPosition);
1561 /* Which position should be current node placed by definitions? */
1562 UIChooserNodeDataPrefixType enmNodeDataType = UIChooserNodeDataPrefixType_Global;
1563 QString strDefinitionName;
1564 switch (pNode->type())
1565 {
1566 case UIChooserNodeType_Machine:
1567 {
1568 enmNodeDataType = UIChooserNodeDataPrefixType_Machine;
1569 strDefinitionName = toOldStyleUuid(pNode->toMachineNode()->id());
1570 break;
1571 }
1572 case UIChooserNodeType_Group:
1573 {
1574 /* Cast to group node: */
1575 UIChooserNodeGroup *pGroupNode = pNode->toGroupNode();
1576 AssertPtrReturn(pGroupNode, iNewNodeDesiredPosition);
1577 switch (pGroupNode->groupType())
1578 {
1579 case UIChooserNodeGroupType_Local: enmNodeDataType = UIChooserNodeDataPrefixType_Local; break;
1580 case UIChooserNodeGroupType_Provider: enmNodeDataType = UIChooserNodeDataPrefixType_Provider; break;
1581 case UIChooserNodeGroupType_Profile: enmNodeDataType = UIChooserNodeDataPrefixType_Profile; break;
1582 default: break;
1583 }
1584 strDefinitionName = pNode->name();
1585 break;
1586 }
1587 default:
1588 break;
1589 }
1590 /* If some position defined: */
1591 const int iNodeDefinitionPosition = getDefinedNodePosition(pParentNode, enmNodeDataType, strDefinitionName);
1592 if (iNodeDefinitionPosition != -1)
1593 {
1594 AssertReturn(iNodeDefinitionPosition != iNewNodeDefinitionPosition, iNewNodeDesiredPosition);
1595 if (iNodeDefinitionPosition < iNewNodeDefinitionPosition)
1596 {
1597 iNewNodeDesiredPosition = i + 1;
1598 break;
1599 }
1600 }
1601 }
1602 }
1603
1604 /* Return desired node position: */
1605 return iNewNodeDesiredPosition;
1606}
1607
1608int UIChooserAbstractModel::getDefinedNodePosition(UIChooserNode *pParentNode, UIChooserNodeDataPrefixType enmDataType, const QString &strName)
1609{
1610 /* Read group definitions: */
1611 const QStringList definitions = gEDataManager->machineGroupDefinitions(pParentNode->fullName());
1612 /* Return 'false' if no definitions found: */
1613 if (definitions.isEmpty())
1614 return -1;
1615
1616 /* Prepare definition reg-exp: */
1617 QString strDefinitionTemplateShort;
1618 QString strDefinitionTemplateFull;
1619 const QString strNodePrefixLocal = prefixToString(UIChooserNodeDataPrefixType_Local);
1620 const QString strNodePrefixProvider = prefixToString(UIChooserNodeDataPrefixType_Provider);
1621 const QString strNodePrefixProfile = prefixToString(UIChooserNodeDataPrefixType_Profile);
1622 const QString strNodePrefixMachine = prefixToString(UIChooserNodeDataPrefixType_Machine);
1623 switch (enmDataType)
1624 {
1625 case UIChooserNodeDataPrefixType_Local:
1626 {
1627 strDefinitionTemplateShort = QString("^[%1%2%3](\\S)*=").arg(strNodePrefixLocal, strNodePrefixProvider, strNodePrefixProfile);
1628 strDefinitionTemplateFull = QString("^%1(\\S)*=%2$").arg(strNodePrefixLocal, strName);
1629 break;
1630 }
1631 case UIChooserNodeDataPrefixType_Provider:
1632 {
1633 strDefinitionTemplateShort = QString("^[%1%2%3](\\S)*=").arg(strNodePrefixLocal, strNodePrefixProvider, strNodePrefixProfile);
1634 strDefinitionTemplateFull = QString("^%1(\\S)*=%2$").arg(strNodePrefixProvider, strName);
1635 break;
1636 }
1637 case UIChooserNodeDataPrefixType_Profile:
1638 {
1639 strDefinitionTemplateShort = QString("^[%1%2%3](\\S)*=").arg(strNodePrefixLocal, strNodePrefixProvider, strNodePrefixProfile);
1640 strDefinitionTemplateFull = QString("^%1(\\S)*=%2$").arg(strNodePrefixProfile, strName);
1641 break;
1642 }
1643 case UIChooserNodeDataPrefixType_Machine:
1644 {
1645 strDefinitionTemplateShort = QString("^%1=").arg(strNodePrefixMachine);
1646 strDefinitionTemplateFull = QString("^%1=%2$").arg(strNodePrefixMachine, strName);
1647 break;
1648 }
1649 default:
1650 return -1;
1651 }
1652 const QRegularExpression definitionRegExpShort(strDefinitionTemplateShort);
1653 const QRegularExpression definitionRegExpFull(strDefinitionTemplateFull);
1654
1655 /* For each the definition: */
1656 int iDefinitionIndex = -1;
1657 foreach (const QString &strDefinition, definitions)
1658 {
1659 /* Check if this definition is of required type: */
1660 const QRegularExpressionMatch mtShort = definitionRegExpShort.match(strDefinition);
1661 if (mtShort.capturedStart() == 0)
1662 {
1663 ++iDefinitionIndex;
1664 /* Check if this definition is exactly what we need: */
1665 const QRegularExpressionMatch mtFull = definitionRegExpFull.match(strDefinition);
1666 if (mtFull.capturedStart() == 0)
1667 return iDefinitionIndex;
1668 }
1669 }
1670
1671 /* Return result: */
1672 return -1;
1673}
1674
1675void UIChooserAbstractModel::createLocalMachineNode(UIChooserNode *pParentNode, const CMachine &comMachine)
1676{
1677 new UIChooserNodeMachine(pParentNode,
1678 getDesiredNodePosition(pParentNode,
1679 UIChooserNodeDataPrefixType_Machine,
1680 toOldStyleUuid(comMachine.GetId())),
1681 comMachine);
1682}
1683
1684void UIChooserAbstractModel::createCloudMachineNode(UIChooserNode *pParentNode, UIFakeCloudVirtualMachineItemState enmState)
1685{
1686 new UIChooserNodeMachine(pParentNode,
1687 0 /* position */,
1688 enmState);
1689}
1690
1691void UIChooserAbstractModel::createCloudMachineNode(UIChooserNode *pParentNode, const CCloudMachine &comMachine)
1692{
1693 UIChooserNodeMachine *pNode = new UIChooserNodeMachine(pParentNode,
1694 getDesiredNodePosition(pParentNode,
1695 UIChooserNodeDataPrefixType_Machine,
1696 toOldStyleUuid(comMachine.GetId())),
1697 comMachine);
1698 /* Request for async node update if necessary: */
1699 if ( m_fKeepCloudNodesUpdated
1700 || !comMachine.GetAccessible())
1701 {
1702 AssertReturnVoid(pNode && pNode->cacheType() == UIVirtualMachineItemType_CloudReal);
1703 UIVirtualMachineItemCloud *pCloudMachineItem = pNode->cache()->toCloud();
1704 pCloudMachineItem->setUpdateRequiredByGlobalReason(m_fKeepCloudNodesUpdated);
1705 pCloudMachineItem->updateInfoAsync(false /* delayed? */);
1706 }
1707}
1708
1709QStringList UIChooserAbstractModel::gatherPossibleGroupNodeNames(UIChooserNode *pCurrentNode, QList<UIChooserNode*> exceptions) const
1710{
1711 /* Prepare result: */
1712 QStringList result;
1713
1714 /* Walk through all the children and make sure there are no exceptions: */
1715 bool fAddCurrent = true;
1716 foreach (UIChooserNode *pChild, pCurrentNode->nodes(UIChooserNodeType_Any))
1717 {
1718 AssertPtrReturn(pChild, result);
1719 if (exceptions.contains(pChild))
1720 fAddCurrent = false;
1721 else
1722 {
1723 if (pChild->type() == UIChooserNodeType_Group)
1724 {
1725 UIChooserNodeGroup *pChildGroup = pChild->toGroupNode();
1726 AssertPtrReturn(pChildGroup, result);
1727 if (pChildGroup->groupType() == UIChooserNodeGroupType_Local)
1728 result << gatherPossibleGroupNodeNames(pChild, exceptions);
1729 }
1730 }
1731 }
1732
1733 /* Add current item if not overridden: */
1734 if (fAddCurrent)
1735 result.prepend(pCurrentNode->fullName());
1736
1737 /* Return result: */
1738 return result;
1739}
1740
1741bool UIChooserAbstractModel::checkIfNodeContainChildWithId(UIChooserNode *pParentNode, const QUuid &uId) const
1742{
1743 /* Check parent-node type: */
1744 AssertPtrReturn(pParentNode, false);
1745 switch (pParentNode->type())
1746 {
1747 case UIChooserNodeType_Machine:
1748 {
1749 /* Check if pParentNode has the passed uId itself: */
1750 UIChooserNodeMachine *pMachineNode = pParentNode->toMachineNode();
1751 AssertPtrReturn(pMachineNode, false);
1752 if (pMachineNode->id() == uId)
1753 return true;
1754 break;
1755 }
1756 case UIChooserNodeType_Group:
1757 {
1758 /* Recursively iterate through children: */
1759 foreach (UIChooserNode *pChildNode, pParentNode->nodes())
1760 if (checkIfNodeContainChildWithId(pChildNode, uId))
1761 return true;
1762 break;
1763 }
1764 default:
1765 break;
1766 }
1767
1768 /* False by default: */
1769 return false;
1770}
1771
1772void UIChooserAbstractModel::saveGroupSettings()
1773{
1774 /* Make sure there is no group settings saving activity: */
1775 if (UIThreadGroupSettingsSave::instance())
1776 return;
1777
1778 /* Prepare full group map: */
1779 QMap<QString, QStringList> groups;
1780 gatherGroupSettings(groups, invisibleRoot());
1781
1782 /* Save information in other thread: */
1783 UIThreadGroupSettingsSave::prepare();
1784 emit sigGroupSavingStateChanged();
1785 connect(UIThreadGroupSettingsSave::instance(), &UIThreadGroupSettingsSave::sigReload,
1786 this, &UIChooserAbstractModel::sltReloadMachine);
1787 UIThreadGroupSettingsSave::instance()->configure(this, m_groups, groups);
1788 UIThreadGroupSettingsSave::instance()->start();
1789 m_groups = groups;
1790}
1791
1792void UIChooserAbstractModel::saveGroupDefinitions()
1793{
1794 /* Make sure there is no group definitions save activity: */
1795 if (UIThreadGroupDefinitionsSave::instance())
1796 return;
1797
1798 /* Prepare full group map: */
1799 QMap<QString, QStringList> groups;
1800 gatherGroupDefinitions(groups, invisibleRoot());
1801
1802 /* Save information in other thread: */
1803 UIThreadGroupDefinitionsSave::prepare();
1804 emit sigGroupSavingStateChanged();
1805 UIThreadGroupDefinitionsSave::instance()->configure(this, groups);
1806 UIThreadGroupDefinitionsSave::instance()->start();
1807}
1808
1809void UIChooserAbstractModel::gatherGroupSettings(QMap<QString, QStringList> &settings,
1810 UIChooserNode *pParentGroup)
1811{
1812 /* Iterate over all the machine-nodes: */
1813 foreach (UIChooserNode *pNode, pParentGroup->nodes(UIChooserNodeType_Machine))
1814 {
1815 /* Make sure it's really machine node: */
1816 AssertPtrReturnVoid(pNode);
1817 UIChooserNodeMachine *pMachineNode = pNode->toMachineNode();
1818 AssertPtrReturnVoid(pMachineNode);
1819 /* Make sure it's local machine node exactly and it's accessible: */
1820 if ( pMachineNode->cacheType() == UIVirtualMachineItemType_Local
1821 && pMachineNode->accessible())
1822 settings[toOldStyleUuid(pMachineNode->id())] << pParentGroup->fullName();
1823 }
1824 /* Iterate over all the group-nodes: */
1825 foreach (UIChooserNode *pNode, pParentGroup->nodes(UIChooserNodeType_Group))
1826 gatherGroupSettings(settings, pNode);
1827}
1828
1829void UIChooserAbstractModel::gatherGroupDefinitions(QMap<QString, QStringList> &definitions,
1830 UIChooserNode *pParentGroup)
1831{
1832 /* Prepare extra-data key for current group: */
1833 const QString strExtraDataKey = pParentGroup->fullName();
1834 /* Iterate over all the global-nodes: */
1835 foreach (UIChooserNode *pNode, pParentGroup->nodes(UIChooserNodeType_Global))
1836 {
1837 /* Append node definition: */
1838 AssertPtrReturnVoid(pNode);
1839 definitions[strExtraDataKey] << pNode->definition(true /* full */);
1840 }
1841 /* Iterate over all the group-nodes: */
1842 foreach (UIChooserNode *pNode, pParentGroup->nodes(UIChooserNodeType_Group))
1843 {
1844 /* Append node definition: */
1845 AssertPtrReturnVoid(pNode);
1846 definitions[strExtraDataKey] << pNode->definition(true /* full */);
1847 /* Go recursively through children: */
1848 gatherGroupDefinitions(definitions, pNode);
1849 }
1850 /* Iterate over all the machine-nodes: */
1851 foreach (UIChooserNode *pNode, pParentGroup->nodes(UIChooserNodeType_Machine))
1852 {
1853 /* Make sure it's really machine node: */
1854 AssertPtrReturnVoid(pNode);
1855 UIChooserNodeMachine *pMachineNode = pNode->toMachineNode();
1856 AssertPtrReturnVoid(pMachineNode);
1857 /* Append node definition, make sure it's local or real cloud machine node only: */
1858 if ( pMachineNode->cacheType() == UIVirtualMachineItemType_Local
1859 || pMachineNode->cacheType() == UIVirtualMachineItemType_CloudReal)
1860 definitions[strExtraDataKey] << pNode->definition(true /* full */);
1861 }
1862}
1863
1864void UIChooserAbstractModel::makeSureGroupSettingsSaveIsFinished()
1865{
1866 /* Cleanup if necessary: */
1867 if (UIThreadGroupSettingsSave::instance())
1868 UIThreadGroupSettingsSave::cleanup();
1869}
1870
1871void UIChooserAbstractModel::makeSureGroupDefinitionsSaveIsFinished()
1872{
1873 /* Cleanup if necessary: */
1874 if (UIThreadGroupDefinitionsSave::instance())
1875 UIThreadGroupDefinitionsSave::cleanup();
1876}
1877
1878UIChooserNode *UIChooserAbstractModel::searchProviderNode(const QUuid &uProviderId)
1879{
1880 /* Search for a list of nodes matching passed name: */
1881 QList<UIChooserNode*> providerNodes;
1882 invisibleRoot()->searchForNodes(uProviderId.toString(),
1883 UIChooserItemSearchFlag_CloudProvider | UIChooserItemSearchFlag_ExactId,
1884 providerNodes);
1885
1886 /* Return 1st node if any: */
1887 return providerNodes.value(0);
1888}
1889
1890UIChooserNode *UIChooserAbstractModel::searchProviderNode(const QString &strProviderShortName)
1891{
1892 /* Search for a list of nodes matching passed name: */
1893 QList<UIChooserNode*> providerNodes;
1894 invisibleRoot()->searchForNodes(strProviderShortName,
1895 UIChooserItemSearchFlag_CloudProvider | UIChooserItemSearchFlag_ExactName,
1896 providerNodes);
1897
1898 /* Return 1st node if any: */
1899 return providerNodes.value(0);
1900}
1901
1902UIChooserNode *UIChooserAbstractModel::searchProfileNode(UIChooserNode *pProviderNode, const QString &strProfileName)
1903{
1904 AssertPtrReturn(pProviderNode, 0);
1905
1906 /* Search for a list of nodes matching passed name: */
1907 QList<UIChooserNode*> profileNodes;
1908 pProviderNode->searchForNodes(strProfileName,
1909 UIChooserItemSearchFlag_CloudProfile | UIChooserItemSearchFlag_ExactName,
1910 profileNodes);
1911
1912 /* Return 1st node if any: */
1913 return profileNodes.value(0);
1914}
1915
1916UIChooserNode *UIChooserAbstractModel::searchProfileNode(const QString &strProviderShortName, const QString &strProfileName)
1917{
1918 /* Wrap method above: */
1919 return searchProfileNode(searchProviderNode(strProviderShortName), strProfileName);
1920}
1921
1922UIChooserNode *UIChooserAbstractModel::searchMachineNode(UIChooserNode *pProfileNode, const QUuid &uMachineId)
1923{
1924 AssertPtrReturn(pProfileNode, 0);
1925
1926 /* Search for a list of nodes matching passed ID: */
1927 QList<UIChooserNode*> machineNodes;
1928 pProfileNode->searchForNodes(uMachineId.toString(),
1929 UIChooserItemSearchFlag_Machine | UIChooserItemSearchFlag_ExactId,
1930 machineNodes);
1931
1932 /* Return 1st node if any: */
1933 return machineNodes.value(0);
1934}
1935
1936UIChooserNode *UIChooserAbstractModel::searchMachineNode(const QString &strProviderShortName, const QString &strProfileName, const QUuid &uMachineId)
1937{
1938 /* Wrap method above: */
1939 return searchMachineNode(searchProfileNode(strProviderShortName, strProfileName), uMachineId);
1940}
1941
1942UIChooserNode *UIChooserAbstractModel::searchFakeNode(UIChooserNode *pProfileNode)
1943{
1944 /* Wrap method above: */
1945 return searchMachineNode(pProfileNode, QUuid());
1946}
1947
1948UIChooserNode *UIChooserAbstractModel::searchFakeNode(const QString &strProviderShortName, const QString &strProfileName)
1949{
1950 /* Wrap method above: */
1951 return searchMachineNode(strProviderShortName, strProfileName, QUuid());
1952}
1953
1954QList<UIChooserNode*> UIChooserAbstractModel::enumerateCloudMachineNodes() const
1955{
1956 /* Search for a list of provider nodes: */
1957 QList<UIChooserNode*> providerNodes;
1958 invisibleRoot()->searchForNodes(QString(),
1959 UIChooserItemSearchFlag_CloudProvider,
1960 providerNodes);
1961
1962 /* Search for a list of profile nodes: */
1963 QList<UIChooserNode*> profileNodes;
1964 foreach (UIChooserNode *pProviderNode, providerNodes)
1965 pProviderNode->searchForNodes(QString(),
1966 UIChooserItemSearchFlag_CloudProfile,
1967 profileNodes);
1968
1969 /* Search for a list of machine nodes: */
1970 QList<UIChooserNode*> machineNodes;
1971 foreach (UIChooserNode *pProfileNode, profileNodes)
1972 pProfileNode->searchForNodes(QString(),
1973 UIChooserItemSearchFlag_Machine,
1974 machineNodes);
1975
1976 return machineNodes;
1977}
1978
1979void UIChooserAbstractModel::stopCloudUpdates(bool fForced /* = false */)
1980{
1981 /* Stop all cloud entity updates currently being performed: */
1982 foreach (const UICloudEntityKey &key, m_cloudEntityKeysBeingUpdated)
1983 {
1984 /* For profiles: */
1985 if (key.m_uMachineId.isNull())
1986 {
1987 /* Search task child by key: */
1988 UIProgressTaskReadCloudMachineList *pTask = findChild<UIProgressTaskReadCloudMachineList*>(key.toString());
1989 AssertPtrReturnVoid(pTask);
1990
1991 /* Wait for cloud profile refresh task to complete,
1992 * then delete the task itself manually: */
1993 if (!fForced)
1994 pTask->cancel();
1995 delete pTask;
1996 }
1997 /* For machines: */
1998 else
1999 {
2000 /* Search machine node: */
2001 UIChooserNode *pNode = searchMachineNode(key.m_strProviderShortName, key.m_strProfileName, key.m_uMachineId);
2002 AssertPtrReturnVoid(pNode);
2003 /* Acquire cloud machine item: */
2004 UIVirtualMachineItemCloud *pCloudMachineItem = pNode->toMachineNode()->cache()->toCloud();
2005 AssertPtrReturnVoid(pCloudMachineItem);
2006
2007 /* Wait for cloud machine refresh task to complete,
2008 * task itself will be deleted with the machine-node: */
2009 pCloudMachineItem->waitForAsyncInfoUpdateFinished();
2010 }
2011 }
2012
2013 /* We haven't let tasks to unregister themselves
2014 * so we have to cleanup task set ourselves: */
2015 m_cloudEntityKeysBeingUpdated.clear();
2016}
2017
2018
2019#include "UIChooserAbstractModel.moc"
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use