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