m_sortAscending = true;
}
- // like SetWidth() but does not ask the header window of the
- // wxDataViewCtrl to reflect the width-change.
- void SetInternalWidth(int width);
-
-
wxString m_title;
int m_width,
m_minWidth;
// called by header window after reorder
void ColumnMoved( wxDataViewColumn* col, unsigned int new_pos );
- // updates the header window after a change in a column setting
- void OnColumnChange();
+ // update the display after a change to an individual column
+ void OnColumnChange(unsigned int idx);
+
+ // update after a change to the number of columns
+ void OnColumnsCountChanged();
wxWindow *GetMainWindow() { return (wxWindow*) m_clientArea; }
#include "wx/event.h"
#include "wx/vector.h"
+#include "wx/overlay.h"
// ----------------------------------------------------------------------------
// wxHeaderCtrl
// event handlers
void OnPaint(wxPaintEvent& event);
void OnMouse(wxMouseEvent& event);
+ void OnCaptureLost(wxMouseCaptureLostEvent& event);
// return the horizontal start position of the given column
int GetColStart(unsigned int idx) const;
// column 1 but close enough to the divider separating it from column 0)
int FindColumnAtPos(int x, bool& onSeparator) const;
+ // end any drag operation currently in progress (resizing or reordering)
+ void EndDragging();
+
+ // and the resizing operation currently in progress and generate an event
+ // about it with its cancelled flag set if width is -1
+ void EndResizing(int width);
+
+ // update the current position of the resizing marker if xPhysical is a
+ // valid physical coordinate value or remove it entirely if it is -1
+ void UpdateResizingMarker(int xPhysical);
+
// number of columns in the control currently
unsigned int m_numColumns;
// index of the column under mouse or -1 if none
unsigned int m_hover;
+ // the column being resized or -1 if there is no resizing operation in
+ // progress
+ unsigned int m_colBeingResized;
+
// the horizontal scroll offset
int m_scrollOffset;
+ // the overlay display used during the dragging operations
+ wxOverlay m_overlay;
+
DECLARE_EVENT_TABLE()
DECLARE_NO_COPY_CLASS(wxHeaderCtrl)
{
public:
wxHeaderCtrlEvent(wxEventType commandType = wxEVT_NULL, int winid = 0)
- : wxNotifyEvent(commandType, winid)
+ : wxNotifyEvent(commandType, winid),
+ m_col(-1),
+ m_width(0),
+ m_cancelled(false)
{
}
wxHeaderCtrlEvent(const wxHeaderCtrlEvent& event)
: wxNotifyEvent(event),
- m_col(event.m_col)
+ m_col(event.m_col),
+ m_width(event.m_width),
+ m_cancelled(event.m_cancelled)
{
}
+ // the column which this event pertains to: valid for all header events
int GetColumn() const { return m_col; }
void SetColumn(int col) { m_col = col; }
+ // the width of the column: valid for column resizing/dragging events only
+ int GetWidth() const { return m_width; }
+ void SetWidth(int width) { m_width = width; }
+
+ // was the drag operation cancelled or did it complete successfully?
+ bool IsCancelled() const { return m_cancelled; }
+ void SetCancelled() { m_cancelled = true; }
+
virtual wxEvent *Clone() const { return new wxHeaderCtrlEvent(*this); }
protected:
// the column affected by the event
int m_col;
+ // the current width for the dragging events
+ int m_width;
+
+ // was the drag operation cancelled?
+ bool m_cancelled;
+
private:
DECLARE_DYNAMIC_CLASS_NO_ASSIGN(wxHeaderCtrlEvent)
};
extern WXDLLIMPEXP_CORE const wxEventType wxEVT_COMMAND_HEADER_SEPARATOR_DCLICK;
+extern WXDLLIMPEXP_CORE const wxEventType wxEVT_COMMAND_HEADER_BEGIN_DRAG;
+extern WXDLLIMPEXP_CORE const wxEventType wxEVT_COMMAND_HEADER_DRAGGING;
+extern WXDLLIMPEXP_CORE const wxEventType wxEVT_COMMAND_HEADER_END_DRAG;
+
typedef void (wxEvtHandler::*wxHeaderCtrlEventFunction)(wxHeaderCtrlEvent&);
#define wxHeaderCtrlEventHandler(func) \
#define EVT_HEADER_SEPARATOR_DCLICK(id, fn) wx__DECLARE_HEADER_EVT(SEPARATOR_DCLICK, id, fn)
+#define EVT_HEADER_BEGIN_DRAG(id, fn) wx__DECLARE_HEADER_EVT(BEGIN_DRAG, id, fn)
+#define EVT_HEADER_DRAGGING(id, fn) wx__DECLARE_HEADER_EVT(DRAGGING, id, fn)
+#define EVT_HEADER_END_DRAG(id, fn) wx__DECLARE_HEADER_EVT(END_DRAG, id, fn)
+
#endif // _WX_HEADERCTRL_H_
enum Operation { Set, Insert };
void DoSetOrInsertItem(Operation oper, unsigned int idx);
- // send an event of the given type for the given column, return true if it
- // was processed
- bool SendEvent(wxEventType evtType, unsigned int idx);
-
- // send a click or double click event (depending on dblclk value) for the
- // click with the given button on the given item
- bool SendClickEvent(bool dblclk, int button, unsigned int idx);
+ // get the event type corresponding to a click or double click event
+ // (depending on dblclk value) with the specified (using MSW convention)
+ // mouse button
+ wxEventType GetClickEventType(bool dblclk, int button);
// the image list: initially NULL, created on demand
(this action is commonly used to resize the column to fit its
contents width and the control provides UpdateColumnWidthToFit() method
to make implementing this easier).
+
+ @event{EVT_HEADER_BEGIN_DRAG(id, func)}
+ The user started to drag the column with the specified index (this
+ can only happen if wxHeaderColumn::IsResizeable() returned true for
+ this column). The event can be vetoed to prevent the control from
+ being resized, if it isn't, the dragging and end drag events will
+ be generated later.
+ @event{EVT_HEADER_DRAGGING(id, func)}
+ The user is dragging the column with the specified index and its
+ current width is wxHeaderCtrlEvent::GetWidth(). The event can be
+ vetoed to stop the dragging operation completely at any time.
+ @event{EVT_HEADER_END_DRAG(id, func)}
+ The user stopped dragging the column. If
+ wxHeaderCtrlEvent::IsCancelled() returns @true, nothing should
+ be done, otherwise the column should normally be resized to the
+ value of wxHeaderCtrlEvent::GetWidth().
@endEventTable
@library{wxcore}
public:
/**
Return the index of the column affected by this event.
+
+ This method can be called for all header control events.
*/
int GetColumn() const;
+
+ /**
+ Return the current width of the column.
+
+ This method can only be called for the dragging events.
+ */
+ int GetWidth() const;
+
+ /**
+ Return @true if the drag operation was cancelled.
+
+ This method can only be called for the end drag event.
+ */
+ bool IsCancelled() const;
};
const wxEventType wxEVT_COMMAND_HEADER_MIDDLE_DCLICK = wxNewEventType();
const wxEventType wxEVT_COMMAND_HEADER_SEPARATOR_DCLICK = wxNewEventType();
+
+const wxEventType wxEVT_COMMAND_HEADER_BEGIN_DRAG = wxNewEventType();
+const wxEventType wxEVT_COMMAND_HEADER_DRAGGING = wxNewEventType();
+const wxEventType wxEVT_COMMAND_HEADER_END_DRAG = wxNewEventType();
int widthContents = owner->GetBestColumnWidth(idx);
owner->GetColumn(idx)->SetWidth(wxMax(widthTitle, widthContents));
+ owner->OnColumnChange(idx);
return true;
}
event.Skip();
}
+ void OnBeginDrag(wxHeaderCtrlEvent& event)
+ {
+ if ( !GetColumn(event.GetColumn()).IsResizeable() )
+ event.Veto();
+ }
+
+ void OnDragging(wxHeaderCtrlEvent& event)
+ {
+ const wxHeaderColumnBase& col = GetColumn(event.GetColumn());
+
+ const int minWidth = col.GetMinWidth();
+ if ( event.GetWidth() < minWidth )
+ event.Veto();
+ }
+
+ void OnEndDrag(wxHeaderCtrlEvent& event)
+ {
+ const unsigned col = event.GetColumn();
+ GetColumn(col).SetWidth(event.GetWidth());
+ GetOwner()->OnColumnChange(col);
+ }
+
DECLARE_EVENT_TABLE()
DECLARE_NO_COPY_CLASS(wxDataViewHeaderWindow)
};
BEGIN_EVENT_TABLE(wxDataViewHeaderWindow, wxHeaderCtrl)
EVT_HEADER_CLICK(wxID_ANY, wxDataViewHeaderWindow::OnClick)
EVT_HEADER_RIGHT_CLICK(wxID_ANY, wxDataViewHeaderWindow::OnRClick)
+
+ EVT_HEADER_BEGIN_DRAG(wxID_ANY, wxDataViewHeaderWindow::OnBeginDrag)
+ EVT_HEADER_DRAGGING(wxID_ANY, wxDataViewHeaderWindow::OnDragging)
+ EVT_HEADER_END_DRAG(wxID_ANY, wxDataViewHeaderWindow::OnEndDrag)
END_EVENT_TABLE()
//-----------------------------------------------------------------------------
return false;
m_cols.Append( col );
- OnColumnChange();
+ OnColumnsCountChanged();
return true;
}
return false;
m_cols.Insert( col );
- OnColumnChange();
+ OnColumnsCountChanged();
return true;
}
return false;
m_cols.Insert( pos, col );
- OnColumnChange();
+ OnColumnsCountChanged();
return true;
}
-void wxDataViewCtrl::OnColumnChange()
+void wxDataViewCtrl::OnColumnChange(unsigned int idx)
+{
+ if ( m_headerArea )
+ m_headerArea->UpdateColumn(idx);
+
+ m_clientArea->UpdateDisplay();
+}
+
+void wxDataViewCtrl::OnColumnsCountChanged()
{
if (m_headerArea)
m_headerArea->SetColumnCount(GetColumnCount());
return m_cols.GetCount();
}
-wxDataViewColumn* wxDataViewCtrl::GetColumn( unsigned int pos ) const
+wxDataViewColumn* wxDataViewCtrl::GetColumn( unsigned int idx ) const
{
- wxDataViewColumnList::const_iterator iter;
- unsigned int i = 0;
- for (iter = m_cols.begin(); iter!=m_cols.end(); iter++)
- {
- if (i == pos)
- return *iter;
-
- if ((*iter)->IsHidden())
- continue;
- i ++;
- }
- return NULL;
+ return m_cols[idx];
}
void wxDataViewCtrl::ColumnMoved( wxDataViewColumn* col, unsigned int new_pos )
return false;
m_cols.Erase(ret);
- OnColumnChange();
+ OnColumnsCountChanged();
return true;
}
bool wxDataViewCtrl::ClearColumns()
{
m_cols.Clear();
- OnColumnChange();
+ OnColumnsCountChanged();
return true;
}
void wxHeaderCtrl::Init()
{
m_numColumns = 0;
- m_hover = COL_NONE;
+ m_hover =
+ m_colBeingResized = COL_NONE;
m_scrollOffset = 0;
}
RefreshRect(rect);
}
+// ----------------------------------------------------------------------------
+// wxHeaderCtrl dragging
+// ----------------------------------------------------------------------------
+
+void wxHeaderCtrl::UpdateResizingMarker(int xPhysical)
+{
+ // unfortunately drawing the marker over the parent window doesn't work as
+ // it's usually covered by another window (the main control view) so just
+ // draw the marker over the header itself, even if it makes it not very
+ // useful
+ wxClientDC dc(this);
+
+ wxDCOverlay dcover(m_overlay, &dc);
+ dcover.Clear();
+
+ if ( xPhysical != -1 )
+ {
+ dc.SetPen(*wxLIGHT_GREY_PEN);
+ dc.DrawLine(xPhysical, 0, xPhysical, GetClientSize().y);
+ }
+}
+
+void wxHeaderCtrl::EndDragging()
+{
+ UpdateResizingMarker(-1);
+
+ m_overlay.Reset();
+}
+
+void wxHeaderCtrl::EndResizing(int width)
+{
+ wxASSERT_MSG( m_colBeingResized != COL_NONE,
+ "shouldn't be called if we're not resizing" );
+
+ EndDragging();
+
+ wxHeaderCtrlEvent event(wxEVT_COMMAND_HEADER_END_DRAG, GetId());
+ event.SetEventObject(this);
+ event.SetColumn(m_colBeingResized);
+ if ( width == -1 )
+ event.SetCancelled();
+ else
+ event.SetWidth(width);
+
+ GetEventHandler()->ProcessEvent(event);
+
+ m_colBeingResized = COL_NONE;
+}
+
// ----------------------------------------------------------------------------
// wxHeaderCtrl event handlers
// ----------------------------------------------------------------------------
EVT_PAINT(wxHeaderCtrl::OnPaint)
EVT_MOUSE_EVENTS(wxHeaderCtrl::OnMouse)
+
+ EVT_MOUSE_CAPTURE_LOST(wxHeaderCtrl::OnCaptureLost)
END_EVENT_TABLE()
void wxHeaderCtrl::OnPaint(wxPaintEvent& WXUNUSED(event))
}
}
+void wxHeaderCtrl::OnCaptureLost(wxMouseCaptureLostEvent& WXUNUSED(event))
+{
+ if ( m_colBeingResized != COL_NONE )
+ EndResizing(-1);
+}
+
void wxHeaderCtrl::OnMouse(wxMouseEvent& mevent)
{
+ // do this in advance to allow simply returning if we're not interested,
+ // we'll undo it if we do handle the event below
mevent.Skip();
+
// account for the control displacement
- const int x = mevent.GetX() - m_scrollOffset;
+ const int xPhysical = mevent.GetX();
+ const int xLogical = xPhysical - m_scrollOffset;
+
+ // first deal with the [continuation of any] dragging operations in
+ // progress
+ if ( m_colBeingResized != COL_NONE )
+ {
+ if ( mevent.LeftUp() )
+ EndResizing(xLogical - GetColStart(m_colBeingResized));
+ else // update the live separator position
+ UpdateResizingMarker(xPhysical);
+
+ return;
+ }
+
// find if the event is over a column at all
bool onSeparator;
const unsigned col = mevent.Leaving()
? (onSeparator = false, COL_NONE)
- : FindColumnAtPos(x, onSeparator);
+ : FindColumnAtPos(xLogical, onSeparator);
+
// update the highlighted column if it changed
if ( col != m_hover )
RefreshColIfNotNone(m_hover);
}
- if ( col == COL_NONE )
- return;
-
// update mouse cursor as it moves around
if ( mevent.Moving() )
{
return;
}
+ // all the other events only make sense when they happen over a column
+ if ( col == COL_NONE )
+ return;
+
+
+ // enter various dragging modes on left mouse press
if ( mevent.LeftDown() )
{
- // TODO
if ( onSeparator )
- // resize column
- ;
- else
- // drag column
+ {
+ // start resizing the column
+ m_colBeingResized = col;
+ UpdateResizingMarker(xPhysical);
+ }
+ else // on column itself
+ {
+ // TODO: drag column
;
+ }
return;
}
- // determine the type of header event corresponding to this mouse event
+ // determine the type of header event corresponding to click events
wxEventType evtType = wxEVT_NULL;
const bool click = mevent.ButtonUp(),
dblclk = mevent.ButtonDClick();
// wxHeaderCtrl events
// ----------------------------------------------------------------------------
-bool wxHeaderCtrl::SendEvent(wxEventType evtType, unsigned int idx)
-{
- wxHeaderCtrlEvent event(evtType, GetId());
- event.SetEventObject(this);
- event.SetColumn(idx);
-
- return GetEventHandler()->ProcessEvent(event);
-}
-
-bool wxHeaderCtrl::SendClickEvent(bool dblclk, int button, unsigned int idx)
+wxEventType wxHeaderCtrl::GetClickEventType(bool dblclk, int button)
{
wxEventType evtType;
switch ( button )
default:
wxFAIL_MSG( wxS("unexpected event type") );
- return false;
+ evtType = wxEVT_NULL;
}
- return SendEvent(evtType, idx);
+ return evtType;
}
bool wxHeaderCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
{
NMHEADER * const nmhdr = (NMHEADER *)lParam;
- const int idx = nmhdr->iItem;
+ wxEventType evtType = wxEVT_NULL;
+ int idx = nmhdr->iItem;
+ int width = 0;
switch ( const UINT code = nmhdr->hdr.code )
{
+ // click events
+ // ------------
+
case HDN_ITEMCLICK:
case HDN_ITEMDBLCLICK:
- if ( SendClickEvent(code == HDN_ITEMDBLCLICK, nmhdr->iButton, idx) )
- return true;
+ evtType = GetClickEventType(code == HDN_ITEMDBLCLICK, nmhdr->iButton);
break;
// although we should get the notifications about the right clicks
case NM_RDBLCLK:
{
POINT pt;
- const int col = wxMSWGetColumnClicked(&nmhdr->hdr, &pt);
- if ( col != wxNOT_FOUND )
- {
- if ( SendClickEvent(code == NM_RDBLCLK, 1, col) )
- return true;
- }
+ idx = wxMSWGetColumnClicked(&nmhdr->hdr, &pt);
+ if ( idx != wxNOT_FOUND )
+ evtType = GetClickEventType(code == NM_RDBLCLK, 1);
//else: ignore clicks outside any column
}
break;
case HDN_DIVIDERDBLCLICK:
- if ( SendEvent(wxEVT_COMMAND_HEADER_SEPARATOR_DCLICK, idx) )
- return true;
+ evtType = wxEVT_COMMAND_HEADER_SEPARATOR_DCLICK;
+ break;
+
+
+ // column resizing events
+ // ----------------------
+
+ // see comments in wxListCtrl::MSWOnNotify() for why we catch both
+ // ASCII and Unicode versions of this message
+ case HDN_BEGINTRACKA:
+ case HDN_BEGINTRACKW:
+ evtType = wxEVT_COMMAND_HEADER_BEGIN_DRAG;
+ // fall through
+
+ case HDN_TRACKA:
+ case HDN_TRACKW:
+ if ( evtType == wxEVT_NULL )
+ evtType = wxEVT_COMMAND_HEADER_DRAGGING;
+ // fall through
+
+ case HDN_ENDTRACKA:
+ case HDN_ENDTRACKW:
+ if ( evtType == wxEVT_NULL )
+ evtType = wxEVT_COMMAND_HEADER_END_DRAG;
+
+ width = nmhdr->pitem->cxy;
break;
}
+
+ // do generate the corresponding wx event
+ if ( evtType != wxEVT_NULL )
+ {
+ wxHeaderCtrlEvent event(evtType, GetId());
+ event.SetEventObject(this);
+ event.SetColumn(idx);
+ event.SetWidth(width);
+
+ if ( GetEventHandler()->ProcessEvent(event) )
+ {
+ if ( !event.IsAllowed() )
+ {
+ // all of HDN_BEGIN{DRAG,TRACK}, HDN_TRACK and HDN_ITEMCHANGING
+ // interpret TRUE return value as meaning to stop the control
+ // default handling of the message
+ *result = TRUE;
+ }
+
+ return true;
+ }
+ }
+
return wxHeaderCtrlBase::MSWOnNotify(idCtrl, lParam, result);
}