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