VirtualBox

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

Last change on this file since 103362 was 103362, checked in by vboxsync, 3 months ago

FE/Qt: bugref:10450: Get rid of QRegExp which is a part of Qt5 legacy API; This is necessary to get rid of Qt5 support lib.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use