VirtualBox

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

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

Copyright year updates by scm.

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette