#pragma hdrstop
#endif
-#include "wx/ribbon/toolbar.h"
-
#if wxUSE_RIBBON
+#include "wx/ribbon/toolbar.h"
#include "wx/ribbon/art.h"
#include "wx/ribbon/bar.h"
#include "wx/dcbuffer.h"
long state;
};
-WX_DEFINE_ARRAY(wxRibbonToolBarToolBase*, wxArrayRibbonToolBarToolBase);
+WX_DEFINE_ARRAY_PTR(wxRibbonToolBarToolBase*, wxArrayRibbonToolBarToolBase);
class wxRibbonToolBarToolGroup
{
wxSize size;
};
-wxDEFINE_EVENT(wxEVT_COMMAND_RIBBONTOOL_CLICKED, wxRibbonToolBarEvent);
-wxDEFINE_EVENT(wxEVT_COMMAND_RIBBONTOOL_DROPDOWN_CLICKED, wxRibbonToolBarEvent);
+wxDEFINE_EVENT(wxEVT_RIBBONTOOLBAR_CLICKED, wxRibbonToolBarEvent);
+wxDEFINE_EVENT(wxEVT_RIBBONTOOLBAR_DROPDOWN_CLICKED, wxRibbonToolBarEvent);
IMPLEMENT_DYNAMIC_CLASS(wxRibbonToolBarEvent, wxCommandEvent)
IMPLEMENT_CLASS(wxRibbonToolBar, wxRibbonControl)
wxRIBBON_BUTTON_HYBRID, NULL);
}
+wxRibbonToolBarToolBase* wxRibbonToolBar::AddToggleTool(
+ int tool_id,
+ const wxBitmap& bitmap,
+ const wxString& help_string)
+{
+ return AddTool(tool_id, bitmap, wxNullBitmap, help_string,
+ wxRIBBON_BUTTON_TOGGLE, NULL);
+}
+
wxRibbonToolBarToolBase* wxRibbonToolBar::AddTool(
int tool_id,
const wxBitmap& bitmap,
const wxString& help_string,
wxRibbonButtonKind kind,
wxObject* client_data)
+{
+ return InsertTool(GetToolCount(), tool_id, bitmap, bitmap_disabled,
+ help_string, kind, client_data);
+}
+
+wxRibbonToolBarToolBase* wxRibbonToolBar::AddSeparator()
+{
+ if(m_groups.Last()->tools.IsEmpty())
+ return NULL;
+
+ AppendGroup();
+ return &m_groups.Last()->dummy_tool;
+}
+
+
+wxRibbonToolBarToolBase* wxRibbonToolBar::InsertTool(
+ size_t pos,
+ int tool_id,
+ const wxBitmap& bitmap,
+ const wxString& help_string,
+ wxRibbonButtonKind kind)
+{
+ return InsertTool(pos, tool_id, bitmap, wxNullBitmap, help_string, kind,
+ NULL);
+}
+
+wxRibbonToolBarToolBase* wxRibbonToolBar::InsertDropdownTool(
+ size_t pos,
+ int tool_id,
+ const wxBitmap& bitmap,
+ const wxString& help_string)
+{
+ return InsertTool(pos, tool_id, bitmap, wxNullBitmap, help_string,
+ wxRIBBON_BUTTON_DROPDOWN, NULL);
+}
+
+wxRibbonToolBarToolBase* wxRibbonToolBar::InsertHybridTool(
+ size_t pos,
+ int tool_id,
+ const wxBitmap& bitmap,
+ const wxString& help_string)
+{
+ return InsertTool(pos, tool_id, bitmap, wxNullBitmap, help_string,
+ wxRIBBON_BUTTON_HYBRID, NULL);
+}
+
+wxRibbonToolBarToolBase* wxRibbonToolBar::InsertToggleTool(
+ size_t pos,
+ int tool_id,
+ const wxBitmap& bitmap,
+ const wxString& help_string)
+{
+ return InsertTool(pos, tool_id, bitmap, wxNullBitmap, help_string,
+ wxRIBBON_BUTTON_TOGGLE, NULL);
+}
+
+wxRibbonToolBarToolBase* wxRibbonToolBar::InsertTool(
+ size_t pos,
+ int tool_id,
+ const wxBitmap& bitmap,
+ const wxBitmap& bitmap_disabled,
+ const wxString& help_string,
+ wxRibbonButtonKind kind,
+ wxObject* client_data)
{
wxASSERT(bitmap.IsOk());
+ // Create the wxRibbonToolBarToolBase with parameters
wxRibbonToolBarToolBase* tool = new wxRibbonToolBarToolBase;
tool->id = tool_id;
tool->bitmap = bitmap;
tool->size = wxSize(0, 0);
tool->state = 0;
- m_groups.Last()->tools.Add(tool);
- return tool;
+ // Find the position where insert tool
+ size_t group_count = m_groups.GetCount();
+ size_t g;
+ for(g = 0; g < group_count; ++g)
+ {
+ wxRibbonToolBarToolGroup* group = m_groups.Item(g);
+ size_t tool_count = group->tools.GetCount();
+ if(pos <= tool_count)
+ {
+ group->tools.Insert(tool, pos);
+ return tool;
+ }
+ pos -= tool_count + 1;
+ }
+ wxFAIL_MSG("Tool position out of toolbar bounds.");
+ return NULL;
}
-wxRibbonToolBarToolBase* wxRibbonToolBar::AddSeparator()
+wxRibbonToolBarToolBase* wxRibbonToolBar::InsertSeparator(size_t pos)
{
+ size_t group_count = m_groups.GetCount();
+ size_t g;
+ for(g = 0; g < group_count; ++g)
+ {
+ if(pos==0) // Prepend group
+ return &InsertGroup(g)->dummy_tool;
+ if(pos==group_count) // Append group
+ return &InsertGroup(g+1)->dummy_tool;
+
+ wxRibbonToolBarToolGroup* group = m_groups.Item(g);
+ size_t tool_count = group->tools.GetCount();
+ if(pos < tool_count)
+ {
+ wxRibbonToolBarToolGroup* new_group = InsertGroup(g+1);
+
+ for(size_t t = pos; t < tool_count; t++)
+ new_group->tools.Add(group->tools[t]);
+ group->tools.RemoveAt(pos, tool_count-pos);
+
+ return &group->dummy_tool;
+ }
+ pos -= tool_count + 1;
+ }
+ // Add an empty group at the end of the bar.
if(m_groups.Last()->tools.IsEmpty())
return NULL;
-
AppendGroup();
return &m_groups.Last()->dummy_tool;
}
+wxRibbonToolBarToolGroup* wxRibbonToolBar::InsertGroup(size_t pos)
+{
+ wxRibbonToolBarToolGroup* group = new wxRibbonToolBarToolGroup;
+ group->position = wxPoint(0, 0);
+ group->size = wxSize(0, 0);
+ m_groups.Insert(group, pos);
+ return group;
+}
+
+void wxRibbonToolBar::ClearTools()
+{
+ size_t count = m_groups.GetCount();
+ size_t i, t;
+ for(i = 0; i < count; ++i)
+ {
+ wxRibbonToolBarToolGroup* group = m_groups.Item(i);
+ size_t tool_count = group->tools.GetCount();
+ for(t = 0; t < tool_count; ++t)
+ {
+ wxRibbonToolBarToolBase* tool = group->tools.Item(t);
+ delete tool;
+ }
+ delete group;
+ }
+ m_groups.Clear();
+}
+
+bool wxRibbonToolBar::DeleteTool(int tool_id)
+{
+ size_t group_count = m_groups.GetCount();
+ size_t g, t;
+ for(g = 0; g < group_count; ++g)
+ {
+ wxRibbonToolBarToolGroup* group = m_groups.Item(g);
+ size_t tool_count = group->tools.GetCount();
+ for(t = 0; t < tool_count; ++t)
+ {
+ wxRibbonToolBarToolBase* tool = group->tools.Item(t);
+ if(tool->id == tool_id)
+ {
+ group->tools.RemoveAt(t);
+ delete tool;
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool wxRibbonToolBar::DeleteToolByPos(size_t pos)
+{
+ size_t group_count = m_groups.GetCount();
+ size_t g, t;
+ for(g = 0; g < group_count; ++g)
+ {
+ wxRibbonToolBarToolGroup* group = m_groups.Item(g);
+ size_t tool_count = group->tools.GetCount();
+ if(pos<tool_count)
+ {
+ // Remove tool
+ wxRibbonToolBarToolBase* tool = group->tools.Item(pos);
+ group->tools.RemoveAt(pos);
+ delete tool;
+ return true;
+ }
+ else if(pos==tool_count)
+ {
+ // Remove separator
+ if(g < group_count - 1)
+ {
+ wxRibbonToolBarToolGroup* next_group = m_groups.Item(g+1);
+ for(t = 0; t < next_group->tools.GetCount(); ++t)
+ group->tools.Add(next_group->tools[t]);
+ m_groups.RemoveAt(g+1);
+ delete next_group;
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+wxRibbonToolBarToolBase* wxRibbonToolBar::FindById(int tool_id)const
+{
+ size_t group_count = m_groups.GetCount();
+ size_t g, t;
+ for(g = 0; g < group_count; ++g)
+ {
+ wxRibbonToolBarToolGroup* group = m_groups.Item(g);
+ size_t tool_count = group->tools.GetCount();
+ for(t = 0; t < tool_count; ++t)
+ {
+ wxRibbonToolBarToolBase* tool = group->tools.Item(t);
+ if(tool->id == tool_id)
+ {
+ return tool;
+ }
+ }
+ }
+ return NULL;
+}
+
+wxRibbonToolBarToolBase* wxRibbonToolBar::GetToolByPos(size_t pos)const
+{
+ size_t group_count = m_groups.GetCount();
+ size_t g;
+ for(g = 0; g < group_count; ++g)
+ {
+ wxRibbonToolBarToolGroup* group = m_groups.Item(g);
+ size_t tool_count = group->tools.GetCount();
+ if(pos<tool_count)
+ {
+ return group->tools[pos];
+ }
+ else if(pos==tool_count)
+ {
+ return NULL;
+ }
+ }
+ return NULL;
+}
+
+size_t wxRibbonToolBar::GetToolCount() const
+{
+ size_t count = 0;
+ for(size_t g = 0; g < m_groups.GetCount(); ++g)
+ {
+ wxRibbonToolBarToolGroup* group = m_groups.Item(g);
+ count += group->tools.GetCount();
+ }
+ // There is a splitter in front of every group except for the first
+ // If only one group, no separator.
+ if(m_groups.GetCount()>1)
+ count += m_groups.GetCount() - 1;
+ return count;
+}
+
+int wxRibbonToolBar::GetToolId(const wxRibbonToolBarToolBase* tool)const
+{
+ wxCHECK_MSG(tool != NULL , wxNOT_FOUND, "The tool pointer must not be NULL");
+ return tool->id;
+}
+
+wxObject* wxRibbonToolBar::GetToolClientData(int tool_id)const
+{
+ wxRibbonToolBarToolBase* tool = FindById(tool_id);
+ wxCHECK_MSG(tool != NULL , NULL, "Invalid tool id");
+ return tool->client_data;
+}
+
+bool wxRibbonToolBar::GetToolEnabled(int tool_id)const
+{
+ wxRibbonToolBarToolBase* tool = FindById(tool_id);
+ wxCHECK_MSG(tool != NULL , false, "Invalid tool id");
+ return (tool->state & wxRIBBON_TOOLBAR_TOOL_DISABLED) == 0;
+}
+
+wxString wxRibbonToolBar::GetToolHelpString(int tool_id)const
+{
+ wxRibbonToolBarToolBase* tool = FindById(tool_id);
+ wxCHECK_MSG(tool != NULL , wxEmptyString, "Invalid tool id");
+ return tool->help_string;
+}
+
+wxRibbonButtonKind wxRibbonToolBar::GetToolKind(int tool_id)const
+{
+ wxRibbonToolBarToolBase* tool = FindById(tool_id);
+ wxCHECK_MSG(tool != NULL , wxRIBBON_BUTTON_NORMAL, "Invalid tool id");
+ return tool->kind;
+}
+
+int wxRibbonToolBar::GetToolPos(int tool_id)const
+{
+ size_t group_count = m_groups.GetCount();
+ size_t g, t;
+ int pos = 0;
+ for(g = 0; g < group_count; ++g)
+ {
+ wxRibbonToolBarToolGroup* group = m_groups.Item(g);
+ size_t tool_count = group->tools.GetCount();
+ for(t = 0; t < tool_count; ++t)
+ {
+ wxRibbonToolBarToolBase* tool = group->tools.Item(t);
+ if(tool->id == tool_id)
+ {
+ return pos;
+ }
+ ++pos;
+ }
+ ++pos; // Increment pos for group separator.
+ }
+ return wxNOT_FOUND;
+}
+
+bool wxRibbonToolBar::GetToolState(int tool_id)const
+{
+ wxRibbonToolBarToolBase* tool = FindById(tool_id);
+ wxCHECK_MSG(tool != NULL , false, "Invalid tool id");
+ return (tool->state & wxRIBBON_TOOLBAR_TOOL_TOGGLED) != 0;
+}
+
wxBitmap wxRibbonToolBar::MakeDisabledBitmap(const wxBitmap& original)
{
wxImage img(original.ConvertToImage());
return false;
}
+void wxRibbonToolBar::SetToolClientData(int tool_id, wxObject* clientData)
+{
+ wxRibbonToolBarToolBase* tool = FindById(tool_id);
+ wxCHECK_RET(tool != NULL , "Invalid tool id");
+ tool->client_data = clientData;
+}
+
+void wxRibbonToolBar::SetToolDisabledBitmap(int tool_id, const wxBitmap &bitmap)
+{
+ wxRibbonToolBarToolBase* tool = FindById(tool_id);
+ wxCHECK_RET(tool != NULL , "Invalid tool id");
+ tool->bitmap_disabled = bitmap;
+}
+
+void wxRibbonToolBar::SetToolHelpString(int tool_id, const wxString& helpString)
+{
+ wxRibbonToolBarToolBase* tool = FindById(tool_id);
+ wxCHECK_RET(tool != NULL , "Invalid tool id");
+ tool->help_string = helpString;
+}
+
+void wxRibbonToolBar::SetToolNormalBitmap(int tool_id, const wxBitmap &bitmap)
+{
+ wxRibbonToolBarToolBase* tool = FindById(tool_id);
+ wxCHECK_RET(tool != NULL , "Invalid tool id");
+ tool->bitmap = bitmap;
+}
+
+void wxRibbonToolBar::EnableTool(int tool_id, bool enable)
+{
+ wxRibbonToolBarToolBase* tool = FindById(tool_id);
+ wxCHECK_RET(tool != NULL , "Invalid tool id");
+ if(enable)
+ {
+ if(tool->state & wxRIBBON_TOOLBAR_TOOL_DISABLED)
+ {
+ tool->state &= ~wxRIBBON_TOOLBAR_TOOL_DISABLED;
+ Refresh();
+ }
+ }
+ else
+ {
+ if((tool->state & wxRIBBON_TOOLBAR_TOOL_DISABLED)==0)
+ {
+ tool->state |= wxRIBBON_TOOLBAR_TOOL_DISABLED;
+ Refresh();
+ }
+ }
+}
+
+void wxRibbonToolBar::ToggleTool(int tool_id, bool checked)
+{
+ wxRibbonToolBarToolBase* tool = FindById(tool_id);
+ wxCHECK_RET(tool != NULL , "Invalid tool id");
+ if(checked)
+ {
+ if((tool->state & wxRIBBON_TOOLBAR_TOOL_TOGGLED) == 0)
+ {
+ tool->state |= wxRIBBON_TOOLBAR_TOOL_TOGGLED;
+ Refresh();
+ }
+ }
+ else
+ {
+ if(tool->state & wxRIBBON_TOOLBAR_TOOL_TOGGLED)
+ {
+ tool->state &= ~wxRIBBON_TOOLBAR_TOOL_TOGGLED;
+ Refresh();
+ }
+ }
+}
+
static int GetSizeInOrientation(wxSize size, wxOrientation orientation)
{
switch(orientation)
tool->size = m_art->GetToolSize(temp_dc, this,
tool->bitmap.GetSize(), tool->kind, t == 0,
t == (tool_count - 1), &tool->dropdown);
- tool->state = tool->state & ~wxRIBBON_TOOLBAR_TOOL_DISABLED;
if(t == 0)
tool->state |= wxRIBBON_TOOLBAR_TOOL_FIRST;
if(t == tool_count - 1)
wxSize* row_sizes = new wxSize[m_nrows_max];
wxOrientation major_axis = m_art->GetFlags() & wxRIBBON_BAR_FLOW_VERTICAL ?
wxVERTICAL : wxHORIZONTAL;
+
SetMinSize(wxSize(0, 0));
+ wxSize minSize(INT_MAX, INT_MAX);
+
+ // See if we're sizing flexibly (i.e. wrapping), and set min size differently
+ bool sizingFlexibly = false;
+ wxRibbonPanel* panel = wxDynamicCast(GetParent(), wxRibbonPanel);
+ if (panel && (panel->GetFlags() & wxRIBBON_PANEL_FLEXIBLE))
+ sizingFlexibly = true;
+
+ // Without this, there will be redundant horizontal space because SetMinSize will
+ // use the smallest possible height (and therefore largest width).
+ if (sizingFlexibly)
+ major_axis = wxHORIZONTAL;
+
for(nrows = m_nrows_min; nrows <= m_nrows_max; ++nrows)
{
for(r = 0; r < nrows; ++r)
size.IncBy(0, row_sizes[r].y);
}
m_sizes[nrows - m_nrows_min] = size;
+
if(GetSizeInOrientation(size, major_axis) < smallest_area)
{
- SetMinSize(size);
smallest_area = GetSizeInOrientation(size, major_axis);
+ SetMinSize(size);
+ }
+
+ if (sizingFlexibly)
+ {
+ if (size.x < minSize.x)
+ minSize.x = size.x;
+ if (size.y < minSize.y)
+ minSize.y = size.y;
}
}
+ if (sizingFlexibly)
+ {
+ // Give it the min size in either direction regardless of row,
+ // so that we're able to vary the size of the panel according to
+ // the space the toolbar takes up.
+ SetMinSize(minSize);
+ }
+ delete[] row_sizes;
+
// Position the groups
wxSizeEvent dummy_event(GetSize());
OnSize(dummy_event);
int row_count = m_nrows_max;
wxOrientation major_axis = m_art->GetFlags() & wxRIBBON_BAR_FLOW_VERTICAL ?
wxVERTICAL : wxHORIZONTAL;
+
+ // See if we're sizing flexibly, and set min size differently
+ bool sizingFlexibly = false;
+ wxRibbonPanel* panel = wxDynamicCast(GetParent(), wxRibbonPanel);
+ if (panel && (panel->GetFlags() & wxRIBBON_PANEL_FLEXIBLE))
+ sizingFlexibly = true;
+
+ // Without this, there will be redundant horizontal space because SetMinSize will
+ // use the smallest possible height (and therefore largest width).
+ if (sizingFlexibly)
+ major_axis = wxHORIZONTAL;
+
+ wxSize bestSize = m_sizes[0];
+
if(m_nrows_max != m_nrows_min)
{
int area = 0;
{
area = GetSizeInOrientation(m_sizes[i], major_axis);
row_count = m_nrows_min + i;
+ bestSize = m_sizes[i];
}
}
}
delete[] row_sizes;
}
+// Finds the best width and height given the parents' width and height
+wxSize wxRibbonToolBar::GetBestSizeForParentSize(const wxSize& parentSize) const
+{
+ if (!m_sizes)
+ return GetMinSize();
+
+ // Choose row count with largest possible area
+ wxSize size = parentSize;
+
+ // A toolbar should maximize its width whether vertical or horizontal, so
+ // force the major axis to be horizontal. Without this, there will be
+ // redundant horizontal space.
+ wxOrientation major_axis = wxHORIZONTAL;
+ wxSize bestSize = m_sizes[0];
+
+ if(m_nrows_max != m_nrows_min)
+ {
+ int area = 0;
+ for(int i = 0; i <= m_nrows_max - m_nrows_min; ++i)
+ {
+ if(m_sizes[i].x <= size.x && m_sizes[i].y <= size.y &&
+ GetSizeInOrientation(m_sizes[i], major_axis) > area)
+ {
+ area = GetSizeInOrientation(m_sizes[i], major_axis);
+ bestSize = m_sizes[i];
+ }
+ }
+ }
+ return bestSize;
+}
+
wxSize wxRibbonToolBar::DoGetBestSize() const
{
return GetMinSize();
{
wxRibbonToolBarToolBase* tool = group->tools.Item(t);
wxRect rect(group->position + tool->position, tool->size);
- m_art->DrawTool(dc, this, rect, tool->bitmap, tool->kind,
- tool->state);
+ if(tool->state & wxRIBBON_TOOLBAR_TOOL_DISABLED)
+ m_art->DrawTool(dc, this, rect, tool->bitmap_disabled,
+ tool->kind, tool->state);
+ else
+ m_art->DrawTool(dc, this, rect, tool->bitmap, tool->kind,
+ tool->state);
}
}
}
}
}
+#if wxUSE_TOOLTIPS
+ if(new_hover)
+ {
+ SetToolTip(new_hover->help_string);
+ }
+ else if(GetToolTip())
+ {
+ UnsetToolTip();
+ }
+#endif
+
+ if(new_hover && new_hover->state & wxRIBBON_TOOLBAR_TOOL_DISABLED)
+ {
+ new_hover = NULL; // A disabled tool can not be hilighted
+ }
+
if(new_hover != m_hover_tool)
{
if(m_hover_tool)
what = wxRIBBON_TOOLBAR_TOOL_DROPDOWN_HOVERED;
new_hover->state |= what;
-
+
if(new_hover == m_active_tool)
{
new_hover->state &= ~wxRIBBON_TOOLBAR_TOOL_ACTIVE_MASK;
{
if(m_active_tool->state & wxRIBBON_TOOLBAR_TOOL_ACTIVE_MASK)
{
- wxEventType evt_type = wxEVT_COMMAND_RIBBONTOOL_CLICKED;
+ wxEventType evt_type = wxEVT_RIBBONTOOLBAR_CLICKED;
if(m_active_tool->state & wxRIBBON_TOOLBAR_TOOL_DROPDOWN_ACTIVE)
- evt_type = wxEVT_COMMAND_RIBBONTOOL_DROPDOWN_CLICKED;
+ evt_type = wxEVT_RIBBONTOOLBAR_DROPDOWN_CLICKED;
wxRibbonToolBarEvent notification(evt_type, m_active_tool->id);
+ if(m_active_tool->kind == wxRIBBON_BUTTON_TOGGLE)
+ {
+ m_active_tool->state ^=
+ wxRIBBON_BUTTONBAR_BUTTON_TOGGLED;
+ notification.SetInt(m_active_tool->state &
+ wxRIBBON_BUTTONBAR_BUTTON_TOGGLED);
+ }
notification.SetEventObject(this);
notification.SetBar(this);
ProcessEvent(notification);
+
+ wxStaticCast(m_parent, wxRibbonPanel)->HideIfExpanded();
+ }
+
+ // Notice that m_active_tool could have been reset by the event handler
+ // above so we need to test it again.
+ if (m_active_tool)
+ {
+ m_active_tool->state &= ~wxRIBBON_TOOLBAR_TOOL_ACTIVE_MASK;
+ m_active_tool = NULL;
+ Refresh(false);
}
- m_active_tool->state &= ~wxRIBBON_TOOLBAR_TOOL_ACTIVE_MASK;
- m_active_tool = NULL;
- Refresh(false);
}
}
}
}
+void wxRibbonToolBar::UpdateWindowUI(long flags)
+{
+ wxWindowBase::UpdateWindowUI(flags);
+
+ // don't waste time updating state of tools in a hidden toolbar
+ if ( !IsShown() )
+ return;
+
+ size_t group_count = m_groups.GetCount();
+ size_t g, t;
+ for(g = 0; g < group_count; ++g)
+ {
+ wxRibbonToolBarToolGroup* group = m_groups.Item(g);
+ size_t tool_count = group->tools.GetCount();
+ for(t = 0; t < tool_count; ++t)
+ {
+ wxRibbonToolBarToolBase* tool = group->tools.Item(t);
+ int id = tool->id;
+
+ wxUpdateUIEvent event(id);
+ event.SetEventObject(this);
+
+ if ( ProcessWindowEvent(event) )
+ {
+ if ( event.GetSetEnabled() )
+ EnableTool(id, event.GetEnabled());
+ if ( event.GetSetChecked() )
+ ToggleTool(id, event.GetChecked());
+ }
+ }
+ }
+}
+
bool wxRibbonToolBarEvent::PopupMenu(wxMenu* menu)
{
wxPoint pos = wxDefaultPosition;