]> git.saurik.com Git - wxWidgets.git/blame_incremental - src/generic/datavgen.cpp
compilation fix: SetLogicalFunction() is defined in dc.cpp, don't define it here
[wxWidgets.git] / src / generic / datavgen.cpp
... / ...
CommitLineData
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
50class wxDataViewCtrl;
51
52static const int SCROLL_UNIT_X = 15;
53
54// the cell padding on the left/right
55static const int PADDING_RIGHTLEFT = 3;
56
57// the cell padding on the top/bottom
58static 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
69class WXDLLIMPEXP_ADV wxDataViewHeaderWindowBase : public wxControl
70{
71public:
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
98protected:
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
111class wxDataViewHeaderWindowMSW : public wxDataViewHeaderWindowBase
112{
113public:
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
137protected:
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
146private:
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
157class wxGenericDataViewHeaderWindow : public wxDataViewHeaderWindowBase
158{
159public:
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
188protected:
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
228private:
229 DECLARE_DYNAMIC_CLASS(wxGenericDataViewHeaderWindow)
230 DECLARE_EVENT_TABLE()
231};
232
233#endif // defined(__WXMSW__)
234
235//-----------------------------------------------------------------------------
236// wxDataViewRenameTimer
237//-----------------------------------------------------------------------------
238
239class wxDataViewRenameTimer: public wxTimer
240{
241private:
242 wxDataViewMainWindow *m_owner;
243
244public:
245 wxDataViewRenameTimer( wxDataViewMainWindow *owner );
246 void Notify();
247};
248
249//-----------------------------------------------------------------------------
250// wxDataViewMainWindow
251//-----------------------------------------------------------------------------
252
253WX_DEFINE_SORTED_USER_EXPORTED_ARRAY_SIZE_T(unsigned int, wxDataViewSelection,
254 WXDLLIMPEXP_ADV);
255
256class wxDataViewMainWindow: public wxWindow
257{
258public:
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
327private:
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
352private:
353 DECLARE_DYNAMIC_CLASS(wxDataViewMainWindow)
354 DECLARE_EVENT_TABLE()
355};
356
357// ---------------------------------------------------------
358// wxGenericDataViewListModelNotifier
359// ---------------------------------------------------------
360
361class wxGenericDataViewListModelNotifier: public wxDataViewListModelNotifier
362{
363public:
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
391IMPLEMENT_ABSTRACT_CLASS(wxDataViewRenderer, wxDataViewRendererBase)
392
393wxDataViewRenderer::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
403wxDataViewRenderer::~wxDataViewRenderer()
404{
405 if (m_dc)
406 delete m_dc;
407}
408
409wxDC *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
427IMPLEMENT_ABSTRACT_CLASS(wxDataViewCustomRenderer, wxDataViewRenderer)
428
429wxDataViewCustomRenderer::wxDataViewCustomRenderer( const wxString &varianttype,
430 wxDataViewCellMode mode, int align ) :
431 wxDataViewRenderer( varianttype, mode, align )
432{
433}
434
435// ---------------------------------------------------------
436// wxDataViewTextRenderer
437// ---------------------------------------------------------
438
439IMPLEMENT_CLASS(wxDataViewTextRenderer, wxDataViewCustomRenderer)
440
441wxDataViewTextRenderer::wxDataViewTextRenderer( const wxString &varianttype,
442 wxDataViewCellMode mode, int align ) :
443 wxDataViewCustomRenderer( varianttype, mode, align )
444{
445}
446
447bool wxDataViewTextRenderer::SetValue( const wxVariant &value )
448{
449 m_text = value.GetString();
450
451 return true;
452}
453
454bool wxDataViewTextRenderer::GetValue( wxVariant& WXUNUSED(value) ) const
455{
456 return false;
457}
458
459bool wxDataViewTextRenderer::HasEditorCtrl()
460{
461 return true;
462}
463
464wxControl* 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
472bool wxDataViewTextRenderer::GetValueFromEditorCtrl( wxControl *editor, wxVariant &value )
473{
474 wxTextCtrl *text = (wxTextCtrl*) editor;
475 value = text->GetValue();
476 return true;
477}
478
479bool 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
492wxSize 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
508IMPLEMENT_CLASS(wxDataViewBitmapRenderer, wxDataViewCustomRenderer)
509
510wxDataViewBitmapRenderer::wxDataViewBitmapRenderer( const wxString &varianttype,
511 wxDataViewCellMode mode, int align ) :
512 wxDataViewCustomRenderer( varianttype, mode, align )
513{
514}
515
516bool 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
526bool wxDataViewBitmapRenderer::GetValue( wxVariant& WXUNUSED(value) ) const
527{
528 return false;
529}
530
531bool 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
541wxSize 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
555IMPLEMENT_ABSTRACT_CLASS(wxDataViewToggleRenderer, wxDataViewCustomRenderer)
556
557wxDataViewToggleRenderer::wxDataViewToggleRenderer( const wxString &varianttype,
558 wxDataViewCellMode mode, int align ) :
559 wxDataViewCustomRenderer( varianttype, mode, align )
560{
561 m_toggle = false;
562}
563
564bool wxDataViewToggleRenderer::SetValue( const wxVariant &value )
565{
566 m_toggle = value.GetBool();
567
568 return true;
569}
570
571bool wxDataViewToggleRenderer::GetValue( wxVariant &WXUNUSED(value) ) const
572{
573 return false;
574}
575
576bool 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
601bool 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
612wxSize wxDataViewToggleRenderer::GetSize() const
613{
614 return wxSize(20,20);
615}
616
617// ---------------------------------------------------------
618// wxDataViewProgressRenderer
619// ---------------------------------------------------------
620
621IMPLEMENT_ABSTRACT_CLASS(wxDataViewProgressRenderer, wxDataViewCustomRenderer)
622
623wxDataViewProgressRenderer::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
631wxDataViewProgressRenderer::~wxDataViewProgressRenderer()
632{
633}
634
635bool 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
645bool wxDataViewProgressRenderer::GetValue( wxVariant &value ) const
646{
647 value = (long) m_value;
648 return true;
649}
650
651bool 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
667wxSize 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
680class wxDataViewDateRendererPopupTransient: public wxPopupTransientWindow
681{
682public:
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
704protected:
705 virtual void OnDismiss()
706 {
707 }
708
709private:
710 DECLARE_EVENT_TABLE()
711};
712
713BEGIN_EVENT_TABLE(wxDataViewDateRendererPopupTransient,wxPopupTransientWindow)
714 EVT_CALENDAR( wxID_ANY, wxDataViewDateRendererPopupTransient::OnCalendar )
715END_EVENT_TABLE()
716
717void 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
728IMPLEMENT_ABSTRACT_CLASS(wxDataViewDateRenderer, wxDataViewCustomRenderer)
729
730wxDataViewDateRenderer::wxDataViewDateRenderer( const wxString &varianttype,
731 wxDataViewCellMode mode, int align ) :
732 wxDataViewCustomRenderer( varianttype, mode, align )
733{
734}
735
736bool wxDataViewDateRenderer::SetValue( const wxVariant &value )
737{
738 m_date = value.GetDateTime();
739
740 return true;
741}
742
743bool wxDataViewDateRenderer::GetValue( wxVariant &value ) const
744{
745 value = m_date;
746 return true;
747}
748
749bool 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
758wxSize 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
767bool 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
791IMPLEMENT_ABSTRACT_CLASS(wxDataViewColumn, wxDataViewColumnBase)
792
793wxDataViewColumn::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
805wxDataViewColumn::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
816wxDataViewColumn::~wxDataViewColumn()
817{
818}
819
820void wxDataViewColumn::Init( int width )
821{
822 m_width = width;
823 m_minWidth = wxDVC_DEFAULT_MINWIDTH;
824 m_ascending = true;
825}
826
827void wxDataViewColumn::SetResizeable( bool resizeable )
828{
829 if (resizeable)
830 m_flags |= wxDATAVIEW_COL_RESIZABLE;
831 else
832 m_flags &= ~wxDATAVIEW_COL_RESIZABLE;
833}
834
835void 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
847void 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
859void wxDataViewColumn::SetSortOrder( bool ascending )
860{
861 m_ascending = ascending;
862
863 // Update header button
864 if (GetOwner())
865 GetOwner()->OnColumnChange();
866}
867
868bool wxDataViewColumn::IsSortOrderAscending() const
869{
870 return m_ascending;
871}
872
873void 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
882void wxDataViewColumn::SetWidth( int width )
883{
884 m_owner->m_headerArea->UpdateDisplay();
885
886 SetInternalWidth(width);
887}
888
889
890//-----------------------------------------------------------------------------
891// wxDataViewHeaderWindowBase
892//-----------------------------------------------------------------------------
893
894void 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:
913int WXDLLIMPEXP_CORE wxMSWGetColumnClicked(NMHDR *nmhdr, POINT *ptClick);
914
915IMPLEMENT_ABSTRACT_CLASS(wxDataViewHeaderWindowMSW, wxWindow)
916
917bool 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
990wxDataViewHeaderWindowMSW::~wxDataViewHeaderWindow()
991{
992 UnsubclassWin();
993}
994
995void 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
1053unsigned 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
1074bool 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
1197void 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
1213void 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
1227IMPLEMENT_ABSTRACT_CLASS(wxGenericDataViewHeaderWindow, wxWindow)
1228BEGIN_EVENT_TABLE(wxGenericDataViewHeaderWindow, wxWindow)
1229 EVT_PAINT (wxGenericDataViewHeaderWindow::OnPaint)
1230 EVT_MOUSE_EVENTS (wxGenericDataViewHeaderWindow::OnMouse)
1231 EVT_SET_FOCUS (wxGenericDataViewHeaderWindow::OnSetFocus)
1232END_EVENT_TABLE()
1233
1234bool 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
1259void 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
1342void wxGenericDataViewHeaderWindow::OnSetFocus( wxFocusEvent &event )
1343{
1344 GetParent()->SetFocus();
1345 event.Skip();
1346}
1347
1348void 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
1464void 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
1486void 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
1504wxDataViewRenameTimer::wxDataViewRenameTimer( wxDataViewMainWindow *owner )
1505{
1506 m_owner = owner;
1507}
1508
1509void wxDataViewRenameTimer::Notify()
1510{
1511 m_owner->OnRenameTimer();
1512}
1513
1514//-----------------------------------------------------------------------------
1515// wxDataViewMainWindow
1516//-----------------------------------------------------------------------------
1517
1518int 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
1526IMPLEMENT_ABSTRACT_CLASS(wxDataViewMainWindow, wxWindow)
1527
1528BEGIN_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)
1534END_EVENT_TABLE()
1535
1536wxDataViewMainWindow::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
1576wxDataViewMainWindow::~wxDataViewMainWindow()
1577{
1578 delete m_renameTimer;
1579}
1580
1581void 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
1610bool wxDataViewMainWindow::RowAppended()
1611{
1612 UpdateDisplay();
1613 return true;
1614}
1615
1616bool wxDataViewMainWindow::RowPrepended()
1617{
1618 UpdateDisplay();
1619 return true;
1620}
1621
1622bool wxDataViewMainWindow::RowInserted( unsigned int WXUNUSED(before) )
1623{
1624 UpdateDisplay();
1625 return true;
1626}
1627
1628bool wxDataViewMainWindow::RowDeleted( unsigned int WXUNUSED(row) )
1629{
1630 UpdateDisplay();
1631 return true;
1632}
1633
1634bool wxDataViewMainWindow::RowChanged( unsigned int WXUNUSED(row) )
1635{
1636 UpdateDisplay();
1637 return true;
1638}
1639
1640bool 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
1652bool wxDataViewMainWindow::RowsReordered( unsigned int *WXUNUSED(new_order) )
1653{
1654 UpdateDisplay();
1655 return true;
1656}
1657
1658bool wxDataViewMainWindow::Cleared()
1659{
1660 UpdateDisplay();
1661 return true;
1662}
1663
1664void wxDataViewMainWindow::UpdateDisplay()
1665{
1666 m_dirty = true;
1667}
1668
1669void wxDataViewMainWindow::OnInternalIdle()
1670{
1671 wxWindow::OnInternalIdle();
1672
1673 if (m_dirty)
1674 {
1675 RecalculateDisplay();
1676 m_dirty = false;
1677 }
1678}
1679
1680void 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
1698void 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
1706void 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
1891int wxDataViewMainWindow::GetCountPerPage() const
1892{
1893 wxSize size = GetClientSize();
1894 return size.y / m_lineHeight;
1895}
1896
1897int 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
1912unsigned 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
1921unsigned 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
1930unsigned int wxDataViewMainWindow::GetRowCount() const
1931{
1932 return wx_const_cast(wxDataViewCtrl*, GetOwner())->GetModel()->GetRowCount();
1933}
1934
1935void wxDataViewMainWindow::ChangeCurrentRow( unsigned int row )
1936{
1937 m_currentRow = row;
1938
1939 // send event
1940}
1941
1942void 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
1969void 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
1989void 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
2015void 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
2026void 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
2035bool wxDataViewMainWindow::IsRowSelected( unsigned int row )
2036{
2037 return (m_selection.Index( row ) != wxNOT_FOUND);
2038}
2039
2040void 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
2052void 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
2071void 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
2087void 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
2134wxRect 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
2145void 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
2219void 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
2448void wxDataViewMainWindow::OnSetFocus( wxFocusEvent &event )
2449{
2450 m_hasFocus = true;
2451
2452 if (HasCurrentRow())
2453 Refresh();
2454
2455 event.Skip();
2456}
2457
2458void 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
2472IMPLEMENT_DYNAMIC_CLASS(wxDataViewCtrl, wxDataViewCtrlBase)
2473
2474BEGIN_EVENT_TABLE(wxDataViewCtrl, wxDataViewCtrlBase)
2475 EVT_SIZE(wxDataViewCtrl::OnSize)
2476END_EVENT_TABLE()
2477
2478wxDataViewCtrl::~wxDataViewCtrl()
2479{
2480 if (m_notifier)
2481 GetModel()->RemoveNotifier( m_notifier );
2482}
2483
2484void wxDataViewCtrl::Init()
2485{
2486 m_notifier = NULL;
2487}
2488
2489bool 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__
2522WXLRESULT 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
2540void 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
2554bool 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
2568bool wxDataViewCtrl::AppendColumn( wxDataViewColumn *col )
2569{
2570 if (!wxDataViewCtrlBase::AppendColumn(col))
2571 return false;
2572
2573 OnColumnChange();
2574 return true;
2575}
2576
2577void wxDataViewCtrl::OnColumnChange()
2578{
2579 if (m_headerArea)
2580 m_headerArea->UpdateDisplay();
2581
2582 m_clientArea->UpdateDisplay();
2583}
2584
2585void wxDataViewCtrl::SetSelection( int row )
2586{
2587 m_clientArea->SelectRow(row, true);
2588}
2589
2590void wxDataViewCtrl::SetSelectionRange( unsigned int from, unsigned int to )
2591{
2592 m_clientArea->SelectRows(from, to, true);
2593}
2594
2595void wxDataViewCtrl::SetSelections( const wxArrayInt& aSelections )
2596{
2597 m_clientArea->Select(aSelections);
2598}
2599
2600void wxDataViewCtrl::Unselect( unsigned int WXUNUSED(row) )
2601{
2602 // FIXME - TODO
2603}
2604
2605bool wxDataViewCtrl::IsSelected( unsigned int WXUNUSED(row) ) const
2606{
2607 // FIXME - TODO
2608
2609 return false;
2610}
2611
2612int wxDataViewCtrl::GetSelection() const
2613{
2614 // FIXME - TODO
2615
2616 return -1;
2617}
2618
2619int 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