]> git.saurik.com Git - wxWidgets.git/blame - src/common/headerctrlcmn.cpp
my previous commit patched the wrong file
[wxWidgets.git] / src / common / headerctrlcmn.cpp
CommitLineData
56873923
VZ
1///////////////////////////////////////////////////////////////////////////////
2// Name: src/common/headerctrlcmn.cpp
3// Purpose: implementation of wxHeaderCtrlBase
4// Author: Vadim Zeitlin
5// Created: 2008-12-02
6// RCS-ID: $Id$
7// Copyright: (c) 2008 Vadim Zeitlin <vadim@wxwidgets.org>
8// Licence: wxWindows licence
9///////////////////////////////////////////////////////////////////////////////
10
11// ============================================================================
12// declarations
13// ============================================================================
14
15// ----------------------------------------------------------------------------
16// headers
17// ----------------------------------------------------------------------------
18
19// for compilers that support precompilation, includes "wx.h".
20#include "wx/wxprec.h"
21
22#ifdef __BORLANDC__
23 #pragma hdrstop
24#endif
25
26#ifndef WX_PRECOMP
c4789bf6 27 #include "wx/menu.h"
56873923
VZ
28#endif // WX_PRECOMP
29
30#include "wx/headerctrl.h"
af67f39d
VZ
31#include "wx/rearrangectrl.h"
32
33namespace
34{
56873923 35
e2bfe673
VZ
36// ----------------------------------------------------------------------------
37// constants
38// ----------------------------------------------------------------------------
39
e2bfe673
VZ
40const unsigned int wxNO_COLUMN = static_cast<unsigned>(-1);
41
af67f39d
VZ
42// ----------------------------------------------------------------------------
43// wxHeaderColumnsRearrangeDialog: dialog for customizing our columns
44// ----------------------------------------------------------------------------
45
46class wxHeaderColumnsRearrangeDialog : public wxRearrangeDialog
47{
48public:
49 wxHeaderColumnsRearrangeDialog(wxWindow *parent,
50 const wxArrayInt& order,
51 const wxArrayString& items)
52 : wxRearrangeDialog
53 (
54 parent,
55 _("Please select the columns to show and define their order:"),
56 _("Customize Columns"),
57 order,
58 items
59 )
60 {
61 }
62};
63
e2bfe673
VZ
64} // anonymous namespace
65
56873923
VZ
66// ============================================================================
67// wxHeaderCtrlBase implementation
68// ============================================================================
69
9a382f1f 70extern WXDLLIMPEXP_DATA_CORE(const char) wxHeaderCtrlNameStr[] = "wxHeaderCtrl";
56873923 71
3bfaa5a7
VZ
72BEGIN_EVENT_TABLE(wxHeaderCtrlBase, wxControl)
73 EVT_HEADER_SEPARATOR_DCLICK(wxID_ANY, wxHeaderCtrlBase::OnSeparatorDClick)
613de0e8 74 EVT_HEADER_RIGHT_CLICK(wxID_ANY, wxHeaderCtrlBase::OnRClick)
3bfaa5a7
VZ
75END_EVENT_TABLE()
76
d8fc3398
VZ
77void wxHeaderCtrlBase::ScrollWindow(int dx,
78 int WXUNUSED_UNLESS_DEBUG(dy),
79 const wxRect * WXUNUSED_UNLESS_DEBUG(rect))
80
81{
82 // this doesn't make sense at all
83 wxASSERT_MSG( !dy, "header window can't be scrolled vertically" );
84
85 // this would actually be nice to support for "frozen" headers but it isn't
86 // supported currently
87 wxASSERT_MSG( !rect, "header window can't be scrolled partially" );
88
89 DoScrollHorz(dx);
90}
91
4635abac
VZ
92void wxHeaderCtrlBase::SetColumnCount(unsigned int count)
93{
613de0e8
VZ
94 if ( count != GetColumnCount() )
95 OnColumnCountChanging(count);
4635abac 96
613de0e8
VZ
97 // still call DoSetCount() even if the count didn't really change in order
98 // to update all the columns
4635abac
VZ
99 DoSetCount(count);
100}
101
702f5349
VZ
102// ----------------------------------------------------------------------------
103// wxHeaderCtrlBase event handling
104// ----------------------------------------------------------------------------
105
3bfaa5a7
VZ
106void wxHeaderCtrlBase::OnSeparatorDClick(wxHeaderCtrlEvent& event)
107{
108 const unsigned col = event.GetColumn();
109
110 int w = wxWindowBase::GetTextExtent(GetColumn(col).GetTitle()).x;
f458d4dd 111 w += 4*GetCharWidth(); // add some arbitrary margins around text
3bfaa5a7
VZ
112
113 if ( !UpdateColumnWidthToFit(col, w) )
114 event.Skip();
115 else
116 UpdateColumn(col);
117}
118
613de0e8
VZ
119void wxHeaderCtrlBase::OnRClick(wxHeaderCtrlEvent& event)
120{
121 if ( !HasFlag(wxHD_ALLOW_HIDE) )
122 {
123 event.Skip();
124 return;
125 }
126
127 ShowColumnsMenu(ScreenToClient(wxGetMousePosition()));
128}
129
702f5349
VZ
130// ----------------------------------------------------------------------------
131// wxHeaderCtrlBase column reordering
132// ----------------------------------------------------------------------------
133
134void wxHeaderCtrlBase::SetColumnsOrder(const wxArrayInt& order)
135{
136 const unsigned count = GetColumnCount();
137 wxCHECK_RET( order.size() == count, "wrong number of columns" );
138
139 // check the array validity
140 wxArrayInt seen(count, 0);
141 for ( unsigned n = 0; n < count; n++ )
142 {
143 const unsigned idx = order[n];
144 wxCHECK_RET( idx < count, "invalid column index" );
145 wxCHECK_RET( !seen[idx], "duplicate column index" );
146
147 seen[idx] = 1;
148 }
149
150 DoSetColumnsOrder(order);
151
152 // TODO-RTL: do we need to reverse the array?
153}
154
da5a641f
VZ
155void wxHeaderCtrlBase::ResetColumnsOrder()
156{
157 const unsigned count = GetColumnCount();
158 wxArrayInt order(count);
159 for ( unsigned n = 0; n < count; n++ )
160 order[n] = n;
161
162 DoSetColumnsOrder(order);
163}
164
702f5349
VZ
165wxArrayInt wxHeaderCtrlBase::GetColumnsOrder() const
166{
167 const wxArrayInt order = DoGetColumnsOrder();
168
169 wxASSERT_MSG( order.size() == GetColumnCount(), "invalid order array" );
170
171 return order;
172}
173
174unsigned int wxHeaderCtrlBase::GetColumnAt(unsigned int pos) const
175{
176 wxCHECK_MSG( pos < GetColumnCount(), wxNO_COLUMN, "invalid position" );
177
178 return GetColumnsOrder()[pos];
179}
180
181unsigned int wxHeaderCtrlBase::GetColumnPos(unsigned int idx) const
182{
183 const unsigned count = GetColumnCount();
184
185 wxCHECK_MSG( idx < count, wxNO_COLUMN, "invalid index" );
186
187 const wxArrayInt order = GetColumnsOrder();
188 for ( unsigned n = 0; n < count; n++ )
189 {
190 if ( (unsigned)order[n] == idx )
191 return n;
192 }
193
194 wxFAIL_MSG( "column unexpectedly not displayed at all" );
195
196 return wxNO_COLUMN;
197}
198
1bb74626
VZ
199/* static */
200void wxHeaderCtrlBase::MoveColumnInOrderArray(wxArrayInt& order,
201 unsigned int idx,
202 unsigned int pos)
203{
204 const unsigned count = order.size();
205
206 wxArrayInt orderNew;
207 orderNew.reserve(count);
208 for ( unsigned n = 0; ; n++ )
209 {
210 // NB: order of checks is important for this to work when the new
211 // column position is the same as the old one
212
213 // insert the column at its new position
214 if ( orderNew.size() == pos )
215 orderNew.push_back(idx);
216
217 if ( n == count )
218 break;
219
220 // delete the column from its old position
221 const unsigned idxOld = order[n];
222 if ( idxOld == idx )
223 continue;
224
225 orderNew.push_back(idxOld);
226 }
227
228 order.swap(orderNew);
229}
230
f6655391
VZ
231void
232wxHeaderCtrlBase::DoResizeColumnIndices(wxArrayInt& colIndices, unsigned int count)
233{
234 // update the column indices array if necessary
235 const unsigned countOld = colIndices.size();
236 if ( count > countOld )
237 {
238 // all new columns have default positions equal to their indices
239 for ( unsigned n = countOld; n < count; n++ )
240 colIndices.push_back(n);
241 }
242 else if ( count < countOld )
243 {
244 // filter out all the positions which are invalid now while keeping the
245 // order of the remaining ones
246 wxArrayInt colIndicesNew;
247 colIndicesNew.reserve(count);
248 for ( unsigned n = 0; n < countOld; n++ )
249 {
250 const unsigned idx = colIndices[n];
251 if ( idx < count )
252 colIndicesNew.push_back(idx);
253 }
254
255 colIndices.swap(colIndicesNew);
256 }
613de0e8 257 //else: count didn't really change, nothing to do
f6655391
VZ
258
259 wxASSERT_MSG( colIndices.size() == count, "logic error" );
260}
261
e8f25dbb
VZ
262// ----------------------------------------------------------------------------
263// wxHeaderCtrl extra UI
264// ----------------------------------------------------------------------------
265
ddd0db96 266void wxHeaderCtrlBase::AddColumnsItems(wxMenu& menu, int idColumnsBase)
e8f25dbb 267{
e8f25dbb
VZ
268 const unsigned count = GetColumnCount();
269 for ( unsigned n = 0; n < count; n++ )
270 {
271 const wxHeaderColumn& col = GetColumn(n);
ddd0db96 272 menu.AppendCheckItem(idColumnsBase + n, col.GetTitle());
e8f25dbb
VZ
273 if ( col.IsShown() )
274 menu.Check(n, true);
275 }
ddd0db96
VZ
276}
277
278bool wxHeaderCtrlBase::ShowColumnsMenu(const wxPoint& pt, const wxString& title)
279{
280 // construct the menu with the entries for all columns
281 wxMenu menu;
282 if ( !title.empty() )
283 menu.SetTitle(title);
284
285 AddColumnsItems(menu);
e8f25dbb 286
613de0e8
VZ
287 // ... and an extra one to show the customization dialog if the user is
288 // allowed to reorder the columns too
ddd0db96 289 const unsigned count = GetColumnCount();
613de0e8
VZ
290 if ( HasFlag(wxHD_ALLOW_REORDER) )
291 {
292 menu.AppendSeparator();
293 menu.Append(count, _("&Customize..."));
294 }
295
296 // do show the menu and get the user selection
297 const int rc = GetPopupMenuSelectionFromUser(menu, pt);
298 if ( rc == wxID_NONE )
299 return false;
300
301 if ( static_cast<unsigned>(rc) == count )
302 {
303 return ShowCustomizeDialog();
304 }
305 else // a column selected from the menu
306 {
307 UpdateColumnVisibility(rc, !GetColumn(rc).IsShown());
308 }
309
310 return true;
311}
312
313bool wxHeaderCtrlBase::ShowCustomizeDialog()
314{
af67f39d
VZ
315 // prepare the data for showing the dialog
316 wxArrayInt order = GetColumnsOrder();
317
318 const unsigned count = GetColumnCount();
319
320 // notice that titles are always in the index order, they will be shown
321 // rearranged according to the display order in the dialog
322 wxArrayString titles;
323 titles.reserve(count);
324 for ( unsigned n = 0; n < count; n++ )
325 titles.push_back(GetColumn(n).GetTitle());
326
327 // this loop is however over positions and not indices
328 unsigned pos;
329 for ( pos = 0; pos < count; pos++ )
330 {
331 int& idx = order[pos];
332 if ( GetColumn(idx).IsHidden() )
333 {
334 // indicate that this one is hidden
335 idx = ~idx;
336 }
337 }
338
339 // do show it
340 wxHeaderColumnsRearrangeDialog dlg(this, order, titles);
341 if ( dlg.ShowModal() != wxID_OK )
342 return false;
343
344 // and apply the changes
345 order = dlg.GetOrder();
346 for ( pos = 0; pos < count; pos++ )
347 {
348 int& idx = order[pos];
349 const bool show = idx >= 0;
350 if ( !show )
351 {
352 // make all indices positive for passing them to SetColumnsOrder()
353 idx = ~idx;
354 }
355
356 if ( show != GetColumn(idx).IsShown() )
357 UpdateColumnVisibility(idx, show);
358 }
359
360 UpdateColumnsOrder(order);
361 SetColumnsOrder(order);
362
363 return true;
e8f25dbb
VZ
364}
365
e2bfe673
VZ
366// ============================================================================
367// wxHeaderCtrlSimple implementation
368// ============================================================================
369
370void wxHeaderCtrlSimple::Init()
371{
372 m_sortKey = wxNO_COLUMN;
373}
374
482d06f8 375const wxHeaderColumn& wxHeaderCtrlSimple::GetColumn(unsigned int idx) const
e2bfe673
VZ
376{
377 return m_cols[idx];
378}
379
380void wxHeaderCtrlSimple::DoInsert(const wxHeaderColumnSimple& col, unsigned int idx)
381{
382 m_cols.insert(m_cols.begin() + idx, col);
383
384 UpdateColumnCount();
385}
386
387void wxHeaderCtrlSimple::DoDelete(unsigned int idx)
388{
389 m_cols.erase(m_cols.begin() + idx);
390 if ( idx == m_sortKey )
391 m_sortKey = wxNO_COLUMN;
392
393 UpdateColumnCount();
394}
395
396void wxHeaderCtrlSimple::DeleteAllColumns()
397{
398 m_cols.clear();
399 m_sortKey = wxNO_COLUMN;
400
401 UpdateColumnCount();
402}
403
404
405void wxHeaderCtrlSimple::DoShowColumn(unsigned int idx, bool show)
406{
407 if ( show != m_cols[idx].IsShown() )
408 {
409 m_cols[idx].SetHidden(!show);
410
411 UpdateColumn(idx);
412 }
413}
414
415void wxHeaderCtrlSimple::DoShowSortIndicator(unsigned int idx, bool ascending)
416{
417 RemoveSortIndicator();
418
419 m_cols[idx].SetAsSortKey(ascending);
420 m_sortKey = idx;
421
422 UpdateColumn(idx);
423}
424
425void wxHeaderCtrlSimple::RemoveSortIndicator()
426{
427 if ( m_sortKey != wxNO_COLUMN )
428 {
429 const unsigned sortOld = m_sortKey;
430 m_sortKey = wxNO_COLUMN;
431
432 m_cols[sortOld].UnsetAsSortKey();
433
434 UpdateColumn(sortOld);
435 }
436}
437
e5a16353
VZ
438bool
439wxHeaderCtrlSimple::UpdateColumnWidthToFit(unsigned int idx, int widthTitle)
440{
441 const int widthContents = GetBestFittingWidth(idx);
442 if ( widthContents == -1 )
443 return false;
444
445 m_cols[idx].SetWidth(wxMax(widthContents, widthTitle));
e5a16353
VZ
446
447 return true;
448}
449
fa3d4aaf
VZ
450// ============================================================================
451// wxHeaderCtrlEvent implementation
452// ============================================================================
453
454IMPLEMENT_DYNAMIC_CLASS(wxHeaderCtrlEvent, wxNotifyEvent)
455
456const wxEventType wxEVT_COMMAND_HEADER_CLICK = wxNewEventType();
457const wxEventType wxEVT_COMMAND_HEADER_RIGHT_CLICK = wxNewEventType();
458const wxEventType wxEVT_COMMAND_HEADER_MIDDLE_CLICK = wxNewEventType();
459
460const wxEventType wxEVT_COMMAND_HEADER_DCLICK = wxNewEventType();
461const wxEventType wxEVT_COMMAND_HEADER_RIGHT_DCLICK = wxNewEventType();
462const wxEventType wxEVT_COMMAND_HEADER_MIDDLE_DCLICK = wxNewEventType();
3bfaa5a7
VZ
463
464const wxEventType wxEVT_COMMAND_HEADER_SEPARATOR_DCLICK = wxNewEventType();
aef252d9 465
396825dc
VZ
466const wxEventType wxEVT_COMMAND_HEADER_BEGIN_RESIZE = wxNewEventType();
467const wxEventType wxEVT_COMMAND_HEADER_RESIZING = wxNewEventType();
468const wxEventType wxEVT_COMMAND_HEADER_END_RESIZE = wxNewEventType();
702f5349
VZ
469
470const wxEventType wxEVT_COMMAND_HEADER_BEGIN_REORDER = wxNewEventType();
471const wxEventType wxEVT_COMMAND_HEADER_END_REORDER = wxNewEventType();
565804f2
VZ
472
473const wxEventType wxEVT_COMMAND_HEADER_DRAGGING_CANCELLED = wxNewEventType();