+ // Update header button
+ if (GetOwner())
+ GetOwner()->OnColumnChange();
+}
+
+void wxDataViewColumn::SetReorderable( bool reorderable )
+{
+ if (reorderable)
+ m_flags |= wxDATAVIEW_COL_REORDERABLE;
+ else
+ m_flags &= ~wxDATAVIEW_COL_REORDERABLE;
+}
+
+void wxDataViewColumn::SetSortOrder( bool ascending )
+{
+ m_ascending = ascending;
+
+ // Update header button
+ if (GetOwner())
+ GetOwner()->OnColumnChange();
+}
+
+bool wxDataViewColumn::IsSortOrderAscending() const
+{
+ return m_ascending;
+}
+
+void wxDataViewColumn::SetInternalWidth( int width )
+{
+ m_width = width;
+
+ // the scrollbars of the wxDataViewCtrl needs to be recalculated!
+ if (m_owner && m_owner->m_clientArea)
+ m_owner->m_clientArea->RecalculateDisplay();
+}
+
+void wxDataViewColumn::SetWidth( int width )
+{
+ if (m_owner->m_headerArea) m_owner->m_headerArea->UpdateDisplay();
+
+ SetInternalWidth(width);
+}
+
+
+//-----------------------------------------------------------------------------
+// wxDataViewHeaderWindowBase
+//-----------------------------------------------------------------------------
+
+void wxDataViewHeaderWindowBase::SendEvent(wxEventType type, unsigned int n)
+{
+ wxWindow *parent = GetParent();
+ wxDataViewEvent le(type, parent->GetId());
+
+ le.SetEventObject(parent);
+ le.SetColumn(n);
+ le.SetDataViewColumn(GetColumn(n));
+ le.SetModel(GetOwner()->GetModel());
+
+ // for events created by wxDataViewHeaderWindow the
+ // row / value fields are not valid
+
+ parent->GetEventHandler()->ProcessEvent(le);
+}
+
+#if defined(__WXMSW__) && USE_NATIVE_HEADER_WINDOW
+
+#ifndef HDS_DRAGDROP
+ #define HDS_DRAGDROP 0x0040
+#endif
+#ifndef HDS_FULLDRAG
+ #define HDS_FULLDRAG 0x0080
+#endif
+
+// implemented in msw/listctrl.cpp:
+int WXDLLIMPEXP_CORE wxMSWGetColumnClicked(NMHDR *nmhdr, POINT *ptClick);
+
+IMPLEMENT_ABSTRACT_CLASS(wxDataViewHeaderWindowMSW, wxWindow)
+
+bool wxDataViewHeaderWindowMSW::Create( wxDataViewCtrl *parent, wxWindowID id,
+ const wxPoint &pos, const wxSize &size,
+ const wxString &name )
+{
+ m_owner = parent;
+
+ m_scrollOffsetX = 0;
+ m_delayedUpdate = false;
+ m_buttonHeight = wxRendererNative::Get().GetHeaderButtonHeight( this );
+
+ int x = pos.x == wxDefaultCoord ? 0 : pos.x,
+ y = pos.y == wxDefaultCoord ? 0 : pos.y,
+ w = size.x == wxDefaultCoord ? 1 : size.x,
+ h = m_buttonHeight;
+
+ wxSize new_size(w,h);
+
+ if ( !CreateControl(parent, id, pos, new_size, 0, wxDefaultValidator, name) )
+ return false;
+
+ // create the native WC_HEADER window:
+ WXHWND hwndParent = (HWND)parent->GetHandle();
+ WXDWORD msStyle = WS_CHILD | HDS_DRAGDROP | HDS_BUTTONS | HDS_HORZ | HDS_HOTTRACK | HDS_FULLDRAG;
+
+ if ( m_isShown )
+ msStyle |= WS_VISIBLE;
+
+ m_hWnd = CreateWindowEx(0,
+ WC_HEADER,
+ (LPCTSTR) NULL,
+ msStyle,
+ x, y, w, h,
+ (HWND)hwndParent,
+ (HMENU)-1,
+ wxGetInstance(),
+ (LPVOID) NULL);
+ if (m_hWnd == NULL)
+ {
+ wxLogLastError(_T("CreateWindowEx"));
+ return false;
+ }
+
+ // we need to subclass the m_hWnd to force wxWindow::HandleNotify
+ // to call wxDataViewHeaderWindow::MSWOnNotify
+ SubclassWin(m_hWnd);
+
+ // the following is required to get the default win's font for
+ // header windows and must be done befor sending the HDM_LAYOUT msg
+ SetFont(GetFont());
+
+ return true;
+}
+
+wxDataViewHeaderWindowMSW::~wxDataViewHeaderWindow()
+{
+ UnsubclassWin();
+}
+
+wxSize wxDataViewHeaderWindowMSW::DoGetBestSize() const
+{
+ return wxSize( 80, m_buttonHeight+2 );
+}
+
+void wxDataViewHeaderWindowMSW::OnInternalIdle()
+{
+ if (m_delayedUpdate)
+ {
+ m_delayedUpdate = false;
+ UpdateDisplay();
+ }
+}
+
+void wxDataViewHeaderWindowMSW::UpdateDisplay()
+{
+ // remove old columns
+ for (int j=0, max=Header_GetItemCount((HWND)m_hWnd); j < max; j++)
+ Header_DeleteItem((HWND)m_hWnd, 0);
+
+ // add the updated array of columns to the header control
+ unsigned int cols = GetOwner()->GetColumnCount();
+ unsigned int added = 0;
+ for (unsigned int i = 0; i < cols; i++)
+ {
+ wxDataViewColumn *col = GetColumn( i );
+ if (col->IsHidden())
+ continue; // don't add it!
+
+ HDITEM hdi;
+ hdi.mask = HDI_TEXT | HDI_FORMAT | HDI_WIDTH;
+ hdi.pszText = (wxChar *) col->GetTitle().wx_str();
+ hdi.cxy = col->GetWidth();
+ hdi.cchTextMax = sizeof(hdi.pszText)/sizeof(hdi.pszText[0]);
+ hdi.fmt = HDF_LEFT | HDF_STRING;
+ //hdi.fmt &= ~(HDF_SORTDOWN|HDF_SORTUP);
+
+ if (col->IsSortable() && GetOwner()->GetSortingColumn() == col)
+ {
+ //The Microsoft Comctrl32.dll 6.0 support SORTUP/SORTDOWN, but they are not default
+ //see http://msdn2.microsoft.com/en-us/library/ms649534.aspx for more detail
+ // VZ: works with 5.81
+ hdi.fmt |= col->IsSortOrderAscending() ? HDF_SORTUP : HDF_SORTDOWN;
+ }
+
+ // lParam is reserved for application's use:
+ // we store there the column index to use it later in MSWOnNotify
+ // (since columns may have been hidden)
+ hdi.lParam = (LPARAM)i;
+
+ // the native wxMSW implementation of the header window
+ // draws the column separator COLUMN_WIDTH_OFFSET pixels
+ // on the right: to correct this effect we make the column
+ // exactly COLUMN_WIDTH_OFFSET wider (for the first column):
+ if (i == 0)
+ hdi.cxy += COLUMN_WIDTH_OFFSET;
+
+ switch (col->GetAlignment())
+ {
+ case wxALIGN_LEFT:
+ hdi.fmt |= HDF_LEFT;
+ break;
+ case wxALIGN_CENTER:
+ case wxALIGN_CENTER_HORIZONTAL:
+ hdi.fmt |= HDF_CENTER;
+ break;
+ case wxALIGN_RIGHT:
+ hdi.fmt |= HDF_RIGHT;
+ break;
+
+ default:
+ // such alignment is not allowed for the column header!
+ break; // wxFAIL;
+ }
+
+ SendMessage((HWND)m_hWnd, HDM_INSERTITEM,
+ (WPARAM)added, (LPARAM)&hdi);
+ added++;
+ }
+}
+
+unsigned int wxDataViewHeaderWindowMSW::GetColumnIdxFromHeader(NMHEADER *nmHDR)
+{
+ unsigned int idx;
+
+ // NOTE: we don't just return nmHDR->iItem because when there are
+ // hidden columns, nmHDR->iItem may be different from
+ // nmHDR->pitem->lParam
+
+ if (nmHDR->pitem && nmHDR->pitem->mask & HDI_LPARAM)
+ {
+ idx = (unsigned int)nmHDR->pitem->lParam;
+ return idx;
+ }
+
+ HDITEM item;
+ item.mask = HDI_LPARAM;
+ Header_GetItem((HWND)m_hWnd, nmHDR->iItem, &item);
+
+ return (unsigned int)item.lParam;
+}
+
+bool wxDataViewHeaderWindowMSW::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
+{
+ NMHDR *nmhdr = (NMHDR *)lParam;
+
+ // is it a message from the header?
+ if ( nmhdr->hwndFrom != (HWND)m_hWnd )
+ return wxWindow::MSWOnNotify(idCtrl, lParam, result);
+
+ NMHEADER *nmHDR = (NMHEADER *)nmhdr;
+ switch ( nmhdr->code )
+ {
+ case HDN_BEGINTRACK:
+ // user has started to resize a column:
+ // do we need to veto it?
+ if (!GetColumn(nmHDR->iItem)->IsResizeable())
+ {
+ // veto it!
+ *result = TRUE;
+ }
+ break;
+
+ case HDN_BEGINDRAG:
+ // user has started to reorder a column
+ if (!GetColumn(nmHDR->iItem)->IsReorderable())
+ {
+ // veto it!
+ *result = TRUE;
+ }
+ break;
+
+ case HDN_ENDDRAG: // user has finished reordering a column
+ {
+ wxDataViewColumn *col = GetColumn(nmHDR->iItem);
+ unsigned int new_pos = nmHDR->pitem->iOrder;
+ m_owner->ColumnMoved( col, new_pos );
+ m_delayedUpdate = true;
+ }
+ break;
+
+ case HDN_ITEMCHANGING:
+ if (nmHDR->pitem != NULL &&
+ (nmHDR->pitem->mask & HDI_WIDTH) != 0)
+ {
+ int minWidth = GetColumnFromHeader(nmHDR)->GetMinWidth();
+ if (nmHDR->pitem->cxy < minWidth)
+ {
+ // do not allow the user to resize this column under
+ // its minimal width:
+ *result = TRUE;
+ }
+ }
+ break;
+
+ case HDN_ITEMCHANGED: // user is resizing a column
+ case HDN_ENDTRACK: // user has finished resizing a column
+
+ // update the width of the modified column:
+ if (nmHDR->pitem != NULL &&
+ (nmHDR->pitem->mask & HDI_WIDTH) != 0)
+ {
+ unsigned int idx = GetColumnIdxFromHeader(nmHDR);
+ unsigned int w = nmHDR->pitem->cxy;
+ wxDataViewColumn *col = GetColumn(idx);
+
+ // see UpdateDisplay() for more info about COLUMN_WIDTH_OFFSET
+ if (idx == 0 && w > COLUMN_WIDTH_OFFSET)
+ w -= COLUMN_WIDTH_OFFSET;
+
+ if (w >= (unsigned)col->GetMinWidth())
+ col->SetInternalWidth(w);
+ }
+ break;
+
+ case HDN_ITEMCLICK:
+ {
+ unsigned int idx = GetColumnIdxFromHeader(nmHDR);
+ wxDataViewModel * model = GetOwner()->GetModel();
+
+ if(nmHDR->iButton == 0)
+ {
+ wxDataViewColumn *col = GetColumn(idx);
+ if(col->IsSortable())
+ {
+ if(model && m_owner->GetSortingColumn() == col)
+ {
+ bool order = col->IsSortOrderAscending();
+ col->SetSortOrder(!order);
+ }
+ else if(model)
+ {
+ m_owner->SetSortingColumn(col);
+ }
+ }
+ UpdateDisplay();
+ if(model)
+ model->Resort();
+ }
+
+ wxEventType evt = nmHDR->iButton == 0 ?
+ wxEVT_COMMAND_DATAVIEW_COLUMN_HEADER_CLICK :
+ wxEVT_COMMAND_DATAVIEW_COLUMN_HEADER_RIGHT_CLICK;
+ SendEvent(evt, idx);
+ }
+ break;
+
+ case NM_RCLICK:
+ {
+ // NOTE: for some reason (i.e. for a bug in Windows)
+ // the HDN_ITEMCLICK notification is not sent on
+ // right clicks, so we need to handle NM_RCLICK
+
+ POINT ptClick;
+ int column = wxMSWGetColumnClicked(nmhdr, &ptClick);
+ if (column != wxNOT_FOUND)
+ {
+ HDITEM item;
+ item.mask = HDI_LPARAM;
+ Header_GetItem((HWND)m_hWnd, column, &item);
+
+ // 'idx' may be different from 'column' if there are
+ // hidden columns...
+ unsigned int idx = (unsigned int)item.lParam;
+ SendEvent(wxEVT_COMMAND_DATAVIEW_COLUMN_HEADER_RIGHT_CLICK,
+ idx);
+ }
+ }
+ break;
+
+ case HDN_GETDISPINFOW:
+ // see wxListCtrl::MSWOnNotify for more info!
+ break;
+
+ case HDN_ITEMDBLCLICK:
+ {
+ unsigned int idx = GetColumnIdxFromHeader(nmHDR);
+ int w = GetOwner()->GetBestColumnWidth(idx);
+
+ // update the native control:
+ HDITEM hd;
+ ZeroMemory(&hd, sizeof(hd));
+ hd.mask = HDI_WIDTH;
+ hd.cxy = w;
+ Header_SetItem(GetHwnd(),
+ nmHDR->iItem, // NOTE: we don't want 'idx' here!
+ &hd);
+
+ // update the wxDataViewColumn class:
+ GetColumn(idx)->SetInternalWidth(w);
+ }
+ break;
+
+ default:
+ return wxWindow::MSWOnNotify(idCtrl, lParam, result);
+ }
+
+ return true;
+}
+
+void wxDataViewHeaderWindowMSW::ScrollWindow(int dx, int WXUNUSED(dy),
+ const wxRect * WXUNUSED(rect))
+{
+ m_scrollOffsetX += dx;
+
+ GetParent()->Layout();
+}
+
+void wxDataViewHeaderWindowMSW::DoSetSize(int x, int y,
+ int w, int h,
+ int f)
+{
+ // TODO: why is there a border + 2px around it?
+ wxControl::DoSetSize( x+m_scrollOffsetX+1, y+1, w-m_scrollOffsetX-2, h-2, f );
+}
+
+#else // !defined(__WXMSW__)
+
+IMPLEMENT_ABSTRACT_CLASS(wxGenericDataViewHeaderWindow, wxWindow)
+BEGIN_EVENT_TABLE(wxGenericDataViewHeaderWindow, wxWindow)
+ EVT_PAINT (wxGenericDataViewHeaderWindow::OnPaint)
+ EVT_MOUSE_EVENTS (wxGenericDataViewHeaderWindow::OnMouse)
+ EVT_SET_FOCUS (wxGenericDataViewHeaderWindow::OnSetFocus)
+END_EVENT_TABLE()
+
+bool wxGenericDataViewHeaderWindow::Create(wxDataViewCtrl *parent, wxWindowID id,
+ const wxPoint &pos, const wxSize &size,
+ const wxString &name )
+{
+ m_owner = parent;
+
+ if (!wxDataViewHeaderWindowBase::Create(parent, id, pos, size, name))
+ return false;
+
+ wxVisualAttributes attr = wxPanel::GetClassDefaultAttributes();
+ SetBackgroundStyle( wxBG_STYLE_CUSTOM );
+ SetOwnForegroundColour( attr.colFg );
+ SetOwnBackgroundColour( attr.colBg );
+ if (!m_hasFont)
+ SetOwnFont( attr.font );
+
+ // set our size hints: wxDataViewCtrl will put this wxWindow inside
+ // a wxBoxSizer and in order to avoid super-big header windows,
+ // we need to set our height as fixed
+ SetMinSize(wxSize(-1, HEADER_WINDOW_HEIGHT));
+ SetMaxSize(wxSize(-1, HEADER_WINDOW_HEIGHT));
+
+ return true;
+}
+
+void wxGenericDataViewHeaderWindow::OnPaint( wxPaintEvent &WXUNUSED(event) )
+{
+ int w, h;
+ GetClientSize( &w, &h );
+
+ wxAutoBufferedPaintDC dc( this );
+
+ dc.SetBackground(GetBackgroundColour());
+ dc.Clear();
+
+ int xpix;
+ m_owner->GetScrollPixelsPerUnit( &xpix, NULL );
+
+ int x;
+ m_owner->GetViewStart( &x, NULL );
+
+ // account for the horz scrollbar offset
+ dc.SetDeviceOrigin( -x * xpix, 0 );