2009-05-08 13:28:41 +00:00
|
|
|
/*
|
|
|
|
* OpenClonk, http://www.openclonk.org
|
|
|
|
*
|
2013-12-17 20:01:09 +00:00
|
|
|
* Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
|
|
|
|
* Copyright (c) 2009-2013, The OpenClonk Team and contributors
|
2009-05-08 13:28:41 +00:00
|
|
|
*
|
2013-12-17 20:01:09 +00:00
|
|
|
* Distributed under the terms of the ISC license; see accompanying file
|
|
|
|
* "COPYING" for details.
|
2009-05-08 13:28:41 +00:00
|
|
|
*
|
2013-12-17 20:01:09 +00:00
|
|
|
* "Clonk" is a registered trademark of Matthes Bender, used with permission.
|
|
|
|
* See accompanying file "TRADEMARK" for details.
|
2009-05-08 13:28:41 +00:00
|
|
|
*
|
2013-12-17 20:01:09 +00:00
|
|
|
* To redistribute this file separately, substitute the full license texts
|
|
|
|
* for the above references.
|
2009-05-08 13:28:41 +00:00
|
|
|
*/
|
|
|
|
// generic user interface
|
|
|
|
// container for a dynamic number of vertically stacked controls
|
|
|
|
|
2016-04-03 18:07:56 +00:00
|
|
|
#include "C4Include.h"
|
|
|
|
#include "gui/C4Gui.h"
|
2011-11-01 22:17:41 +00:00
|
|
|
|
2016-04-03 18:07:56 +00:00
|
|
|
#include "gui/C4MouseControl.h"
|
2010-04-28 21:43:25 +00:00
|
|
|
#include <algorithm>
|
2009-05-08 13:28:41 +00:00
|
|
|
|
2010-03-28 18:58:01 +00:00
|
|
|
namespace C4GUI
|
|
|
|
{
|
2009-05-08 13:28:41 +00:00
|
|
|
|
|
|
|
|
|
|
|
// ----------------------------------------------------
|
|
|
|
// ListBox
|
|
|
|
|
2010-03-28 18:58:01 +00:00
|
|
|
ListBox::ListBox(const C4Rect &rtBounds, int32_t iMultiColItemWidth) : Control(rtBounds), iMultiColItemWidth(iMultiColItemWidth), iColCount(1)
|
|
|
|
, pSelectedItem(NULL), pSelectionChangeHandler(NULL), pSelectionDblClickHandler(NULL), fDrawBackground(true), fDrawBorder(false), fSelectionDisabled(false)
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2010-03-28 18:58:01 +00:00
|
|
|
// calc client rect
|
|
|
|
UpdateOwnPos();
|
|
|
|
// create content scroll window
|
|
|
|
pClientWindow = new ScrollWindow(this);
|
|
|
|
// calc column count
|
|
|
|
UpdateColumnCount();
|
|
|
|
// create key bindings
|
|
|
|
pKeyContext = new C4KeyBinding(C4KeyCodeEx(K_MENU), "GUIListBoxContext", KEYSCOPE_Gui,
|
|
|
|
new ControlKeyCB<ListBox>(*this, &ListBox::KeyContext), C4CustomKey::PRIO_Ctrl);
|
|
|
|
C4CustomKey::CodeList keys;
|
|
|
|
keys.push_back(C4KeyCodeEx(K_UP));
|
2016-02-17 19:58:12 +00:00
|
|
|
if (Config.Controls.GamepadGuiControl) ControllerKeys::Up(keys);
|
2010-03-28 18:58:01 +00:00
|
|
|
pKeyUp = new C4KeyBinding(keys, "GUIListBoxUp", KEYSCOPE_Gui,
|
|
|
|
new ControlKeyCB<ListBox>(*this, &ListBox::KeyUp), C4CustomKey::PRIO_Ctrl);
|
|
|
|
keys.clear();
|
|
|
|
keys.push_back(C4KeyCodeEx(K_DOWN));
|
2016-02-17 19:58:12 +00:00
|
|
|
if (Config.Controls.GamepadGuiControl) ControllerKeys::Down(keys);
|
2010-03-28 18:58:01 +00:00
|
|
|
pKeyDown = new C4KeyBinding(keys, "GUIListBoxDown", KEYSCOPE_Gui,
|
|
|
|
new ControlKeyCB<ListBox>(*this, &ListBox::KeyDown), C4CustomKey::PRIO_Ctrl);
|
|
|
|
keys.clear();
|
|
|
|
keys.push_back(C4KeyCodeEx(K_LEFT));
|
2016-02-17 19:58:12 +00:00
|
|
|
if (Config.Controls.GamepadGuiControl) ControllerKeys::Left(keys);
|
2010-03-28 18:58:01 +00:00
|
|
|
pKeyLeft = new C4KeyBinding(keys, "GUIListBoxLeft", KEYSCOPE_Gui,
|
|
|
|
new ControlKeyCB<ListBox>(*this, &ListBox::KeyLeft), C4CustomKey::PRIO_Ctrl);
|
|
|
|
keys.clear();
|
|
|
|
keys.push_back(C4KeyCodeEx(K_RIGHT));
|
2016-02-17 19:58:12 +00:00
|
|
|
if (Config.Controls.GamepadGuiControl) ControllerKeys::Right(keys);
|
2010-03-28 18:58:01 +00:00
|
|
|
pKeyRight = new C4KeyBinding(keys, "GUIListBoxRight", KEYSCOPE_Gui,
|
|
|
|
new ControlKeyCB<ListBox>(*this, &ListBox::KeyRight), C4CustomKey::PRIO_Ctrl);
|
|
|
|
pKeyPageUp = new C4KeyBinding(C4KeyCodeEx(K_PAGEUP), "GUIListBoxPageUp", KEYSCOPE_Gui,
|
|
|
|
new ControlKeyCB<ListBox>(*this, &ListBox::KeyPageUp), C4CustomKey::PRIO_Ctrl);
|
|
|
|
pKeyPageDown = new C4KeyBinding(C4KeyCodeEx(K_PAGEDOWN), "GUIListBoxPageDown", KEYSCOPE_Gui,
|
|
|
|
new ControlKeyCB<ListBox>(*this, &ListBox::KeyPageDown), C4CustomKey::PRIO_Ctrl);
|
|
|
|
pKeyHome = new C4KeyBinding(C4KeyCodeEx(K_HOME), "GUIListBoxHome", KEYSCOPE_Gui,
|
|
|
|
new ControlKeyCB<ListBox>(*this, &ListBox::KeyHome), C4CustomKey::PRIO_Ctrl);
|
|
|
|
pKeyEnd = new C4KeyBinding(C4KeyCodeEx(K_END), "GUIListBoxEnd", KEYSCOPE_Gui,
|
|
|
|
new ControlKeyCB<ListBox>(*this, &ListBox::KeyEnd), C4CustomKey::PRIO_Ctrl);
|
|
|
|
// "activate" current item
|
|
|
|
keys.clear();
|
|
|
|
keys.push_back(C4KeyCodeEx(K_RETURN));
|
|
|
|
keys.push_back(C4KeyCodeEx(K_RETURN, KEYS_Alt));
|
|
|
|
if (Config.Controls.GamepadGuiControl)
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2016-02-17 19:58:12 +00:00
|
|
|
ControllerKeys::Ok(keys);
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
2010-03-28 18:58:01 +00:00
|
|
|
pKeyActivate = new C4KeyBinding(keys, "GUIListActivate", KEYSCOPE_Gui,
|
|
|
|
new ControlKeyCB<ListBox>(*this, &ListBox::KeyActivate), C4CustomKey::PRIO_Ctrl);
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
|
|
|
|
2010-03-28 18:58:01 +00:00
|
|
|
ListBox::~ListBox()
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2010-03-28 18:58:01 +00:00
|
|
|
delete pKeyActivate;
|
|
|
|
delete pKeyEnd;
|
|
|
|
delete pKeyHome;
|
|
|
|
delete pKeyPageDown;
|
|
|
|
delete pKeyPageUp;
|
|
|
|
delete pKeyRight;
|
|
|
|
delete pKeyLeft;
|
|
|
|
delete pKeyDown;
|
|
|
|
delete pKeyUp;
|
|
|
|
delete pKeyContext;
|
|
|
|
if (pSelectionDblClickHandler) pSelectionDblClickHandler->DeRef();
|
|
|
|
if (pSelectionChangeHandler) pSelectionChangeHandler->DeRef();
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
|
|
|
|
2010-03-28 18:58:01 +00:00
|
|
|
void ListBox::DrawElement(C4TargetFacet &cgo)
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2010-03-28 18:58:01 +00:00
|
|
|
if (fDrawBackground)
|
2011-10-03 14:30:18 +00:00
|
|
|
pDraw->DrawBoxDw(cgo.Surface, cgo.TargetX+rcBounds.x, cgo.TargetY+rcBounds.y, cgo.TargetX+rcBounds.x+rcBounds.Wdt-1, cgo.TargetY+rcBounds.y+rcBounds.Hgt-1, 0x7f000000);
|
2010-03-28 18:58:01 +00:00
|
|
|
if (fDrawBorder) Draw3DFrame(cgo);
|
|
|
|
// listbox bg: mark selected item
|
|
|
|
if (!pClientWindow) return;
|
|
|
|
if (pSelectedItem)
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2010-03-28 18:58:01 +00:00
|
|
|
C4Rect rcSelArea = pSelectedItem->GetBounds();
|
|
|
|
rcSelArea.x += GetClientRect().x;
|
|
|
|
rcSelArea.y += GetClientRect().y + pClientWindow->GetClientRect().y;
|
|
|
|
// clip
|
|
|
|
if (rcSelArea.y < GetClientRect().y)
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2010-03-28 18:58:01 +00:00
|
|
|
rcSelArea.Hgt -= GetClientRect().y - rcSelArea.y;
|
|
|
|
rcSelArea.y = GetClientRect().y;
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
2015-11-15 12:53:01 +00:00
|
|
|
rcSelArea.Hgt = std::min(rcSelArea.Hgt, GetClientRect().y + GetClientRect().Hgt - rcSelArea.y);
|
2010-03-28 18:58:01 +00:00
|
|
|
// draw
|
|
|
|
if (rcSelArea.Hgt>=0)
|
2011-10-03 14:30:18 +00:00
|
|
|
pDraw->DrawBoxDw(cgo.Surface, rcSelArea.x+cgo.TargetX, rcSelArea.y+cgo.TargetY,
|
2010-03-28 18:58:01 +00:00
|
|
|
rcSelArea.x+rcSelArea.Wdt+cgo.TargetX-1, rcSelArea.y+rcSelArea.Hgt+cgo.TargetY-1,
|
|
|
|
HasDrawFocus() ? C4GUI_ListBoxSelColor : C4GUI_ListBoxInactSelColor);
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
2010-03-28 18:58:01 +00:00
|
|
|
// draw delimeter bars
|
|
|
|
Element *pCurr = pClientWindow->GetFirst();
|
|
|
|
if (!pCurr) return;
|
|
|
|
while ((pCurr = pCurr->GetNext()))
|
|
|
|
if (pCurr->GetListItemTopSpacingBar())
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2010-03-28 18:58:01 +00:00
|
|
|
int32_t iYSpace = pCurr->GetListItemTopSpacing();
|
|
|
|
int32_t iY = pCurr->GetBounds().y + GetClientRect().y + pClientWindow->GetClientRect().y - iYSpace/2;
|
|
|
|
int32_t iX0 = pCurr->GetBounds().x + GetClientRect().x + C4GUI_ListBoxBarIndent;
|
|
|
|
int32_t iX1 = iX0 + pClientWindow->GetClientRect().Wdt - 2*C4GUI_ListBoxBarIndent;
|
|
|
|
// clip
|
|
|
|
if (iY < GetClientRect().y || iY >= GetClientRect().y+GetClientRect().Hgt) continue;
|
|
|
|
// draw
|
2011-10-03 14:30:18 +00:00
|
|
|
pDraw->DrawLineDw(cgo.Surface, (float)(iX0+cgo.TargetX), (float)(iY+cgo.TargetY), (float)(iX1+cgo.TargetX), (float)(iY+cgo.TargetY), C4GUI_ListBoxBarColor);
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-03-28 18:58:01 +00:00
|
|
|
void ListBox::MouseInput(CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam)
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2010-03-28 18:58:01 +00:00
|
|
|
// inherited
|
|
|
|
Control::MouseInput(rMouse, iButton, iX, iY, dwKeyParam);
|
|
|
|
// safety
|
|
|
|
if (pClientWindow)
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2010-03-28 18:58:01 +00:00
|
|
|
// check list area bounds
|
|
|
|
if (pClientWindow->GetBounds().Contains(iX, iY))
|
|
|
|
// left btn down: select item (regardless of key states)
|
|
|
|
if (iButton == C4MC_Button_LeftDown || iButton == C4MC_Button_LeftDouble)
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2010-03-28 18:58:01 +00:00
|
|
|
// reset selection
|
|
|
|
Element *pPrevSelectedItem = pSelectedItem;
|
|
|
|
pSelectedItem = NULL;
|
|
|
|
// get client component the mouse is over
|
|
|
|
iX -= GetMarginLeft(); iY -= GetMarginTop();
|
|
|
|
iY += pClientWindow->GetScrollY();
|
|
|
|
for (Element *pCurr = pClientWindow->GetFirst(); pCurr; pCurr = pCurr->GetNext())
|
|
|
|
if (pCurr->GetBounds().Contains(iX, iY))
|
|
|
|
pSelectedItem = pCurr;
|
|
|
|
// selection change sound
|
|
|
|
if (pSelectedItem != pPrevSelectedItem) SelectionChanged(true);
|
|
|
|
// item double-clicked? Callback
|
|
|
|
if (iButton == C4MC_Button_LeftDouble && pSelectedItem)
|
|
|
|
if (pSelectionDblClickHandler) pSelectionDblClickHandler->DoCall(pSelectedItem);
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-03-28 18:58:01 +00:00
|
|
|
void ListBox::UpdateColumnCount()
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2010-03-28 18:58:01 +00:00
|
|
|
if (iMultiColItemWidth && pClientWindow)
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2010-03-28 18:58:01 +00:00
|
|
|
// multicoloumn-listbox
|
2015-11-15 12:53:01 +00:00
|
|
|
iColCount = std::max<int32_t>(pClientWindow->GetClientRect().Wdt / iMultiColItemWidth, 1);
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
2010-03-28 18:58:01 +00:00
|
|
|
else
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2010-03-28 18:58:01 +00:00
|
|
|
// regular 1-col-listbox
|
|
|
|
iColCount = 1;
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-03-28 18:58:01 +00:00
|
|
|
int32_t ListBox::ContractToElementHeight()
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2010-03-28 18:58:01 +00:00
|
|
|
if (!pClientWindow) return 0;
|
|
|
|
// calc superfluous bottom space
|
|
|
|
int32_t iExtraSpace = pClientWindow->GetBounds().Hgt - pClientWindow->GetClientRect().Hgt;
|
|
|
|
if (iExtraSpace <= 0) return 0;
|
|
|
|
// contract by it
|
|
|
|
C4Rect rcNewBounds = GetBounds();
|
|
|
|
rcNewBounds.Hgt -= iExtraSpace;
|
|
|
|
SetBounds(rcNewBounds);
|
|
|
|
return iExtraSpace;
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
|
|
|
|
2010-03-28 18:58:01 +00:00
|
|
|
void ListBox::OnGetFocus(bool fByMouse)
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2010-03-28 18:58:01 +00:00
|
|
|
// inherited (tooltip)
|
|
|
|
Control::OnGetFocus(fByMouse);
|
|
|
|
// select list item if none is selected (only for keyboard; mouse will select with left-click anyway)
|
|
|
|
if (!pSelectedItem && pClientWindow && !fByMouse)
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2010-03-28 18:58:01 +00:00
|
|
|
pSelectedItem = pClientWindow->GetFirstContained();
|
|
|
|
SelectionChanged(false);
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-03-28 18:58:01 +00:00
|
|
|
bool ListBox::KeyContext()
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2010-03-28 18:58:01 +00:00
|
|
|
// key: context menu
|
|
|
|
if (pSelectedItem && pSelectedItem->DoContext()) return true;
|
|
|
|
return false;
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
|
|
|
|
2010-03-28 18:58:01 +00:00
|
|
|
bool ListBox::KeyUp()
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2010-03-28 18:58:01 +00:00
|
|
|
// key: selection up
|
|
|
|
Element *pPrevSelectedItem = pSelectedItem;
|
|
|
|
if (!pSelectedItem)
|
|
|
|
// select last
|
|
|
|
pSelectedItem = pClientWindow->GetLastContained();
|
|
|
|
else
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2010-03-28 18:58:01 +00:00
|
|
|
// select prev row
|
|
|
|
int32_t cnt = iColCount;
|
|
|
|
while (pSelectedItem && cnt--) pSelectedItem = pSelectedItem->GetPrev();
|
|
|
|
if (!pSelectedItem) pSelectedItem = pPrevSelectedItem; // was in start row
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
2010-03-28 18:58:01 +00:00
|
|
|
// selection might have changed
|
|
|
|
if (pSelectedItem != pPrevSelectedItem) SelectionChanged(true);
|
|
|
|
return true;
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
|
|
|
|
2010-03-28 18:58:01 +00:00
|
|
|
bool ListBox::KeyDown()
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2010-03-28 18:58:01 +00:00
|
|
|
// key: selection down
|
|
|
|
Element *pPrevSelectedItem = pSelectedItem;
|
|
|
|
if (!pSelectedItem)
|
|
|
|
// select first
|
|
|
|
pSelectedItem = pClientWindow->GetFirstContained();
|
|
|
|
else
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2010-03-28 18:58:01 +00:00
|
|
|
// select next row
|
|
|
|
int32_t cnt = iColCount;
|
|
|
|
while (pSelectedItem && cnt--) pSelectedItem = pSelectedItem->GetNext();
|
|
|
|
if (!pSelectedItem) pSelectedItem = pPrevSelectedItem; // was in end row
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
2010-03-28 18:58:01 +00:00
|
|
|
// selection might have changed
|
|
|
|
if (pSelectedItem != pPrevSelectedItem) SelectionChanged(true);
|
|
|
|
return true;
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
|
|
|
|
2010-03-28 18:58:01 +00:00
|
|
|
bool ListBox::KeyLeft()
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2010-03-28 18:58:01 +00:00
|
|
|
// key: Selection left
|
|
|
|
// only in multi-col-listboxes
|
|
|
|
if (!IsMultiColumn()) return false;
|
|
|
|
Element *pPrevSelectedItem = pSelectedItem;
|
|
|
|
if (!pSelectedItem)
|
|
|
|
// select last
|
|
|
|
pSelectedItem = pClientWindow->GetLastContained();
|
|
|
|
else
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2010-03-28 18:58:01 +00:00
|
|
|
// select prev
|
|
|
|
if (pSelectedItem->GetPrev()) pSelectedItem = pSelectedItem->GetPrev();
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
2010-03-28 18:58:01 +00:00
|
|
|
// selection might have changed
|
|
|
|
if (pSelectedItem != pPrevSelectedItem) SelectionChanged(true);
|
|
|
|
return true;
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
|
|
|
|
2010-03-28 18:58:01 +00:00
|
|
|
bool ListBox::KeyRight()
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2010-03-28 18:58:01 +00:00
|
|
|
// key: Selection right
|
|
|
|
// only in multi-col-listboxes
|
|
|
|
if (!IsMultiColumn()) return false;
|
|
|
|
Element *pPrevSelectedItem = pSelectedItem;
|
|
|
|
if (!pSelectedItem)
|
|
|
|
// select first
|
|
|
|
pSelectedItem = pClientWindow->GetFirstContained();
|
|
|
|
else
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2010-03-28 18:58:01 +00:00
|
|
|
// select next
|
|
|
|
if (pSelectedItem->GetNext()) pSelectedItem = pSelectedItem->GetNext();
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
2010-03-28 18:58:01 +00:00
|
|
|
// selection might have changed
|
|
|
|
if (pSelectedItem != pPrevSelectedItem) SelectionChanged(true);
|
|
|
|
return true;
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
|
|
|
|
2010-03-28 18:58:01 +00:00
|
|
|
bool ListBox::KeyPageDown()
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2010-03-28 18:58:01 +00:00
|
|
|
// key: selection one page down
|
|
|
|
// start from first item or selected
|
|
|
|
Element *pNextSelectedItem = pSelectedItem ? pSelectedItem : pClientWindow->GetFirstContained(), *pNext;
|
|
|
|
if (!pNextSelectedItem) return false;
|
|
|
|
if ((pNext = pNextSelectedItem->GetNext()))
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2010-03-28 18:58:01 +00:00
|
|
|
pNextSelectedItem = pNext;
|
|
|
|
// if this is not the last, visible item in the list: go down until item is no longer fully in view
|
|
|
|
if (pClientWindow->IsRangeInView(pNextSelectedItem->GetBounds().y, pNextSelectedItem->GetBounds().Hgt))
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2010-03-28 18:58:01 +00:00
|
|
|
while ((pNext = pNextSelectedItem->GetNext()))
|
|
|
|
if (pClientWindow->IsRangeInView(pNext->GetBounds().y, pNext->GetBounds().Hgt))
|
|
|
|
pNextSelectedItem = pNext;
|
|
|
|
else
|
|
|
|
break;
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
2010-03-28 18:58:01 +00:00
|
|
|
else
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2010-03-28 18:58:01 +00:00
|
|
|
// selected item was last visible: Just scroll one page down and select last visible
|
|
|
|
pClientWindow->ScrollPages(+1);
|
|
|
|
pNextSelectedItem = pClientWindow->GetLastContained();
|
|
|
|
while (!pClientWindow->IsRangeInView(pNextSelectedItem->GetBounds().y, pNextSelectedItem->GetBounds().Hgt))
|
|
|
|
if ((pNext = pNextSelectedItem->GetPrev())) pNextSelectedItem = pNext; else break;
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
|
|
|
}
|
2010-03-28 18:58:01 +00:00
|
|
|
// selection might have changed
|
|
|
|
if (pSelectedItem != pNextSelectedItem)
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2010-03-28 18:58:01 +00:00
|
|
|
pSelectedItem = pNextSelectedItem;
|
|
|
|
SelectionChanged(true);
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
2010-03-28 18:58:01 +00:00
|
|
|
return true;
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
|
|
|
|
2010-03-28 18:58:01 +00:00
|
|
|
bool ListBox::KeyPageUp()
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2010-03-28 18:58:01 +00:00
|
|
|
// key: selection one page up
|
|
|
|
// start from last item or selected
|
|
|
|
Element *pNextSelectedItem = pSelectedItem ? pSelectedItem : pClientWindow->GetLastContained(), *pNext;
|
|
|
|
if (!pNextSelectedItem) return false;
|
|
|
|
if ((pNext = pNextSelectedItem->GetPrev()))
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2010-03-28 18:58:01 +00:00
|
|
|
pNextSelectedItem = pNext;
|
|
|
|
// if this is not the first, visible item in the list: go up until item is no longer fully in view
|
|
|
|
if (pClientWindow->IsRangeInView(pNextSelectedItem->GetBounds().y, pNextSelectedItem->GetBounds().Hgt))
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2010-03-28 18:58:01 +00:00
|
|
|
while ((pNext = pNextSelectedItem->GetPrev()))
|
|
|
|
if (pClientWindow->IsRangeInView(pNext->GetBounds().y, pNext->GetBounds().Hgt))
|
|
|
|
pNextSelectedItem = pNext;
|
|
|
|
else
|
|
|
|
break;
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
2010-03-28 18:58:01 +00:00
|
|
|
else
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2010-03-28 18:58:01 +00:00
|
|
|
// selected item was last visible: Just scroll one page up and select first visible
|
|
|
|
pClientWindow->ScrollPages(-1);
|
|
|
|
pNextSelectedItem = pClientWindow->GetFirstContained();
|
|
|
|
while (!pClientWindow->IsRangeInView(pNextSelectedItem->GetBounds().y, pNextSelectedItem->GetBounds().Hgt))
|
|
|
|
if ((pNext = pNextSelectedItem->GetNext())) pNextSelectedItem = pNext; else break;
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
|
|
|
}
|
2010-03-28 18:58:01 +00:00
|
|
|
// selection might have changed
|
|
|
|
if (pSelectedItem != pNextSelectedItem)
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2010-03-28 18:58:01 +00:00
|
|
|
pSelectedItem = pNextSelectedItem;
|
|
|
|
SelectionChanged(true);
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
2010-03-28 18:58:01 +00:00
|
|
|
return true;
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
|
|
|
|
2010-03-28 18:58:01 +00:00
|
|
|
bool ListBox::KeyHome()
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2010-03-28 18:58:01 +00:00
|
|
|
// key: selection to first item
|
|
|
|
Element *pPrevSelectedItem = pSelectedItem;
|
|
|
|
pSelectedItem = pClientWindow->GetFirstContained();
|
|
|
|
// selection might have changed
|
|
|
|
if (pSelectedItem != pPrevSelectedItem) SelectionChanged(true);
|
|
|
|
return true;
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
|
|
|
|
2010-03-28 18:58:01 +00:00
|
|
|
bool ListBox::KeyEnd()
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2010-03-28 18:58:01 +00:00
|
|
|
// key: selection to last item
|
|
|
|
Element *pPrevSelectedItem = pSelectedItem;
|
|
|
|
pSelectedItem = pClientWindow->GetLastContained();
|
|
|
|
// selection might have changed
|
|
|
|
if (pSelectedItem != pPrevSelectedItem) SelectionChanged(true);
|
|
|
|
return true;
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
|
|
|
|
2010-03-28 18:58:01 +00:00
|
|
|
bool ListBox::KeyActivate()
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2010-03-28 18:58:01 +00:00
|
|
|
// process as doubleclick
|
|
|
|
if (pSelectedItem && pSelectionDblClickHandler)
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2010-03-28 18:58:01 +00:00
|
|
|
pSelectionDblClickHandler->DoCall(pSelectedItem);
|
|
|
|
return true;
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
2010-03-28 18:58:01 +00:00
|
|
|
return false;
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
|
|
|
|
2010-03-28 18:58:01 +00:00
|
|
|
void ListBox::ScrollItemInView(Element *pItem)
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2010-03-28 18:58:01 +00:00
|
|
|
// safety
|
|
|
|
if (!pItem) return;
|
|
|
|
// scroll covered range into view
|
|
|
|
pClientWindow->ScrollRangeInView(pItem->GetBounds().y, pItem->GetBounds().Hgt);
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
|
|
|
|
2010-03-28 18:58:01 +00:00
|
|
|
void ListBox::UpdateElementPositions()
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2010-03-28 18:58:01 +00:00
|
|
|
// safety
|
|
|
|
if (!pClientWindow) return;
|
|
|
|
// first item at zero offset
|
|
|
|
Element *pCurr = pClientWindow->GetFirst();
|
|
|
|
int iOverallHgt;
|
|
|
|
if (pCurr)
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2010-03-28 18:58:01 +00:00
|
|
|
if (!iMultiColItemWidth)
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2010-03-28 18:58:01 +00:00
|
|
|
// Single column box: All stacked vertically
|
|
|
|
if (pCurr->GetBounds().y)
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2010-03-28 18:58:01 +00:00
|
|
|
pCurr->GetBounds().y = 0;
|
|
|
|
pCurr->UpdateOwnPos();
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
2010-09-19 17:01:31 +00:00
|
|
|
if(pCurr->fVisible) iOverallHgt = pCurr->GetBounds().Hgt;
|
|
|
|
else iOverallHgt = 0;
|
2010-03-28 18:58:01 +00:00
|
|
|
// others stacked under it
|
|
|
|
while ((pCurr = pCurr->GetNext()))
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2010-09-19 17:01:31 +00:00
|
|
|
if(!pCurr->fVisible) continue; //Do not reserve space for hidden elements
|
2010-03-28 18:58:01 +00:00
|
|
|
int32_t iYSpace = pCurr->GetListItemTopSpacing();
|
|
|
|
int32_t iNewY = iOverallHgt + iYSpace;
|
|
|
|
iOverallHgt += pCurr->GetBounds().Hgt + iYSpace;
|
|
|
|
if (iNewY != pCurr->GetBounds().y)
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2010-03-28 18:58:01 +00:00
|
|
|
pCurr->GetBounds().y = iNewY;
|
|
|
|
pCurr->UpdateOwnPos();
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2010-03-28 18:58:01 +00:00
|
|
|
else
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2010-03-28 18:58:01 +00:00
|
|
|
// Multi column box: Keep element size; reposition horizontally+vertically
|
|
|
|
int32_t y=0, iLineHgt=0, col=0;
|
|
|
|
for (; pCurr; pCurr=pCurr->GetNext())
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2010-03-28 18:58:01 +00:00
|
|
|
const C4Rect &rcCurrBounds = pCurr->GetBounds();
|
2015-11-15 12:53:01 +00:00
|
|
|
iLineHgt = std::max<int32_t>(rcCurrBounds.Hgt, iLineHgt);
|
2010-03-28 18:58:01 +00:00
|
|
|
int32_t x = col * iMultiColItemWidth;
|
|
|
|
if (rcCurrBounds.x != x || rcCurrBounds.y != y || rcCurrBounds.Wdt != iMultiColItemWidth)
|
|
|
|
pCurr->SetBounds(C4Rect(x,y,iMultiColItemWidth,rcCurrBounds.Hgt));
|
|
|
|
if (++col >= iColCount)
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2010-03-28 18:58:01 +00:00
|
|
|
col = 0;
|
|
|
|
y += iLineHgt;
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
|
|
|
}
|
2010-03-28 18:58:01 +00:00
|
|
|
iOverallHgt = y + iLineHgt;
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
|
|
|
}
|
2010-03-28 18:58:01 +00:00
|
|
|
else
|
|
|
|
iOverallHgt = 0;
|
|
|
|
// update scrolling
|
|
|
|
pClientWindow->SetClientHeight(iOverallHgt);
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
|
|
|
|
2010-03-28 18:58:01 +00:00
|
|
|
void ListBox::UpdateElementPosition(Element *pOfElement, int32_t iIndent)
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2010-03-28 18:58:01 +00:00
|
|
|
// resize it
|
|
|
|
C4Rect &rcChildBounds = pOfElement->GetBounds();
|
|
|
|
rcChildBounds.x = iIndent;
|
|
|
|
rcChildBounds.Wdt = GetItemWidth() - iIndent ;
|
|
|
|
pOfElement->UpdateOwnPos();
|
|
|
|
// re-stack elements
|
|
|
|
UpdateElementPositions();
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
|
|
|
|
2010-03-28 18:58:01 +00:00
|
|
|
void ListBox::RemoveElement(Element *pChild)
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2010-03-28 18:58:01 +00:00
|
|
|
// inherited
|
|
|
|
Control::RemoveElement(pChild);
|
|
|
|
// clear selection var
|
|
|
|
if (pChild == pSelectedItem)
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2010-03-28 18:58:01 +00:00
|
|
|
pSelectedItem = NULL;
|
|
|
|
SelectionChanged(false);
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
2010-03-28 18:58:01 +00:00
|
|
|
// position update in AfterElementRemoval
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
|
|
|
|
2010-03-28 18:58:01 +00:00
|
|
|
bool ListBox::AddElement(Element *pChild, int32_t iIndent)
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2010-03-28 18:58:01 +00:00
|
|
|
// fail if no client window is present
|
|
|
|
if (!pClientWindow) return false;
|
|
|
|
// add to scroll window
|
|
|
|
pClientWindow->AddElement(pChild);
|
|
|
|
// resize to horizontal list extents
|
|
|
|
C4Rect &rcChildBounds = pChild->GetBounds();
|
|
|
|
rcChildBounds.x = iIndent;
|
|
|
|
rcChildBounds.Wdt = GetItemWidth() - iIndent ;
|
|
|
|
// reposition to end of list
|
|
|
|
if (pChild->GetPrev())
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2010-03-28 18:58:01 +00:00
|
|
|
if (iMultiColItemWidth)
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2010-03-28 18:58:01 +00:00
|
|
|
rcChildBounds.y = pChild->GetPrev()->GetBounds().y;
|
|
|
|
int32_t col = pChild->GetPrev()->GetBounds().x / iMultiColItemWidth + 1;
|
|
|
|
if (col >= iColCount)
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2010-03-28 18:58:01 +00:00
|
|
|
col = 0;
|
|
|
|
int32_t cnt = iColCount;
|
|
|
|
int32_t iPrevLineHgt = 0;
|
|
|
|
Element *pPrevChild = pChild->GetPrev();
|
|
|
|
while (cnt-- && pPrevChild)
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2015-11-15 12:53:01 +00:00
|
|
|
iPrevLineHgt = std::max<int32_t>(iPrevLineHgt, pPrevChild->GetBounds().Hgt);
|
2010-03-28 18:58:01 +00:00
|
|
|
pPrevChild = pPrevChild->GetPrev();
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
2010-03-28 18:58:01 +00:00
|
|
|
rcChildBounds.y += iPrevLineHgt;
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
2010-03-28 18:58:01 +00:00
|
|
|
rcChildBounds.x = col * iMultiColItemWidth;
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
2010-03-28 18:58:01 +00:00
|
|
|
else
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2010-03-28 18:58:01 +00:00
|
|
|
rcChildBounds.y = pChild->GetPrev()->GetBounds().y + pChild->GetPrev()->GetBounds().Hgt + pChild->GetListItemTopSpacing();
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
|
|
|
}
|
2010-03-28 18:58:01 +00:00
|
|
|
else
|
|
|
|
rcChildBounds.y = 0;
|
|
|
|
pChild->UpdateOwnPos();
|
|
|
|
// update scrolling
|
|
|
|
pClientWindow->SetClientHeight(rcChildBounds.y+rcChildBounds.Hgt);
|
|
|
|
// success
|
|
|
|
return true;
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
|
|
|
|
2010-03-28 18:58:01 +00:00
|
|
|
bool ListBox::InsertElement(Element *pChild, Element *pInsertBefore, int32_t iIndent)
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2010-03-28 18:58:01 +00:00
|
|
|
// fail if no client window is present
|
|
|
|
if (!pClientWindow) return false;
|
|
|
|
// add to scroll window
|
|
|
|
pClientWindow->InsertElement(pChild, pInsertBefore);
|
|
|
|
// resize to horizontal list extents
|
|
|
|
C4Rect &rcChildBounds = pChild->GetBounds();
|
|
|
|
rcChildBounds.x = iIndent;
|
|
|
|
rcChildBounds.Wdt = GetItemWidth() - iIndent ;
|
|
|
|
pChild->UpdateOwnPos();
|
|
|
|
// update all element positions (and scrolling)
|
|
|
|
UpdateElementPositions();
|
|
|
|
// done, success
|
|
|
|
return true;
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
|
|
|
|
2010-03-28 18:58:01 +00:00
|
|
|
void ListBox::ElementSizeChanged(Element *pOfElement)
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2010-03-28 18:58:01 +00:00
|
|
|
// inherited
|
|
|
|
if (pOfElement->GetParent() == this)
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2010-03-28 18:58:01 +00:00
|
|
|
Control::ElementSizeChanged(pOfElement);
|
|
|
|
// update col count if list element container was resized
|
|
|
|
UpdateColumnCount();
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
2010-03-28 18:58:01 +00:00
|
|
|
// update positions of all list items
|
|
|
|
UpdateElementPositions();
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
|
|
|
|
2010-03-28 18:58:01 +00:00
|
|
|
void ListBox::ElementPosChanged(Element *pOfElement)
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2010-03-28 18:58:01 +00:00
|
|
|
// inherited
|
|
|
|
if (pOfElement->GetParent() == this)
|
|
|
|
Control::ElementSizeChanged(pOfElement);
|
|
|
|
// update positions of all list items
|
|
|
|
UpdateElementPositions();
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
|
|
|
|
2010-03-28 18:58:01 +00:00
|
|
|
void ListBox::SelectionChanged(bool fByUser)
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2010-03-28 18:58:01 +00:00
|
|
|
// selections disabled?
|
|
|
|
if (fSelectionDisabled) { pSelectedItem = NULL; return; }
|
|
|
|
// any selection?
|
|
|
|
if (pSelectedItem)
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2010-03-28 18:58:01 +00:00
|
|
|
// effect
|
2015-12-15 05:25:19 +00:00
|
|
|
if (fByUser) GUISound("UI::Select");
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
2010-03-28 18:58:01 +00:00
|
|
|
// callback (caution: May do periluous things...)
|
|
|
|
if (pSelectionChangeHandler) pSelectionChangeHandler->DoCall(pSelectedItem);
|
|
|
|
// let's hope it wasn't perilous enough to delete this,
|
|
|
|
// because scrolling the item into view must be done AFTER the callback, as the callback might resize
|
|
|
|
if (pSelectedItem) ScrollItemInView(pSelectedItem);
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
|
|
|
|
2010-03-28 18:58:01 +00:00
|
|
|
void ListBox::SelectEntry(Element *pNewSel, bool fByUser)
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2010-03-28 18:58:01 +00:00
|
|
|
assert(!pNewSel || pNewSel->GetParent() == pClientWindow);
|
|
|
|
if (pSelectedItem == pNewSel) return;
|
|
|
|
pSelectedItem = pNewSel;
|
|
|
|
SelectionChanged(fByUser);
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
|
|
|
|
2010-03-28 18:58:01 +00:00
|
|
|
bool ListBox::CharIn(const char * c)
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2010-03-28 18:58:01 +00:00
|
|
|
// Jump to first/next entry beginning with typed letter
|
|
|
|
Element *pSel = GetSelectedItem();
|
|
|
|
Element *pStartCheck = pSel;
|
|
|
|
if (pSel) pSel = pSel->GetNext();
|
|
|
|
if (!pSel)
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2010-03-28 18:58:01 +00:00
|
|
|
pSel = GetFirst();
|
|
|
|
if (!pSel) return false;
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
2010-03-28 18:58:01 +00:00
|
|
|
while (pSel != pStartCheck && !pSel->CheckNameHotkey(c))
|
|
|
|
if (!(pSel = pSel->GetNext()))
|
|
|
|
if (pStartCheck)
|
|
|
|
// list end reached while another entry had been selected before: Re-check start of list
|
|
|
|
pSel = GetFirst();
|
|
|
|
// ok, change selection - might do nothing if list was cycled, which is OK
|
|
|
|
if (pSel)
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2010-03-28 18:58:01 +00:00
|
|
|
SelectEntry(pSel, true);
|
|
|
|
return true;
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
2010-03-28 18:58:01 +00:00
|
|
|
return Control::CharIn(c);
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
|
|
|
|
2010-03-28 18:58:01 +00:00
|
|
|
class SortCompareElements
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
|
|
|
void *par;
|
|
|
|
ListBox::SortFunction SortFunc;
|
|
|
|
|
|
|
|
public:
|
2010-01-25 04:00:59 +00:00
|
|
|
SortCompareElements(ListBox::SortFunction SortFunc, void *par) : par(par), SortFunc(SortFunc) {}
|
2009-05-08 13:28:41 +00:00
|
|
|
|
|
|
|
int operator()(const Element *pEl1, const Element *pEl2)
|
2010-03-28 18:58:01 +00:00
|
|
|
{ return (*SortFunc)(pEl1, pEl2, par)>0; }
|
2009-05-08 13:28:41 +00:00
|
|
|
};
|
|
|
|
|
2010-03-28 18:58:01 +00:00
|
|
|
void ListBox::SortElements(SortFunction SortFunc, void *par)
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2010-03-28 18:58:01 +00:00
|
|
|
// sort list items:
|
|
|
|
// create an array of all list items, sort it, and reorder them afterwards
|
|
|
|
if (!pClientWindow) return;
|
|
|
|
int32_t iElemCount = pClientWindow->GetElementCount();
|
|
|
|
if (iElemCount <= 1) return;
|
|
|
|
Element **ppElements = new Element *[iElemCount];
|
|
|
|
try
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2010-03-28 18:58:01 +00:00
|
|
|
int32_t i=0;
|
|
|
|
for (Element *pEl = pClientWindow->GetFirst(); pEl; pEl = pEl->GetNext())
|
|
|
|
ppElements[i++] = pEl;
|
|
|
|
std::sort(ppElements, ppElements+iElemCount, SortCompareElements(SortFunc, par));
|
|
|
|
for (i=0; i<iElemCount; ++i)
|
|
|
|
pClientWindow->ReaddElement(ppElements[i]);
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
2010-03-28 18:58:01 +00:00
|
|
|
catch (...)
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2010-03-28 18:58:01 +00:00
|
|
|
delete [] ppElements;
|
|
|
|
throw;
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
2010-03-28 18:58:01 +00:00
|
|
|
delete [] ppElements;
|
|
|
|
UpdateElementPositions();
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
|
|
|
|
2010-01-25 04:00:59 +00:00
|
|
|
} // end of namespace
|
2009-05-08 13:28:41 +00:00
|
|
|
|