]> git.saurik.com Git - wxWidgets.git/blob - src/generic/datavgen.cpp
c03a08ff924d13158e20d04ddff9c399ccd203c2
[wxWidgets.git] / src / generic / datavgen.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/generic/datavgen.cpp
3 // Purpose: wxDataViewCtrl generic implementation
4 // Author: Robert Roebling
5 // Modified by: Francesco Montorsi, Guru Kathiresan, Otto Wyss
6 // Id: $Id$
7 // Copyright: (c) 1998 Robert Roebling
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10
11 // For compilers that support precompilation, includes "wx.h".
12 #include "wx/wxprec.h"
13
14 #ifdef __BORLANDC__
15 #pragma hdrstop
16 #endif
17
18 #if wxUSE_DATAVIEWCTRL
19
20 #include "wx/dataview.h"
21
22 #ifdef wxUSE_GENERICDATAVIEWCTRL
23
24 #ifndef WX_PRECOMP
25 #ifdef __WXMSW__
26 #include "wx/msw/private.h"
27 #include "wx/msw/wrapwin.h"
28 #include "wx/msw/wrapcctl.h" // include <commctrl.h> "properly"
29 #endif
30 #include "wx/sizer.h"
31 #include "wx/log.h"
32 #include "wx/dcclient.h"
33 #include "wx/timer.h"
34 #include "wx/settings.h"
35 #include "wx/msgdlg.h"
36 #include "wx/dcscreen.h"
37 #endif
38
39 #include "wx/stockitem.h"
40 #include "wx/calctrl.h"
41 #include "wx/popupwin.h"
42 #include "wx/renderer.h"
43 #include "wx/dcbuffer.h"
44 #include "wx/icon.h"
45
46 //-----------------------------------------------------------------------------
47 // classes
48 //-----------------------------------------------------------------------------
49
50 class wxDataViewCtrl;
51
52 static const int SCROLL_UNIT_X = 15;
53
54 // the cell padding on the left/right
55 static const int PADDING_RIGHTLEFT = 3;
56
57 // the cell padding on the top/bottom
58 static const int PADDING_TOPBOTTOM = 1;
59
60
61 //-----------------------------------------------------------------------------
62 // wxDataViewHeaderWindow
63 //-----------------------------------------------------------------------------
64
65 #define USE_NATIVE_HEADER_WINDOW 1
66
67 // NB: for some reason, this class must be dllexport'ed or we get warnings from
68 // MSVC in DLL build
69 class WXDLLIMPEXP_ADV wxDataViewHeaderWindowBase : public wxControl
70 {
71 public:
72 wxDataViewHeaderWindowBase()
73 { m_owner = NULL; }
74
75 bool Create(wxDataViewCtrl *parent, wxWindowID id,
76 const wxPoint &pos, const wxSize &size,
77 const wxString &name)
78 {
79 return wxWindow::Create(parent, id, pos, size, wxNO_BORDER, name);
80 }
81
82 void SetOwner( wxDataViewCtrl* owner ) { m_owner = owner; }
83 wxDataViewCtrl *GetOwner() { return m_owner; }
84
85 // called on column addition/removal
86 virtual void UpdateDisplay() { /* by default, do nothing */ }
87
88 // returns the n-th column
89 virtual wxDataViewColumn *GetColumn(unsigned int n)
90 {
91 wxASSERT(m_owner);
92 wxDataViewColumn *ret = m_owner->GetColumn(n);
93 wxASSERT(ret);
94
95 return ret;
96 }
97
98 protected:
99 wxDataViewCtrl *m_owner;
100
101 // sends an event generated from the n-th wxDataViewColumn
102 void SendEvent(wxEventType type, unsigned int n);
103 };
104
105 // on wxMSW the header window (only that part however) can be made native!
106 #if defined(__WXMSW__) && USE_NATIVE_HEADER_WINDOW
107
108 #define COLUMN_WIDTH_OFFSET 2
109 #define wxDataViewHeaderWindowMSW wxDataViewHeaderWindow
110
111 class wxDataViewHeaderWindowMSW : public wxDataViewHeaderWindowBase
112 {
113 public:
114
115 wxDataViewHeaderWindowMSW( wxDataViewCtrl *parent,
116 wxWindowID id,
117 const wxPoint &pos = wxDefaultPosition,
118 const wxSize &size = wxDefaultSize,
119 const wxString &name = wxT("wxdataviewctrlheaderwindow") )
120 {
121 Create(parent, id, pos, size, name);
122 }
123
124 bool Create(wxDataViewCtrl *parent, wxWindowID id,
125 const wxPoint &pos, const wxSize &size,
126 const wxString &name);
127
128 ~wxDataViewHeaderWindowMSW();
129
130 // called when any column setting is changed and/or changed
131 // the column count
132 virtual void UpdateDisplay();
133
134 // called when the main window gets scrolled
135 virtual void ScrollWindow(int dx, int dy, const wxRect *rect = NULL);
136
137 protected:
138 virtual bool MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result);
139 virtual void DoSetSize(int x, int y, int width, int height, int sizeFlags);
140
141 unsigned int GetColumnIdxFromHeader(NMHEADER *nmHDR);
142
143 wxDataViewColumn *GetColumnFromHeader(NMHEADER *nmHDR)
144 { return GetColumn(GetColumnIdxFromHeader(nmHDR)); }
145
146 private:
147 DECLARE_DYNAMIC_CLASS(wxDataViewHeaderWindowMSW)
148 };
149
150 #else // !defined(__WXMSW__)
151
152 #define HEADER_WINDOW_HEIGHT 25
153 #define HEADER_HORIZ_BORDER 5
154 #define HEADER_VERT_BORDER 3
155 #define wxGenericDataViewHeaderWindow wxDataViewHeaderWindow
156
157 class wxGenericDataViewHeaderWindow : public wxDataViewHeaderWindowBase
158 {
159 public:
160 wxGenericDataViewHeaderWindow( wxDataViewCtrl *parent,
161 wxWindowID id,
162 const wxPoint &pos = wxDefaultPosition,
163 const wxSize &size = wxDefaultSize,
164 const wxString &name = wxT("wxdataviewctrlheaderwindow") )
165 {
166 Init();
167 Create(parent, id, pos, size, name);
168 }
169
170 bool Create(wxDataViewCtrl *parent, wxWindowID id,
171 const wxPoint &pos, const wxSize &size,
172 const wxString &name);
173
174 ~wxGenericDataViewHeaderWindow()
175 {
176 delete m_resizeCursor;
177 }
178
179 // event handlers:
180
181 void OnPaint( wxPaintEvent &event );
182 void OnMouse( wxMouseEvent &event );
183 void OnSetFocus( wxFocusEvent &event );
184
185
186 protected:
187
188 // vars used for column resizing:
189
190 wxCursor *m_resizeCursor;
191 const wxCursor *m_currentCursor;
192 bool m_isDragging;
193
194 bool m_dirty; // needs refresh?
195 int m_column; // index of the column being resized
196 int m_currentX; // divider line position in logical (unscrolled) coords
197 int m_minX; // minimal position beyond which the divider line
198 // can't be dragged in logical coords
199
200 // the pen used to draw the current column width drag line
201 // when resizing the columsn
202 wxPen m_penCurrent;
203
204
205 // internal utilities:
206
207 void Init()
208 {
209 m_currentCursor = (wxCursor *) NULL;
210 m_resizeCursor = new wxCursor( wxCURSOR_SIZEWE );
211
212 m_isDragging = false;
213 m_dirty = false;
214
215 m_column = wxNOT_FOUND;
216 m_currentX = 0;
217 m_minX = 0;
218
219 wxColour col = wxSystemSettings::GetColour(wxSYS_COLOUR_3DLIGHT);
220 m_penCurrent = wxPen(col, 1, wxSOLID);
221 }
222
223 void DrawCurrent();
224 void AdjustDC(wxDC& dc);
225
226 private:
227 DECLARE_DYNAMIC_CLASS(wxGenericDataViewHeaderWindow)
228 DECLARE_EVENT_TABLE()
229 };
230
231 #endif // defined(__WXMSW__)
232
233 //-----------------------------------------------------------------------------
234 // wxDataViewRenameTimer
235 //-----------------------------------------------------------------------------
236
237 class wxDataViewRenameTimer: public wxTimer
238 {
239 private:
240 wxDataViewMainWindow *m_owner;
241
242 public:
243 wxDataViewRenameTimer( wxDataViewMainWindow *owner );
244 void Notify();
245 };
246
247 //-----------------------------------------------------------------------------
248 // wxDataViewMainWindow
249 //-----------------------------------------------------------------------------
250
251 WX_DEFINE_SORTED_USER_EXPORTED_ARRAY_SIZE_T(unsigned int, wxDataViewSelection,
252 WXDLLIMPEXP_ADV);
253
254 class wxDataViewMainWindow: public wxWindow
255 {
256 public:
257 wxDataViewMainWindow( wxDataViewCtrl *parent,
258 wxWindowID id,
259 const wxPoint &pos = wxDefaultPosition,
260 const wxSize &size = wxDefaultSize,
261 const wxString &name = wxT("wxdataviewctrlmainwindow") );
262 virtual ~wxDataViewMainWindow();
263
264 // notifications from wxDataViewListModel
265 bool RowAppended();
266 bool RowPrepended();
267 bool RowInserted( unsigned int before );
268 bool RowDeleted( unsigned int row );
269 bool RowChanged( unsigned int row );
270 bool ValueChanged( unsigned int col, unsigned int row );
271 bool RowsReordered( unsigned int *new_order );
272 bool Cleared();
273
274 void SetOwner( wxDataViewCtrl* owner ) { m_owner = owner; }
275 wxDataViewCtrl *GetOwner() { return m_owner; }
276 const wxDataViewCtrl *GetOwner() const { return m_owner; }
277
278 void OnPaint( wxPaintEvent &event );
279 void OnArrowChar(unsigned int newCurrent, const wxKeyEvent& event);
280 void OnChar( wxKeyEvent &event );
281 void OnMouse( wxMouseEvent &event );
282 void OnSetFocus( wxFocusEvent &event );
283 void OnKillFocus( wxFocusEvent &event );
284
285 void UpdateDisplay();
286 void RecalculateDisplay();
287 void OnInternalIdle();
288
289 void OnRenameTimer();
290
291 void ScrollWindow( int dx, int dy, const wxRect *rect = NULL );
292
293 bool HasCurrentRow() { return m_currentRow != (unsigned int)-1; }
294 void ChangeCurrentRow( unsigned int row );
295
296 bool IsSingleSel() const { return !GetParent()->HasFlag(wxDV_MULTIPLE); }
297 bool IsEmpty() { return GetRowCount() == 0; }
298
299 int GetCountPerPage() const;
300 int GetEndOfLastCol() const;
301 unsigned int GetFirstVisibleRow() const;
302 unsigned int GetLastVisibleRow() const;
303 unsigned int GetRowCount() const;
304
305 void Select( const wxArrayInt& aSelections );
306 void SelectAllRows( bool on );
307 void SelectRow( unsigned int row, bool on );
308 void SelectRows( unsigned int from, unsigned int to, bool on );
309 void ReverseRowSelection( unsigned int row );
310 bool IsRowSelected( unsigned int row );
311
312 void RefreshRow( unsigned int row );
313 void RefreshRows( unsigned int from, unsigned int to );
314 void RefreshRowsAfter( unsigned int firstRow );
315
316 // returns the colour to be used for drawing the rules
317 wxColour GetRuleColour() const
318 {
319 return wxSystemSettings::GetColour(wxSYS_COLOUR_3DLIGHT);
320 }
321
322 //void EnsureVisible( unsigned int row );
323 wxRect GetLineRect( unsigned int row ) const;
324
325 private:
326 wxDataViewCtrl *m_owner;
327 int m_lineHeight;
328 bool m_dirty;
329
330 wxDataViewColumn *m_currentCol;
331 unsigned int m_currentRow;
332 wxDataViewSelection m_selection;
333
334 wxDataViewRenameTimer *m_renameTimer;
335 bool m_lastOnSame;
336
337 bool m_hasFocus;
338
339 int m_dragCount;
340 wxPoint m_dragStart;
341
342 // for double click logic
343 unsigned int m_lineLastClicked,
344 m_lineBeforeLastClicked,
345 m_lineSelectSingleOnUp;
346
347 // the pen used to draw horiz/vertical rules
348 wxPen m_penRule;
349
350 private:
351 DECLARE_DYNAMIC_CLASS(wxDataViewMainWindow)
352 DECLARE_EVENT_TABLE()
353 };
354
355 // ---------------------------------------------------------
356 // wxGenericDataViewListModelNotifier
357 // ---------------------------------------------------------
358
359 class wxGenericDataViewListModelNotifier: public wxDataViewListModelNotifier
360 {
361 public:
362 wxGenericDataViewListModelNotifier( wxDataViewMainWindow *mainWindow )
363 { m_mainWindow = mainWindow; }
364
365 virtual bool RowAppended()
366 { return m_mainWindow->RowAppended(); }
367 virtual bool RowPrepended()
368 { return m_mainWindow->RowPrepended(); }
369 virtual bool RowInserted( unsigned int before )
370 { return m_mainWindow->RowInserted( before ); }
371 virtual bool RowDeleted( unsigned int row )
372 { return m_mainWindow->RowDeleted( row ); }
373 virtual bool RowChanged( unsigned int row )
374 { return m_mainWindow->RowChanged( row ); }
375 virtual bool ValueChanged( unsigned int col, unsigned int row )
376 { return m_mainWindow->ValueChanged( col, row ); }
377 virtual bool RowsReordered( unsigned int *new_order )
378 { return m_mainWindow->RowsReordered( new_order ); }
379 virtual bool Cleared()
380 { return m_mainWindow->Cleared(); }
381
382 wxDataViewMainWindow *m_mainWindow;
383 };
384
385 // ---------------------------------------------------------
386 // wxDataViewRenderer
387 // ---------------------------------------------------------
388
389 IMPLEMENT_ABSTRACT_CLASS(wxDataViewRenderer, wxDataViewRendererBase)
390
391 wxDataViewRenderer::wxDataViewRenderer( const wxString &varianttype,
392 wxDataViewCellMode mode,
393 int align) :
394 wxDataViewRendererBase( varianttype, mode, align )
395 {
396 m_dc = NULL;
397 m_align = align;
398 m_mode = mode;
399 }
400
401 wxDataViewRenderer::~wxDataViewRenderer()
402 {
403 if (m_dc)
404 delete m_dc;
405 }
406
407 wxDC *wxDataViewRenderer::GetDC()
408 {
409 if (m_dc == NULL)
410 {
411 if (GetOwner() == NULL)
412 return NULL;
413 if (GetOwner()->GetOwner() == NULL)
414 return NULL;
415 m_dc = new wxClientDC( GetOwner()->GetOwner() );
416 }
417
418 return m_dc;
419 }
420
421 // ---------------------------------------------------------
422 // wxDataViewCustomRenderer
423 // ---------------------------------------------------------
424
425 IMPLEMENT_ABSTRACT_CLASS(wxDataViewCustomRenderer, wxDataViewRenderer)
426
427 wxDataViewCustomRenderer::wxDataViewCustomRenderer( const wxString &varianttype,
428 wxDataViewCellMode mode, int align ) :
429 wxDataViewRenderer( varianttype, mode, align )
430 {
431 }
432
433 // ---------------------------------------------------------
434 // wxDataViewTextRenderer
435 // ---------------------------------------------------------
436
437 IMPLEMENT_CLASS(wxDataViewTextRenderer, wxDataViewCustomRenderer)
438
439 wxDataViewTextRenderer::wxDataViewTextRenderer( const wxString &varianttype,
440 wxDataViewCellMode mode, int align ) :
441 wxDataViewCustomRenderer( varianttype, mode, align )
442 {
443 }
444
445 bool wxDataViewTextRenderer::SetValue( const wxVariant &value )
446 {
447 m_text = value.GetString();
448
449 return true;
450 }
451
452 bool wxDataViewTextRenderer::GetValue( wxVariant& WXUNUSED(value) ) const
453 {
454 return false;
455 }
456
457 bool wxDataViewTextRenderer::HasEditorCtrl()
458 {
459 return true;
460 }
461
462 wxControl* wxDataViewTextRenderer::CreateEditorCtrl( wxWindow *parent,
463 wxRect labelRect, const wxVariant &value )
464 {
465 return new wxTextCtrl( parent, wxID_ANY, value,
466 wxPoint(labelRect.x,labelRect.y),
467 wxSize(labelRect.width,labelRect.height) );
468 }
469
470 bool wxDataViewTextRenderer::GetValueFromEditorCtrl( wxControl *editor, wxVariant &value )
471 {
472 wxTextCtrl *text = (wxTextCtrl*) editor;
473 value = text->GetValue();
474 return true;
475 }
476
477 bool wxDataViewTextRenderer::Render( wxRect cell, wxDC *dc, int state )
478 {
479 wxDataViewCtrl *view = GetOwner()->GetOwner();
480 wxColour col = (state & wxDATAVIEW_CELL_SELECTED) ?
481 wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT) :
482 view->GetForegroundColour();
483
484 dc->SetTextForeground(col);
485 dc->DrawText( m_text, cell.x, cell.y );
486
487 return true;
488 }
489
490 wxSize wxDataViewTextRenderer::GetSize() const
491 {
492 const wxDataViewCtrl *view = GetView();
493 if (!m_text.empty())
494 {
495 int x,y;
496 view->GetTextExtent( m_text, &x, &y );
497 return wxSize( x, y );
498 }
499 return wxSize(80,20);
500 }
501
502 // ---------------------------------------------------------
503 // wxDataViewBitmapRenderer
504 // ---------------------------------------------------------
505
506 IMPLEMENT_CLASS(wxDataViewBitmapRenderer, wxDataViewCustomRenderer)
507
508 wxDataViewBitmapRenderer::wxDataViewBitmapRenderer( const wxString &varianttype,
509 wxDataViewCellMode mode, int align ) :
510 wxDataViewCustomRenderer( varianttype, mode, align )
511 {
512 }
513
514 bool wxDataViewBitmapRenderer::SetValue( const wxVariant &value )
515 {
516 if (value.GetType() == wxT("wxBitmap"))
517 m_bitmap << value;
518 if (value.GetType() == wxT("wxIcon"))
519 m_icon << value;
520
521 return true;
522 }
523
524 bool wxDataViewBitmapRenderer::GetValue( wxVariant& WXUNUSED(value) ) const
525 {
526 return false;
527 }
528
529 bool wxDataViewBitmapRenderer::Render( wxRect cell, wxDC *dc, int WXUNUSED(state) )
530 {
531 if (m_bitmap.Ok())
532 dc->DrawBitmap( m_bitmap, cell.x, cell.y );
533 else if (m_icon.Ok())
534 dc->DrawIcon( m_icon, cell.x, cell.y );
535
536 return true;
537 }
538
539 wxSize wxDataViewBitmapRenderer::GetSize() const
540 {
541 if (m_bitmap.Ok())
542 return wxSize( m_bitmap.GetWidth(), m_bitmap.GetHeight() );
543 else if (m_icon.Ok())
544 return wxSize( m_icon.GetWidth(), m_icon.GetHeight() );
545
546 return wxSize(16,16);
547 }
548
549 // ---------------------------------------------------------
550 // wxDataViewToggleRenderer
551 // ---------------------------------------------------------
552
553 IMPLEMENT_ABSTRACT_CLASS(wxDataViewToggleRenderer, wxDataViewCustomRenderer)
554
555 wxDataViewToggleRenderer::wxDataViewToggleRenderer( const wxString &varianttype,
556 wxDataViewCellMode mode, int align ) :
557 wxDataViewCustomRenderer( varianttype, mode, align )
558 {
559 m_toggle = false;
560 }
561
562 bool wxDataViewToggleRenderer::SetValue( const wxVariant &value )
563 {
564 m_toggle = value.GetBool();
565
566 return true;
567 }
568
569 bool wxDataViewToggleRenderer::GetValue( wxVariant &WXUNUSED(value) ) const
570 {
571 return false;
572 }
573
574 bool wxDataViewToggleRenderer::Render( wxRect cell, wxDC *dc, int WXUNUSED(state) )
575 {
576 // User wxRenderer here
577
578 wxRect rect;
579 rect.x = cell.x + cell.width/2 - 10;
580 rect.width = 20;
581 rect.y = cell.y + cell.height/2 - 10;
582 rect.height = 20;
583
584 int flags = 0;
585 if (m_toggle)
586 flags |= wxCONTROL_CHECKED;
587 if (GetMode() != wxDATAVIEW_CELL_ACTIVATABLE)
588 flags |= wxCONTROL_DISABLED;
589
590 wxRendererNative::Get().DrawCheckBox(
591 GetOwner()->GetOwner(),
592 *dc,
593 rect,
594 flags );
595
596 return true;
597 }
598
599 bool wxDataViewToggleRenderer::Activate( wxRect WXUNUSED(cell),
600 wxDataViewListModel *model,
601 unsigned int col, unsigned int row )
602 {
603 bool value = !m_toggle;
604 wxVariant variant = value;
605 model->SetValue( variant, col, row );
606 model->ValueChanged( col, row );
607 return true;
608 }
609
610 wxSize wxDataViewToggleRenderer::GetSize() const
611 {
612 return wxSize(20,20);
613 }
614
615 // ---------------------------------------------------------
616 // wxDataViewProgressRenderer
617 // ---------------------------------------------------------
618
619 IMPLEMENT_ABSTRACT_CLASS(wxDataViewProgressRenderer, wxDataViewCustomRenderer)
620
621 wxDataViewProgressRenderer::wxDataViewProgressRenderer( const wxString &label,
622 const wxString &varianttype, wxDataViewCellMode mode, int align ) :
623 wxDataViewCustomRenderer( varianttype, mode, align )
624 {
625 m_label = label;
626 m_value = 0;
627 }
628
629 wxDataViewProgressRenderer::~wxDataViewProgressRenderer()
630 {
631 }
632
633 bool wxDataViewProgressRenderer::SetValue( const wxVariant &value )
634 {
635 m_value = (long) value;
636
637 if (m_value < 0) m_value = 0;
638 if (m_value > 100) m_value = 100;
639
640 return true;
641 }
642
643 bool wxDataViewProgressRenderer::GetValue( wxVariant &value ) const
644 {
645 value = (long) m_value;
646 return true;
647 }
648
649 bool wxDataViewProgressRenderer::Render( wxRect cell, wxDC *dc, int WXUNUSED(state) )
650 {
651 double pct = (double)m_value / 100.0;
652 wxRect bar = cell;
653 bar.width = (int)(cell.width * pct);
654 dc->SetPen( *wxTRANSPARENT_PEN );
655 dc->SetBrush( *wxBLUE_BRUSH );
656 dc->DrawRectangle( bar );
657
658 dc->SetBrush( *wxTRANSPARENT_BRUSH );
659 dc->SetPen( *wxBLACK_PEN );
660 dc->DrawRectangle( cell );
661
662 return true;
663 }
664
665 wxSize wxDataViewProgressRenderer::GetSize() const
666 {
667 return wxSize(40,12);
668 }
669
670 // ---------------------------------------------------------
671 // wxDataViewDateRenderer
672 // ---------------------------------------------------------
673
674 #define wxUSE_DATE_RENDERER_POPUP (wxUSE_CALENDARCTRL && wxUSE_POPUPWIN)
675
676 #if wxUSE_DATE_RENDERER_POPUP
677
678 class wxDataViewDateRendererPopupTransient: public wxPopupTransientWindow
679 {
680 public:
681 wxDataViewDateRendererPopupTransient( wxWindow* parent, wxDateTime *value,
682 wxDataViewListModel *model, unsigned int col, unsigned int row ) :
683 wxPopupTransientWindow( parent, wxBORDER_SIMPLE )
684 {
685 m_model = model;
686 m_col = col;
687 m_row = row;
688 m_cal = new wxCalendarCtrl( this, wxID_ANY, *value );
689 wxBoxSizer *sizer = new wxBoxSizer( wxHORIZONTAL );
690 sizer->Add( m_cal, 1, wxGROW );
691 SetSizer( sizer );
692 sizer->Fit( this );
693 }
694
695 void OnCalendar( wxCalendarEvent &event );
696
697 wxCalendarCtrl *m_cal;
698 wxDataViewListModel *m_model;
699 unsigned int m_col;
700 unsigned int m_row;
701
702 protected:
703 virtual void OnDismiss()
704 {
705 }
706
707 private:
708 DECLARE_EVENT_TABLE()
709 };
710
711 BEGIN_EVENT_TABLE(wxDataViewDateRendererPopupTransient,wxPopupTransientWindow)
712 EVT_CALENDAR( wxID_ANY, wxDataViewDateRendererPopupTransient::OnCalendar )
713 END_EVENT_TABLE()
714
715 void wxDataViewDateRendererPopupTransient::OnCalendar( wxCalendarEvent &event )
716 {
717 wxDateTime date = event.GetDate();
718 wxVariant value = date;
719 m_model->SetValue( value, m_col, m_row );
720 m_model->ValueChanged( m_col, m_row );
721 DismissAndNotify();
722 }
723
724 #endif // wxUSE_DATE_RENDERER_POPUP
725
726 IMPLEMENT_ABSTRACT_CLASS(wxDataViewDateRenderer, wxDataViewCustomRenderer)
727
728 wxDataViewDateRenderer::wxDataViewDateRenderer( const wxString &varianttype,
729 wxDataViewCellMode mode, int align ) :
730 wxDataViewCustomRenderer( varianttype, mode, align )
731 {
732 }
733
734 bool wxDataViewDateRenderer::SetValue( const wxVariant &value )
735 {
736 m_date = value.GetDateTime();
737
738 return true;
739 }
740
741 bool wxDataViewDateRenderer::GetValue( wxVariant &value ) const
742 {
743 value = m_date;
744 return true;
745 }
746
747 bool wxDataViewDateRenderer::Render( wxRect cell, wxDC *dc, int WXUNUSED(state) )
748 {
749 dc->SetFont( GetOwner()->GetOwner()->GetFont() );
750 wxString tmp = m_date.FormatDate();
751 dc->DrawText( tmp, cell.x, cell.y );
752
753 return true;
754 }
755
756 wxSize wxDataViewDateRenderer::GetSize() const
757 {
758 const wxDataViewCtrl* view = GetView();
759 wxString tmp = m_date.FormatDate();
760 wxCoord x,y,d;
761 view->GetTextExtent( tmp, &x, &y, &d );
762 return wxSize(x,y+d);
763 }
764
765 bool wxDataViewDateRenderer::Activate( wxRect WXUNUSED(cell), wxDataViewListModel *model,
766 unsigned int col, unsigned int row )
767 {
768 wxVariant variant;
769 model->GetValue( variant, col, row );
770 wxDateTime value = variant.GetDateTime();
771
772 #if wxUSE_DATE_RENDERER_POPUP
773 wxDataViewDateRendererPopupTransient *popup = new wxDataViewDateRendererPopupTransient(
774 GetOwner()->GetOwner()->GetParent(), &value, model, col, row );
775 wxPoint pos = wxGetMousePosition();
776 popup->Move( pos );
777 popup->Layout();
778 popup->Popup( popup->m_cal );
779 #else // !wxUSE_DATE_RENDERER_POPUP
780 wxMessageBox(value.Format());
781 #endif // wxUSE_DATE_RENDERER_POPUP/!wxUSE_DATE_RENDERER_POPUP
782 return true;
783 }
784
785 // ---------------------------------------------------------
786 // wxDataViewColumn
787 // ---------------------------------------------------------
788
789 IMPLEMENT_ABSTRACT_CLASS(wxDataViewColumn, wxDataViewColumnBase)
790
791 wxDataViewColumn::wxDataViewColumn( const wxString &title, wxDataViewRenderer *cell,
792 unsigned int model_column,
793 int width, wxAlignment align, int flags ) :
794 wxDataViewColumnBase( title, cell, model_column, width, align, flags )
795 {
796 SetAlignment(align);
797 SetTitle(title);
798 SetFlags(flags);
799
800 Init(width < 0 ? wxDVC_DEFAULT_WIDTH : width);
801 }
802
803 wxDataViewColumn::wxDataViewColumn( const wxBitmap &bitmap, wxDataViewRenderer *cell,
804 unsigned int model_column,
805 int width, wxAlignment align, int flags ) :
806 wxDataViewColumnBase( bitmap, cell, model_column, width, align, flags )
807 {
808 SetAlignment(align);
809 SetFlags(flags);
810
811 Init(width < 0 ? wxDVC_TOGGLE_DEFAULT_WIDTH : width);
812 }
813
814 wxDataViewColumn::~wxDataViewColumn()
815 {
816 }
817
818 void wxDataViewColumn::Init( int width )
819 {
820 m_width = width;
821 m_minWidth = wxDVC_DEFAULT_MINWIDTH;
822 }
823
824 void wxDataViewColumn::SetResizeable( bool resizeable )
825 {
826 if (resizeable)
827 m_flags |= wxDATAVIEW_COL_RESIZABLE;
828 else
829 m_flags &= ~wxDATAVIEW_COL_RESIZABLE;
830 }
831
832 void wxDataViewColumn::SetHidden( bool hidden )
833 {
834 if (hidden)
835 m_flags |= wxDATAVIEW_COL_HIDDEN;
836 else
837 m_flags &= ~wxDATAVIEW_COL_HIDDEN;
838
839 // tell our owner to e.g. update its scrollbars:
840 if (GetOwner())
841 GetOwner()->OnColumnChange();
842 }
843
844 void wxDataViewColumn::SetSortable( bool sortable )
845 {
846 if (sortable)
847 m_flags |= wxDATAVIEW_COL_SORTABLE;
848 else
849 m_flags &= ~wxDATAVIEW_COL_SORTABLE;
850 }
851
852 void wxDataViewColumn::SetSortOrder( bool WXUNUSED(ascending) )
853 {
854 // TODO
855 }
856
857 bool wxDataViewColumn::IsSortOrderAscending() const
858 {
859 // TODO
860 return true;
861 }
862
863 void wxDataViewColumn::SetInternalWidth( int width )
864 {
865 m_width = width;
866
867 // the scrollbars of the wxDataViewCtrl needs to be recalculated!
868 if (m_owner && m_owner->m_clientArea)
869 m_owner->m_clientArea->RecalculateDisplay();
870 }
871
872 void wxDataViewColumn::SetWidth( int width )
873 {
874 m_owner->m_headerArea->UpdateDisplay();
875
876 SetInternalWidth(width);
877 }
878
879
880 //-----------------------------------------------------------------------------
881 // wxDataViewHeaderWindowBase
882 //-----------------------------------------------------------------------------
883
884 void wxDataViewHeaderWindowBase::SendEvent(wxEventType type, unsigned int n)
885 {
886 wxWindow *parent = GetParent();
887 wxDataViewEvent le(type, parent->GetId());
888
889 le.SetEventObject(parent);
890 le.SetColumn(n);
891 le.SetDataViewColumn(GetColumn(n));
892 le.SetModel(GetOwner()->GetModel());
893
894 // for events created by wxDataViewHeaderWindow the
895 // row / value fields are not valid
896
897 parent->GetEventHandler()->ProcessEvent(le);
898 }
899
900 #if defined(__WXMSW__) && USE_NATIVE_HEADER_WINDOW
901
902 // implemented in msw/listctrl.cpp:
903 int WXDLLIMPEXP_CORE wxMSWGetColumnClicked(NMHDR *nmhdr, POINT *ptClick);
904
905 IMPLEMENT_ABSTRACT_CLASS(wxDataViewHeaderWindowMSW, wxWindow)
906
907 bool wxDataViewHeaderWindowMSW::Create( wxDataViewCtrl *parent, wxWindowID id,
908 const wxPoint &pos, const wxSize &size,
909 const wxString &name )
910 {
911 m_owner = parent;
912
913 if ( !CreateControl(parent, id, pos, size, 0, wxDefaultValidator, name) )
914 return false;
915
916 int x = pos.x == wxDefaultCoord ? 0 : pos.x,
917 y = pos.y == wxDefaultCoord ? 0 : pos.y,
918 w = size.x == wxDefaultCoord ? 1 : size.x,
919 h = size.y == wxDefaultCoord ? 22 : size.y;
920
921 // create the native WC_HEADER window:
922 WXHWND hwndParent = (HWND)parent->GetHandle();
923 WXDWORD msStyle = WS_CHILD | HDS_BUTTONS | HDS_HORZ | HDS_HOTTRACK | HDS_FULLDRAG;
924 m_hWnd = CreateWindowEx(0,
925 WC_HEADER,
926 (LPCTSTR) NULL,
927 msStyle,
928 x, y, w, h,
929 (HWND)hwndParent,
930 (HMENU)-1,
931 wxGetInstance(),
932 (LPVOID) NULL);
933 if (m_hWnd == NULL)
934 {
935 wxLogLastError(_T("CreateWindowEx"));
936 return false;
937 }
938
939 // we need to subclass the m_hWnd to force wxWindow::HandleNotify
940 // to call wxDataViewHeaderWindow::MSWOnNotify
941 SubclassWin(m_hWnd);
942
943 // the following is required to get the default win's font for
944 // header windows and must be done befor sending the HDM_LAYOUT msg
945 SetFont(GetFont());
946
947 RECT rcParent;
948 HDLAYOUT hdl;
949 WINDOWPOS wp;
950
951 // Retrieve the bounding rectangle of the parent window's
952 // client area, and then request size and position values
953 // from the header control.
954 ::GetClientRect((HWND)hwndParent, &rcParent);
955
956 hdl.prc = &rcParent;
957 hdl.pwpos = &wp;
958 if (!SendMessage((HWND)m_hWnd, HDM_LAYOUT, 0, (LPARAM) &hdl))
959 {
960 wxLogLastError(_T("SendMessage"));
961 return false;
962 }
963
964 // Set the size, position, and visibility of the header control.
965 SetWindowPos((HWND)m_hWnd,
966 wp.hwndInsertAfter,
967 wp.x, wp.y,
968 wp.cx, wp.cy,
969 wp.flags | SWP_SHOWWINDOW);
970
971 // set our size hints: wxDataViewCtrl will put this wxWindow inside
972 // a wxBoxSizer and in order to avoid super-big header windows,
973 // we need to set our height as fixed
974 SetMinSize(wxSize(-1, wp.cy));
975 SetMaxSize(wxSize(-1, wp.cy));
976
977 return true;
978 }
979
980 wxDataViewHeaderWindowMSW::~wxDataViewHeaderWindow()
981 {
982 UnsubclassWin();
983 }
984
985 void wxDataViewHeaderWindowMSW::UpdateDisplay()
986 {
987 // remove old columns
988 for (int j=0, max=Header_GetItemCount((HWND)m_hWnd); j < max; j++)
989 Header_DeleteItem((HWND)m_hWnd, 0);
990
991 // add the updated array of columns to the header control
992 unsigned int cols = GetOwner()->GetColumnCount();
993 unsigned int added = 0;
994 for (unsigned int i = 0; i < cols; i++)
995 {
996 wxDataViewColumn *col = GetColumn( i );
997 if (col->IsHidden())
998 continue; // don't add it!
999
1000 HDITEM hdi;
1001 hdi.mask = HDI_TEXT | HDI_FORMAT | HDI_WIDTH;
1002 hdi.pszText = (wxChar *) col->GetTitle().wx_str();
1003 hdi.cxy = col->GetWidth();
1004 hdi.cchTextMax = sizeof(hdi.pszText)/sizeof(hdi.pszText[0]);
1005 hdi.fmt = HDF_LEFT | HDF_STRING;
1006
1007 // lParam is reserved for application's use:
1008 // we store there the column index to use it later in MSWOnNotify
1009 // (since columns may have been hidden)
1010 hdi.lParam = (LPARAM)i;
1011
1012 // the native wxMSW implementation of the header window
1013 // draws the column separator COLUMN_WIDTH_OFFSET pixels
1014 // on the right: to correct this effect we make the column
1015 // exactly COLUMN_WIDTH_OFFSET wider (for the first column):
1016 if (i == 0)
1017 hdi.cxy += COLUMN_WIDTH_OFFSET;
1018
1019 switch (col->GetAlignment())
1020 {
1021 case wxALIGN_LEFT:
1022 hdi.fmt |= HDF_LEFT;
1023 break;
1024 case wxALIGN_CENTER:
1025 case wxALIGN_CENTER_HORIZONTAL:
1026 hdi.fmt |= HDF_CENTER;
1027 break;
1028 case wxALIGN_RIGHT:
1029 hdi.fmt |= HDF_RIGHT;
1030 break;
1031
1032 default:
1033 // such alignment is not allowed for the column header!
1034 wxFAIL;
1035 }
1036
1037 SendMessage((HWND)m_hWnd, HDM_INSERTITEM,
1038 (WPARAM)added, (LPARAM)&hdi);
1039 added++;
1040 }
1041 }
1042
1043 unsigned int wxDataViewHeaderWindowMSW::GetColumnIdxFromHeader(NMHEADER *nmHDR)
1044 {
1045 unsigned int idx;
1046
1047 // NOTE: we don't just return nmHDR->iItem because when there are
1048 // hidden columns, nmHDR->iItem may be different from
1049 // nmHDR->pitem->lParam
1050
1051 if (nmHDR->pitem && nmHDR->pitem->mask & HDI_LPARAM)
1052 {
1053 idx = (unsigned int)nmHDR->pitem->lParam;
1054 return idx;
1055 }
1056
1057 HDITEM item;
1058 item.mask = HDI_LPARAM;
1059 Header_GetItem((HWND)m_hWnd, nmHDR->iItem, &item);
1060
1061 return (unsigned int)item.lParam;
1062 }
1063
1064 bool wxDataViewHeaderWindowMSW::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
1065 {
1066 NMHDR *nmhdr = (NMHDR *)lParam;
1067
1068 // is it a message from the header?
1069 if ( nmhdr->hwndFrom != (HWND)m_hWnd )
1070 return wxWindow::MSWOnNotify(idCtrl, lParam, result);
1071
1072 NMHEADER *nmHDR = (NMHEADER *)nmhdr;
1073 switch ( nmhdr->code )
1074 {
1075 case HDN_BEGINTRACK:
1076 // user has started to resize a column:
1077 // do we need to veto it?
1078 if (!GetColumn(nmHDR->iItem)->IsResizeable())
1079 {
1080 // veto it!
1081 *result = TRUE;
1082 }
1083 break;
1084
1085 case HDN_BEGINDRAG:
1086 // user has started to reorder a column
1087 break;
1088
1089 case HDN_ITEMCHANGING:
1090 if (nmHDR->pitem != NULL &&
1091 (nmHDR->pitem->mask & HDI_WIDTH) != 0)
1092 {
1093 int minWidth = GetColumnFromHeader(nmHDR)->GetMinWidth();
1094 if (nmHDR->pitem->cxy < minWidth)
1095 {
1096 // do not allow the user to resize this column under
1097 // its minimal width:
1098 *result = TRUE;
1099 }
1100 }
1101 break;
1102
1103 case HDN_ITEMCHANGED: // user is resizing a column
1104 case HDN_ENDTRACK: // user has finished resizing a column
1105 case HDN_ENDDRAG: // user has finished reordering a column
1106
1107 // update the width of the modified column:
1108 if (nmHDR->pitem != NULL &&
1109 (nmHDR->pitem->mask & HDI_WIDTH) != 0)
1110 {
1111 unsigned int idx = GetColumnIdxFromHeader(nmHDR);
1112 unsigned int w = nmHDR->pitem->cxy;
1113 wxDataViewColumn *col = GetColumn(idx);
1114
1115 // see UpdateDisplay() for more info about COLUMN_WIDTH_OFFSET
1116 if (idx == 0 && w > COLUMN_WIDTH_OFFSET)
1117 w -= COLUMN_WIDTH_OFFSET;
1118
1119 if (w >= (unsigned)col->GetMinWidth())
1120 col->SetInternalWidth(w);
1121 }
1122 break;
1123
1124 case HDN_ITEMCLICK:
1125 {
1126 unsigned int idx = GetColumnIdxFromHeader(nmHDR);
1127 wxEventType evt = nmHDR->iButton == 0 ?
1128 wxEVT_COMMAND_DATAVIEW_COLUMN_HEADER_CLICK :
1129 wxEVT_COMMAND_DATAVIEW_COLUMN_HEADER_RIGHT_CLICK;
1130 SendEvent(evt, idx);
1131 }
1132 break;
1133
1134 case NM_RCLICK:
1135 {
1136 // NOTE: for some reason (i.e. for a bug in Windows)
1137 // the HDN_ITEMCLICK notification is not sent on
1138 // right clicks, so we need to handle NM_RCLICK
1139
1140 POINT ptClick;
1141 int column = wxMSWGetColumnClicked(nmhdr, &ptClick);
1142 if (column != wxNOT_FOUND)
1143 {
1144 HDITEM item;
1145 item.mask = HDI_LPARAM;
1146 Header_GetItem((HWND)m_hWnd, column, &item);
1147
1148 // 'idx' may be different from 'column' if there are
1149 // hidden columns...
1150 unsigned int idx = (unsigned int)item.lParam;
1151 SendEvent(wxEVT_COMMAND_DATAVIEW_COLUMN_HEADER_RIGHT_CLICK,
1152 idx);
1153 }
1154 }
1155 break;
1156
1157 case HDN_GETDISPINFOW:
1158 // see wxListCtrl::MSWOnNotify for more info!
1159 break;
1160
1161 case HDN_ITEMDBLCLICK:
1162 {
1163 unsigned int idx = GetColumnIdxFromHeader(nmHDR);
1164 int w = GetOwner()->GetBestColumnWidth(idx);
1165
1166 // update the native control:
1167 HDITEM hd;
1168 ZeroMemory(&hd, sizeof(hd));
1169 hd.mask = HDI_WIDTH;
1170 hd.cxy = w;
1171 Header_SetItem(GetHwnd(),
1172 nmHDR->iItem, // NOTE: we don't want 'idx' here!
1173 &hd);
1174
1175 // update the wxDataViewColumn class:
1176 GetColumn(idx)->SetInternalWidth(w);
1177 }
1178 break;
1179
1180 default:
1181 return wxWindow::MSWOnNotify(idCtrl, lParam, result);
1182 }
1183
1184 return true;
1185 }
1186
1187 void wxDataViewHeaderWindowMSW::ScrollWindow(int WXUNUSED(dx), int WXUNUSED(dy),
1188 const wxRect *WXUNUSED(rect))
1189 {
1190 wxSize ourSz = GetClientSize();
1191 wxSize ownerSz = m_owner->GetClientSize();
1192
1193 // where should the (logical) origin of this window be placed?
1194 int x1 = 0, y1 = 0;
1195 m_owner->CalcUnscrolledPosition(0, 0, &x1, &y1);
1196
1197 // put this window on top of our parent and
1198 SetWindowPos((HWND)m_hWnd, HWND_TOP, -x1, 0,
1199 ownerSz.GetWidth() + x1, ourSz.GetHeight(),
1200 SWP_SHOWWINDOW);
1201 }
1202
1203 void wxDataViewHeaderWindowMSW::DoSetSize(int WXUNUSED(x), int WXUNUSED(y),
1204 int WXUNUSED(w), int WXUNUSED(h),
1205 int WXUNUSED(f))
1206 {
1207 // the wxDataViewCtrl's internal wxBoxSizer will call this function when
1208 // the wxDataViewCtrl window gets resized: the following dummy call
1209 // to ScrollWindow() is required in order to get this header window
1210 // correctly repainted when it's (horizontally) scrolled:
1211
1212 ScrollWindow(0, 0);
1213 }
1214
1215 #else // !defined(__WXMSW__)
1216
1217 IMPLEMENT_ABSTRACT_CLASS(wxGenericDataViewHeaderWindow, wxWindow)
1218 BEGIN_EVENT_TABLE(wxGenericDataViewHeaderWindow, wxWindow)
1219 EVT_PAINT (wxGenericDataViewHeaderWindow::OnPaint)
1220 EVT_MOUSE_EVENTS (wxGenericDataViewHeaderWindow::OnMouse)
1221 EVT_SET_FOCUS (wxGenericDataViewHeaderWindow::OnSetFocus)
1222 END_EVENT_TABLE()
1223
1224 bool wxGenericDataViewHeaderWindow::Create(wxDataViewCtrl *parent, wxWindowID id,
1225 const wxPoint &pos, const wxSize &size,
1226 const wxString &name )
1227 {
1228 m_owner = parent;
1229
1230 if (!wxDataViewHeaderWindowBase::Create(parent, id, pos, size, name))
1231 return false;
1232
1233 wxVisualAttributes attr = wxPanel::GetClassDefaultAttributes();
1234 SetBackgroundStyle( wxBG_STYLE_CUSTOM );
1235 SetOwnForegroundColour( attr.colFg );
1236 SetOwnBackgroundColour( attr.colBg );
1237 if (!m_hasFont)
1238 SetOwnFont( attr.font );
1239
1240 // set our size hints: wxDataViewCtrl will put this wxWindow inside
1241 // a wxBoxSizer and in order to avoid super-big header windows,
1242 // we need to set our height as fixed
1243 SetMinSize(wxSize(-1, HEADER_WINDOW_HEIGHT));
1244 SetMaxSize(wxSize(-1, HEADER_WINDOW_HEIGHT));
1245
1246 return true;
1247 }
1248
1249 void wxGenericDataViewHeaderWindow::OnPaint( wxPaintEvent &WXUNUSED(event) )
1250 {
1251 int w, h;
1252 GetClientSize( &w, &h );
1253
1254 wxAutoBufferedPaintDC dc( this );
1255
1256 dc.SetBackground(GetBackgroundColour());
1257 dc.Clear();
1258
1259 int xpix;
1260 m_owner->GetScrollPixelsPerUnit( &xpix, NULL );
1261
1262 int x;
1263 m_owner->GetViewStart( &x, NULL );
1264
1265 // account for the horz scrollbar offset
1266 dc.SetDeviceOrigin( -x * xpix, 0 );
1267
1268 dc.SetFont( GetFont() );
1269
1270 unsigned int cols = GetOwner()->GetColumnCount();
1271 unsigned int i;
1272 int xpos = 0;
1273 for (i = 0; i < cols; i++)
1274 {
1275 wxDataViewColumn *col = GetColumn( i );
1276 if (col->IsHidden())
1277 continue; // skip it!
1278
1279 int cw = col->GetWidth();
1280 int ch = h;
1281
1282 wxRendererNative::Get().DrawHeaderButton
1283 (
1284 this,
1285 dc,
1286 wxRect(xpos, 0, cw, ch-1),
1287 m_parent->IsEnabled() ? 0
1288 : (int)wxCONTROL_DISABLED
1289 );
1290
1291 // align as required the column title:
1292 int x = xpos;
1293 wxSize titleSz = dc.GetTextExtent(col->GetTitle());
1294 switch (col->GetAlignment())
1295 {
1296 case wxALIGN_LEFT:
1297 x += HEADER_HORIZ_BORDER;
1298 break;
1299 case wxALIGN_CENTER:
1300 case wxALIGN_CENTER_HORIZONTAL:
1301 x += (cw - titleSz.GetWidth() - 2 * HEADER_HORIZ_BORDER)/2;
1302 break;
1303 case wxALIGN_RIGHT:
1304 x += cw - titleSz.GetWidth() - HEADER_HORIZ_BORDER;
1305 break;
1306 }
1307
1308 // always center the title vertically:
1309 int y = wxMax((ch - titleSz.GetHeight()) / 2, HEADER_VERT_BORDER);
1310
1311 dc.SetClippingRegion( xpos+HEADER_HORIZ_BORDER,
1312 HEADER_VERT_BORDER,
1313 wxMax(cw - 2 * HEADER_HORIZ_BORDER, 1), // width
1314 wxMax(ch - 2 * HEADER_VERT_BORDER, 1)); // height
1315 dc.DrawText( col->GetTitle(), x, y );
1316 dc.DestroyClippingRegion();
1317
1318 xpos += cw;
1319 }
1320 }
1321
1322 void wxGenericDataViewHeaderWindow::OnSetFocus( wxFocusEvent &event )
1323 {
1324 GetParent()->SetFocus();
1325 event.Skip();
1326 }
1327
1328 void wxGenericDataViewHeaderWindow::OnMouse( wxMouseEvent &event )
1329 {
1330 // we want to work with logical coords
1331 int x;
1332 m_owner->CalcUnscrolledPosition(event.GetX(), 0, &x, NULL);
1333 int y = event.GetY();
1334
1335 if (m_isDragging)
1336 {
1337 // we don't draw the line beyond our window,
1338 // but we allow dragging it there
1339 int w = 0;
1340 GetClientSize( &w, NULL );
1341 m_owner->CalcUnscrolledPosition(w, 0, &w, NULL);
1342 w -= 6;
1343
1344 // erase the line if it was drawn
1345 if (m_currentX < w)
1346 DrawCurrent();
1347
1348 if (event.ButtonUp())
1349 {
1350 m_isDragging = false;
1351 if (HasCapture())
1352 ReleaseMouse();
1353
1354 m_dirty = true;
1355
1356 GetColumn(m_column)->SetWidth(m_currentX - m_minX);
1357
1358 Refresh();
1359 GetOwner()->Refresh();
1360 }
1361 else
1362 {
1363 m_currentX = wxMax(m_minX + 7, x);
1364
1365 // draw in the new location
1366 if (m_currentX < w) DrawCurrent();
1367 }
1368
1369 }
1370 else // not dragging
1371 {
1372 m_minX = 0;
1373 m_column = wxNOT_FOUND;
1374
1375 bool hit_border = false;
1376
1377 // end of the current column
1378 int xpos = 0;
1379
1380 // find the column where this event occured
1381 int countCol = m_owner->GetColumnCount();
1382 for (int column = 0; column < countCol; column++)
1383 {
1384 wxDataViewColumn *p = GetColumn(column);
1385
1386 if (p->IsHidden())
1387 continue; // skip if not shown
1388
1389 xpos += p->GetWidth();
1390 m_column = column;
1391 if ((abs(x-xpos) < 3) && (y < 22))
1392 {
1393 hit_border = true;
1394 break;
1395 }
1396
1397 if (x < xpos)
1398 {
1399 // inside the column
1400 break;
1401 }
1402
1403 m_minX = xpos;
1404 }
1405
1406 if (m_column == wxNOT_FOUND)
1407 return;
1408
1409 bool resizeable = GetColumn(m_column)->IsResizeable();
1410 if (event.LeftDClick() && resizeable)
1411 {
1412 GetColumn(m_column)->SetWidth(GetOwner()->GetBestColumnWidth(m_column));
1413 Refresh();
1414 }
1415 else if (event.LeftDown() || event.RightUp())
1416 {
1417 if (hit_border && event.LeftDown() && resizeable)
1418 {
1419 m_isDragging = true;
1420 CaptureMouse();
1421 m_currentX = x;
1422 DrawCurrent();
1423 }
1424 else // click on a column
1425 {
1426 wxEventType evt = event.LeftDown() ?
1427 wxEVT_COMMAND_DATAVIEW_COLUMN_HEADER_CLICK :
1428 wxEVT_COMMAND_DATAVIEW_COLUMN_HEADER_RIGHT_CLICK;
1429 SendEvent(evt, m_column);
1430 }
1431 }
1432 else if (event.Moving())
1433 {
1434 if (hit_border && resizeable)
1435 m_currentCursor = m_resizeCursor;
1436 else
1437 m_currentCursor = wxSTANDARD_CURSOR;
1438
1439 SetCursor(*m_currentCursor);
1440 }
1441 }
1442 }
1443
1444 void wxGenericDataViewHeaderWindow::DrawCurrent()
1445 {
1446 int x1 = m_currentX;
1447 int y1 = 0;
1448 ClientToScreen (&x1, &y1);
1449
1450 int x2 = m_currentX-1;
1451 #ifdef __WXMSW__
1452 ++x2; // but why ????
1453 #endif
1454 int y2 = 0;
1455 m_owner->GetClientSize( NULL, &y2 );
1456 m_owner->ClientToScreen( &x2, &y2 );
1457
1458 wxScreenDC dc;
1459 dc.SetLogicalFunction(wxINVERT);
1460 dc.SetPen(m_penCurrent);
1461 dc.SetBrush(*wxTRANSPARENT_BRUSH);
1462 AdjustDC(dc);
1463 dc.DrawLine(x1, y1, x2, y2);
1464 }
1465
1466 void wxGenericDataViewHeaderWindow::AdjustDC(wxDC& dc)
1467 {
1468 int xpix, x;
1469
1470 m_owner->GetScrollPixelsPerUnit( &xpix, NULL );
1471 m_owner->GetViewStart( &x, NULL );
1472
1473 // shift the DC origin to match the position of the main window horizontal
1474 // scrollbar: this allows us to always use logical coords
1475 dc.SetDeviceOrigin( -x * xpix, 0 );
1476 }
1477
1478 #endif // defined(__WXMSW__)
1479
1480 //-----------------------------------------------------------------------------
1481 // wxDataViewRenameTimer
1482 //-----------------------------------------------------------------------------
1483
1484 wxDataViewRenameTimer::wxDataViewRenameTimer( wxDataViewMainWindow *owner )
1485 {
1486 m_owner = owner;
1487 }
1488
1489 void wxDataViewRenameTimer::Notify()
1490 {
1491 m_owner->OnRenameTimer();
1492 }
1493
1494 //-----------------------------------------------------------------------------
1495 // wxDataViewMainWindow
1496 //-----------------------------------------------------------------------------
1497
1498 int LINKAGEMODE wxDataViewSelectionCmp( unsigned int row1, unsigned int row2 )
1499 {
1500 if (row1 > row2) return 1;
1501 if (row1 == row2) return 0;
1502 return -1;
1503 }
1504
1505
1506 IMPLEMENT_ABSTRACT_CLASS(wxDataViewMainWindow, wxWindow)
1507
1508 BEGIN_EVENT_TABLE(wxDataViewMainWindow,wxWindow)
1509 EVT_PAINT (wxDataViewMainWindow::OnPaint)
1510 EVT_MOUSE_EVENTS (wxDataViewMainWindow::OnMouse)
1511 EVT_SET_FOCUS (wxDataViewMainWindow::OnSetFocus)
1512 EVT_KILL_FOCUS (wxDataViewMainWindow::OnKillFocus)
1513 EVT_CHAR (wxDataViewMainWindow::OnChar)
1514 END_EVENT_TABLE()
1515
1516 wxDataViewMainWindow::wxDataViewMainWindow( wxDataViewCtrl *parent, wxWindowID id,
1517 const wxPoint &pos, const wxSize &size, const wxString &name ) :
1518 wxWindow( parent, id, pos, size, wxWANTS_CHARS, name ),
1519 m_selection( wxDataViewSelectionCmp )
1520
1521 {
1522 SetOwner( parent );
1523
1524 m_lastOnSame = false;
1525 m_renameTimer = new wxDataViewRenameTimer( this );
1526
1527 // TODO: user better initial values/nothing selected
1528 m_currentCol = NULL;
1529 m_currentRow = 0;
1530
1531 // TODO: we need to calculate this smartly
1532 m_lineHeight =
1533 #ifdef __WXMSW__
1534 17;
1535 #else
1536 20;
1537 #endif
1538 wxASSERT(m_lineHeight > 2*PADDING_TOPBOTTOM);
1539
1540 m_dragCount = 0;
1541 m_dragStart = wxPoint(0,0);
1542 m_lineLastClicked = (unsigned int) -1;
1543 m_lineBeforeLastClicked = (unsigned int) -1;
1544 m_lineSelectSingleOnUp = (unsigned int) -1;
1545
1546 m_hasFocus = false;
1547
1548 SetBackgroundStyle( wxBG_STYLE_CUSTOM );
1549 SetBackgroundColour( *wxWHITE );
1550
1551 m_penRule = wxPen(GetRuleColour(), 1, wxSOLID);
1552
1553 UpdateDisplay();
1554 }
1555
1556 wxDataViewMainWindow::~wxDataViewMainWindow()
1557 {
1558 delete m_renameTimer;
1559 }
1560
1561 void wxDataViewMainWindow::OnRenameTimer()
1562 {
1563 // We have to call this here because changes may just have
1564 // been made and no screen update taken place.
1565 if ( m_dirty )
1566 wxSafeYield();
1567
1568 int xpos = 0;
1569 unsigned int cols = GetOwner()->GetColumnCount();
1570 unsigned int i;
1571 for (i = 0; i < cols; i++)
1572 {
1573 wxDataViewColumn *c = GetOwner()->GetColumn( i );
1574 if (c->IsHidden())
1575 continue; // skip it!
1576
1577 if (c == m_currentCol)
1578 break;
1579 xpos += c->GetWidth();
1580 }
1581 wxRect labelRect( xpos, m_currentRow * m_lineHeight,
1582 m_currentCol->GetWidth(), m_lineHeight );
1583
1584 GetOwner()->CalcScrolledPosition( labelRect.x, labelRect.y,
1585 &labelRect.x, &labelRect.y);
1586
1587 m_currentCol->GetRenderer()->StartEditing( m_currentRow, labelRect );
1588 }
1589
1590 bool wxDataViewMainWindow::RowAppended()
1591 {
1592 UpdateDisplay();
1593 return true;
1594 }
1595
1596 bool wxDataViewMainWindow::RowPrepended()
1597 {
1598 UpdateDisplay();
1599 return true;
1600 }
1601
1602 bool wxDataViewMainWindow::RowInserted( unsigned int WXUNUSED(before) )
1603 {
1604 UpdateDisplay();
1605 return true;
1606 }
1607
1608 bool wxDataViewMainWindow::RowDeleted( unsigned int WXUNUSED(row) )
1609 {
1610 UpdateDisplay();
1611 return true;
1612 }
1613
1614 bool wxDataViewMainWindow::RowChanged( unsigned int WXUNUSED(row) )
1615 {
1616 UpdateDisplay();
1617 return true;
1618 }
1619
1620 bool wxDataViewMainWindow::ValueChanged( unsigned int WXUNUSED(col), unsigned int row )
1621 {
1622 // NOTE: to be valid, we cannot use e.g. INT_MAX - 1
1623 #define MAX_VIRTUAL_WIDTH 100000
1624
1625 wxRect rect( 0, row*m_lineHeight, MAX_VIRTUAL_WIDTH, m_lineHeight );
1626 m_owner->CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
1627 Refresh( true, &rect );
1628
1629 return true;
1630 }
1631
1632 bool wxDataViewMainWindow::RowsReordered( unsigned int *WXUNUSED(new_order) )
1633 {
1634 UpdateDisplay();
1635 return true;
1636 }
1637
1638 bool wxDataViewMainWindow::Cleared()
1639 {
1640 UpdateDisplay();
1641 return true;
1642 }
1643
1644 void wxDataViewMainWindow::UpdateDisplay()
1645 {
1646 m_dirty = true;
1647 }
1648
1649 void wxDataViewMainWindow::OnInternalIdle()
1650 {
1651 wxWindow::OnInternalIdle();
1652
1653 if (m_dirty)
1654 {
1655 RecalculateDisplay();
1656 m_dirty = false;
1657 }
1658 }
1659
1660 void wxDataViewMainWindow::RecalculateDisplay()
1661 {
1662 wxDataViewListModel *model = GetOwner()->GetModel();
1663 if (!model)
1664 {
1665 Refresh();
1666 return;
1667 }
1668
1669 int width = GetEndOfLastCol();
1670 int height = model->GetRowCount() * m_lineHeight;
1671
1672 SetVirtualSize( width, height );
1673 GetOwner()->SetScrollRate( 10, m_lineHeight );
1674
1675 Refresh();
1676 }
1677
1678 void wxDataViewMainWindow::ScrollWindow( int dx, int dy, const wxRect *rect )
1679 {
1680 wxWindow::ScrollWindow( dx, dy, rect );
1681
1682 if (GetOwner()->m_headerArea)
1683 GetOwner()->m_headerArea->ScrollWindow( dx, 0 );
1684 }
1685
1686 void wxDataViewMainWindow::OnPaint( wxPaintEvent &WXUNUSED(event) )
1687 {
1688 wxDataViewListModel *model = GetOwner()->GetModel();
1689 wxAutoBufferedPaintDC dc( this );
1690
1691 // prepare the DC
1692 dc.SetBackground(GetBackgroundColour());
1693 dc.Clear();
1694 GetOwner()->PrepareDC( dc );
1695 dc.SetFont( GetFont() );
1696
1697 wxRect update = GetUpdateRegion().GetBox();
1698 m_owner->CalcUnscrolledPosition( update.x, update.y, &update.x, &update.y );
1699
1700 // compute which items needs to be redrawn
1701 unsigned int item_start = wxMax( 0, (update.y / m_lineHeight) );
1702 unsigned int item_count =
1703 wxMin( (int)(((update.y + update.height) / m_lineHeight) - item_start + 1),
1704 (int)(model->GetRowCount() - item_start) );
1705 unsigned int item_last = item_start + item_count;
1706
1707 // compute which columns needs to be redrawn
1708 unsigned int cols = GetOwner()->GetColumnCount();
1709 unsigned int col_start = 0;
1710 unsigned int x_start = 0;
1711 for (x_start = 0; col_start < cols; col_start++)
1712 {
1713 wxDataViewColumn *col = GetOwner()->GetColumn(col_start);
1714 if (col->IsHidden())
1715 continue; // skip it!
1716
1717 unsigned int w = col->GetWidth();
1718 if (x_start+w >= (unsigned int)update.x)
1719 break;
1720
1721 x_start += w;
1722 }
1723
1724 unsigned int col_last = col_start;
1725 unsigned int x_last = x_start;
1726 for (; col_last < cols; col_last++)
1727 {
1728 wxDataViewColumn *col = GetOwner()->GetColumn(col_last);
1729 if (col->IsHidden())
1730 continue; // skip it!
1731
1732 if (x_last > (unsigned int)update.GetRight())
1733 break;
1734
1735 x_last += col->GetWidth();
1736 }
1737
1738 // Draw horizontal rules if required
1739 if ( m_owner->HasFlag(wxDV_HORIZ_RULES) )
1740 {
1741 dc.SetPen(m_penRule);
1742 dc.SetBrush(*wxTRANSPARENT_BRUSH);
1743
1744 for (unsigned int i = item_start; i <= item_last+1; i++)
1745 {
1746 int y = i * m_lineHeight;
1747 dc.DrawLine(x_start, y, x_last, y);
1748 }
1749 }
1750
1751 // Draw vertical rules if required
1752 if ( m_owner->HasFlag(wxDV_VERT_RULES) )
1753 {
1754 dc.SetPen(m_penRule);
1755 dc.SetBrush(*wxTRANSPARENT_BRUSH);
1756
1757 int x = x_start;
1758 for (unsigned int i = col_start; i < col_last; i++)
1759 {
1760 wxDataViewColumn *col = GetOwner()->GetColumn(i);
1761 if (col->IsHidden())
1762 continue; // skip it
1763
1764 dc.DrawLine(x, item_start * m_lineHeight,
1765 x, item_last * m_lineHeight);
1766
1767 x += col->GetWidth();
1768 }
1769
1770 // Draw last vertical rule
1771 dc.DrawLine(x, item_start * m_lineHeight,
1772 x, item_last * m_lineHeight);
1773 }
1774
1775 // redraw the background for the items which are selected/current
1776 for (unsigned int item = item_start; item < item_last; item++)
1777 {
1778 bool selected = m_selection.Index( item ) != wxNOT_FOUND;
1779 if (selected || item == m_currentRow)
1780 {
1781 int flags = selected ? (int)wxCONTROL_SELECTED : 0;
1782 if (item == m_currentRow)
1783 flags |= wxCONTROL_CURRENT;
1784 if (m_hasFocus)
1785 flags |= wxCONTROL_FOCUSED;
1786
1787 wxRect rect( x_start, item*m_lineHeight, x_last, m_lineHeight );
1788 wxRendererNative::Get().DrawItemSelectionRect
1789 (
1790 this,
1791 dc,
1792 rect,
1793 flags
1794 );
1795 }
1796 }
1797
1798 // redraw all cells for all rows which must be repainted and for all columns
1799 wxRect cell_rect;
1800 cell_rect.x = x_start;
1801 cell_rect.height = m_lineHeight; // -1 is for the horizontal rules
1802 for (unsigned int i = col_start; i < col_last; i++)
1803 {
1804 wxDataViewColumn *col = GetOwner()->GetColumn( i );
1805 wxDataViewRenderer *cell = col->GetRenderer();
1806 cell_rect.width = col->GetWidth();
1807
1808 if (col->IsHidden())
1809 continue; // skipt it!
1810
1811 for (unsigned int item = item_start; item < item_last; item++)
1812 {
1813 // get the cell value and set it into the renderer
1814 wxVariant value;
1815 model->GetValue( value, col->GetModelColumn(), item );
1816 cell->SetValue( value );
1817
1818 // update the y offset
1819 cell_rect.y = item * m_lineHeight;
1820
1821 // cannot be bigger than allocated space
1822 wxSize size = cell->GetSize();
1823 size.x = wxMin( size.x + 2*PADDING_RIGHTLEFT, cell_rect.width );
1824 size.y = wxMin( size.y + 2*PADDING_TOPBOTTOM, cell_rect.height );
1825
1826 wxRect item_rect(cell_rect.GetTopLeft(), size);
1827 int align = cell->GetAlignment();
1828
1829 // horizontal alignment:
1830 item_rect.x = cell_rect.x;
1831 if (align & wxALIGN_CENTER_HORIZONTAL)
1832 item_rect.x = cell_rect.x + (cell_rect.width / 2) - (size.x / 2);
1833 else if (align & wxALIGN_RIGHT)
1834 item_rect.x = cell_rect.x + cell_rect.width - size.x;
1835 //else: wxALIGN_LEFT is the default
1836
1837 // vertical alignment:
1838 item_rect.y = cell_rect.y;
1839 if (align & wxALIGN_CENTER_VERTICAL)
1840 item_rect.y = cell_rect.y + (cell_rect.height / 2) - (size.y / 2);
1841 else if (align & wxALIGN_BOTTOM)
1842 item_rect.y = cell_rect.y + cell_rect.height - size.y;
1843 //else: wxALIGN_TOP is the default
1844
1845 // add padding
1846 item_rect.x += PADDING_RIGHTLEFT;
1847 item_rect.y += PADDING_TOPBOTTOM;
1848 item_rect.width = size.x - 2 * PADDING_RIGHTLEFT;
1849 item_rect.height = size.y - 2 * PADDING_TOPBOTTOM;
1850
1851 int state = 0;
1852 if (m_selection.Index(item) != wxNOT_FOUND)
1853 state |= wxDATAVIEW_CELL_SELECTED;
1854
1855 // TODO: it would be much more efficient to create a clipping
1856 // region for the entire column being rendered (in the OnPaint
1857 // of wxDataViewMainWindow) instead of a single clip region for
1858 // each cell. However it would mean that each renderer should
1859 // respect the given wxRect's top & bottom coords, eventually
1860 // violating only the left & right coords - however the user can
1861 // make its own renderer and thus we cannot be sure of that.
1862 dc.SetClippingRegion( item_rect );
1863 cell->Render( item_rect, &dc, state );
1864 dc.DestroyClippingRegion();
1865 }
1866
1867 cell_rect.x += cell_rect.width;
1868 }
1869 }
1870
1871 int wxDataViewMainWindow::GetCountPerPage() const
1872 {
1873 wxSize size = GetClientSize();
1874 return size.y / m_lineHeight;
1875 }
1876
1877 int wxDataViewMainWindow::GetEndOfLastCol() const
1878 {
1879 int width = 0;
1880 unsigned int i;
1881 for (i = 0; i < GetOwner()->GetColumnCount(); i++)
1882 {
1883 const wxDataViewColumn *c =
1884 wx_const_cast(wxDataViewCtrl*, GetOwner())->GetColumn( i );
1885
1886 if (!c->IsHidden())
1887 width += c->GetWidth();
1888 }
1889 return width;
1890 }
1891
1892 unsigned int wxDataViewMainWindow::GetFirstVisibleRow() const
1893 {
1894 int x = 0;
1895 int y = 0;
1896 m_owner->CalcUnscrolledPosition( x, y, &x, &y );
1897
1898 return y / m_lineHeight;
1899 }
1900
1901 unsigned int wxDataViewMainWindow::GetLastVisibleRow() const
1902 {
1903 wxSize client_size = GetClientSize();
1904 m_owner->CalcUnscrolledPosition( client_size.x, client_size.y,
1905 &client_size.x, &client_size.y );
1906
1907 return wxMin( GetRowCount()-1, ((unsigned)client_size.y/m_lineHeight)+1 );
1908 }
1909
1910 unsigned int wxDataViewMainWindow::GetRowCount() const
1911 {
1912 return wx_const_cast(wxDataViewCtrl*, GetOwner())->GetModel()->GetRowCount();
1913 }
1914
1915 void wxDataViewMainWindow::ChangeCurrentRow( unsigned int row )
1916 {
1917 m_currentRow = row;
1918
1919 // send event
1920 }
1921
1922 void wxDataViewMainWindow::SelectAllRows( bool on )
1923 {
1924 if (IsEmpty())
1925 return;
1926
1927 if (on)
1928 {
1929 m_selection.Clear();
1930 for (unsigned int i = 0; i < GetRowCount(); i++)
1931 m_selection.Add( i );
1932 Refresh();
1933 }
1934 else
1935 {
1936 unsigned int first_visible = GetFirstVisibleRow();
1937 unsigned int last_visible = GetLastVisibleRow();
1938 unsigned int i;
1939 for (i = 0; i < m_selection.GetCount(); i++)
1940 {
1941 unsigned int row = m_selection[i];
1942 if ((row >= first_visible) && (row <= last_visible))
1943 RefreshRow( row );
1944 }
1945 m_selection.Clear();
1946 }
1947 }
1948
1949 void wxDataViewMainWindow::SelectRow( unsigned int row, bool on )
1950 {
1951 if (m_selection.Index( row ) == wxNOT_FOUND)
1952 {
1953 if (on)
1954 {
1955 m_selection.Add( row );
1956 RefreshRow( row );
1957 }
1958 }
1959 else
1960 {
1961 if (!on)
1962 {
1963 m_selection.Remove( row );
1964 RefreshRow( row );
1965 }
1966 }
1967 }
1968
1969 void wxDataViewMainWindow::SelectRows( unsigned int from, unsigned int to, bool on )
1970 {
1971 if (from > to)
1972 {
1973 unsigned int tmp = from;
1974 from = to;
1975 to = tmp;
1976 }
1977
1978 unsigned int i;
1979 for (i = from; i <= to; i++)
1980 {
1981 if (m_selection.Index( i ) == wxNOT_FOUND)
1982 {
1983 if (on)
1984 m_selection.Add( i );
1985 }
1986 else
1987 {
1988 if (!on)
1989 m_selection.Remove( i );
1990 }
1991 }
1992 RefreshRows( from, to );
1993 }
1994
1995 void wxDataViewMainWindow::Select( const wxArrayInt& aSelections )
1996 {
1997 for (size_t i=0; i < aSelections.GetCount(); i++)
1998 {
1999 int n = aSelections[i];
2000
2001 m_selection.Add( n );
2002 RefreshRow( n );
2003 }
2004 }
2005
2006 void wxDataViewMainWindow::ReverseRowSelection( unsigned int row )
2007 {
2008 if (m_selection.Index( row ) == wxNOT_FOUND)
2009 m_selection.Add( row );
2010 else
2011 m_selection.Remove( row );
2012 RefreshRow( row );
2013 }
2014
2015 bool wxDataViewMainWindow::IsRowSelected( unsigned int row )
2016 {
2017 return (m_selection.Index( row ) != wxNOT_FOUND);
2018 }
2019
2020 void wxDataViewMainWindow::RefreshRow( unsigned int row )
2021 {
2022 wxRect rect( 0, row*m_lineHeight, GetEndOfLastCol(), m_lineHeight );
2023 m_owner->CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
2024
2025 wxSize client_size = GetClientSize();
2026 wxRect client_rect( 0, 0, client_size.x, client_size.y );
2027 wxRect intersect_rect = client_rect.Intersect( rect );
2028 if (intersect_rect.width > 0)
2029 Refresh( true, &intersect_rect );
2030 }
2031
2032 void wxDataViewMainWindow::RefreshRows( unsigned int from, unsigned int to )
2033 {
2034 if (from > to)
2035 {
2036 unsigned int tmp = to;
2037 to = from;
2038 from = tmp;
2039 }
2040
2041 wxRect rect( 0, from*m_lineHeight, GetEndOfLastCol(), (to-from+1) * m_lineHeight );
2042 m_owner->CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
2043
2044 wxSize client_size = GetClientSize();
2045 wxRect client_rect( 0, 0, client_size.x, client_size.y );
2046 wxRect intersect_rect = client_rect.Intersect( rect );
2047 if (intersect_rect.width > 0)
2048 Refresh( true, &intersect_rect );
2049 }
2050
2051 void wxDataViewMainWindow::RefreshRowsAfter( unsigned int firstRow )
2052 {
2053 unsigned int count = GetRowCount();
2054 if (firstRow > count)
2055 return;
2056
2057 wxRect rect( 0, firstRow*m_lineHeight, GetEndOfLastCol(), count * m_lineHeight );
2058 m_owner->CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
2059
2060 wxSize client_size = GetClientSize();
2061 wxRect client_rect( 0, 0, client_size.x, client_size.y );
2062 wxRect intersect_rect = client_rect.Intersect( rect );
2063 if (intersect_rect.width > 0)
2064 Refresh( true, &intersect_rect );
2065 }
2066
2067 void wxDataViewMainWindow::OnArrowChar(unsigned int newCurrent, const wxKeyEvent& event)
2068 {
2069 wxCHECK_RET( newCurrent < GetRowCount(),
2070 _T("invalid item index in OnArrowChar()") );
2071
2072 // if there is no selection, we cannot move it anywhere
2073 if (!HasCurrentRow())
2074 return;
2075
2076 unsigned int oldCurrent = m_currentRow;
2077
2078 // in single selection we just ignore Shift as we can't select several
2079 // items anyhow
2080 if ( event.ShiftDown() && !IsSingleSel() )
2081 {
2082 RefreshRow( oldCurrent );
2083
2084 ChangeCurrentRow( newCurrent );
2085
2086 // select all the items between the old and the new one
2087 if ( oldCurrent > newCurrent )
2088 {
2089 newCurrent = oldCurrent;
2090 oldCurrent = m_currentRow;
2091 }
2092
2093 SelectRows( oldCurrent, newCurrent, true );
2094 }
2095 else // !shift
2096 {
2097 RefreshRow( oldCurrent );
2098
2099 // all previously selected items are unselected unless ctrl is held
2100 if ( !event.ControlDown() )
2101 SelectAllRows(false);
2102
2103 ChangeCurrentRow( newCurrent );
2104
2105 if ( !event.ControlDown() )
2106 SelectRow( m_currentRow, true );
2107 else
2108 RefreshRow( m_currentRow );
2109 }
2110
2111 //EnsureVisible( m_currentRow );
2112 }
2113
2114 wxRect wxDataViewMainWindow::GetLineRect( unsigned int row ) const
2115 {
2116 wxRect rect;
2117 rect.x = 0;
2118 rect.y = m_lineHeight * row;
2119 rect.width = GetEndOfLastCol();
2120 rect.height = m_lineHeight;
2121
2122 return rect;
2123 }
2124
2125 void wxDataViewMainWindow::OnChar( wxKeyEvent &event )
2126 {
2127 if (event.GetKeyCode() == WXK_TAB)
2128 {
2129 wxNavigationKeyEvent nevent;
2130 nevent.SetWindowChange( event.ControlDown() );
2131 nevent.SetDirection( !event.ShiftDown() );
2132 nevent.SetEventObject( GetParent()->GetParent() );
2133 nevent.SetCurrentFocus( m_parent );
2134 if (GetParent()->GetParent()->GetEventHandler()->ProcessEvent( nevent ))
2135 return;
2136 }
2137
2138 // no item -> nothing to do
2139 if (!HasCurrentRow())
2140 {
2141 event.Skip();
2142 return;
2143 }
2144
2145 // don't use m_linesPerPage directly as it might not be computed yet
2146 const int pageSize = GetCountPerPage();
2147 wxCHECK_RET( pageSize, _T("should have non zero page size") );
2148
2149 switch ( event.GetKeyCode() )
2150 {
2151 case WXK_UP:
2152 if ( m_currentRow > 0 )
2153 OnArrowChar( m_currentRow - 1, event );
2154 break;
2155
2156 case WXK_DOWN:
2157 if ( m_currentRow < GetRowCount() - 1 )
2158 OnArrowChar( m_currentRow + 1, event );
2159 break;
2160
2161 case WXK_END:
2162 if (!IsEmpty())
2163 OnArrowChar( GetRowCount() - 1, event );
2164 break;
2165
2166 case WXK_HOME:
2167 if (!IsEmpty())
2168 OnArrowChar( 0, event );
2169 break;
2170
2171 case WXK_PAGEUP:
2172 {
2173 int steps = pageSize - 1;
2174 int index = m_currentRow - steps;
2175 if (index < 0)
2176 index = 0;
2177
2178 OnArrowChar( index, event );
2179 }
2180 break;
2181
2182 case WXK_PAGEDOWN:
2183 {
2184 int steps = pageSize - 1;
2185 unsigned int index = m_currentRow + steps;
2186 unsigned int count = GetRowCount();
2187 if ( index >= count )
2188 index = count - 1;
2189
2190 OnArrowChar( index, event );
2191 }
2192 break;
2193
2194 default:
2195 event.Skip();
2196 }
2197 }
2198
2199 void wxDataViewMainWindow::OnMouse( wxMouseEvent &event )
2200 {
2201 if (event.GetEventType() == wxEVT_MOUSEWHEEL)
2202 {
2203 // let the base handle mouse wheel events.
2204 event.Skip();
2205 return;
2206 }
2207
2208 int x = event.GetX();
2209 int y = event.GetY();
2210 m_owner->CalcUnscrolledPosition( x, y, &x, &y );
2211
2212 wxDataViewColumn *col = NULL;
2213
2214 int xpos = 0;
2215 unsigned int cols = GetOwner()->GetColumnCount();
2216 unsigned int i;
2217 for (i = 0; i < cols; i++)
2218 {
2219 wxDataViewColumn *c = GetOwner()->GetColumn( i );
2220 if (c->IsHidden())
2221 continue; // skip it!
2222
2223 if (x < xpos + c->GetWidth())
2224 {
2225 col = c;
2226 break;
2227 }
2228 xpos += c->GetWidth();
2229 }
2230 if (!col)
2231 return;
2232 wxDataViewRenderer *cell = col->GetRenderer();
2233
2234 unsigned int current = y / m_lineHeight;
2235
2236 if ((current > GetRowCount()) || (x > GetEndOfLastCol()))
2237 {
2238 // Unselect all if below the last row ?
2239 return;
2240 }
2241
2242 wxDataViewListModel *model = GetOwner()->GetModel();
2243
2244 if (event.Dragging())
2245 {
2246 if (m_dragCount == 0)
2247 {
2248 // we have to report the raw, physical coords as we want to be
2249 // able to call HitTest(event.m_pointDrag) from the user code to
2250 // get the item being dragged
2251 m_dragStart = event.GetPosition();
2252 }
2253
2254 m_dragCount++;
2255
2256 if (m_dragCount != 3)
2257 return;
2258
2259 if (event.LeftIsDown())
2260 {
2261 // Notify cell about drag
2262 }
2263 return;
2264 }
2265 else
2266 {
2267 m_dragCount = 0;
2268 }
2269
2270 bool forceClick = false;
2271
2272 if (event.ButtonDClick())
2273 {
2274 m_renameTimer->Stop();
2275 m_lastOnSame = false;
2276 }
2277
2278 if (event.LeftDClick())
2279 {
2280 if ( current == m_lineLastClicked )
2281 {
2282 if (cell->GetMode() == wxDATAVIEW_CELL_ACTIVATABLE)
2283 {
2284 wxVariant value;
2285 model->GetValue( value, col->GetModelColumn(), current );
2286 cell->SetValue( value );
2287 wxRect cell_rect( xpos, current * m_lineHeight,
2288 col->GetWidth(), m_lineHeight );
2289 cell->Activate( cell_rect, model, col->GetModelColumn(), current );
2290 }
2291 return;
2292 }
2293 else
2294 {
2295 // The first click was on another item, so don't interpret this as
2296 // a double click, but as a simple click instead
2297 forceClick = true;
2298 }
2299 }
2300
2301 if (event.LeftUp())
2302 {
2303 if (m_lineSelectSingleOnUp != (unsigned int)-1)
2304 {
2305 // select single line
2306 SelectAllRows( false );
2307 SelectRow( m_lineSelectSingleOnUp, true );
2308 }
2309
2310 if (m_lastOnSame)
2311 {
2312 if ((col == m_currentCol) && (current == m_currentRow) &&
2313 (cell->GetMode() == wxDATAVIEW_CELL_EDITABLE) )
2314 {
2315 m_renameTimer->Start( 100, true );
2316 }
2317 }
2318
2319 m_lastOnSame = false;
2320 m_lineSelectSingleOnUp = (unsigned int)-1;
2321 }
2322 else
2323 {
2324 // This is necessary, because after a DnD operation in
2325 // from and to ourself, the up event is swallowed by the
2326 // DnD code. So on next non-up event (which means here and
2327 // now) m_lineSelectSingleOnUp should be reset.
2328 m_lineSelectSingleOnUp = (unsigned int)-1;
2329 }
2330
2331 if (event.RightDown())
2332 {
2333 m_lineBeforeLastClicked = m_lineLastClicked;
2334 m_lineLastClicked = current;
2335
2336 // If the item is already selected, do not update the selection.
2337 // Multi-selections should not be cleared if a selected item is clicked.
2338 if (!IsRowSelected(current))
2339 {
2340 SelectAllRows(false);
2341 ChangeCurrentRow(current);
2342 SelectRow(m_currentRow,true);
2343 }
2344
2345 // notify cell about right click
2346 // cell->...
2347
2348 // Allow generation of context menu event
2349 event.Skip();
2350 }
2351 else if (event.MiddleDown())
2352 {
2353 // notify cell about middle click
2354 // cell->...
2355 }
2356 if (event.LeftDown() || forceClick)
2357 {
2358 #ifdef __WXMSW__
2359 SetFocus();
2360 #endif
2361
2362 m_lineBeforeLastClicked = m_lineLastClicked;
2363 m_lineLastClicked = current;
2364
2365 unsigned int oldCurrentRow = m_currentRow;
2366 bool oldWasSelected = IsRowSelected(m_currentRow);
2367
2368 bool cmdModifierDown = event.CmdDown();
2369 if ( IsSingleSel() || !(cmdModifierDown || event.ShiftDown()) )
2370 {
2371 if ( IsSingleSel() || !IsRowSelected(current) )
2372 {
2373 SelectAllRows( false );
2374
2375 ChangeCurrentRow(current);
2376
2377 SelectRow(m_currentRow,true);
2378 }
2379 else // multi sel & current is highlighted & no mod keys
2380 {
2381 m_lineSelectSingleOnUp = current;
2382 ChangeCurrentRow(current); // change focus
2383 }
2384 }
2385 else // multi sel & either ctrl or shift is down
2386 {
2387 if (cmdModifierDown)
2388 {
2389 ChangeCurrentRow(current);
2390
2391 ReverseRowSelection(m_currentRow);
2392 }
2393 else if (event.ShiftDown())
2394 {
2395 ChangeCurrentRow(current);
2396
2397 unsigned int lineFrom = oldCurrentRow,
2398 lineTo = current;
2399
2400 if ( lineTo < lineFrom )
2401 {
2402 lineTo = lineFrom;
2403 lineFrom = m_currentRow;
2404 }
2405
2406 SelectRows(lineFrom, lineTo, true);
2407 }
2408 else // !ctrl, !shift
2409 {
2410 // test in the enclosing if should make it impossible
2411 wxFAIL_MSG( _T("how did we get here?") );
2412 }
2413 }
2414
2415 if (m_currentRow != oldCurrentRow)
2416 RefreshRow( oldCurrentRow );
2417
2418 wxDataViewColumn *oldCurrentCol = m_currentCol;
2419
2420 // Update selection here...
2421 m_currentCol = col;
2422
2423 m_lastOnSame = !forceClick && ((col == oldCurrentCol) &&
2424 (current == oldCurrentRow)) && oldWasSelected;
2425 }
2426 }
2427
2428 void wxDataViewMainWindow::OnSetFocus( wxFocusEvent &event )
2429 {
2430 m_hasFocus = true;
2431
2432 if (HasCurrentRow())
2433 Refresh();
2434
2435 event.Skip();
2436 }
2437
2438 void wxDataViewMainWindow::OnKillFocus( wxFocusEvent &event )
2439 {
2440 m_hasFocus = false;
2441
2442 if (HasCurrentRow())
2443 Refresh();
2444
2445 event.Skip();
2446 }
2447
2448 //-----------------------------------------------------------------------------
2449 // wxDataViewCtrl
2450 //-----------------------------------------------------------------------------
2451
2452 IMPLEMENT_DYNAMIC_CLASS(wxDataViewCtrl, wxDataViewCtrlBase)
2453
2454 BEGIN_EVENT_TABLE(wxDataViewCtrl, wxDataViewCtrlBase)
2455 EVT_SIZE(wxDataViewCtrl::OnSize)
2456 END_EVENT_TABLE()
2457
2458 wxDataViewCtrl::~wxDataViewCtrl()
2459 {
2460 if (m_notifier)
2461 GetModel()->RemoveNotifier( m_notifier );
2462 }
2463
2464 void wxDataViewCtrl::Init()
2465 {
2466 m_notifier = NULL;
2467 }
2468
2469 bool wxDataViewCtrl::Create(wxWindow *parent, wxWindowID id,
2470 const wxPoint& pos, const wxSize& size,
2471 long style, const wxValidator& validator )
2472 {
2473 if (!wxControl::Create( parent, id, pos, size,
2474 style | wxScrolledWindowStyle|wxSUNKEN_BORDER, validator))
2475 return false;
2476
2477 Init();
2478
2479 #ifdef __WXMAC__
2480 MacSetClipChildren( true ) ;
2481 #endif
2482
2483 m_clientArea = new wxDataViewMainWindow( this, wxID_ANY );
2484
2485 if (HasFlag(wxDV_NO_HEADER))
2486 m_headerArea = NULL;
2487 else
2488 m_headerArea = new wxDataViewHeaderWindow( this, wxID_ANY );
2489
2490 SetTargetWindow( m_clientArea );
2491
2492 wxBoxSizer *sizer = new wxBoxSizer( wxVERTICAL );
2493 if (m_headerArea)
2494 sizer->Add( m_headerArea, 0, wxGROW );
2495 sizer->Add( m_clientArea, 1, wxGROW );
2496 SetSizer( sizer );
2497
2498 return true;
2499 }
2500
2501 #ifdef __WXMSW__
2502 WXLRESULT wxDataViewCtrl::MSWWindowProc(WXUINT nMsg,
2503 WXWPARAM wParam,
2504 WXLPARAM lParam)
2505 {
2506 WXLRESULT rc = wxDataViewCtrlBase::MSWWindowProc(nMsg, wParam, lParam);
2507
2508 #ifndef __WXWINCE__
2509 // we need to process arrows ourselves for scrolling
2510 if ( nMsg == WM_GETDLGCODE )
2511 {
2512 rc |= DLGC_WANTARROWS;
2513 }
2514 #endif
2515
2516 return rc;
2517 }
2518 #endif
2519
2520 void wxDataViewCtrl::OnSize( wxSizeEvent &WXUNUSED(event) )
2521 {
2522 // We need to override OnSize so that our scrolled
2523 // window a) does call Layout() to use sizers for
2524 // positioning the controls but b) does not query
2525 // the sizer for their size and use that for setting
2526 // the scrollable area as set that ourselves by
2527 // calling SetScrollbar() further down.
2528
2529 Layout();
2530
2531 AdjustScrollbars();
2532 }
2533
2534 bool wxDataViewCtrl::AssociateModel( wxDataViewListModel *model )
2535 {
2536 if (!wxDataViewCtrlBase::AssociateModel( model ))
2537 return false;
2538
2539 m_notifier = new wxGenericDataViewListModelNotifier( m_clientArea );
2540
2541 model->AddNotifier( m_notifier );
2542
2543 m_clientArea->UpdateDisplay();
2544
2545 return true;
2546 }
2547
2548 bool wxDataViewCtrl::AppendColumn( wxDataViewColumn *col )
2549 {
2550 if (!wxDataViewCtrlBase::AppendColumn(col))
2551 return false;
2552
2553 OnColumnChange();
2554 return true;
2555 }
2556
2557 void wxDataViewCtrl::OnColumnChange()
2558 {
2559 if (m_headerArea)
2560 m_headerArea->UpdateDisplay();
2561
2562 m_clientArea->UpdateDisplay();
2563 }
2564
2565 void wxDataViewCtrl::SetSelection( int row )
2566 {
2567 m_clientArea->SelectRow(row, true);
2568 }
2569
2570 void wxDataViewCtrl::SetSelectionRange( unsigned int from, unsigned int to )
2571 {
2572 m_clientArea->SelectRows(from, to, true);
2573 }
2574
2575 void wxDataViewCtrl::SetSelections( const wxArrayInt& aSelections )
2576 {
2577 m_clientArea->Select(aSelections);
2578 }
2579
2580 void wxDataViewCtrl::Unselect( unsigned int WXUNUSED(row) )
2581 {
2582 // FIXME - TODO
2583 }
2584
2585 bool wxDataViewCtrl::IsSelected( unsigned int WXUNUSED(row) ) const
2586 {
2587 // FIXME - TODO
2588
2589 return false;
2590 }
2591
2592 int wxDataViewCtrl::GetSelection() const
2593 {
2594 // FIXME - TODO
2595
2596 return -1;
2597 }
2598
2599 int wxDataViewCtrl::GetSelections(wxArrayInt& WXUNUSED(aSelections) ) const
2600 {
2601 // FIXME - TODO
2602
2603 return 0;
2604 }
2605
2606 #endif
2607 // !wxUSE_GENERICDATAVIEWCTRL
2608
2609 #endif
2610 // wxUSE_DATAVIEWCTRL