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