UIGChooserItemGroup.cpp revision e15ccd304b1ec3a8d6900d48022db40dfc7136ff
/* $Id$ */
/** @file
*
* VBox frontends: Qt GUI ("VirtualBox"):
* UIGChooserItemGroup 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.
*/
/* Qt includes: */
#include <QPainter>
#include <QStyleOptionGraphicsItem>
#include <QGraphicsSceneDragDropEvent>
#include <QLineEdit>
#include <QGraphicsProxyWidget>
#include <QGraphicsScene>
#include <QHBoxLayout>
#include <QMenu>
/* GUI includes: */
#include "UIGChooserItemGroup.h"
#include "UIGChooserItemMachine.h"
#include "UIGChooserModel.h"
#include "UIIconPool.h"
#include "UIGraphicsRotatorButton.h"
#include "UIGChooserView.h"
/* static */
: UIGChooserItem(0, false /* temporary? */)
, m_fClosed(false)
, m_fMainRoot(true)
{
/* Prepare: */
prepare();
/* Add item to the scene: */
/* Translate finally: */
/* Prepare main-root-item connections: */
}
bool fMainRoot)
: UIGChooserItem(0, true /* temporary? */)
{
/* Prepare: */
prepare();
/* Add item to the scene: */
/* Copy content to 'this': */
copyContent(pCopyFrom, this);
/* Translate finally: */
/* Init: */
}
bool fOpened /* = false */,
int iPosition /* = -1 */)
, m_fMainRoot(false)
{
/* Prepare: */
prepare();
/* Add item to the parent: */
connect(this, SIGNAL(sigToggleFinished()), model(), SIGNAL(sigToggleFinished()), Qt::QueuedConnection);
/* Translate finally: */
/* Init: */
}
int iPosition /* = -1 */)
, m_fMainRoot(false)
{
/* Prepare: */
prepare();
/* Add item to the parent: */
/* Copy content to 'this': */
copyContent(pCopyFrom, this);
/* Translate finally: */
/* Init: */
}
{
/* Delete all the items: */
clearItems();
/* If that item is focused: */
{
/* Unset the focus: */
model()->setFocusItem(0);
}
/* If that item is in selection list: */
{
/* Remove item from the selection list: */
model()->removeFromCurrentItems(this);
}
/* If that item is in navigation list: */
{
/* Remove item from the navigation list: */
model()->removeFromNavigationList(this);
}
/* Remove item from the parent: */
if (parentItem())
parentItem()->removeItem(this);
}
{
return m_strName;
}
{
/* Return "/" for main root-item: */
if (isMainRoot())
return "/";
/* Get full parent name, append with '/' if not yet appended: */
/* Return full item name based on parent prefix: */
return strFullParentName + name();
}
{
}
{
/* Something changed? */
return;
/* Remember new name: */
/* Update linked values: */
}
bool UIGChooserItemGroup::isClosed() const
{
}
bool UIGChooserItemGroup::isOpened() const
{
}
{
}
{
}
{
/* Check each machine-item: */
return true;
/* Found nothing? */
return false;
}
{
/* Check each machine-item: */
return true;
/* Check each group-item: */
return true;
/* Found nothing? */
return false;
}
{
/* Not for root: */
if (isRoot())
return;
/* Lock name-editor: */
m_pNameEditor->hide();
/* Enumerate all the group names: */
/* If proposed name is empty or not unique, reject it: */
return;
/* We should replace forbidden symbols
* with ... well, probably underscores: */
/* Set new name, save settings: */
model()->saveGroupSettings();
}
{
/* Not for root: */
if (isRoot())
return;
/* Toggle started: */
if (!isTemporary())
/* Setup animation: */
/* Group closed, we are opening it: */
if (m_fClosed)
{
/* Toggle-state and navigation will be
* updated on toggle-finish signal! */
}
/* Group opened, we are closing it: */
else
{
/* Update toggle-state: */
m_fClosed = true;
/* Update geometry: */
/* Update navigation: */
model()->updateNavigation();
/* Relayout model: */
model()->updateLayout();
}
}
{
/* Not for root: */
if (isRoot())
return;
/* Update toggle-state: */
/* Update geometry: */
/* Update navigation: */
model()->updateNavigation();
/* Relayout model: */
model()->updateLayout();
/* Update toggle-button tool-tip: */
/* Toggle finished: */
if (!isTemporary())
}
void UIGChooserItemGroup::sltIndentRoot()
{
/* Unhover before indenting: */
setHovered(false);
/* Indent to this root: */
model()->indentRoot(this);
}
void UIGChooserItemGroup::sltUnindentRoot()
{
/* Unhover before unindenting: */
setHovered(false);
/* Unindent to previous root: */
model()->unindentRoot();
}
{
/* Provide other members with required data: */
switch (iKey)
{
/* Layout hints: */
case GroupItemData_HorizonalMargin: return 5;
case GroupItemData_VerticalMargin: return 5;
case GroupItemData_MajorSpacing: return 10;
case GroupItemData_MinorSpacing: return 3;
case GroupItemData_RootIndent: return 2;
/* Default: */
default: break;
}
return QVariant();
}
void UIGChooserItemGroup::prepare()
{
/* Buttons: */
m_pToggleButton = 0;
m_pEnterButton = 0;
m_pExitButton = 0;
/* Name editor: */
m_pNameEditorWidget = 0;
m_pNameEditor = 0;
/* Painting stuff: */
m_iAdditionalHeight = 0;
m_iCornerRadius = 10;
m_iBlackoutDarkness = 110;
m_nameFont = font();
m_infoFont = font();
m_minimumHeaderSize = QSize(0, 0);
/* Items except roots: */
if (!isRoot())
{
/* Setup toggle-button: */
m_pToggleButton->hide();
/* Setup enter-button: */
m_pEnterButton->hide();
/* Setup name-editor: */
m_pNameEditor = new QGraphicsProxyWidget(this);
m_pNameEditor->hide();
}
/* Items except main root: */
if (!isMainRoot())
{
/* Setup exit-button: */
m_pExitButton->hide();
}
/* Button sizes: */
}
/* static */
{
/* Copy group-items: */
/* Copy machine-items: */
}
{
/* Update linked values: */
}
void UIGChooserItemGroup::updateVisibleName()
{
/* Not for main root: */
if (isMainRoot())
return;
/* Prepare variables: */
/* Left margin: */
if (isRoot())
/* Button width: */
if (isRoot())
else
/* Spacing between button and name: */
if (isHovered())
{
/* Spacing between name and info: */
/* Group info width: */
if (!m_groupItems.isEmpty())
/* Machine info width: */
if (!m_machineItems.isEmpty())
/* Spacing + button width: */
if (!isRoot())
}
/* Right margin: */
if (isRoot())
/* Calculate new visible name and name-size: */
/* Update linked values: */
if (m_visibleNameSize != visibleNameSize)
{
}
if (m_strVisibleName != strVisibleName)
{
update();
}
}
{
/* Not for main root: */
if (isMainRoot())
return;
/* Update item info attributes: */
QString strInfoMachines = m_machineItems.isEmpty() ? QString() : QString::number(m_machineItems.size());
/* Update linked values: */
bool fSomethingChanged = false;
if (m_strInfoGroups != strInfoGroups)
{
fSomethingChanged = true;
}
if (m_strInfoMachines != strInfoMachines)
{
fSomethingChanged = true;
}
if (m_infoSizeGroups != infoSizeGroups)
{
fSomethingChanged = true;
}
if (m_infoSizeMachines != infoSizeMachines)
{
fSomethingChanged = true;
}
if (fSomethingChanged)
{
}
}
{
/* Not for main root: */
if (isMainRoot())
return;
/* Prepare variables: */
/* Calculate minimum visible name size: */
/* Calculate minimum width: */
int iHeaderWidth = 0;
/* Button width: */
if (isRoot())
else
iHeaderWidth += /* Spacing between button and name: */
/* Minimum name width: */
/* Spacing between name and info: */
/* Group info width: */
if (!m_groupItems.isEmpty())
/* Machine info width: */
if (!m_machineItems.isEmpty())
/* Spacing + button width: */
if (!isRoot())
/* Calculate maximum height: */
/* Button height: */
if (isRoot())
else
heights /* Minimum name height: */
/* Group info heights: */
/* Machine info heights: */
/* Button height: */
if (!isRoot())
int iHeaderHeight = 0;
/* Calculate new minimum header size: */
/* Is there something changed? */
if (m_minimumHeaderSize == minimumHeaderSize)
return;
/* Update linked values: */
}
void UIGChooserItemGroup::updateToolTip()
{
/* Not for main root: */
if (isMainRoot())
return;
/* Prepare variables: */
/* Should we add name? */
{
/* Template: */
/* Append value: */
}
/* Should we add group info? */
{
/* Template: */
QString strGroupCount = tr("%n group(s)", "Group item tool-tip / Group info", items(UIGChooserItemType_Group).size());
/* Append value: */
QString strValue = tr("<nobr>%1</nobr>", "Group item tool-tip / Group info wrapper").arg(strGroupCount);
toolTipInfo << strValue;
}
/* Should we add machine info? */
{
/* Check if 'this' group contains started VMs: */
int iCountOfStartedMachineItems = 0;
/* Template: */
QString strMachineCount = tr("%n machine(s)", "Group item tool-tip / Machine info", items(UIGChooserItemType_Machine).size());
QString strStartedMachineCount = tr("(%n running)", "Group item tool-tip / Running machine info", iCountOfStartedMachineItems);
/* Append value: */
tr("<nobr>%1 %2</nobr>", "Group item tool-tip / Machine info wrapper, including running").arg(strMachineCount).arg(strStartedMachineCount);
toolTipInfo << strValue;
}
/* Set tool-tip: */
}
{
/* Is toggle-button created? */
if (!m_pToggleButton)
return;
/* Update toggle-button tool-tip: */
}
void UIGChooserItemGroup::retranslateUi()
{
/* Update group tool-tip: */
/* Update button tool-tips: */
if (m_pEnterButton)
if (m_pExitButton)
}
void UIGChooserItemGroup::show()
{
/* Call to base-class: */
UIGChooserItem::show();
/* Show children: */
if (!isClosed())
}
void UIGChooserItemGroup::hide()
{
/* Call to base-class: */
UIGChooserItem::hide();
/* Hide children: */
}
void UIGChooserItemGroup::startEditing()
{
/* Not for root: */
if (isRoot())
return;
/* Not while saving groups: */
if (model()->isGroupSavingInProgress())
return;
/* Unlock name-editor: */
m_pNameEditor->show();
}
{
/* Check item type: */
{
case UIGChooserItemType_Group:
{
else
break;
}
{
else
break;
}
default:
{
AssertMsgFailed(("Invalid item type!"));
break;
}
}
/* Update linked values: */
}
{
/* Check item type: */
{
case UIGChooserItemType_Group:
{
break;
}
{
break;
}
default:
{
AssertMsgFailed(("Invalid item type!"));
break;
}
}
/* Update linked values: */
}
{
/* Check item type: */
switch (type)
{
default: AssertMsgFailed(("Invalid item type!")); break;
}
/* Update linked values: */
}
QList<UIGChooserItem*> UIGChooserItemGroup::items(UIGChooserItemType type /* = UIGChooserItemType_Any */) const
{
switch (type)
{
case UIGChooserItemType_Any: return items(UIGChooserItemType_Group) + items(UIGChooserItemType_Machine);
case UIGChooserItemType_Group: return m_groupItems;
case UIGChooserItemType_Machine: return m_machineItems;
default: break;
}
return QList<UIGChooserItem*>();
}
{
switch (type)
{
case UIGChooserItemType_Any:
case UIGChooserItemType_Group:
return !m_groupItems.isEmpty();
return !m_machineItems.isEmpty();
}
return false;
}
{
switch (type)
{
case UIGChooserItemType_Any:
{
break;
}
case UIGChooserItemType_Group:
{
break;
}
{
break;
}
}
/* Update linked values: */
}
{
/* Update all the required items recursively: */
}
{
/* Remove all the required items recursively: */
}
UIGChooserItem* UIGChooserItemGroup::searchForItem(const QString &strSearchTag, int iItemSearchFlags)
{
/* Are we searching among group-items? */
{
/* Are we searching by the exact name? */
{
/* Exact name matches? */
if (name() == strSearchTag)
return this;
}
/* Are we searching by the few first symbols? */
else
{
/* Name starts with passed symbols? */
return this;
}
}
/* Search among all the children, but machines first: */
return pFoundItem;
return pFoundItem;
/* Found nothing? */
return 0;
}
{
/* If this group-item have at least one machine-item: */
/* Return the first machine-item: */
/* If this group-item have at least one group-item: */
else if (hasItems(UIGChooserItemType_Group))
/* Return the first machine-item of the first group-item: */
/* Found nothing? */
return 0;
}
void UIGChooserItemGroup::sortItems()
{
/* Sort group-items: */
/* Sort machine-items: */
/* Update model: */
model()->updateNavigation();
model()->updateLayout();
}
void UIGChooserItemGroup::updateLayout()
{
/* Prepare variables: */
int iPreviousVerticalIndent = 0;
/* Header (root-item): */
if (isRoot())
{
/* Header (main root-item): */
if (isMainRoot())
{
/* Prepare body indent: */
}
/* Header (non-main root-item): */
else
{
/* Hide unnecessary buttons: */
if (m_pToggleButton)
m_pToggleButton->hide();
if (m_pEnterButton)
m_pEnterButton->hide();
/* Exit-button: */
if (m_pExitButton)
{
/* Prepare variables: */
/* Layout exit-button: */
/* Show exit-button: */
m_pExitButton->show();
}
/* Prepare body indent: */
}
}
/* Header (non-root-item): */
else
{
/* Hide unnecessary button: */
if (m_pExitButton)
m_pExitButton->hide();
/* Toggle-button: */
if (m_pToggleButton)
{
/* Prepare variables: */
/* Layout toggle-button: */
int iToggleButtonX = iHorizontalMargin;
/* Show toggle-button: */
m_pToggleButton->show();
}
/* Enter-button: */
if (m_pEnterButton)
{
/* Prepare variables: */
/* Layout enter-button: */
}
/* Name-editor: */
if (m_pNameEditor && m_pNameEditorWidget)
{
/* Prepare variables: */
/* Layout name-editor: */
int iNameEditorY = 1;
m_pNameEditorWidget->resize(geometry().width() - iNameEditorX - iHorizontalMargin, m_pNameEditorWidget->height());
}
/* Prepare body indent: */
}
/* No body for closed group: */
if (isClosed())
{
/* Hide all the items: */
}
/* Body for opened group: */
else
{
/* Prepare variables: */
/* Layout all the items: */
{
/* Show if hidden: */
/* Get item height-hint: */
/* Set item position: */
/* Set item size: */
/* Relayout group: */
pItem->updateLayout();
/* Update indent for next items: */
}
}
}
{
/* Calculating proposed width: */
int iProposedWidth = 0;
/* Main root-item: */
if (isMainRoot())
{
/* Main root-item always takes body into account: */
if (hasItems())
{
/* Prepare variables: */
/* We have to take every child width into account: */
int iMaximumChildWidth = 0;
/* And 2 indents at last - left and right: */
}
}
/* Other items, including temporary roots: */
else
{
/* Prepare variables: */
/* Basically we have to take header width into account: */
/* But if group-item is opened: */
if (fOpenedGroup)
{
/* We have to take every child width into account: */
int iMaximumChildWidth = 0;
}
/* And 2 margins at last - left and right: */
}
/* Return result: */
return iProposedWidth;
}
{
/* Prepare variables: */
/* Calculating proposed height: */
int iProposedHeight = 0;
/* Main root-item: */
if (isMainRoot())
{
/* Main root-item always takes body into account: */
if (hasItems())
{
/* Prepare variables: */
/* Main root-item have 2 indents - top and bottom: */
/* And every existing: */
{
/* Child height: */
/* And interline spacing: */
}
/* Excpect the last one spacing: */
}
}
/* Other items, including temporary roots: */
else
{
/* Prepare variables: */
/* Group-item header have 2 margins - top and bottom: */
/* And header content height to take into account: */
/* But if group-item is opened: */
if (fOpenedGroup)
{
/* Prepare variables: */
/* We should take into spacing between header and body: */
/* Every existing: */
{
/* Child height: */
/* And interline spacing: */
}
/* Excpect the last one spacing: */
/* And bottom margin at last: */
}
/* Finally, additional height during animation: */
}
/* Return result: */
return iProposedHeight;
}
int UIGChooserItemGroup::minimumWidthHint() const
{
return minimumWidthHint(isOpened());
}
int UIGChooserItemGroup::minimumHeightHint() const
{
return minimumHeightHint(isOpened());
}
{
}
QSizeF UIGChooserItemGroup::sizeHint(Qt::SizeHint which, const QSizeF &constraint /* = QSizeF() */) const
{
/* If Qt::MinimumSize requested: */
return minimumSizeHint(isOpened());
/* Else call to base-class: */
}
{
/* Ask item to paint itself into pixmap: */
return pixmap;
}
{
/* No drops while saving groups: */
if (model()->isGroupSavingInProgress())
return false;
/* Get mime: */
/* If drag token is shown, its up to parent to decide: */
if (where != DragToken_Off)
/* Else we should check mime format: */
{
/* Get passed group-item: */
const UIGChooserItemMimeData *pCastedMimeData = qobject_cast<const UIGChooserItemMimeData*>(pMimeData);
/* Make sure passed group is mutable within this group: */
return false;
/* Make sure passed group is not 'this': */
if (pItem == this)
return false;
/* Make sure passed group is not among our parents: */
const UIGChooserItem *pTestedWidget = this;
{
if (pItem == pParentOfTestedWidget)
return false;
}
return true;
}
{
/* Get passed machine-item: */
const UIGChooserItemMimeData *pCastedMimeData = qobject_cast<const UIGChooserItemMimeData*>(pMimeData);
/* Make sure passed machine is mutable within this group: */
return false;
switch (pEvent->proposedAction())
{
case Qt::MoveAction:
{
/* Make sure passed item is ours or there is no other item with such id: */
}
case Qt::CopyAction:
{
/* Make sure there is no other item with such id: */
}
}
}
/* That was invalid mime: */
return false;
}
void UIGChooserItemGroup::processDrop(QGraphicsSceneDragDropEvent *pEvent, UIGChooserItem *pFromWho, DragToken where)
{
/* Get mime: */
/* Check mime format: */
{
switch (pEvent->proposedAction())
{
case Qt::MoveAction:
case Qt::CopyAction:
{
/* Remember scene: */
/* Get passed group-item: */
/* Check if we have position information: */
{
/* Make sure sender item if our child: */
{
if (where == DragToken_Down)
++iPosition;
}
}
/* Copy passed item into this group: */
if (isClosed())
open(false);
/* If proposed action is 'move': */
{
/* Delete passed item: */
delete pItem;
}
/* Update model: */
pModel->updateLayout();
break;
}
default:
break;
}
}
{
switch (pEvent->proposedAction())
{
case Qt::MoveAction:
case Qt::CopyAction:
{
/* Remember scene: */
/* Get passed item: */
/* Check if we have position information: */
{
/* Make sure sender item if our child: */
{
if (where == DragToken_Down)
++iPosition;
}
}
/* Copy passed machine-item into this group: */
UIGChooserItem *pNewMachineItem = new UIGChooserItemMachine(this, pItem->toMachineItem(), iPosition);
if (isClosed())
open(false);
/* If proposed action is 'move': */
{
/* Delete passed item: */
delete pItem;
}
/* Update model: */
pModel->updateLayout();
break;
}
default:
break;
}
}
}
void UIGChooserItemGroup::resetDragToken()
{
/* Reset drag token for this item: */
if (dragTokenPlace() != DragToken_Off)
{
update();
}
/* Reset drag tokens for all the items: */
pItem->resetDragToken();
}
{
return new UIGChooserItemMimeData(this);
}
{
/* Call to base-class: */
/* What is the new geometry? */
/* Should we update visible name? */
/* Remember the new geometry: */
}
{
/* Skip if hovered: */
if (isHovered())
return;
/* Prepare variables: */
/* Skip if hovered part out of the header: */
if (pos.y() >= iFullHeaderHeight)
return;
/* Call to base-class: */
/* Update linked values: */
}
{
/* Skip if not hovered: */
if (!isHovered())
return;
/* Call to base-class: */
/* Update linked values: */
}
void UIGChooserItemGroup::paint(QPainter *pPainter, const QStyleOptionGraphicsItem *pOption, QWidget* /* pWidget = 0 */)
{
/* Setup: */
/* Paint background: */
/* Paint header: */
}
{
/* Save painter: */
/* Prepare color: */
/* Root-item: */
if (isRoot())
{
/* Main root-item: */
if (isMainRoot())
{
/* Simple and clear: */
}
/* Non-main root-item: */
else
{
/* Prepare variables: */
/* Add clipping: */
path.closeSubpath();
/* Fill background: */
/* Stroke path: */
pPainter->setClipping(false);
}
}
/* Non-root-item: */
else
{
/* Prepare variables: */
/* Add clipping: */
path.closeSubpath();
/* Calculate top rectangle: */
/* Prepare top gradient: */
/* Fill top rectangle: */
{
/* Calculate middle rectangle: */
/* Paint all the stuff: */
}
/* Stroke path: */
pPainter->setClipping(false);
/* Paint drag token UP? */
if (dragTokenPlace() != DragToken_Off)
{
if (dragTokenPlace() == DragToken_Up)
{
}
else if (dragTokenPlace() == DragToken_Down)
{
}
}
}
/* Restore painter: */
}
{
/* Not for main root: */
if (isMainRoot())
return;
/* Prepare variables: */
/* Configure painter color: */
/* Update buttons: */
if (m_pToggleButton)
if (m_pEnterButton)
if (m_pExitButton)
/* Paint name: */
int iNameX = iHorizontalMargin;
if (isRoot())
else
iNameX += iMajorSpacing;
paintText(/* Painter: */
/* Point to paint in: */
/* Font to paint text: */
/* Paint device: */
model()->paintDevice(),
/* Text to paint: */
/* Should we add more info? */
if (isHovered())
{
/* Show enter-button: */
if (!isRoot() && m_pEnterButton)
m_pEnterButton->show();
/* Prepare variables: */
/* Indent: */
if (!isRoot())
/* Should we draw machine count info? */
if (!m_strInfoMachines.isEmpty())
{
paintText(/* Painter: */
/* Point to paint in: */
/* Font to paint text: */
/* Paint device: */
model()->paintDevice(),
/* Text to paint: */
int iMachinePixmapX = iHorizontalIndent;
paintPixmap(/* Painter: */
/* Rectangle to paint in: */
/* Pixmap to paint: */
}
/* Should we draw group count info? */
if (!m_strInfoGroups.isEmpty())
{
paintText(/* Painter: */
/* Point to paint in: */
/* Font to paint text: */
/* Paint device: */
model()->paintDevice(),
/* Text to paint: */
int iGroupPixmapX = iHorizontalIndent;
paintPixmap(/* Painter: */
/* Rectangle to paint in: */
/* Pixmap to paint: */
}
}
else
{
/* Hide enter-button: */
if (m_pEnterButton)
m_pEnterButton->hide();
}
}
{
/* Only for item with button: */
if (!m_pToggleButton)
return;
/* Recalculate animation parameters: */
}
{
model()->updateLayout();
}
int UIGChooserItemGroup::additionalHeight() const
{
return m_iAdditionalHeight;
}
, m_pLineEdit(0)
, m_pTemporaryMenu(0)
{
/* Create line-edit: */
m_pLineEdit->setTextMargins(0, 0, 0, 0);
/* Create main-layout: */
pLayout->setContentsMargins(0, 0, 0, 0);
/* Add line-edit into main-layout: */
/* Install event-filter: */
m_pLineEdit->installEventFilter(this);
}
{
return m_pLineEdit->text();
}
{
}
{
}
void UIGroupRenameEditor::setFocus()
{
m_pLineEdit->setFocus();
}
{
/* Process only events sent to line-edit: */
if (pWatched != m_pLineEdit)
/* Handle events: */
{
case QEvent::ContextMenu:
{
/* Handle context-menu event: */
/* Filter out this event: */
return true;
}
{
if (!m_pTemporaryMenu)
break;
}
default:
break;
}
/* Call to base-class: */
}
{
/* Prepare variables: */
/* Create context-menu: */
/* Determine global position: */
/* Show context menu: */
/* Delete context menu: */
delete m_pTemporaryMenu;
m_pTemporaryMenu = 0;
delete pMenu;
}