/* $Id$ */
/** @file
* VBox Qt GUI - UIGChooserModel class implementation.
*/
/*
* Copyright (C) 2012-2013 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*/
# include <precomp.h>
#else /* !VBOX_WITH_PRECOMPILED_HEADERS */
/* Qt includes: */
# include <QGraphicsScene>
# include <QGraphicsView>
# include <QRegExp>
# include <QGraphicsSceneMouseEvent>
# include <QGraphicsSceneContextMenuEvent>
# include <QPropertyAnimation>
# include <QScrollBar>
# include <QTimer>
/* GUI includes: */
# include "UIGChooser.h"
# include "UIGChooserModel.h"
# include "UIGChooserItemGroup.h"
# include "UIGChooserItemMachine.h"
# include "UIExtraDataDefs.h"
# include "VBoxGlobal.h"
# include "UIMessageCenter.h"
# include "UIExtraDataManager.h"
# include "UIActionPoolSelector.h"
# include "UIGChooserHandlerMouse.h"
# include "UIGChooserHandlerKeyboard.h"
# include "UIWizardNewVM.h"
# include "UISelectorWindow.h"
# include "UIVirtualBoxEventHandler.h"
/* COM includes: */
# include "CVirtualBox.h"
# include "CMachine.h"
# include "CMedium.h"
#endif /* !VBOX_WITH_PRECOMPILED_HEADERS */
#include <QParallelAnimationGroup>
/* Type defs: */
, m_pScene(0)
, m_fSliding(false)
, m_pLeftRoot(0)
, m_pRightRoot(0)
, m_pAfterSlidingFocus(0)
, m_pMouseHandler(0)
, m_pKeyboardHandler(0)
, m_iScrollingTokenSize(30)
, m_fIsScrollingInProgress(false)
, m_pContextMenuGroup(0)
, m_pLookupTimer(0)
{
/* Prepare scene: */
prepareScene();
/* Prepare root: */
prepareRoot();
/* Prepare lookup: */
/* Prepare context-menu: */
/* Prepare handlers: */
/* Prepare connections: */
}
{
/* Cleanup handlers: */
/* Prepare context-menu: */
/* Cleanup lookup: */
/* Cleanup root: */
cleanupRoot();
/* Cleanup scene: */
cleanupScene();
}
{
/* Load group tree: */
/* Update navigation: */
/* Update layout: */
updateLayout();
/* Load last selected item: */
}
{
/* Save last selected item: */
/* Currently we are not saving group descriptors
* (which reflecting group toggle-state) on-the-fly
* So, for now we are additionally save group orders
* when exiting application: */
/* Make sure all saving steps complete: */
}
{
return chooser()->actionPool();
}
{
return m_pScene;
}
{
return 0;
}
QGraphicsItem* UIGChooserModel::itemAt(const QPointF &position, const QTransform &deviceTransform /* = QTransform() */) const
{
}
{
/* No layout updates while sliding: */
if (m_fSliding)
return;
/* Initialize variables: */
/* Set root-item position: */
/* Set root-item size: */
/* Relayout root-item: */
root()->updateLayout();
/* Make sure root-item is shown: */
}
{
return m_navigationList;
}
{
}
{
}
{
/* Return first machine-item of the current-item: */
}
{
/* Gather list of current unique machine-items: */
/* Reintegrate machine-items into valid format: */
return currentMachineList;
}
{
/* Return first of current items, if any: */
}
{
return m_currentItems;
}
{
/* Is there something seems to be changed? */
if (m_currentItems == items)
return;
/* Remember old current-item list: */
/* Clear current current-item list: */
/* Iterate over all the passed items: */
{
/* If navigation list contains iterated-item: */
{
/* Add that item to current: */
}
else
AssertMsgFailed(("Passed item not in navigation list!"));
}
/* Is there something really changed? */
if (oldCurrentItems == m_currentItems)
return;
/* Update all the old items (they are no longer selected): */
/* Update all the new items (they are selected): */
/* Notify about selection changes: */
}
{
/* Call for wrapper above: */
/* Move focus to current-item: */
}
{
/* Ignore if empty definition passed: */
if (strDefinition.isEmpty())
return;
/* Parse definition: */
UIGChooserItem *pItem = 0;
/* Its a group-item definition? */
if (strItemType == "g")
{
/* Search for group-item with passed descriptor (name): */
}
/* Its a machine-item definition? */
else if (strItemType == "m")
{
/* Check if machine-item with passed descriptor (name or id) registered: */
{
/* Search for machine-item with required name: */
}
}
/* Make sure found item is in navigation list: */
return;
/* Call for wrapper above: */
}
{
/* Call for wrapper above: */
setCurrentItem(0);
}
{
/* Call for wrapper above: */
}
{
/* Prepare filtered list: */
/* Call for wrapper above: */
}
{
/* Make sure selection item list is never empty
* if at least one item (for example 'focus') present: */
if (!currentItem() && focusItem())
/* Notify listeners about selection change: */
}
{
}
{
/* Make sure at least one item selected: */
if (currentItems().isEmpty())
return false;
/* Determine the parent group of the first item: */
/* Make sure this parent is not main root-item: */
if (pFirstParent == mainRoot())
return false;
/* Enumerate current-item set: */
/* Enumerate first parent children set: */
/* Check if both sets contains the same: */
return currentItemSet == firstParentItemSet;
}
{
return m_pFocusItem;
}
{
/* Make sure real focus unset: */
/* Is there something changed? */
if (m_pFocusItem == pItem)
return;
/* Remember old focus-item: */
/* Set new focus-item: */
/* Disconnect old focus-item (if any): */
if (pOldFocusItem)
/* Connect new focus-item (if any): */
if (m_pFocusItem)
/* Notify listeners about focus change: */
}
{
return m_rootStack.first();
}
{
return m_rootStack.last();
}
{
/* Do nothing if sliding already: */
if (m_fSliding)
return;
/* We are sliding: */
m_fSliding = true;
/* Hiding root: */
/* Create left root: */
m_pLeftRoot->setPos(0, 0);
/* Create right root: */
/* Indent root: */
/* Slide root: */
slideRoot(true);
}
{
/* Do nothing if sliding already: */
if (m_fSliding)
return;
/* We are sliding: */
m_fSliding = true;
/* Hiding root: */
/* Create left root: */
m_pLeftRoot = new UIGChooserItemGroup(scene(), m_rootStack.at(m_rootStack.size() - 2)->toGroupItem(), fLeftRootIsMain);
/* Create right root: */
m_pRightRoot->setPos(0, 0);
/* Unindent root: */
m_pAfterSlidingFocus = root();
/* Slide root: */
slideRoot(false);
}
{
return m_fSliding;
}
{
}
{
}
/* static */
{
/* Enumerate all the group names: */
/* Prepare reg-exp: */
/* Search for the maximum index: */
int iMinimumPossibleNumber = 0;
{
}
/* Prepare result: */
return strResult;
}
{
}
{
/* Make sure real focus unset: */
/* Remember new drag-object: */
connect(m_pCurrentDragObject, SIGNAL(destroyed(QObject*)), this, SLOT(sltCurrentDragObjectDestroyed()));
}
{
/* Restart timer to reset lookup-string: */
m_pLookupTimer->start();
/* Prepare item: */
/* We are starting to look from the current position: */
/* Are we looking for the 1. same symbol or for the 2. composed word? */
const QString strLookupString = m_strLookupString.isEmpty() || m_strLookupString == strLookupSymbol ?
/* Are we looking from the 1. subsequent position or from the 2. same one? */
/* If first position feats the bounds: */
{
/* We have to look starting from the first position: */
{
{
break;
}
}
}
/* If the item was not found: */
if (!pItem && iFirstIndex > 0)
{
/* We have to try to look from the beginning of the list: */
{
{
break;
}
}
}
/* If that item was found: */
if (pItem)
{
/* Choose it: */
/* Append lookup symbol: */
if (m_strLookupString != strLookupSymbol)
}
}
{
return m_pLookupTimer->isActive();
}
{
}
{
return UIGroupDefinitionSaveThread::instance() ||
}
{
/* Update machine-items with passed id: */
}
{
/* Update machine-items with passed id: */
}
{
/* New VM registered? */
if (fRegistered)
{
/* Search for corresponding machine: */
/* Should we show this machine? */
{
/* Add new machine-item: */
addMachineIntoTheTree(machine, true);
/* And update model: */
updateLayout();
}
}
/* Existing VM unregistered? */
else
{
/* Remove machine-items with passed id: */
/* Update model: */
updateLayout();
/* Make sure current-item present, if possible: */
/* Make sure focus-item present, if possible: */
else if (!focusItem() && currentItem())
/* Notify about current-item change: */
}
}
{
/* Update machine-items with passed id: */
}
{
/* Update machine-items with passed id: */
}
{
/* Relayout: */
updateLayout();
}
{
AssertMsgFailed(("Focus item destroyed!"));
}
{
/* Update left root: */
}
{
/* Update right root: */
}
{
/* Delete temporary roots: */
delete m_pLeftRoot;
m_pLeftRoot = 0;
delete m_pRightRoot;
m_pRightRoot = 0;
/* We are no more sliding: */
m_fSliding = false;
/* Update model: */
updateLayout();
if (m_pAfterSlidingFocus)
{
m_pAfterSlidingFocus = 0;
}
else
{
if (!navigationList().isEmpty())
else
}
}
{
/* Check if action is enabled: */
return;
/* Only for single selected group: */
if (!isSingleGroupSelected())
return;
/* Start editing group name: */
currentItem()->startEditing();
}
{
/* Check if action is enabled: */
return;
/* Only for single selected group: */
if (!isSingleGroupSelected())
return;
/* Sorting group: */
currentItem()->sortItems();
}
{
/* Check if action is enabled: */
return;
/* Make sure focus item is of group type! */
/* Check if we have collisions with our siblings: */
{
if (pCollisionSibling)
{
{
{
return;
}
}
{
else
return;
}
}
}
/* Copy all the children into our parent: */
{
continue;
{
case UIGChooserItemType_Group:
{
break;
}
{
break;
}
}
}
/* Delete focus group: */
delete focusItem();
/* And update model: */
updateLayout();
}
{
/* Check if action is enabled: */
return;
/* Choose the parent: */
UIGChooserItem *pGroup = 0;
if (isSingleGroupSelected())
pGroup = currentItem();
else if (!currentItems().isEmpty())
if (pGroup)
/* Start the new vm wizard: */
if (pWizard)
delete pWizard;
}
{
/* Check if action is enabled: */
return;
/* Create new group in the current root: */
UIGChooserItemGroup *pNewGroupItem = new UIGChooserItemGroup(root(), uniqueGroupName(root()), true);
/* Enumerate all the currently chosen items: */
{
/* For each of known types: */
{
case UIGChooserItemType_Group:
{
/* Avoid name collisions: */
break;
/* Add name to busy: */
/* Copy or move group-item: */
delete pItem;
break;
}
{
/* Avoid name collisions: */
break;
/* Add name to busy: */
/* Copy or move machine item: */
delete pItem;
break;
}
}
}
/* Update model: */
updateLayout();
}
{
/* Remove all the items first: */
/* Wipe out empty groups: */
/* Show machine if we should: */
/* And update model: */
updateLayout();
/* Make sure at least one item selected after that: */
/* Notify listeners about selection change: */
}
{
/* Check if action is enabled: */
return;
/* Only if some item selected: */
if (!currentItem())
return;
/* Sorting parent group: */
}
{
/* Check if action is enabled: */
return;
/* Gather list of current unique inaccessible machine-items: */
/* For each machine-item: */
{
/* Recache: */
/* Become accessible? */
if (pItem->accessible())
{
/* Machine name: */
/* We should reload this machine: */
/* Select first of reloaded items: */
if (!pSelectedItem)
}
}
/* Some item to be selected? */
if (pSelectedItem)
{
}
}
{
/* Check if action is enabled: */
return;
/* Enumerate all the selected machine-items: */
/* Enumerate all the existing machine-items: */
/* Prepare arrays: */
/* For each selected machine-item: */
{
/* Get machine-item id: */
/* We already decided for that machine? */
{
/* To remove similar machine items? */
continue;
}
/* Selected copy count: */
int iSelectedCopyCount = 0;
/* Existing copy count: */
int iExistingCopyCount = 0;
/* If selected copy count equal to existing copy count,
* we will propose ro unregister machine fully else
* we will just propose to remove selected items: */
if (fVerdict)
else
}
/* If we have something to remove: */
if (!itemsToRemove.isEmpty())
/* If we have something to unregister: */
if (!machinesToUnregister.isEmpty())
}
{
/* Should we scroll? */
if (!m_fIsScrollingInProgress)
return;
/* Reset scrolling progress: */
m_fIsScrollingInProgress = false;
/* Request still valid? */
if (mousePos.y() < m_iScrollingTokenSize)
{
{
/* Backward scrolling: */
m_fIsScrollingInProgress = true;
}
}
{
{
/* Forward scrolling: */
m_fIsScrollingInProgress = true;
}
}
}
{
root()->resetDragToken();
}
{
}
{
m_pLookupTimer->stop();
m_strLookupString = QString();
}
{
}
{
}
{
}
{
switch (iKey)
{
case ChooserModelData_Margin: return 0;
default: break;
}
return QVariant();
}
{
m_pScene = new QGraphicsScene(this);
m_pScene->installEventFilter(this);
}
{
}
{
m_pLookupTimer = new QTimer(this);
m_pLookupTimer->setSingleShot(true);
}
{
/* Context menu for group(s): */
m_pContextMenuGroup = new QMenu;
/* Context menu for machine(s): */
m_pContextMenuMachine = new QMenu;
m_pContextMenuMachine->addAction(actionPool()->action(UIActionIndexST_M_Machine_S_ShowInFileManager));
this, SLOT(sltCreateNewMachine()));
this, SLOT(sltCreateNewMachine()));
this, SLOT(sltEditGroupName()));
this, SLOT(sltUngroupSelectedGroup()));
this, SLOT(sltRemoveSelectedMachine()));
this, SLOT(sltGroupSelectedMachines()));
this, SLOT(sltPerformRefreshAction()));
this, SLOT(sltPerformRefreshAction()));
this, SLOT(sltSortParentGroup()));
this, SLOT(sltSortGroup()));
connect(this, SIGNAL(sigStartGroupSaving()), this, SLOT(sltGroupSavingStart()), Qt::QueuedConnection);
}
{
m_pMouseHandler = new UIGChooserHandlerMouse(this);
m_pKeyboardHandler = new UIGChooserHandlerKeyboard(this);
}
{
/* Setup parent connections: */
/* Setup global connections: */
}
{
/* Load last selected item (choose first if unable to load): */
}
{
/* Save last selected item: */
gEDataManager->setSelectorWindowLastItemChosen(currentItem() ? currentItem()->definition() : QString());
}
{
delete m_pKeyboardHandler;
m_pKeyboardHandler = 0;
delete m_pMouseHandler;
m_pMouseHandler = 0;
}
{
delete m_pContextMenuGroup;
m_pContextMenuGroup = 0;
delete m_pContextMenuMachine;
}
{
delete m_pLookupTimer;
m_pLookupTimer = 0;
}
{
delete mainRoot();
m_rootStack.clear();
}
{
delete m_pScene;
m_pScene = 0;
}
{
/* Process only scene events: */
/* Process only item focused by model: */
/* Checking event-type: */
{
/* Keyboard handler: */
case QEvent::KeyRelease:
/* Mouse handler: */
case QEvent::GraphicsSceneMousePress:
return m_pMouseHandler->handle(static_cast<QGraphicsSceneMouseEvent*>(pEvent), UIMouseEventType_Press);
case QEvent::GraphicsSceneMouseRelease:
return m_pMouseHandler->handle(static_cast<QGraphicsSceneMouseEvent*>(pEvent), UIMouseEventType_Release);
return m_pMouseHandler->handle(static_cast<QGraphicsSceneMouseEvent*>(pEvent), UIMouseEventType_DoubleClick);
/* Context-menu handler: */
case QEvent::GraphicsSceneContextMenu:
/* Drag&drop scroll-event handler: */
case QEvent::GraphicsSceneDragMove:
}
/* Call to base-class: */
}
{
/* Prepare navigation list: */
/* Iterate over all the group-items: */
{
}
/* Iterate over all the machine-items: */
/* Return navigation list: */
return navigationItems;
}
{
/* Set the real focus to null: */
scene()->setFocusItem(0);
}
{
/* Animation group: */
/* Left root animation: */
{
connect(pLeftAnimation, SIGNAL(valueChanged(const QVariant&)), this, SLOT(sltLeftRootSlidingProgress()));
}
/* Right root animation: */
{
connect(pRightAnimation, SIGNAL(valueChanged(const QVariant&)), this, SLOT(sltRightRootSlidingProgress()));
}
/* Start animation: */
pAnimation->start();
}
{
/* Cleanup all the group-items recursively first: */
/* If parent has no items: */
{
/* Cleanup if that is non-root item: */
delete pParent;
/* Unindent if that is root item: */
unindentRoot();
}
}
{
/* Confirm machine-items removal: */
return;
/* Remove all the passed items: */
delete pItem;
/* And update model: */
updateLayout();
if (!navigationList().isEmpty())
else
}
{
/* Populate machine list: */
{
}
/* Confirm machine removal: */
if (iResultCode == AlertButton_Cancel)
return;
/* For every selected item: */
{
/* Get iterated machine: */
if (iResultCode == AlertButton_Choice1)
{
/* Unregister machine first: */
{
continue;
}
/* Prepare cleanup progress: */
{
continue;
}
/* And show cleanup progress finally: */
{
continue;
}
}
{
/* Unregister machine first: */
{
continue;
}
/* Finally close all media, deliberately ignoring errors: */
{
}
}
}
}
{
/* Whats the reason? */
{
{
/* First of all we should look for an item under cursor: */
{
/* If this item of known type? */
{
case UIGChooserItemType_Group:
{
/* Get group-item: */
/* Make sure thats not root: */
if (pGroupItem->isRoot())
return false;
/* Is this group-item only the one selected? */
{
/* Group context menu in that case: */
return true;
}
}
{
return true;
}
default:
break;
}
}
return true;
}
{
/* Get first selected item: */
{
/* If this item of known type? */
{
case UIGChooserItemType_Group:
{
/* Is this group-item only the one selected? */
{
/* Group context menu in that case: */
return true;
}
}
{
return true;
}
default:
break;
}
}
return true;
}
default:
break;
}
/* Pass others context menu events: */
return false;
}
{
/* Which type of context-menu requested? */
switch (type)
{
/* For group? */
{
break;
}
/* For machine(s)? */
{
break;
}
}
/* Clear status-bar: */
}
{
/* Do we scrolling already? */
return false;
/* Get view: */
/* Check scroll-area: */
if ((eventPoint.y() < m_iScrollingTokenSize) ||
{
/* Set scrolling in progress: */
m_fIsScrollingInProgress = true;
/* Start scrolling: */
}
/* Pass event: */
return false;
}
{
/* Add all the approved machines we have into the group-tree: */
LogRelFlow(("UIGChooserModel: Loading VMs...\n"));
{
}
LogRelFlow(("UIGChooserModel: VMs loaded.\n"));
}
void UIGChooserModel::addMachineIntoTheTree(const CMachine &machine, bool fMakeItVisible /* = false */)
{
/* Make sure passed VM is not NULL: */
LogRelFlow(("UIGChooserModel: ERROR: Passed VM is NULL!\n"));
/* Which VM we are loading: */
LogRelFlow(("UIGChooserModel: Loading VM with ID={%s}...\n", machine.GetId().toAscii().constData()));
/* Is that machine accessible? */
if (machine.GetAccessible())
{
/* VM is accessible: */
/* Which groups passed machine attached to? */
{
/* Remove last '/' if any: */
/* Create machine-item with found group-item as parent: */
LogRelFlow(("UIGChooserModel: Creating item for VM {%s} in group {%s}.\n", strName.toAscii().constData(),
}
/* Update group definitions: */
}
/* Inaccessible machine: */
else
{
/* VM is accessible: */
/* Create machine-item with main-root group-item as parent: */
}
}
UIGChooserItem* UIGChooserModel::getGroupItem(const QString &strName, UIGChooserItem *pParentItem, bool fAllGroupsOpened)
{
/* Check passed stuff: */
return pParentItem;
/* Prepare variables: */
/* Passed group name equal to first sub-name: */
{
/* Make sure first-suffix is NOT empty: */
/* Trying to get group-item among our children: */
{
{
pFoundGroupItem->open(false);
return pFoundItem;
}
}
}
/* Found nothing? Creating: */
new UIGChooserItemGroup(/* Parent item and desired group name: */
/* Should be new group opened when created? */
/* Which position new group-item should be placed in? */
return strSecondSuffix.isEmpty() ? pNewGroupItem : getGroupItem(strFirstSuffix, pNewGroupItem, fAllGroupsOpened);
}
{
/* Read group definitions: */
const QStringList definitions = gEDataManager->selectorWindowGroupsDefinitions(pParentItem->fullName());
/* Return 'false' if no definitions found: */
if (definitions.isEmpty())
return false;
/* Prepare required group definition reg-exp: */
/* For each the group definition: */
{
/* Check if this is required definition: */
{
/* Get group descriptor: */
return true;
}
}
/* Return 'false' by default: */
return false;
}
int UIGChooserModel::getDesiredPosition(UIGChooserItem *pParentItem, UIGChooserItemType type, const QString &strName)
{
/* End of list (by default)? */
/* Which position should be new item placed by definitions: */
/* If some position wanted: */
if (iNewItemDefinitionPosition != -1)
{
/* Start of list if some definition present: */
/* We have to check all the existing item positions: */
{
/* Get current item: */
/* Which position should be current item placed by definitions? */
QString();
/* If some position wanted: */
if (iItemDefinitionPosition != -1)
{
{
iNewItemDesiredPosition = i + 1;
break;
}
}
}
}
/* Return desired item position: */
return iNewItemDesiredPosition;
}
int UIGChooserModel::positionFromDefinitions(UIGChooserItem *pParentItem, UIGChooserItemType type, const QString &strName)
{
/* Read group definitions: */
const QStringList definitions = gEDataManager->selectorWindowGroupsDefinitions(pParentItem->fullName());
/* Return 'false' if no definitions found: */
if (definitions.isEmpty())
return -1;
/* Prepare definition reg-exp: */
switch (type)
{
case UIGChooserItemType_Group:
break;
break;
default: return -1;
}
/* For each the definition: */
{
/* Check if this definition is of required type: */
{
/* Check if this definition is exactly what we need: */
return iDefinitionIndex;
}
}
/* Return result: */
return -1;
}
{
/* Create corresponding item: */
new UIGChooserItemMachine(/* Parent item and corresponding machine: */
/* Which position new group-item should be placed in? */
}
{
/* Make sure there is no group save activity: */
if (UIGroupDefinitionSaveThread::instance())
return;
/* Prepare full group map: */
/* Save information in other thread: */
}
{
/* Make sure there is no group save activity: */
if (UIGroupOrderSaveThread::instance())
return;
/* Prepare full group map: */
/* Save information in other thread: */
}
{
/* Iterate over all the machine-items: */
if (pMachineItem->accessible())
/* Iterate over all the group-items: */
}
{
/* Prepare extra-data key for current group: */
/* Iterate over all the group-items: */
{
}
/* Iterate over all the machine-items: */
}
{
/* Cleanup if necessary: */
if (UIGroupDefinitionSaveThread::instance())
}
{
/* Cleanup if necessary: */
if (UIGroupOrderSaveThread::instance())
}
/* static */
/* static */
{
return m_spInstance;
}
/* static */
{
/* Make sure instance not prepared: */
if (m_spInstance)
return;
/* Crate instance: */
}
/* static */
{
/* Make sure instance prepared: */
if (!m_spInstance)
return;
/* Crate instance: */
delete m_spInstance;
}
{
}
{
/* Assign instance: */
m_spInstance = this;
}
{
/* Wait: */
wait();
/* Erase instance: */
m_spInstance = 0;
}
{
/* COM prepare: */
COMBase::InitializeCOM(false);
/* For every particular machine ID: */
{
/* Make sure group set changed: */
if (newGroupSet == oldGroupSet)
continue;
/* The next steps are subsequent.
* Every of them is mandatory in order to continue
* with common cleanup in case of failure.
* We have to simulate a try-catch block. */
do
{
/* 1. Open session: */
break;
/* 2. Get session machine: */
break;
/* 3. Set new groups: */
{
break;
}
/* 4. Save settings: */
{
break;
}
} while (0);
/* Cleanup if necessary: */
}
/* Notify listeners about completeness: */
emit sigComplete();
/* COM cleanup: */
COMBase::CleanupCOM();
}
/* static */
/* static */
{
return m_spInstance;
}
/* static */
{
/* Make sure instance not prepared: */
if (m_spInstance)
return;
/* Crate instance: */
}
/* static */
{
/* Make sure instance prepared: */
if (!m_spInstance)
return;
/* Crate instance: */
delete m_spInstance;
}
{
}
{
/* Assign instance: */
m_spInstance = this;
}
{
/* Wait: */
wait();
/* Erase instance: */
m_spInstance = 0;
}
{
/* COM prepare: */
COMBase::InitializeCOM(false);
/* Clear all the extra-data records related to group definitions: */
/* For every particular group definition: */
/* Notify listeners about completeness: */
emit sigComplete();
/* COM cleanup: */
COMBase::CleanupCOM();
}