]> git.saurik.com Git - wxWidgets.git/blob - src/generic/datavgen.cpp
Use PBM_SETMARQUEE to implement indeterminate mode in wxMSW wxGauge.
[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, Bo Yang
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 #include "wx/frame.h"
38 #endif
39
40 #include "wx/stockitem.h"
41 #include "wx/calctrl.h"
42 #include "wx/popupwin.h"
43 #include "wx/renderer.h"
44 #include "wx/dcbuffer.h"
45 #include "wx/icon.h"
46 #include "wx/list.h"
47 #include "wx/listimpl.cpp"
48 #include "wx/imaglist.h"
49 #include "wx/headerctrl.h"
50 #include "wx/dnd.h"
51
52 //-----------------------------------------------------------------------------
53 // classes
54 //-----------------------------------------------------------------------------
55
56 class wxDataViewColumn;
57 class wxDataViewHeaderWindow;
58 class wxDataViewCtrl;
59
60 //-----------------------------------------------------------------------------
61 // classes
62 //-----------------------------------------------------------------------------
63
64 static const int SCROLL_UNIT_X = 15;
65
66 // the cell padding on the left/right
67 static const int PADDING_RIGHTLEFT = 3;
68
69 // the expander space margin
70 static const int EXPANDER_MARGIN = 4;
71
72 #ifdef __WXMSW__
73 static const int EXPANDER_OFFSET = 4;
74 #else
75 static const int EXPANDER_OFFSET = 1;
76 #endif
77
78 // Below is the compare stuff.
79 // For the generic implementation, both the leaf nodes and the nodes are sorted for
80 // fast search when needed
81 static wxDataViewModel* g_model;
82 static int g_column = -2;
83 static bool g_asending = true;
84
85 //-----------------------------------------------------------------------------
86 // wxDataViewColumn
87 //-----------------------------------------------------------------------------
88
89 void wxDataViewColumn::Init(int width, wxAlignment align, int flags)
90 {
91 m_width = width == wxCOL_WIDTH_DEFAULT ? wxDVC_DEFAULT_WIDTH : width;
92 m_minWidth = 0;
93 m_align = align;
94 m_flags = flags;
95 m_sort = false;
96 m_sortAscending = true;
97 }
98
99 void wxDataViewColumn::UpdateDisplay()
100 {
101 if (m_owner)
102 {
103 int idx = m_owner->GetColumnIndex( this );
104 m_owner->OnColumnChange( idx );
105 }
106 }
107
108 //-----------------------------------------------------------------------------
109 // wxDataViewHeaderWindow
110 //-----------------------------------------------------------------------------
111
112 class wxDataViewHeaderWindow : public wxHeaderCtrl
113 {
114 public:
115 wxDataViewHeaderWindow(wxDataViewCtrl *parent)
116 : wxHeaderCtrl(parent)
117 {
118 }
119
120 wxDataViewCtrl *GetOwner() const
121 { return static_cast<wxDataViewCtrl *>(GetParent()); }
122
123 protected:
124 // implement/override wxHeaderCtrl functions by forwarding them to the main
125 // control
126 virtual const wxHeaderColumn& GetColumn(unsigned int idx) const
127 {
128 return *(GetOwner()->GetColumn(idx));
129 }
130
131 // FIXME: currently unused
132 virtual bool UpdateColumnWidthToFit(unsigned int idx, int widthTitle)
133 {
134 wxDataViewCtrl * const owner = GetOwner();
135
136 int widthContents = owner->GetBestColumnWidth(idx);
137 owner->GetColumn(idx)->SetWidth(wxMax(widthTitle, widthContents));
138 owner->OnColumnChange(idx);
139
140 return true;
141 }
142
143 private:
144 bool SendEvent(wxEventType type, unsigned int n)
145 {
146 wxDataViewCtrl * const owner = GetOwner();
147 wxDataViewEvent event(type, owner->GetId());
148
149 event.SetEventObject(owner);
150 event.SetColumn(n);
151 event.SetDataViewColumn(owner->GetColumn(n));
152 event.SetModel(owner->GetModel());
153
154 // for events created by wxDataViewHeaderWindow the
155 // row / value fields are not valid
156 return owner->GetEventHandler()->ProcessEvent(event);
157 }
158
159 void OnClick(wxHeaderCtrlEvent& event)
160 {
161 const unsigned idx = event.GetColumn();
162
163 if ( SendEvent(wxEVT_COMMAND_DATAVIEW_COLUMN_HEADER_CLICK, idx) )
164 return;
165
166 // default handling for the column click is to sort by this column or
167 // toggle its sort order
168 wxDataViewCtrl * const owner = GetOwner();
169 wxDataViewColumn * const col = owner->GetColumn(idx);
170 if ( !col->IsSortable() )
171 {
172 // no default handling for non-sortable columns
173 event.Skip();
174 return;
175 }
176
177 if ( col->IsSortKey() )
178 {
179 // already using this column for sorting, just change the order
180 col->ToggleSortOrder();
181 }
182 else // not using this column for sorting yet
183 {
184 // first unset the old sort column if any
185 int oldSortKey = owner->GetSortingColumnIndex();
186 if ( oldSortKey != wxNOT_FOUND )
187 {
188 owner->GetColumn(oldSortKey)->UnsetAsSortKey();
189 owner->OnColumnChange(oldSortKey);
190 }
191
192 owner->SetSortingColumnIndex(idx);
193 col->SetAsSortKey();
194 }
195
196 wxDataViewModel * const model = owner->GetModel();
197 if ( model )
198 model->Resort();
199
200 owner->OnColumnChange(idx);
201 }
202
203 void OnRClick(wxHeaderCtrlEvent& event)
204 {
205 if ( !SendEvent(wxEVT_COMMAND_DATAVIEW_COLUMN_HEADER_RIGHT_CLICK,
206 event.GetColumn()) )
207 event.Skip();
208 }
209
210 void OnResize(wxHeaderCtrlEvent& event)
211 {
212 wxDataViewCtrl * const owner = GetOwner();
213
214 const unsigned col = event.GetColumn();
215 owner->GetColumn(col)->SetWidth(event.GetWidth());
216 GetOwner()->OnColumnChange(col);
217 }
218
219 void OnEndReorder(wxHeaderCtrlEvent& event)
220 {
221 wxDataViewCtrl * const owner = GetOwner();
222 owner->ColumnMoved(owner->GetColumn(event.GetColumn()),
223 event.GetNewOrder());
224 }
225
226 DECLARE_EVENT_TABLE()
227 wxDECLARE_NO_COPY_CLASS(wxDataViewHeaderWindow);
228 };
229
230 BEGIN_EVENT_TABLE(wxDataViewHeaderWindow, wxHeaderCtrl)
231 EVT_HEADER_CLICK(wxID_ANY, wxDataViewHeaderWindow::OnClick)
232 EVT_HEADER_RIGHT_CLICK(wxID_ANY, wxDataViewHeaderWindow::OnRClick)
233
234 EVT_HEADER_RESIZING(wxID_ANY, wxDataViewHeaderWindow::OnResize)
235 EVT_HEADER_END_RESIZE(wxID_ANY, wxDataViewHeaderWindow::OnResize)
236
237 EVT_HEADER_END_REORDER(wxID_ANY, wxDataViewHeaderWindow::OnEndReorder)
238 END_EVENT_TABLE()
239
240 //-----------------------------------------------------------------------------
241 // wxDataViewRenameTimer
242 //-----------------------------------------------------------------------------
243
244 class wxDataViewRenameTimer: public wxTimer
245 {
246 private:
247 wxDataViewMainWindow *m_owner;
248
249 public:
250 wxDataViewRenameTimer( wxDataViewMainWindow *owner );
251 void Notify();
252 };
253
254 //-----------------------------------------------------------------------------
255 // wxDataViewTreeNode
256 //-----------------------------------------------------------------------------
257
258 class wxDataViewTreeNode;
259 WX_DEFINE_ARRAY( wxDataViewTreeNode *, wxDataViewTreeNodes );
260 WX_DEFINE_ARRAY( void* , wxDataViewTreeLeaves);
261
262 int LINKAGEMODE wxGenericTreeModelNodeCmp( wxDataViewTreeNode ** node1,
263 wxDataViewTreeNode ** node2);
264 int LINKAGEMODE wxGenericTreeModelItemCmp( void ** id1, void ** id2);
265
266 class wxDataViewTreeNode
267 {
268 public:
269 wxDataViewTreeNode( wxDataViewTreeNode * parent = NULL )
270 {
271 m_parent = parent;
272 if (!parent)
273 m_open = true;
274 else
275 m_open = false;
276 m_hasChildren = false;
277 m_subTreeCount = 0;
278 }
279
280 ~wxDataViewTreeNode()
281 {
282 }
283
284 wxDataViewTreeNode * GetParent() const { return m_parent; }
285 void SetParent( wxDataViewTreeNode * parent ) { m_parent = parent; }
286 wxDataViewTreeNodes & GetNodes() { return m_nodes; }
287 wxDataViewTreeLeaves & GetChildren() { return m_leaves; }
288
289 void AddNode( wxDataViewTreeNode * node )
290 {
291 m_leaves.Add( node->GetItem().GetID() );
292 if (g_column >= -1)
293 m_leaves.Sort( &wxGenericTreeModelItemCmp );
294 m_nodes.Add( node );
295 if (g_column >= -1)
296 m_nodes.Sort( &wxGenericTreeModelNodeCmp );
297 }
298 void AddLeaf( void * leaf )
299 {
300 m_leaves.Add( leaf );
301 if (g_column >= -1)
302 m_leaves.Sort( &wxGenericTreeModelItemCmp );
303 }
304
305 wxDataViewItem & GetItem() { return m_item; }
306 const wxDataViewItem & GetItem() const { return m_item; }
307 void SetItem( const wxDataViewItem & item ) { m_item = item; }
308
309 unsigned int GetChildrenNumber() const { return m_leaves.GetCount(); }
310 unsigned int GetNodeNumber() const { return m_nodes.GetCount(); }
311 int GetIndentLevel() const
312 {
313 int ret = 0;
314 const wxDataViewTreeNode * node = this;
315 while( node->GetParent()->GetParent() != NULL )
316 {
317 node = node->GetParent();
318 ret ++;
319 }
320 return ret;
321 }
322
323 bool IsOpen() const
324 {
325 return m_open;
326 }
327
328 void ToggleOpen()
329 {
330 int len = m_nodes.GetCount();
331 int sum = 0;
332 for ( int i = 0;i < len; i ++)
333 sum += m_nodes[i]->GetSubTreeCount();
334
335 sum += m_leaves.GetCount();
336 if (m_open)
337 {
338 ChangeSubTreeCount(-sum);
339 m_open = !m_open;
340 }
341 else
342 {
343 m_open = !m_open;
344 ChangeSubTreeCount(sum);
345 }
346 }
347 bool HasChildren() const { return m_hasChildren; }
348 void SetHasChildren( bool has ){ m_hasChildren = has; }
349
350 void SetSubTreeCount( int num ) { m_subTreeCount = num; }
351 int GetSubTreeCount() const { return m_subTreeCount; }
352 void ChangeSubTreeCount( int num )
353 {
354 if( !m_open )
355 return;
356 m_subTreeCount += num;
357 if( m_parent )
358 m_parent->ChangeSubTreeCount(num);
359 }
360
361 void Resort()
362 {
363 if (g_column >= -1)
364 {
365 m_nodes.Sort( &wxGenericTreeModelNodeCmp );
366 int len = m_nodes.GetCount();
367 for (int i = 0; i < len; i ++)
368 m_nodes[i]->Resort();
369 m_leaves.Sort( &wxGenericTreeModelItemCmp );
370 }
371 }
372
373 private:
374 wxDataViewTreeNode *m_parent;
375 wxDataViewTreeNodes m_nodes;
376 wxDataViewTreeLeaves m_leaves;
377 wxDataViewItem m_item;
378 bool m_open;
379 bool m_hasChildren;
380 int m_subTreeCount;
381 };
382
383 int LINKAGEMODE wxGenericTreeModelNodeCmp( wxDataViewTreeNode ** node1,
384 wxDataViewTreeNode ** node2)
385 {
386 return g_model->Compare( (*node1)->GetItem(), (*node2)->GetItem(), g_column, g_asending );
387 }
388
389 int LINKAGEMODE wxGenericTreeModelItemCmp( void ** id1, void ** id2)
390 {
391 return g_model->Compare( *id1, *id2, g_column, g_asending );
392 }
393
394
395 //-----------------------------------------------------------------------------
396 // wxDataViewMainWindow
397 //-----------------------------------------------------------------------------
398
399 WX_DEFINE_SORTED_USER_EXPORTED_ARRAY_SIZE_T(unsigned int, wxDataViewSelection,
400 WXDLLIMPEXP_ADV);
401 WX_DECLARE_LIST(wxDataViewItem, ItemList);
402 WX_DEFINE_LIST(ItemList)
403
404 class wxDataViewMainWindow: public wxWindow
405 {
406 public:
407 wxDataViewMainWindow( wxDataViewCtrl *parent,
408 wxWindowID id,
409 const wxPoint &pos = wxDefaultPosition,
410 const wxSize &size = wxDefaultSize,
411 const wxString &name = wxT("wxdataviewctrlmainwindow") );
412 virtual ~wxDataViewMainWindow();
413
414 bool IsVirtualList() const { return m_root == NULL; }
415
416 // notifications from wxDataViewModel
417 bool ItemAdded( const wxDataViewItem &parent, const wxDataViewItem &item );
418 bool ItemDeleted( const wxDataViewItem &parent, const wxDataViewItem &item );
419 bool ItemChanged( const wxDataViewItem &item );
420 bool ValueChanged( const wxDataViewItem &item, unsigned int col );
421 bool Cleared();
422 void Resort()
423 {
424 if (!IsVirtualList())
425 {
426 SortPrepare();
427 m_root->Resort();
428 }
429 UpdateDisplay();
430 }
431
432 void SortPrepare()
433 {
434 g_model = GetOwner()->GetModel();
435 wxDataViewColumn* col = GetOwner()->GetSortingColumn();
436 if( !col )
437 {
438 if (g_model->HasDefaultCompare())
439 g_column = -1;
440 else
441 g_column = -2;
442
443 g_asending = true;
444 return;
445 }
446 g_column = col->GetModelColumn();
447 g_asending = col->IsSortOrderAscending();
448 }
449
450 void SetOwner( wxDataViewCtrl* owner ) { m_owner = owner; }
451 wxDataViewCtrl *GetOwner() { return m_owner; }
452 const wxDataViewCtrl *GetOwner() const { return m_owner; }
453
454 #if wxUSE_DRAG_AND_DROP
455 wxBitmap CreateItemBitmap( unsigned int row, int &indent );
456 #endif // wxUSE_DRAG_AND_DROP
457 void OnPaint( wxPaintEvent &event );
458 void OnArrowChar(unsigned int newCurrent, const wxKeyEvent& event);
459 void OnChar( wxKeyEvent &event );
460 void OnMouse( wxMouseEvent &event );
461 void OnSetFocus( wxFocusEvent &event );
462 void OnKillFocus( wxFocusEvent &event );
463
464 void UpdateDisplay();
465 void RecalculateDisplay();
466 void OnInternalIdle();
467
468 void OnRenameTimer();
469
470 void ScrollWindow( int dx, int dy, const wxRect *rect = NULL );
471 void ScrollTo( int rows, int column );
472
473 bool HasCurrentRow() { return m_currentRow != (unsigned int)-1; }
474 void ChangeCurrentRow( unsigned int row );
475
476 bool IsSingleSel() const { return !GetParent()->HasFlag(wxDV_MULTIPLE); }
477 bool IsEmpty() { return GetRowCount() == 0; }
478
479 int GetCountPerPage() const;
480 int GetEndOfLastCol() const;
481 unsigned int GetFirstVisibleRow() const;
482
483 // I change this method to un const because in the tree view,
484 // the displaying number of the tree are changing along with the
485 // expanding/collapsing of the tree nodes
486 unsigned int GetLastVisibleRow();
487 unsigned int GetRowCount();
488
489 wxDataViewItem GetSelection() const;
490 wxDataViewSelection GetSelections(){ return m_selection; }
491 void SetSelections( const wxDataViewSelection & sel )
492 { m_selection = sel; UpdateDisplay(); }
493 void Select( const wxArrayInt& aSelections );
494 void SelectAllRows( bool on );
495 void SelectRow( unsigned int row, bool on );
496 void SelectRows( unsigned int from, unsigned int to, bool on );
497 void ReverseRowSelection( unsigned int row );
498 bool IsRowSelected( unsigned int row );
499 void SendSelectionChangedEvent( const wxDataViewItem& item);
500
501 void RefreshRow( unsigned int row );
502 void RefreshRows( unsigned int from, unsigned int to );
503 void RefreshRowsAfter( unsigned int firstRow );
504
505 // returns the colour to be used for drawing the rules
506 wxColour GetRuleColour() const
507 {
508 return wxSystemSettings::GetColour(wxSYS_COLOUR_3DLIGHT);
509 }
510
511 wxRect GetLineRect( unsigned int row ) const;
512
513 int GetLineStart( unsigned int row ) const; // row * m_lineHeight in fixed mode
514 int GetLineHeight( unsigned int row ) const; // m_lineHeight in fixed mode
515 int GetLineAt( unsigned int y ) const; // y / m_lineHeight in fixed mode
516
517 // Some useful functions for row and item mapping
518 wxDataViewItem GetItemByRow( unsigned int row ) const;
519 int GetRowByItem( const wxDataViewItem & item ) const;
520
521 // Methods for building the mapping tree
522 void BuildTree( wxDataViewModel * model );
523 void DestroyTree();
524 void HitTest( const wxPoint & point, wxDataViewItem & item, wxDataViewColumn* &column );
525 wxRect GetItemRect( const wxDataViewItem & item, const wxDataViewColumn* column );
526
527 void Expand( unsigned int row );
528 void Collapse( unsigned int row );
529 bool IsExpanded( unsigned int row ) const;
530 bool HasChildren( unsigned int row ) const;
531
532 #if wxUSE_DRAG_AND_DROP
533 bool EnableDragSource( const wxDataFormat &format );
534 bool EnableDropTarget( const wxDataFormat &format );
535
536 void RemoveDropHint();
537 wxDragResult OnDragOver( wxDataFormat format, wxCoord x, wxCoord y, wxDragResult def );
538 bool OnDrop( wxDataFormat format, wxCoord x, wxCoord y );
539 wxDragResult OnData( wxDataFormat format, wxCoord x, wxCoord y, wxDragResult def );
540 void OnLeave();
541 #endif // wxUSE_DRAG_AND_DROP
542
543 private:
544 wxDataViewTreeNode * GetTreeNodeByRow( unsigned int row ) const;
545 // We did not need this temporarily
546 // wxDataViewTreeNode * GetTreeNodeByItem( const wxDataViewItem & item );
547
548 int RecalculateCount();
549
550 wxDataViewEvent SendExpanderEvent( wxEventType type, const wxDataViewItem & item );
551
552 wxDataViewTreeNode * FindNode( const wxDataViewItem & item );
553
554 private:
555 wxDataViewCtrl *m_owner;
556 int m_lineHeight;
557 bool m_dirty;
558
559 wxDataViewColumn *m_currentCol;
560 unsigned int m_currentRow;
561 wxDataViewSelection m_selection;
562
563 wxDataViewRenameTimer *m_renameTimer;
564 bool m_lastOnSame;
565
566 bool m_hasFocus;
567
568 #if wxUSE_DRAG_AND_DROP
569 int m_dragCount;
570 wxPoint m_dragStart;
571
572 bool m_dragEnabled;
573 wxDataFormat m_dragFormat;
574
575 bool m_dropEnabled;
576 wxDataFormat m_dropFormat;
577 bool m_dropHint;
578 unsigned int m_dropHintLine;
579 #endif // wxUSE_DRAG_AND_DROP
580
581 // for double click logic
582 unsigned int m_lineLastClicked,
583 m_lineBeforeLastClicked,
584 m_lineSelectSingleOnUp;
585
586 // the pen used to draw horiz/vertical rules
587 wxPen m_penRule;
588
589 // the pen used to draw the expander and the lines
590 wxPen m_penExpander;
591
592 // This is the tree structure of the model
593 wxDataViewTreeNode * m_root;
594 int m_count;
595
596 // This is the tree node under the cursor
597 wxDataViewTreeNode * m_underMouse;
598
599 private:
600 DECLARE_DYNAMIC_CLASS(wxDataViewMainWindow)
601 DECLARE_EVENT_TABLE()
602 };
603
604 // ---------------------------------------------------------
605 // wxGenericDataViewModelNotifier
606 // ---------------------------------------------------------
607
608 class wxGenericDataViewModelNotifier: public wxDataViewModelNotifier
609 {
610 public:
611 wxGenericDataViewModelNotifier( wxDataViewMainWindow *mainWindow )
612 { m_mainWindow = mainWindow; }
613
614 virtual bool ItemAdded( const wxDataViewItem & parent, const wxDataViewItem & item )
615 { return m_mainWindow->ItemAdded( parent , item ); }
616 virtual bool ItemDeleted( const wxDataViewItem &parent, const wxDataViewItem &item )
617 { return m_mainWindow->ItemDeleted( parent, item ); }
618 virtual bool ItemChanged( const wxDataViewItem & item )
619 { return m_mainWindow->ItemChanged(item); }
620 virtual bool ValueChanged( const wxDataViewItem & item , unsigned int col )
621 { return m_mainWindow->ValueChanged( item, col ); }
622 virtual bool Cleared()
623 { return m_mainWindow->Cleared(); }
624 virtual void Resort()
625 { m_mainWindow->Resort(); }
626
627 wxDataViewMainWindow *m_mainWindow;
628 };
629
630 // ---------------------------------------------------------
631 // wxDataViewRenderer
632 // ---------------------------------------------------------
633
634 IMPLEMENT_ABSTRACT_CLASS(wxDataViewRenderer, wxDataViewRendererBase)
635
636 wxDataViewRenderer::wxDataViewRenderer( const wxString &varianttype,
637 wxDataViewCellMode mode,
638 int align) :
639 wxDataViewCustomRendererBase( varianttype, mode, align )
640 {
641 m_align = align;
642 m_mode = mode;
643 m_ellipsizeMode = wxELLIPSIZE_MIDDLE;
644 m_dc = NULL;
645 }
646
647 wxDataViewRenderer::~wxDataViewRenderer()
648 {
649 delete m_dc;
650 }
651
652 wxDC *wxDataViewRenderer::GetDC()
653 {
654 if (m_dc == NULL)
655 {
656 if (GetOwner() == NULL)
657 return NULL;
658 if (GetOwner()->GetOwner() == NULL)
659 return NULL;
660 m_dc = new wxClientDC( GetOwner()->GetOwner() );
661 }
662
663 return m_dc;
664 }
665
666 void wxDataViewRenderer::SetAlignment( int align )
667 {
668 m_align=align;
669 }
670
671 int wxDataViewRenderer::GetAlignment() const
672 {
673 return m_align;
674 }
675
676 // ---------------------------------------------------------
677 // wxDataViewCustomRenderer
678 // ---------------------------------------------------------
679
680 IMPLEMENT_ABSTRACT_CLASS(wxDataViewCustomRenderer, wxDataViewRenderer)
681
682 wxDataViewCustomRenderer::wxDataViewCustomRenderer( const wxString &varianttype,
683 wxDataViewCellMode mode, int align ) :
684 wxDataViewRenderer( varianttype, mode, align )
685 {
686 }
687
688 // ---------------------------------------------------------
689 // wxDataViewTextRenderer
690 // ---------------------------------------------------------
691
692 IMPLEMENT_CLASS(wxDataViewTextRenderer, wxDataViewRenderer)
693
694 wxDataViewTextRenderer::wxDataViewTextRenderer( const wxString &varianttype,
695 wxDataViewCellMode mode, int align ) :
696 wxDataViewRenderer( varianttype, mode, align )
697 {
698 }
699
700 bool wxDataViewTextRenderer::SetValue( const wxVariant &value )
701 {
702 m_text = value.GetString();
703
704 return true;
705 }
706
707 bool wxDataViewTextRenderer::GetValue( wxVariant& WXUNUSED(value) ) const
708 {
709 return false;
710 }
711
712 bool wxDataViewTextRenderer::HasEditorCtrl() const
713 {
714 return true;
715 }
716
717 wxControl* wxDataViewTextRenderer::CreateEditorCtrl( wxWindow *parent,
718 wxRect labelRect, const wxVariant &value )
719 {
720 wxTextCtrl* ctrl = new wxTextCtrl( parent, wxID_ANY, value,
721 wxPoint(labelRect.x,labelRect.y),
722 wxSize(labelRect.width,labelRect.height) );
723
724 // select the text in the control an place the cursor at the end
725 ctrl->SetInsertionPointEnd();
726 ctrl->SelectAll();
727
728 return ctrl;
729 }
730
731 bool wxDataViewTextRenderer::GetValueFromEditorCtrl( wxControl *editor, wxVariant &value )
732 {
733 wxTextCtrl *text = (wxTextCtrl*) editor;
734 value = text->GetValue();
735 return true;
736 }
737
738 bool wxDataViewTextRenderer::Render(wxRect rect, wxDC *dc, int state)
739 {
740 RenderText(m_text, 0, rect, dc, state);
741 return true;
742 }
743
744 wxSize wxDataViewTextRenderer::GetSize() const
745 {
746 const wxDataViewCtrl *view = GetView();
747 if (!m_text.empty())
748 return view->wxWindowBase::GetTextExtent( m_text );
749 return wxSize(wxDVC_DEFAULT_RENDERER_SIZE,wxDVC_DEFAULT_RENDERER_SIZE);
750 }
751
752 // ---------------------------------------------------------
753 // wxDataViewBitmapRenderer
754 // ---------------------------------------------------------
755
756 IMPLEMENT_CLASS(wxDataViewBitmapRenderer, wxDataViewRenderer)
757
758 wxDataViewBitmapRenderer::wxDataViewBitmapRenderer( const wxString &varianttype,
759 wxDataViewCellMode mode, int align ) :
760 wxDataViewRenderer( varianttype, mode, align )
761 {
762 }
763
764 bool wxDataViewBitmapRenderer::SetValue( const wxVariant &value )
765 {
766 if (value.GetType() == wxT("wxBitmap"))
767 m_bitmap << value;
768 if (value.GetType() == wxT("wxIcon"))
769 m_icon << value;
770
771 return true;
772 }
773
774 bool wxDataViewBitmapRenderer::GetValue( wxVariant& WXUNUSED(value) ) const
775 {
776 return false;
777 }
778
779 bool wxDataViewBitmapRenderer::Render( wxRect cell, wxDC *dc, int WXUNUSED(state) )
780 {
781 if (m_bitmap.Ok())
782 dc->DrawBitmap( m_bitmap, cell.x, cell.y );
783 else if (m_icon.Ok())
784 dc->DrawIcon( m_icon, cell.x, cell.y );
785
786 return true;
787 }
788
789 wxSize wxDataViewBitmapRenderer::GetSize() const
790 {
791 if (m_bitmap.Ok())
792 return wxSize( m_bitmap.GetWidth(), m_bitmap.GetHeight() );
793 else if (m_icon.Ok())
794 return wxSize( m_icon.GetWidth(), m_icon.GetHeight() );
795
796 return wxSize(wxDVC_DEFAULT_RENDERER_SIZE,wxDVC_DEFAULT_RENDERER_SIZE);
797 }
798
799 // ---------------------------------------------------------
800 // wxDataViewToggleRenderer
801 // ---------------------------------------------------------
802
803 IMPLEMENT_ABSTRACT_CLASS(wxDataViewToggleRenderer, wxDataViewRenderer)
804
805 wxDataViewToggleRenderer::wxDataViewToggleRenderer( const wxString &varianttype,
806 wxDataViewCellMode mode, int align ) :
807 wxDataViewRenderer( varianttype, mode, align )
808 {
809 m_toggle = false;
810 }
811
812 bool wxDataViewToggleRenderer::SetValue( const wxVariant &value )
813 {
814 m_toggle = value.GetBool();
815
816 return true;
817 }
818
819 bool wxDataViewToggleRenderer::GetValue( wxVariant &WXUNUSED(value) ) const
820 {
821 return false;
822 }
823
824 bool wxDataViewToggleRenderer::Render( wxRect cell, wxDC *dc, int WXUNUSED(state) )
825 {
826 int flags = 0;
827 if (m_toggle)
828 flags |= wxCONTROL_CHECKED;
829 if (GetMode() != wxDATAVIEW_CELL_ACTIVATABLE)
830 flags |= wxCONTROL_DISABLED;
831
832 // check boxes we draw must always have the same, standard size (if it's
833 // bigger than the cell size the checkbox will be truncated because the
834 // caller had set the clipping rectangle to prevent us from drawing outside
835 // the cell)
836 cell.SetSize(GetSize());
837
838 wxRendererNative::Get().DrawCheckBox(
839 GetOwner()->GetOwner(),
840 *dc,
841 cell,
842 flags );
843
844 return true;
845 }
846
847 bool wxDataViewToggleRenderer::Activate( wxRect WXUNUSED(cell),
848 wxDataViewModel *model,
849 const wxDataViewItem & item, unsigned int col)
850 {
851 model->ChangeValue(!m_toggle, item, col);
852 return true;
853 }
854
855 wxSize wxDataViewToggleRenderer::GetSize() const
856 {
857 // the window parameter is not used by GetCheckBoxSize() so it's
858 // safe to pass NULL
859 return wxRendererNative::Get().GetCheckBoxSize(NULL);
860 }
861
862 // ---------------------------------------------------------
863 // wxDataViewProgressRenderer
864 // ---------------------------------------------------------
865
866 IMPLEMENT_ABSTRACT_CLASS(wxDataViewProgressRenderer, wxDataViewRenderer)
867
868 wxDataViewProgressRenderer::wxDataViewProgressRenderer( const wxString &label,
869 const wxString &varianttype, wxDataViewCellMode mode, int align ) :
870 wxDataViewRenderer( varianttype, mode, align )
871 {
872 m_label = label;
873 m_value = 0;
874 }
875
876 bool wxDataViewProgressRenderer::SetValue( const wxVariant &value )
877 {
878 m_value = (long) value;
879
880 if (m_value < 0) m_value = 0;
881 if (m_value > 100) m_value = 100;
882
883 return true;
884 }
885
886 bool wxDataViewProgressRenderer::GetValue( wxVariant &value ) const
887 {
888 value = (long) m_value;
889 return true;
890 }
891
892 bool
893 wxDataViewProgressRenderer::Render(wxRect rect, wxDC *dc, int WXUNUSED(state))
894 {
895 // deflate the rect to leave a small border between bars in adjacent rows
896 wxRect bar = rect.Deflate(0, 1);
897
898 dc->SetBrush( *wxTRANSPARENT_BRUSH );
899 dc->SetPen( *wxBLACK_PEN );
900 dc->DrawRectangle( bar );
901
902 bar.width = (int)(bar.width * m_value / 100.);
903 dc->SetPen( *wxTRANSPARENT_PEN );
904
905 const wxDataViewItemAttr& attr = GetAttr();
906 dc->SetBrush( attr.HasColour() ? wxBrush(attr.GetColour())
907 : *wxBLUE_BRUSH );
908 dc->DrawRectangle( bar );
909
910 return true;
911 }
912
913 wxSize wxDataViewProgressRenderer::GetSize() const
914 {
915 return wxSize(40,12);
916 }
917
918 // ---------------------------------------------------------
919 // wxDataViewDateRenderer
920 // ---------------------------------------------------------
921
922 #define wxUSE_DATE_RENDERER_POPUP (wxUSE_CALENDARCTRL && wxUSE_POPUPWIN)
923
924 #if wxUSE_DATE_RENDERER_POPUP
925
926 class wxDataViewDateRendererPopupTransient: public wxPopupTransientWindow
927 {
928 public:
929 wxDataViewDateRendererPopupTransient( wxWindow* parent, wxDateTime *value,
930 wxDataViewModel *model, const wxDataViewItem & item, unsigned int col) :
931 wxPopupTransientWindow( parent, wxBORDER_SIMPLE ),
932 m_item( item )
933 {
934 m_model = model;
935 m_col = col;
936 m_cal = new wxCalendarCtrl( this, wxID_ANY, *value );
937 wxBoxSizer *sizer = new wxBoxSizer( wxHORIZONTAL );
938 sizer->Add( m_cal, 1, wxGROW );
939 SetSizer( sizer );
940 sizer->Fit( this );
941 }
942
943 void OnCalendar( wxCalendarEvent &event );
944
945 wxCalendarCtrl *m_cal;
946 wxDataViewModel *m_model;
947 unsigned int m_col;
948 const wxDataViewItem & m_item;
949
950 protected:
951 virtual void OnDismiss()
952 {
953 }
954
955 private:
956 DECLARE_EVENT_TABLE()
957 };
958
959 BEGIN_EVENT_TABLE(wxDataViewDateRendererPopupTransient,wxPopupTransientWindow)
960 EVT_CALENDAR( wxID_ANY, wxDataViewDateRendererPopupTransient::OnCalendar )
961 END_EVENT_TABLE()
962
963 void wxDataViewDateRendererPopupTransient::OnCalendar( wxCalendarEvent &event )
964 {
965 m_model->ChangeValue( event.GetDate(), m_item, m_col );
966 DismissAndNotify();
967 }
968
969 #endif // wxUSE_DATE_RENDERER_POPUP
970
971 IMPLEMENT_ABSTRACT_CLASS(wxDataViewDateRenderer, wxDataViewRenderer)
972
973 wxDataViewDateRenderer::wxDataViewDateRenderer( const wxString &varianttype,
974 wxDataViewCellMode mode, int align ) :
975 wxDataViewRenderer( varianttype, mode, align )
976 {
977 }
978
979 bool wxDataViewDateRenderer::SetValue( const wxVariant &value )
980 {
981 m_date = value.GetDateTime();
982
983 return true;
984 }
985
986 bool wxDataViewDateRenderer::GetValue( wxVariant &value ) const
987 {
988 value = m_date;
989 return true;
990 }
991
992 bool wxDataViewDateRenderer::Render( wxRect cell, wxDC *dc, int state )
993 {
994 wxString tmp = m_date.FormatDate();
995 RenderText( tmp, 0, cell, dc, state );
996 return true;
997 }
998
999 wxSize wxDataViewDateRenderer::GetSize() const
1000 {
1001 const wxDataViewCtrl* view = GetView();
1002 wxString tmp = m_date.FormatDate();
1003 wxCoord x,y,d;
1004 view->GetTextExtent( tmp, &x, &y, &d );
1005 return wxSize(x,y+d);
1006 }
1007
1008 bool wxDataViewDateRenderer::Activate( wxRect WXUNUSED(cell), wxDataViewModel *model,
1009 const wxDataViewItem & item, unsigned int col )
1010 {
1011 wxVariant variant;
1012 model->GetValue( variant, item, col );
1013 wxDateTime value = variant.GetDateTime();
1014
1015 #if wxUSE_DATE_RENDERER_POPUP
1016 wxDataViewDateRendererPopupTransient *popup = new wxDataViewDateRendererPopupTransient(
1017 GetOwner()->GetOwner()->GetParent(), &value, model, item, col);
1018 wxPoint pos = wxGetMousePosition();
1019 popup->Move( pos );
1020 popup->Layout();
1021 popup->Popup( popup->m_cal );
1022 #else // !wxUSE_DATE_RENDERER_POPUP
1023 wxMessageBox(value.Format());
1024 #endif // wxUSE_DATE_RENDERER_POPUP/!wxUSE_DATE_RENDERER_POPUP
1025 return true;
1026 }
1027
1028 // ---------------------------------------------------------
1029 // wxDataViewIconTextRenderer
1030 // ---------------------------------------------------------
1031
1032 IMPLEMENT_CLASS(wxDataViewIconTextRenderer, wxDataViewRenderer)
1033
1034 wxDataViewIconTextRenderer::wxDataViewIconTextRenderer(
1035 const wxString &varianttype, wxDataViewCellMode mode, int align ) :
1036 wxDataViewRenderer( varianttype, mode, align )
1037 {
1038 SetMode(mode);
1039 SetAlignment(align);
1040 }
1041
1042 bool wxDataViewIconTextRenderer::SetValue( const wxVariant &value )
1043 {
1044 m_value << value;
1045 return true;
1046 }
1047
1048 bool wxDataViewIconTextRenderer::GetValue( wxVariant& WXUNUSED(value) ) const
1049 {
1050 return false;
1051 }
1052
1053 bool wxDataViewIconTextRenderer::Render(wxRect rect, wxDC *dc, int state)
1054 {
1055 int xoffset = 0;
1056
1057 const wxIcon& icon = m_value.GetIcon();
1058 if ( icon.IsOk() )
1059 {
1060 dc->DrawIcon(icon, rect.x, rect.y + (rect.height - icon.GetHeight())/2);
1061 xoffset = icon.GetWidth()+4;
1062 }
1063
1064 RenderText(m_value.GetText(), xoffset, rect, dc, state);
1065
1066 return true;
1067 }
1068
1069 wxSize wxDataViewIconTextRenderer::GetSize() const
1070 {
1071 const wxDataViewCtrl *view = GetView();
1072 if (!m_value.GetText().empty())
1073 {
1074 int x,y;
1075 view->GetTextExtent( m_value.GetText(), &x, &y );
1076
1077 if (m_value.GetIcon().IsOk())
1078 x += m_value.GetIcon().GetWidth() + 4;
1079 return wxSize( x, y );
1080 }
1081 return wxSize(80,20);
1082 }
1083
1084 wxControl* wxDataViewIconTextRenderer::CreateEditorCtrl(wxWindow *parent, wxRect labelRect, const wxVariant& value)
1085 {
1086 wxDataViewIconText iconText;
1087 iconText << value;
1088
1089 wxString text = iconText.GetText();
1090
1091 // adjust the label rect to take the width of the icon into account
1092 if (iconText.GetIcon().IsOk())
1093 {
1094 int w = iconText.GetIcon().GetWidth() + 4;
1095 labelRect.x += w;
1096 labelRect.width -= w;
1097 }
1098
1099 wxTextCtrl* ctrl = new wxTextCtrl( parent, wxID_ANY, text,
1100 wxPoint(labelRect.x,labelRect.y),
1101 wxSize(labelRect.width,labelRect.height) );
1102
1103 // select the text in the control an place the cursor at the end
1104 ctrl->SetInsertionPointEnd();
1105 ctrl->SelectAll();
1106
1107 return ctrl;
1108 }
1109
1110 bool wxDataViewIconTextRenderer::GetValueFromEditorCtrl( wxControl *editor, wxVariant& value )
1111 {
1112 wxTextCtrl *text = (wxTextCtrl*) editor;
1113
1114 wxDataViewIconText iconText(text->GetValue(), m_value.GetIcon());
1115 value << iconText;
1116 return true;
1117 }
1118
1119 //-----------------------------------------------------------------------------
1120 // wxDataViewDropTarget
1121 //-----------------------------------------------------------------------------
1122
1123 #if wxUSE_DRAG_AND_DROP
1124
1125 class wxBitmapCanvas: public wxWindow
1126 {
1127 public:
1128 wxBitmapCanvas( wxWindow *parent, const wxBitmap &bitmap, const wxSize &size ) :
1129 wxWindow( parent, wxID_ANY, wxPoint(0,0), size )
1130 {
1131 m_bitmap = bitmap;
1132 Connect( wxEVT_PAINT, wxPaintEventHandler(wxBitmapCanvas::OnPaint) );
1133 }
1134
1135 void OnPaint( wxPaintEvent &WXUNUSED(event) )
1136 {
1137 wxPaintDC dc(this);
1138 dc.DrawBitmap( m_bitmap, 0, 0);
1139 }
1140
1141 wxBitmap m_bitmap;
1142 };
1143
1144 class wxDataViewDropSource: public wxDropSource
1145 {
1146 public:
1147 wxDataViewDropSource( wxDataViewMainWindow *win, unsigned int row ) :
1148 wxDropSource( win )
1149 {
1150 m_win = win;
1151 m_row = row;
1152 m_hint = NULL;
1153 }
1154
1155 ~wxDataViewDropSource()
1156 {
1157 delete m_hint;
1158 }
1159
1160 virtual bool GiveFeedback( wxDragResult WXUNUSED(effect) )
1161 {
1162 wxPoint pos = wxGetMousePosition();
1163
1164 if (!m_hint)
1165 {
1166 int liney = m_win->GetLineStart( m_row );
1167 int linex = 0;
1168 m_win->GetOwner()->CalcUnscrolledPosition( 0, liney, NULL, &liney );
1169 m_win->ClientToScreen( &linex, &liney );
1170 m_dist_x = pos.x - linex;
1171 m_dist_y = pos.y - liney;
1172
1173 int indent = 0;
1174 wxBitmap ib = m_win->CreateItemBitmap( m_row, indent );
1175 m_dist_x -= indent;
1176 m_hint = new wxFrame( m_win->GetParent(), wxID_ANY, wxEmptyString,
1177 wxPoint(pos.x - m_dist_x, pos.y + 5 ),
1178 ib.GetSize(),
1179 wxFRAME_TOOL_WINDOW |
1180 wxFRAME_FLOAT_ON_PARENT |
1181 wxFRAME_NO_TASKBAR |
1182 wxNO_BORDER );
1183 new wxBitmapCanvas( m_hint, ib, ib.GetSize() );
1184 m_hint->Show();
1185 }
1186 else
1187 {
1188 m_hint->Move( pos.x - m_dist_x, pos.y + 5 );
1189 m_hint->SetTransparent( 128 );
1190 }
1191
1192 return false;
1193 }
1194
1195 wxDataViewMainWindow *m_win;
1196 unsigned int m_row;
1197 wxFrame *m_hint;
1198 int m_dist_x,m_dist_y;
1199 };
1200
1201
1202 class wxDataViewDropTarget: public wxDropTarget
1203 {
1204 public:
1205 wxDataViewDropTarget( wxDataObject *obj, wxDataViewMainWindow *win ) :
1206 wxDropTarget( obj )
1207 {
1208 m_win = win;
1209 }
1210
1211 virtual wxDragResult OnDragOver( wxCoord x, wxCoord y, wxDragResult def )
1212 {
1213 wxDataFormat format = GetMatchingPair();
1214 if (format == wxDF_INVALID)
1215 return wxDragNone;
1216 return m_win->OnDragOver( format, x, y, def);
1217 }
1218
1219 virtual bool OnDrop( wxCoord x, wxCoord y )
1220 {
1221 wxDataFormat format = GetMatchingPair();
1222 if (format == wxDF_INVALID)
1223 return false;
1224 return m_win->OnDrop( format, x, y );
1225 }
1226
1227 virtual wxDragResult OnData( wxCoord x, wxCoord y, wxDragResult def )
1228 {
1229 wxDataFormat format = GetMatchingPair();
1230 if (format == wxDF_INVALID)
1231 return wxDragNone;
1232 if (!GetData())
1233 return wxDragNone;
1234 return m_win->OnData( format, x, y, def );
1235 }
1236
1237 virtual void OnLeave()
1238 { m_win->OnLeave(); }
1239
1240 wxDataViewMainWindow *m_win;
1241 };
1242
1243 #endif // wxUSE_DRAG_AND_DROP
1244
1245 //-----------------------------------------------------------------------------
1246 // wxDataViewRenameTimer
1247 //-----------------------------------------------------------------------------
1248
1249 wxDataViewRenameTimer::wxDataViewRenameTimer( wxDataViewMainWindow *owner )
1250 {
1251 m_owner = owner;
1252 }
1253
1254 void wxDataViewRenameTimer::Notify()
1255 {
1256 m_owner->OnRenameTimer();
1257 }
1258
1259 //-----------------------------------------------------------------------------
1260 // wxDataViewMainWindow
1261 //-----------------------------------------------------------------------------
1262
1263 // The tree building helper, declared firstly
1264 static void BuildTreeHelper( wxDataViewModel * model, wxDataViewItem & item,
1265 wxDataViewTreeNode * node);
1266
1267 int LINKAGEMODE wxDataViewSelectionCmp( unsigned int row1, unsigned int row2 )
1268 {
1269 if (row1 > row2) return 1;
1270 if (row1 == row2) return 0;
1271 return -1;
1272 }
1273
1274 IMPLEMENT_ABSTRACT_CLASS(wxDataViewMainWindow, wxWindow)
1275
1276 BEGIN_EVENT_TABLE(wxDataViewMainWindow,wxWindow)
1277 EVT_PAINT (wxDataViewMainWindow::OnPaint)
1278 EVT_MOUSE_EVENTS (wxDataViewMainWindow::OnMouse)
1279 EVT_SET_FOCUS (wxDataViewMainWindow::OnSetFocus)
1280 EVT_KILL_FOCUS (wxDataViewMainWindow::OnKillFocus)
1281 EVT_CHAR (wxDataViewMainWindow::OnChar)
1282 END_EVENT_TABLE()
1283
1284 wxDataViewMainWindow::wxDataViewMainWindow( wxDataViewCtrl *parent, wxWindowID id,
1285 const wxPoint &pos, const wxSize &size, const wxString &name ) :
1286 wxWindow( parent, id, pos, size, wxWANTS_CHARS|wxBORDER_NONE, name ),
1287 m_selection( wxDataViewSelectionCmp )
1288
1289 {
1290 SetOwner( parent );
1291
1292 m_lastOnSame = false;
1293 m_renameTimer = new wxDataViewRenameTimer( this );
1294
1295 // TODO: user better initial values/nothing selected
1296 m_currentCol = NULL;
1297 m_currentRow = 0;
1298
1299 m_lineHeight = wxMax( 17, GetCharHeight() + 2 ); // 17 = mini icon height + 1
1300
1301 #if wxUSE_DRAG_AND_DROP
1302 m_dragCount = 0;
1303 m_dragStart = wxPoint(0,0);
1304
1305 m_dragEnabled = false;
1306 m_dropEnabled = false;
1307 m_dropHint = false;
1308 m_dropHintLine = (unsigned int) -1;
1309 #endif // wxUSE_DRAG_AND_DROP
1310
1311 m_lineLastClicked = (unsigned int) -1;
1312 m_lineBeforeLastClicked = (unsigned int) -1;
1313 m_lineSelectSingleOnUp = (unsigned int) -1;
1314
1315 m_hasFocus = false;
1316
1317 SetBackgroundColour( *wxWHITE );
1318
1319 SetBackgroundStyle(wxBG_STYLE_CUSTOM);
1320
1321 m_penRule = wxPen(GetRuleColour());
1322
1323 // compose a pen whichcan draw black lines
1324 // TODO: maybe there is something system colour to use
1325 m_penExpander = wxPen(wxColour(0,0,0));
1326
1327 m_root = new wxDataViewTreeNode( NULL );
1328 m_root->SetHasChildren(true);
1329
1330 // Make m_count = -1 will cause the class recaculate the real displaying number of rows.
1331 m_count = -1;
1332 m_underMouse = NULL;
1333 UpdateDisplay();
1334 }
1335
1336 wxDataViewMainWindow::~wxDataViewMainWindow()
1337 {
1338 DestroyTree();
1339 delete m_renameTimer;
1340 }
1341
1342
1343 #if wxUSE_DRAG_AND_DROP
1344 bool wxDataViewMainWindow::EnableDragSource( const wxDataFormat &format )
1345 {
1346 m_dragFormat = format;
1347 m_dragEnabled = format != wxDF_INVALID;
1348
1349 return true;
1350 }
1351
1352 bool wxDataViewMainWindow::EnableDropTarget( const wxDataFormat &format )
1353 {
1354 m_dropFormat = format;
1355 m_dropEnabled = format != wxDF_INVALID;
1356
1357 if (m_dropEnabled)
1358 SetDropTarget( new wxDataViewDropTarget( new wxCustomDataObject( format ), this ) );
1359
1360 return true;
1361 }
1362
1363 void wxDataViewMainWindow::RemoveDropHint()
1364 {
1365 if (m_dropHint)
1366 {
1367 m_dropHint = false;
1368 RefreshRow( m_dropHintLine );
1369 m_dropHintLine = (unsigned int) -1;
1370 }
1371 }
1372
1373 wxDragResult wxDataViewMainWindow::OnDragOver( wxDataFormat format, wxCoord x,
1374 wxCoord y, wxDragResult def )
1375 {
1376 int xx = x;
1377 int yy = y;
1378 m_owner->CalcUnscrolledPosition( xx, yy, &xx, &yy );
1379 unsigned int row = GetLineAt( yy );
1380
1381 if ((row >= GetRowCount()) || (yy > GetEndOfLastCol()))
1382 {
1383 RemoveDropHint();
1384 return wxDragNone;
1385 }
1386
1387 wxDataViewItem item = GetItemByRow( row );
1388
1389 wxDataViewModel *model = GetOwner()->GetModel();
1390
1391 wxDataViewEvent event( wxEVT_COMMAND_DATAVIEW_ITEM_DROP_POSSIBLE, m_owner->GetId() );
1392 event.SetEventObject( m_owner );
1393 event.SetItem( item );
1394 event.SetModel( model );
1395 event.SetDataFormat( format );
1396 if (!m_owner->HandleWindowEvent( event ))
1397 {
1398 RemoveDropHint();
1399 return wxDragNone;
1400 }
1401
1402 if (!event.IsAllowed())
1403 {
1404 RemoveDropHint();
1405 return wxDragNone;
1406 }
1407
1408
1409 if (m_dropHint && (row != m_dropHintLine))
1410 RefreshRow( m_dropHintLine );
1411 m_dropHint = true;
1412 m_dropHintLine = row;
1413 RefreshRow( row );
1414
1415 return def;
1416 }
1417
1418 bool wxDataViewMainWindow::OnDrop( wxDataFormat format, wxCoord x, wxCoord y )
1419 {
1420 RemoveDropHint();
1421
1422 int xx = x;
1423 int yy = y;
1424 m_owner->CalcUnscrolledPosition( xx, yy, &xx, &yy );
1425 unsigned int row = GetLineAt( yy );
1426
1427 if ((row >= GetRowCount()) || (yy > GetEndOfLastCol()))
1428 return false;
1429
1430 wxDataViewItem item = GetItemByRow( row );
1431
1432 wxDataViewModel *model = GetOwner()->GetModel();
1433
1434 wxDataViewEvent event( wxEVT_COMMAND_DATAVIEW_ITEM_DROP_POSSIBLE, m_owner->GetId() );
1435 event.SetEventObject( m_owner );
1436 event.SetItem( item );
1437 event.SetModel( model );
1438 event.SetDataFormat( format );
1439 if (!m_owner->HandleWindowEvent( event ))
1440 return false;
1441
1442 if (!event.IsAllowed())
1443 return false;
1444
1445 return true;
1446 }
1447
1448 wxDragResult wxDataViewMainWindow::OnData( wxDataFormat format, wxCoord x, wxCoord y,
1449 wxDragResult def )
1450 {
1451 int xx = x;
1452 int yy = y;
1453 m_owner->CalcUnscrolledPosition( xx, yy, &xx, &yy );
1454 unsigned int row = GetLineAt( yy );
1455
1456 if ((row >= GetRowCount()) || (yy > GetEndOfLastCol()))
1457 return wxDragNone;
1458
1459 wxDataViewItem item = GetItemByRow( row );
1460
1461 wxDataViewModel *model = GetOwner()->GetModel();
1462
1463 wxCustomDataObject *obj = (wxCustomDataObject *) GetDropTarget()->GetDataObject();
1464
1465 wxDataViewEvent event( wxEVT_COMMAND_DATAVIEW_ITEM_DROP, m_owner->GetId() );
1466 event.SetEventObject( m_owner );
1467 event.SetItem( item );
1468 event.SetModel( model );
1469 event.SetDataFormat( format );
1470 event.SetDataSize( obj->GetSize() );
1471 event.SetDataBuffer( obj->GetData() );
1472 if (!m_owner->HandleWindowEvent( event ))
1473 return wxDragNone;
1474
1475 if (!event.IsAllowed())
1476 return wxDragNone;
1477
1478 return def;
1479 }
1480
1481 void wxDataViewMainWindow::OnLeave()
1482 {
1483 RemoveDropHint();
1484 }
1485
1486 wxBitmap wxDataViewMainWindow::CreateItemBitmap( unsigned int row, int &indent )
1487 {
1488 int height = GetLineHeight( row );
1489 int width = 0;
1490 unsigned int cols = GetOwner()->GetColumnCount();
1491 unsigned int col;
1492 for (col = 0; col < cols; col++)
1493 {
1494 wxDataViewColumn *column = GetOwner()->GetColumnAt(col);
1495 if (column->IsHidden())
1496 continue; // skip it!
1497 width += column->GetWidth();
1498 }
1499
1500 indent = 0;
1501 if (!IsVirtualList())
1502 {
1503 wxDataViewTreeNode *node = GetTreeNodeByRow(row);
1504 indent = GetOwner()->GetIndent() * node->GetIndentLevel();
1505 indent = indent + m_lineHeight;
1506 // try to use the m_lineHeight as the expander space
1507
1508 if(!node->HasChildren())
1509 delete node;
1510 }
1511 width -= indent;
1512
1513 wxBitmap bitmap( width, height );
1514 wxMemoryDC dc( bitmap );
1515 dc.SetFont( GetFont() );
1516 dc.SetPen( *wxBLACK_PEN );
1517 dc.SetBrush( *wxWHITE_BRUSH );
1518 dc.DrawRectangle( 0,0,width,height );
1519
1520 wxDataViewModel *model = m_owner->GetModel();
1521
1522 wxDataViewColumn *expander = GetOwner()->GetExpanderColumn();
1523 if (!expander)
1524 {
1525 // TODO-RTL: last column for RTL support
1526 expander = GetOwner()->GetColumnAt( 0 );
1527 GetOwner()->SetExpanderColumn(expander);
1528 }
1529
1530
1531 int x = 0;
1532 for (col = 0; col < cols; col++)
1533 {
1534 wxDataViewColumn *column = GetOwner()->GetColumnAt( col );
1535 wxDataViewRenderer *cell = column->GetRenderer();
1536
1537 if (column->IsHidden())
1538 continue; // skip it!
1539
1540 width = column->GetWidth();
1541
1542 if (column == expander)
1543 width -= indent;
1544
1545 wxVariant value;
1546 wxDataViewItem item = GetItemByRow( row );
1547 model->GetValue( value, item, column->GetModelColumn());
1548 cell->SetValue( value );
1549
1550 wxDataViewItemAttr attr;
1551 model->GetAttr(item, column->GetModelColumn(), attr);
1552 cell->SetAttr(attr);
1553
1554 wxRect item_rect(x, 0, width, height);
1555 item_rect.Deflate(PADDING_RIGHTLEFT, 0);
1556
1557 // dc.SetClippingRegion( item_rect );
1558 cell->WXCallRender(item_rect, &dc, 0);
1559 // dc.DestroyClippingRegion();
1560
1561 x += width;
1562 }
1563
1564 return bitmap;
1565 }
1566
1567 #endif // wxUSE_DRAG_AND_DROP
1568
1569
1570 void wxDataViewMainWindow::OnPaint( wxPaintEvent &WXUNUSED(event) )
1571 {
1572 wxDataViewModel *model = GetOwner()->GetModel();
1573 wxAutoBufferedPaintDC dc( this );
1574
1575 #ifdef __WXMSW__
1576 dc.SetBrush(GetOwner()->GetBackgroundColour());
1577 dc.SetPen( *wxTRANSPARENT_PEN );
1578 dc.DrawRectangle(GetClientSize());
1579 #endif
1580
1581 // prepare the DC
1582 GetOwner()->PrepareDC( dc );
1583 dc.SetFont( GetFont() );
1584
1585 wxRect update = GetUpdateRegion().GetBox();
1586 m_owner->CalcUnscrolledPosition( update.x, update.y, &update.x, &update.y );
1587
1588 // compute which items needs to be redrawn
1589 unsigned int item_start = GetLineAt( wxMax(0,update.y) );
1590 unsigned int item_count =
1591 wxMin( (int)( GetLineAt( wxMax(0,update.y+update.height) ) - item_start + 1),
1592 (int)(GetRowCount( ) - item_start));
1593 unsigned int item_last = item_start + item_count;
1594 // Get the parent of DataViewCtrl
1595 wxWindow *parent = GetParent()->GetParent();
1596 wxDataViewEvent cache_event(wxEVT_COMMAND_DATAVIEW_CACHE_HINT, parent->GetId());
1597 cache_event.SetEventObject(GetParent());
1598 cache_event.SetCache(item_start, item_last - 1);
1599 parent->ProcessWindowEvent(cache_event);
1600
1601 // compute which columns needs to be redrawn
1602 unsigned int cols = GetOwner()->GetColumnCount();
1603 if ( !cols )
1604 {
1605 // we assume that we have at least one column below and painting an
1606 // empty control is unnecessary anyhow
1607 return;
1608 }
1609
1610 unsigned int col_start = 0;
1611 unsigned int x_start;
1612 for (x_start = 0; col_start < cols; col_start++)
1613 {
1614 wxDataViewColumn *col = GetOwner()->GetColumnAt(col_start);
1615 if (col->IsHidden())
1616 continue; // skip it!
1617
1618 unsigned int w = col->GetWidth();
1619 if (x_start+w >= (unsigned int)update.x)
1620 break;
1621
1622 x_start += w;
1623 }
1624
1625 unsigned int col_last = col_start;
1626 unsigned int x_last = x_start;
1627 for (; col_last < cols; col_last++)
1628 {
1629 wxDataViewColumn *col = GetOwner()->GetColumnAt(col_last);
1630 if (col->IsHidden())
1631 continue; // skip it!
1632
1633 if (x_last > (unsigned int)update.GetRight())
1634 break;
1635
1636 x_last += col->GetWidth();
1637 }
1638
1639 // Draw horizontal rules if required
1640 if ( m_owner->HasFlag(wxDV_HORIZ_RULES) )
1641 {
1642 dc.SetPen(m_penRule);
1643 dc.SetBrush(*wxTRANSPARENT_BRUSH);
1644
1645 for (unsigned int i = item_start; i <= item_last; i++)
1646 {
1647 int y = GetLineStart( i );
1648 dc.DrawLine(x_start, y, x_last, y);
1649 }
1650 }
1651
1652 // Draw vertical rules if required
1653 if ( m_owner->HasFlag(wxDV_VERT_RULES) )
1654 {
1655 dc.SetPen(m_penRule);
1656 dc.SetBrush(*wxTRANSPARENT_BRUSH);
1657
1658 int x = x_start;
1659 for (unsigned int i = col_start; i < col_last; i++)
1660 {
1661 wxDataViewColumn *col = GetOwner()->GetColumnAt(i);
1662 if (col->IsHidden())
1663 continue; // skip it
1664
1665 dc.DrawLine(x, GetLineStart( item_start ),
1666 x, GetLineStart( item_last ) );
1667
1668 x += col->GetWidth();
1669 }
1670
1671 // Draw last vertical rule
1672 dc.DrawLine(x, GetLineStart( item_start ),
1673 x, GetLineStart( item_last ) );
1674 }
1675
1676 // redraw the background for the items which are selected/current
1677 for (unsigned int item = item_start; item < item_last; item++)
1678 {
1679 bool selected = m_selection.Index( item ) != wxNOT_FOUND;
1680 if (selected || item == m_currentRow)
1681 {
1682 int flags = selected ? (int)wxCONTROL_SELECTED : 0;
1683 if (item == m_currentRow)
1684 flags |= wxCONTROL_CURRENT;
1685 if (m_hasFocus)
1686 flags |= wxCONTROL_FOCUSED;
1687
1688 wxRect rect( x_start, GetLineStart( item ),
1689 x_last - x_start, GetLineHeight( item ) );
1690 wxRendererNative::Get().DrawItemSelectionRect
1691 (
1692 this,
1693 dc,
1694 rect,
1695 flags
1696 );
1697 }
1698 }
1699
1700 #if wxUSE_DRAG_AND_DROP
1701 if (m_dropHint)
1702 {
1703 wxRect rect( x_start, GetLineStart( m_dropHintLine ),
1704 x_last - x_start, GetLineHeight( m_dropHintLine ) );
1705 dc.SetPen( *wxBLACK_PEN );
1706 dc.SetBrush( *wxTRANSPARENT_BRUSH );
1707 dc.DrawRectangle( rect );
1708 }
1709 #endif // wxUSE_DRAG_AND_DROP
1710
1711 wxDataViewColumn *expander = GetOwner()->GetExpanderColumn();
1712 if (!expander)
1713 {
1714 // TODO-RTL: last column for RTL support
1715 expander = GetOwner()->GetColumnAt( 0 );
1716 GetOwner()->SetExpanderColumn(expander);
1717 }
1718
1719 // redraw all cells for all rows which must be repainted and all columns
1720 wxRect cell_rect;
1721 cell_rect.x = x_start;
1722 for (unsigned int i = col_start; i < col_last; i++)
1723 {
1724 wxDataViewColumn *col = GetOwner()->GetColumnAt( i );
1725 wxDataViewRenderer *cell = col->GetRenderer();
1726 cell_rect.width = col->GetWidth();
1727
1728 if ( col->IsHidden() || cell_rect.width <= 0 )
1729 continue; // skip it!
1730
1731 for (unsigned int item = item_start; item < item_last; item++)
1732 {
1733 // get the cell value and set it into the renderer
1734 wxVariant value;
1735 wxDataViewTreeNode *node = NULL;
1736 wxDataViewItem dataitem;
1737
1738 if (!IsVirtualList())
1739 {
1740 node = GetTreeNodeByRow(item);
1741 if( node == NULL )
1742 continue;
1743
1744 dataitem = node->GetItem();
1745
1746 if ((i > 0) && model->IsContainer(dataitem) &&
1747 !model->HasContainerColumns(dataitem))
1748 continue;
1749 }
1750 else
1751 {
1752 dataitem = wxDataViewItem( wxUIntToPtr(item+1) );
1753 }
1754
1755 model->GetValue( value, dataitem, col->GetModelColumn());
1756 cell->SetValue( value );
1757
1758 wxDataViewItemAttr attr;
1759 model->GetAttr(dataitem, col->GetModelColumn(), attr);
1760 cell->SetAttr(attr);
1761
1762 // update cell_rect
1763 cell_rect.y = GetLineStart( item );
1764 cell_rect.height = GetLineHeight( item );
1765
1766 // deal with the expander
1767 int indent = 0;
1768 if ((!IsVirtualList()) && (col == expander))
1769 {
1770 // Calculate the indent first
1771 indent = GetOwner()->GetIndent() * node->GetIndentLevel();
1772
1773 // we reserve m_lineHeight of horizontal space for the expander
1774 // but leave EXPANDER_MARGIN around the expander itself
1775 int exp_x = cell_rect.x + indent + EXPANDER_MARGIN;
1776
1777 indent += m_lineHeight;
1778
1779 // draw expander if needed and visible
1780 if ( node->HasChildren() && exp_x < cell_rect.GetRight() )
1781 {
1782 dc.SetPen( m_penExpander );
1783 dc.SetBrush( wxNullBrush );
1784
1785 int exp_size = m_lineHeight - 2*EXPANDER_MARGIN;
1786 int exp_y = cell_rect.y + (cell_rect.height - exp_size)/2
1787 + EXPANDER_MARGIN - EXPANDER_OFFSET;
1788
1789 const wxRect rect(exp_x, exp_y, exp_size, exp_size);
1790
1791 int flag = 0;
1792 if ( m_underMouse == node )
1793 flag |= wxCONTROL_CURRENT;
1794 if ( node->IsOpen() )
1795 flag |= wxCONTROL_EXPANDED;
1796
1797 // ensure that we don't overflow the cell (which might
1798 // happen if the column is very narrow)
1799 wxDCClipper clip(dc, cell_rect);
1800
1801 wxRendererNative::Get().DrawTreeItemButton( this, dc, rect, flag);
1802 }
1803
1804 // force the expander column to left-center align
1805 cell->SetAlignment( wxALIGN_CENTER_VERTICAL );
1806 }
1807 if (node && !node->HasChildren())
1808 {
1809 // Yes, if the node does not have any child, it must be a leaf which
1810 // mean that it is a temporarily created by GetTreeNodeByRow
1811 wxDELETE(node);
1812 }
1813
1814 wxRect item_rect = cell_rect;
1815 item_rect.Deflate(PADDING_RIGHTLEFT, 0);
1816
1817 // account for the tree indent (harmless if we're not indented)
1818 item_rect.x += indent;
1819 item_rect.width -= indent;
1820
1821 if ( item_rect.width <= 0 )
1822 continue;
1823
1824 int state = 0;
1825 if (m_hasFocus && (m_selection.Index(item) != wxNOT_FOUND))
1826 state |= wxDATAVIEW_CELL_SELECTED;
1827
1828 // TODO: it would be much more efficient to create a clipping
1829 // region for the entire column being rendered (in the OnPaint
1830 // of wxDataViewMainWindow) instead of a single clip region for
1831 // each cell. However it would mean that each renderer should
1832 // respect the given wxRect's top & bottom coords, eventually
1833 // violating only the left & right coords - however the user can
1834 // make its own renderer and thus we cannot be sure of that.
1835 wxDCClipper clip(dc, item_rect);
1836
1837 cell->WXCallRender(item_rect, &dc, state);
1838 }
1839
1840 cell_rect.x += cell_rect.width;
1841 }
1842 }
1843
1844 void wxDataViewMainWindow::OnRenameTimer()
1845 {
1846 // We have to call this here because changes may just have
1847 // been made and no screen update taken place.
1848 if ( m_dirty )
1849 {
1850 // TODO: use wxTheApp->SafeYieldFor(NULL, wxEVT_CATEGORY_UI) instead
1851 // (needs to be tested!)
1852 wxSafeYield();
1853 }
1854
1855 wxDataViewItem item = GetItemByRow( m_currentRow );
1856
1857 wxRect labelRect = GetItemRect(item, m_currentCol);
1858
1859 m_currentCol->GetRenderer()->StartEditing( item, labelRect );
1860 }
1861
1862 //-----------------------------------------------------------------------------
1863 // Helper class for do operation on the tree node
1864 //-----------------------------------------------------------------------------
1865 class DoJob
1866 {
1867 public:
1868 DoJob() { }
1869 virtual ~DoJob() { }
1870
1871 // The return value control how the tree-walker tranverse the tree
1872 // 0: Job done, stop tranverse and return
1873 // 1: Ignore the current node's subtree and continue
1874 // 2: Job not done, continue
1875 enum { OK = 0 , IGR = 1, CONT = 2 };
1876 virtual int operator() ( wxDataViewTreeNode * node ) = 0;
1877 virtual int operator() ( void * n ) = 0;
1878 };
1879
1880 bool Walker( wxDataViewTreeNode * node, DoJob & func )
1881 {
1882 if( node==NULL )
1883 return false;
1884
1885 switch( func( node ) )
1886 {
1887 case DoJob::OK :
1888 return true;
1889 case DoJob::IGR:
1890 return false;
1891 case DoJob::CONT:
1892 default:
1893 ;
1894 }
1895
1896 wxDataViewTreeNodes nodes = node->GetNodes();
1897 wxDataViewTreeLeaves leaves = node->GetChildren();
1898
1899 int len_nodes = nodes.GetCount();
1900 int len = leaves.GetCount();
1901 int i = 0, nodes_i = 0;
1902
1903 for(; i < len; i ++ )
1904 {
1905 void * n = leaves[i];
1906 if( nodes_i < len_nodes && n == nodes[nodes_i]->GetItem().GetID() )
1907 {
1908 wxDataViewTreeNode * nd = nodes[nodes_i];
1909 nodes_i++;
1910
1911 if( Walker( nd , func ) )
1912 return true;
1913
1914 }
1915 else
1916 switch( func( n ) )
1917 {
1918 case DoJob::OK :
1919 return true;
1920 case DoJob::IGR:
1921 continue;
1922 case DoJob::CONT:
1923 default:
1924 ;
1925 }
1926 }
1927 return false;
1928 }
1929
1930 bool wxDataViewMainWindow::ItemAdded(const wxDataViewItem & parent, const wxDataViewItem & item)
1931 {
1932 if (IsVirtualList())
1933 {
1934 wxDataViewVirtualListModel *list_model =
1935 (wxDataViewVirtualListModel*) GetOwner()->GetModel();
1936 m_count = list_model->GetCount();
1937 UpdateDisplay();
1938 return true;
1939 }
1940
1941 SortPrepare();
1942
1943 wxDataViewTreeNode * node;
1944 node = FindNode(parent);
1945
1946 if( node == NULL )
1947 return false;
1948
1949 node->SetHasChildren( true );
1950
1951 if( g_model->IsContainer( item ) )
1952 {
1953 wxDataViewTreeNode * newnode = new wxDataViewTreeNode( node );
1954 newnode->SetItem(item);
1955 newnode->SetHasChildren( true );
1956 node->AddNode( newnode);
1957 }
1958 else
1959 node->AddLeaf( item.GetID() );
1960
1961 node->ChangeSubTreeCount(1);
1962
1963 m_count = -1;
1964 UpdateDisplay();
1965
1966 return true;
1967 }
1968
1969 static void DestroyTreeHelper( wxDataViewTreeNode * node);
1970
1971 bool wxDataViewMainWindow::ItemDeleted(const wxDataViewItem& parent,
1972 const wxDataViewItem& item)
1973 {
1974 if (IsVirtualList())
1975 {
1976 wxDataViewVirtualListModel *list_model =
1977 (wxDataViewVirtualListModel*) GetOwner()->GetModel();
1978 m_count = list_model->GetCount();
1979
1980 if( m_currentRow > GetRowCount() )
1981 m_currentRow = m_count - 1;
1982
1983 // TODO: why empty the entire selection?
1984 m_selection.Empty();
1985
1986 UpdateDisplay();
1987
1988 return true;
1989 }
1990
1991 wxDataViewTreeNode * node = FindNode(parent);
1992
1993 wxCHECK_MSG( node != NULL, false, "item not found" );
1994 wxCHECK_MSG( node->GetChildren().Index( item.GetID() ) != wxNOT_FOUND,
1995 false, "item not found" );
1996
1997 int sub = -1;
1998 node->GetChildren().Remove( item.GetID() );
1999 // Manipolate selection
2000 if( m_selection.GetCount() > 1 )
2001 {
2002 m_selection.Empty();
2003 }
2004 bool isContainer = false;
2005 wxDataViewTreeNodes nds = node->GetNodes();
2006 for (size_t i = 0; i < nds.GetCount(); i ++)
2007 {
2008 if (nds[i]->GetItem() == item)
2009 {
2010 isContainer = true;
2011 break;
2012 }
2013 }
2014 if( isContainer )
2015 {
2016 wxDataViewTreeNode * n = NULL;
2017 wxDataViewTreeNodes nodes = node->GetNodes();
2018 int len = nodes.GetCount();
2019 for( int i = 0; i < len; i ++)
2020 {
2021 if( nodes[i]->GetItem() == item )
2022 {
2023 n = nodes[i];
2024 break;
2025 }
2026 }
2027
2028 wxCHECK_MSG( n != NULL, false, "item not found" );
2029
2030 node->GetNodes().Remove( n );
2031 sub -= n->GetSubTreeCount();
2032 ::DestroyTreeHelper(n);
2033 }
2034 // Make the row number invalid and get a new valid one when user call GetRowCount
2035 m_count = -1;
2036 node->ChangeSubTreeCount(sub);
2037
2038 // Change the current row to the last row if the current exceed the max row number
2039 if( m_currentRow > GetRowCount() )
2040 m_currentRow = m_count - 1;
2041
2042 UpdateDisplay();
2043
2044 return true;
2045 }
2046
2047 bool wxDataViewMainWindow::ItemChanged(const wxDataViewItem & item)
2048 {
2049 SortPrepare();
2050 g_model->Resort();
2051
2052 // Send event
2053 wxWindow *parent = GetParent();
2054 wxDataViewEvent le(wxEVT_COMMAND_DATAVIEW_ITEM_VALUE_CHANGED, parent->GetId());
2055 le.SetEventObject(parent);
2056 le.SetModel(GetOwner()->GetModel());
2057 le.SetItem(item);
2058 parent->GetEventHandler()->ProcessEvent(le);
2059
2060 return true;
2061 }
2062
2063 bool wxDataViewMainWindow::ValueChanged( const wxDataViewItem & item, unsigned int col )
2064 {
2065 // NOTE: to be valid, we cannot use e.g. INT_MAX - 1
2066 /*#define MAX_VIRTUAL_WIDTH 100000
2067
2068 wxRect rect( 0, row*m_lineHeight, MAX_VIRTUAL_WIDTH, m_lineHeight );
2069 m_owner->CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
2070 Refresh( true, &rect );
2071
2072 return true;
2073 */
2074 SortPrepare();
2075 g_model->Resort();
2076
2077 // Send event
2078 wxWindow *parent = GetParent();
2079 wxDataViewEvent le(wxEVT_COMMAND_DATAVIEW_ITEM_VALUE_CHANGED, parent->GetId());
2080 le.SetEventObject(parent);
2081 le.SetModel(GetOwner()->GetModel());
2082 le.SetItem(item);
2083 le.SetColumn(col);
2084 le.SetDataViewColumn(GetOwner()->GetColumn(col));
2085 parent->GetEventHandler()->ProcessEvent(le);
2086
2087 return true;
2088 }
2089
2090 bool wxDataViewMainWindow::Cleared()
2091 {
2092 DestroyTree();
2093
2094 SortPrepare();
2095 BuildTree( GetOwner()->GetModel() );
2096
2097 UpdateDisplay();
2098
2099 return true;
2100 }
2101
2102 void wxDataViewMainWindow::UpdateDisplay()
2103 {
2104 m_dirty = true;
2105 m_underMouse = NULL;
2106 }
2107
2108 void wxDataViewMainWindow::OnInternalIdle()
2109 {
2110 wxWindow::OnInternalIdle();
2111
2112 if (m_dirty)
2113 {
2114 RecalculateDisplay();
2115 m_dirty = false;
2116 }
2117 }
2118
2119 void wxDataViewMainWindow::RecalculateDisplay()
2120 {
2121 wxDataViewModel *model = GetOwner()->GetModel();
2122 if (!model)
2123 {
2124 Refresh();
2125 return;
2126 }
2127
2128 int width = GetEndOfLastCol();
2129 int height = GetLineStart( GetRowCount() );
2130
2131 SetVirtualSize( width, height );
2132 GetOwner()->SetScrollRate( 10, m_lineHeight );
2133
2134 Refresh();
2135 }
2136
2137 void wxDataViewMainWindow::ScrollWindow( int dx, int dy, const wxRect *rect )
2138 {
2139 wxWindow::ScrollWindow( dx, dy, rect );
2140
2141 if (GetOwner()->m_headerArea)
2142 GetOwner()->m_headerArea->ScrollWindow( dx, 0 );
2143 }
2144
2145 void wxDataViewMainWindow::ScrollTo( int rows, int column )
2146 {
2147 int x, y;
2148 m_owner->GetScrollPixelsPerUnit( &x, &y );
2149 int sy = GetLineStart( rows )/y;
2150 int sx = 0;
2151 if( column != -1 )
2152 {
2153 wxRect rect = GetClientRect();
2154 int colnum = 0;
2155 int x_start, w = 0;
2156 int xx, yy, xe;
2157 m_owner->CalcUnscrolledPosition( rect.x, rect.y, &xx, &yy );
2158 for (x_start = 0; colnum < column; colnum++)
2159 {
2160 wxDataViewColumn *col = GetOwner()->GetColumnAt(colnum);
2161 if (col->IsHidden())
2162 continue; // skip it!
2163
2164 w = col->GetWidth();
2165 x_start += w;
2166 }
2167
2168 int x_end = x_start + w;
2169 xe = xx + rect.width;
2170 if( x_end > xe )
2171 {
2172 sx = ( xx + x_end - xe )/x;
2173 }
2174 if( x_start < xx )
2175 {
2176 sx = x_start/x;
2177 }
2178 }
2179 m_owner->Scroll( sx, sy );
2180 }
2181
2182 int wxDataViewMainWindow::GetCountPerPage() const
2183 {
2184 wxSize size = GetClientSize();
2185 return size.y / m_lineHeight;
2186 }
2187
2188 int wxDataViewMainWindow::GetEndOfLastCol() const
2189 {
2190 int width = 0;
2191 unsigned int i;
2192 for (i = 0; i < GetOwner()->GetColumnCount(); i++)
2193 {
2194 const wxDataViewColumn *c =
2195 const_cast<wxDataViewCtrl*>(GetOwner())->GetColumnAt( i );
2196
2197 if (!c->IsHidden())
2198 width += c->GetWidth();
2199 }
2200 return width;
2201 }
2202
2203 unsigned int wxDataViewMainWindow::GetFirstVisibleRow() const
2204 {
2205 int x = 0;
2206 int y = 0;
2207 m_owner->CalcUnscrolledPosition( x, y, &x, &y );
2208
2209 return GetLineAt( y );
2210 }
2211
2212 unsigned int wxDataViewMainWindow::GetLastVisibleRow()
2213 {
2214 wxSize client_size = GetClientSize();
2215 m_owner->CalcUnscrolledPosition( client_size.x, client_size.y,
2216 &client_size.x, &client_size.y );
2217
2218 // we should deal with the pixel here
2219 unsigned int row = GetLineAt(client_size.y) - 1;
2220
2221 return wxMin( GetRowCount()-1, row );
2222 }
2223
2224 unsigned int wxDataViewMainWindow::GetRowCount()
2225 {
2226 if ( m_count == -1 )
2227 {
2228 m_count = RecalculateCount();
2229 UpdateDisplay();
2230 }
2231 return m_count;
2232 }
2233
2234 void wxDataViewMainWindow::ChangeCurrentRow( unsigned int row )
2235 {
2236 m_currentRow = row;
2237
2238 // send event
2239 }
2240
2241 void wxDataViewMainWindow::SelectAllRows( bool on )
2242 {
2243 if (IsEmpty())
2244 return;
2245
2246 if (on)
2247 {
2248 m_selection.Clear();
2249 for (unsigned int i = 0; i < GetRowCount(); i++)
2250 m_selection.Add( i );
2251 Refresh();
2252 }
2253 else
2254 {
2255 unsigned int first_visible = GetFirstVisibleRow();
2256 unsigned int last_visible = GetLastVisibleRow();
2257 unsigned int i;
2258 for (i = 0; i < m_selection.GetCount(); i++)
2259 {
2260 unsigned int row = m_selection[i];
2261 if ((row >= first_visible) && (row <= last_visible))
2262 RefreshRow( row );
2263 }
2264 m_selection.Clear();
2265 }
2266 }
2267
2268 void wxDataViewMainWindow::SelectRow( unsigned int row, bool on )
2269 {
2270 if (m_selection.Index( row ) == wxNOT_FOUND)
2271 {
2272 if (on)
2273 {
2274 m_selection.Add( row );
2275 RefreshRow( row );
2276 }
2277 }
2278 else
2279 {
2280 if (!on)
2281 {
2282 m_selection.Remove( row );
2283 RefreshRow( row );
2284 }
2285 }
2286 }
2287
2288 void wxDataViewMainWindow::SelectRows( unsigned int from, unsigned int to, bool on )
2289 {
2290 if (from > to)
2291 {
2292 unsigned int tmp = from;
2293 from = to;
2294 to = tmp;
2295 }
2296
2297 unsigned int i;
2298 for (i = from; i <= to; i++)
2299 {
2300 if (m_selection.Index( i ) == wxNOT_FOUND)
2301 {
2302 if (on)
2303 m_selection.Add( i );
2304 }
2305 else
2306 {
2307 if (!on)
2308 m_selection.Remove( i );
2309 }
2310 }
2311 RefreshRows( from, to );
2312 }
2313
2314 void wxDataViewMainWindow::Select( const wxArrayInt& aSelections )
2315 {
2316 for (size_t i=0; i < aSelections.GetCount(); i++)
2317 {
2318 int n = aSelections[i];
2319
2320 m_selection.Add( n );
2321 RefreshRow( n );
2322 }
2323 }
2324
2325 void wxDataViewMainWindow::ReverseRowSelection( unsigned int row )
2326 {
2327 if (m_selection.Index( row ) == wxNOT_FOUND)
2328 m_selection.Add( row );
2329 else
2330 m_selection.Remove( row );
2331 RefreshRow( row );
2332 }
2333
2334 bool wxDataViewMainWindow::IsRowSelected( unsigned int row )
2335 {
2336 return (m_selection.Index( row ) != wxNOT_FOUND);
2337 }
2338
2339 void wxDataViewMainWindow::SendSelectionChangedEvent( const wxDataViewItem& item)
2340 {
2341 wxWindow *parent = GetParent();
2342 wxDataViewEvent le(wxEVT_COMMAND_DATAVIEW_SELECTION_CHANGED, parent->GetId());
2343
2344 le.SetEventObject(parent);
2345 le.SetModel(GetOwner()->GetModel());
2346 le.SetItem( item );
2347
2348 parent->GetEventHandler()->ProcessEvent(le);
2349 }
2350
2351 void wxDataViewMainWindow::RefreshRow( unsigned int row )
2352 {
2353 wxRect rect( 0, GetLineStart( row ), GetEndOfLastCol(), GetLineHeight( row ) );
2354 m_owner->CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
2355
2356 wxSize client_size = GetClientSize();
2357 wxRect client_rect( 0, 0, client_size.x, client_size.y );
2358 wxRect intersect_rect = client_rect.Intersect( rect );
2359 if (intersect_rect.width > 0)
2360 Refresh( true, &intersect_rect );
2361 }
2362
2363 void wxDataViewMainWindow::RefreshRows( unsigned int from, unsigned int to )
2364 {
2365 if (from > to)
2366 {
2367 unsigned int tmp = to;
2368 to = from;
2369 from = tmp;
2370 }
2371
2372 wxRect rect( 0, GetLineStart( from ), GetEndOfLastCol(), GetLineStart( (to-from+1) ) );
2373 m_owner->CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
2374
2375 wxSize client_size = GetClientSize();
2376 wxRect client_rect( 0, 0, client_size.x, client_size.y );
2377 wxRect intersect_rect = client_rect.Intersect( rect );
2378 if (intersect_rect.width > 0)
2379 Refresh( true, &intersect_rect );
2380 }
2381
2382 void wxDataViewMainWindow::RefreshRowsAfter( unsigned int firstRow )
2383 {
2384 wxSize client_size = GetClientSize();
2385 int start = GetLineStart( firstRow );
2386 m_owner->CalcScrolledPosition( start, 0, &start, NULL );
2387 if (start > client_size.y) return;
2388
2389 wxRect rect( 0, start, client_size.x, client_size.y - start );
2390
2391 Refresh( true, &rect );
2392 }
2393
2394 void wxDataViewMainWindow::OnArrowChar(unsigned int newCurrent, const wxKeyEvent& event)
2395 {
2396 wxCHECK_RET( newCurrent < GetRowCount(),
2397 wxT("invalid item index in OnArrowChar()") );
2398
2399 // if there is no selection, we cannot move it anywhere
2400 if (!HasCurrentRow())
2401 return;
2402
2403 unsigned int oldCurrent = m_currentRow;
2404
2405 // in single selection we just ignore Shift as we can't select several
2406 // items anyhow
2407 if ( event.ShiftDown() && !IsSingleSel() )
2408 {
2409 RefreshRow( oldCurrent );
2410
2411 ChangeCurrentRow( newCurrent );
2412
2413 // select all the items between the old and the new one
2414 if ( oldCurrent > newCurrent )
2415 {
2416 newCurrent = oldCurrent;
2417 oldCurrent = m_currentRow;
2418 }
2419
2420 SelectRows( oldCurrent, newCurrent, true );
2421 if (oldCurrent!=newCurrent)
2422 SendSelectionChangedEvent(GetItemByRow(m_selection[0]));
2423 }
2424 else // !shift
2425 {
2426 RefreshRow( oldCurrent );
2427
2428 // all previously selected items are unselected unless ctrl is held
2429 if ( !event.ControlDown() )
2430 SelectAllRows(false);
2431
2432 ChangeCurrentRow( newCurrent );
2433
2434 if ( !event.ControlDown() )
2435 {
2436 SelectRow( m_currentRow, true );
2437 SendSelectionChangedEvent(GetItemByRow(m_currentRow));
2438 }
2439 else
2440 RefreshRow( m_currentRow );
2441 }
2442
2443 GetOwner()->EnsureVisible( m_currentRow, -1 );
2444 }
2445
2446 wxRect wxDataViewMainWindow::GetLineRect( unsigned int row ) const
2447 {
2448 wxRect rect;
2449 rect.x = 0;
2450 rect.y = GetLineStart( row );
2451 rect.width = GetEndOfLastCol();
2452 rect.height = GetLineHeight( row );
2453
2454 return rect;
2455 }
2456
2457 int wxDataViewMainWindow::GetLineStart( unsigned int row ) const
2458 {
2459 const wxDataViewModel *model = GetOwner()->GetModel();
2460
2461 if (GetOwner()->GetWindowStyle() & wxDV_VARIABLE_LINE_HEIGHT)
2462 {
2463 // TODO make more efficient
2464
2465 int start = 0;
2466
2467 unsigned int r;
2468 for (r = 0; r < row; r++)
2469 {
2470 const wxDataViewTreeNode* node = GetTreeNodeByRow(r);
2471 if (!node) return start;
2472
2473 wxDataViewItem item = node->GetItem();
2474
2475 if (node && !node->HasChildren())
2476 {
2477 // Yes, if the node does not have any child, it must be a leaf which
2478 // mean that it is a temporarily created by GetTreeNodeByRow
2479 wxDELETE(node);
2480 }
2481
2482 unsigned int cols = GetOwner()->GetColumnCount();
2483 unsigned int col;
2484 int height = m_lineHeight;
2485 for (col = 0; col < cols; col++)
2486 {
2487 const wxDataViewColumn *column = GetOwner()->GetColumn(col);
2488 if (column->IsHidden())
2489 continue; // skip it!
2490
2491 if ((col != 0) &&
2492 model->IsContainer(item) &&
2493 !model->HasContainerColumns(item))
2494 continue; // skip it!
2495
2496 wxVariant value;
2497 model->GetValue( value, item, column->GetModelColumn() );
2498
2499 wxDataViewRenderer *renderer =
2500 const_cast<wxDataViewRenderer*>(column->GetRenderer());
2501 renderer->SetValue( value );
2502 height = wxMax( height, renderer->GetSize().y );
2503 }
2504
2505 start += height;
2506 }
2507
2508 return start;
2509 }
2510 else
2511 {
2512 return row * m_lineHeight;
2513 }
2514 }
2515
2516 int wxDataViewMainWindow::GetLineAt( unsigned int y ) const
2517 {
2518 const wxDataViewModel *model = GetOwner()->GetModel();
2519
2520 // check for the easy case first
2521 if ( !GetOwner()->HasFlag(wxDV_VARIABLE_LINE_HEIGHT) )
2522 return y / m_lineHeight;
2523
2524 // TODO make more efficient
2525 unsigned int row = 0;
2526 unsigned int yy = 0;
2527 for (;;)
2528 {
2529 const wxDataViewTreeNode* node = GetTreeNodeByRow(row);
2530 if (!node)
2531 {
2532 // not really correct...
2533 return row + ((y-yy) / m_lineHeight);
2534 }
2535
2536 wxDataViewItem item = node->GetItem();
2537
2538 if (node && !node->HasChildren())
2539 {
2540 // Yes, if the node does not have any child, it must be a leaf which
2541 // mean that it is a temporarily created by GetTreeNodeByRow
2542 wxDELETE(node);
2543 }
2544
2545 unsigned int cols = GetOwner()->GetColumnCount();
2546 unsigned int col;
2547 int height = m_lineHeight;
2548 for (col = 0; col < cols; col++)
2549 {
2550 const wxDataViewColumn *column = GetOwner()->GetColumn(col);
2551 if (column->IsHidden())
2552 continue; // skip it!
2553
2554 if ((col != 0) &&
2555 model->IsContainer(item) &&
2556 !model->HasContainerColumns(item))
2557 continue; // skip it!
2558
2559 wxVariant value;
2560 model->GetValue( value, item, column->GetModelColumn() );
2561
2562 wxDataViewRenderer *renderer =
2563 const_cast<wxDataViewRenderer*>(column->GetRenderer());
2564 renderer->SetValue( value );
2565 height = wxMax( height, renderer->GetSize().y );
2566 }
2567
2568 yy += height;
2569 if (y < yy)
2570 return row;
2571
2572 row++;
2573 }
2574 }
2575
2576 int wxDataViewMainWindow::GetLineHeight( unsigned int row ) const
2577 {
2578 const wxDataViewModel *model = GetOwner()->GetModel();
2579
2580 if (GetOwner()->GetWindowStyle() & wxDV_VARIABLE_LINE_HEIGHT)
2581 {
2582 wxASSERT( !IsVirtualList() );
2583
2584 const wxDataViewTreeNode* node = GetTreeNodeByRow(row);
2585 // wxASSERT( node );
2586 if (!node) return m_lineHeight;
2587
2588 wxDataViewItem item = node->GetItem();
2589
2590 if (node && !node->HasChildren())
2591 {
2592 // Yes, if the node does not have any child, it must be a leaf which
2593 // mean that it is a temporarily created by GetTreeNodeByRow
2594 wxDELETE(node);
2595 }
2596
2597 int height = m_lineHeight;
2598
2599 unsigned int cols = GetOwner()->GetColumnCount();
2600 unsigned int col;
2601 for (col = 0; col < cols; col++)
2602 {
2603 const wxDataViewColumn *column = GetOwner()->GetColumn(col);
2604 if (column->IsHidden())
2605 continue; // skip it!
2606
2607 if ((col != 0) &&
2608 model->IsContainer(item) &&
2609 !model->HasContainerColumns(item))
2610 continue; // skip it!
2611
2612 wxVariant value;
2613 model->GetValue( value, item, column->GetModelColumn() );
2614
2615 wxDataViewRenderer *renderer =
2616 const_cast<wxDataViewRenderer*>(column->GetRenderer());
2617 renderer->SetValue( value );
2618 height = wxMax( height, renderer->GetSize().y );
2619 }
2620
2621 return height;
2622 }
2623 else
2624 {
2625 return m_lineHeight;
2626 }
2627 }
2628
2629 class RowToItemJob: public DoJob
2630 {
2631 public:
2632 RowToItemJob( unsigned int row , int current )
2633 { this->row = row; this->current = current; }
2634 virtual ~RowToItemJob() {}
2635
2636 virtual int operator() ( wxDataViewTreeNode * node )
2637 {
2638 current ++;
2639 if( current == static_cast<int>(row))
2640 {
2641 ret = node->GetItem();
2642 return DoJob::OK;
2643 }
2644
2645 if( node->GetSubTreeCount() + current < static_cast<int>(row) )
2646 {
2647 current += node->GetSubTreeCount();
2648 return DoJob::IGR;
2649 }
2650 else
2651 {
2652 // If the current has no child node, we can find the desired item of the row
2653 // number directly.
2654 // This if can speed up finding in some case, and will has a very good effect
2655 // when it comes to list view
2656 if( node->GetNodes().GetCount() == 0)
2657 {
2658 int index = static_cast<int>(row) - current - 1;
2659 ret = node->GetChildren().Item( index );
2660 return DoJob::OK;
2661 }
2662 return DoJob::CONT;
2663 }
2664 }
2665
2666 virtual int operator() ( void * n )
2667 {
2668 current ++;
2669 if( current == static_cast<int>(row))
2670 {
2671 ret = wxDataViewItem( n );
2672 return DoJob::OK;
2673 }
2674 return DoJob::CONT;
2675 }
2676
2677 wxDataViewItem GetResult() const
2678 { return ret; }
2679
2680 private:
2681 unsigned int row;
2682 int current;
2683 wxDataViewItem ret;
2684 };
2685
2686 wxDataViewItem wxDataViewMainWindow::GetItemByRow(unsigned int row) const
2687 {
2688 if (IsVirtualList())
2689 {
2690 return wxDataViewItem( wxUIntToPtr(row+1) );
2691 }
2692 else
2693 {
2694 RowToItemJob job( row, -2 );
2695 Walker( m_root , job );
2696 return job.GetResult();
2697 }
2698 }
2699
2700 class RowToTreeNodeJob: public DoJob
2701 {
2702 public:
2703 RowToTreeNodeJob( unsigned int row , int current, wxDataViewTreeNode * node )
2704 {
2705 this->row = row;
2706 this->current = current;
2707 ret = NULL;
2708 parent = node;
2709 }
2710 virtual ~RowToTreeNodeJob(){ }
2711
2712 virtual int operator() ( wxDataViewTreeNode * node )
2713 {
2714 current ++;
2715 if( current == static_cast<int>(row))
2716 {
2717 ret = node;
2718 return DoJob::OK;
2719 }
2720
2721 if( node->GetSubTreeCount() + current < static_cast<int>(row) )
2722 {
2723 current += node->GetSubTreeCount();
2724 return DoJob::IGR;
2725 }
2726 else
2727 {
2728 parent = node;
2729
2730 // If the current node has no children, we can find the desired item of the
2731 // row number directly.
2732 // This if can speed up finding in some case, and will have a very good
2733 // effect for list views.
2734 if( node->GetNodes().GetCount() == 0)
2735 {
2736 int index = static_cast<int>(row) - current - 1;
2737 void * n = node->GetChildren().Item( index );
2738 ret = new wxDataViewTreeNode( parent );
2739 ret->SetItem( wxDataViewItem( n ));
2740 ret->SetHasChildren(false);
2741 return DoJob::OK;
2742 }
2743 return DoJob::CONT;
2744 }
2745 }
2746
2747 virtual int operator() ( void * n )
2748 {
2749 current ++;
2750 if( current == static_cast<int>(row))
2751 {
2752 ret = new wxDataViewTreeNode( parent );
2753 ret->SetItem( wxDataViewItem( n ));
2754 ret->SetHasChildren(false);
2755 return DoJob::OK;
2756 }
2757
2758 return DoJob::CONT;
2759 }
2760
2761 wxDataViewTreeNode * GetResult() const
2762 { return ret; }
2763
2764 private:
2765 unsigned int row;
2766 int current;
2767 wxDataViewTreeNode * ret;
2768 wxDataViewTreeNode * parent;
2769 };
2770
2771 wxDataViewTreeNode * wxDataViewMainWindow::GetTreeNodeByRow(unsigned int row) const
2772 {
2773 wxASSERT( !IsVirtualList() );
2774
2775 RowToTreeNodeJob job( row , -2, m_root );
2776 Walker( m_root , job );
2777 return job.GetResult();
2778 }
2779
2780 wxDataViewEvent wxDataViewMainWindow::SendExpanderEvent( wxEventType type,
2781 const wxDataViewItem & item )
2782 {
2783 wxWindow *parent = GetParent();
2784 wxDataViewEvent le(type, parent->GetId());
2785
2786 le.SetEventObject(parent);
2787 le.SetModel(GetOwner()->GetModel());
2788 le.SetItem( item );
2789
2790 parent->GetEventHandler()->ProcessEvent(le);
2791 return le;
2792 }
2793
2794 bool wxDataViewMainWindow::IsExpanded( unsigned int row ) const
2795 {
2796 if (IsVirtualList())
2797 return false;
2798
2799 wxDataViewTreeNode * node = GetTreeNodeByRow(row);
2800 if (!node)
2801 return false;
2802
2803 if (!node->HasChildren())
2804 {
2805 delete node;
2806 return false;
2807 }
2808
2809 return node->IsOpen();
2810 }
2811
2812 bool wxDataViewMainWindow::HasChildren( unsigned int row ) const
2813 {
2814 if (IsVirtualList())
2815 return false;
2816
2817 wxDataViewTreeNode * node = GetTreeNodeByRow(row);
2818 if (!node)
2819 return false;
2820
2821 if (!node->HasChildren())
2822 {
2823 delete node;
2824 return false;
2825 }
2826
2827 return true;
2828 }
2829
2830 void wxDataViewMainWindow::Expand( unsigned int row )
2831 {
2832 if (IsVirtualList())
2833 return;
2834
2835 wxDataViewTreeNode * node = GetTreeNodeByRow(row);
2836 if (!node)
2837 return;
2838
2839 if (!node->HasChildren())
2840 {
2841 delete node;
2842 return;
2843 }
2844
2845 if (!node->IsOpen())
2846 {
2847 wxDataViewEvent e =
2848 SendExpanderEvent(wxEVT_COMMAND_DATAVIEW_ITEM_EXPANDING, node->GetItem());
2849
2850 // Check if the user prevent expanding
2851 if( e.GetSkipped() )
2852 return;
2853
2854 node->ToggleOpen();
2855
2856 // build the children of current node
2857 if( node->GetChildrenNumber() == 0 )
2858 {
2859 SortPrepare();
2860 ::BuildTreeHelper(GetOwner()->GetModel(), node->GetItem(), node);
2861 }
2862
2863 // By expanding the node all row indices that are currently in the selection list
2864 // and are greater than our node have become invalid. So we have to correct that now.
2865 const unsigned rowAdjustment = node->GetSubTreeCount();
2866 for(unsigned i=0; i<m_selection.size(); ++i)
2867 {
2868 const unsigned testRow = m_selection[i];
2869 // all rows above us are not affected, so skip them
2870 if(testRow <= row)
2871 continue;
2872
2873 m_selection[i] += rowAdjustment;
2874 }
2875
2876 if(m_currentRow > row)
2877 ChangeCurrentRow(m_currentRow + rowAdjustment);
2878
2879 m_count = -1;
2880 UpdateDisplay();
2881 // Send the expanded event
2882 SendExpanderEvent(wxEVT_COMMAND_DATAVIEW_ITEM_EXPANDED,node->GetItem());
2883 }
2884 }
2885
2886 void wxDataViewMainWindow::Collapse(unsigned int row)
2887 {
2888 if (IsVirtualList())
2889 return;
2890
2891 wxDataViewTreeNode *node = GetTreeNodeByRow(row);
2892 if (!node)
2893 return;
2894
2895 if (!node->HasChildren())
2896 {
2897 delete node;
2898 return;
2899 }
2900
2901 if (node->IsOpen())
2902 {
2903 wxDataViewEvent e =
2904 SendExpanderEvent(wxEVT_COMMAND_DATAVIEW_ITEM_COLLAPSING,node->GetItem());
2905 if( e.GetSkipped() )
2906 return;
2907
2908 // Find out if there are selected items below the current node.
2909 bool selectCollapsingRow = false;
2910 const unsigned rowAdjustment = node->GetSubTreeCount();
2911 unsigned maxRowToBeTested = row + rowAdjustment;
2912 for(unsigned i=0; i<m_selection.size(); ++i)
2913 {
2914 const unsigned testRow = m_selection[i];
2915 if(testRow > row && testRow <= maxRowToBeTested)
2916 {
2917 selectCollapsingRow = true;
2918 // get out as soon as we have found a node that is selected
2919 break;
2920 }
2921 }
2922
2923 node->ToggleOpen();
2924
2925 // If the node to be closed has selected items the user won't see those any longer.
2926 // We select the collapsing node in this case.
2927 if(selectCollapsingRow)
2928 {
2929 SelectAllRows(false);
2930 ChangeCurrentRow(row);
2931 SelectRow(row, true);
2932 SendSelectionChangedEvent(GetItemByRow(row));
2933 }
2934 else
2935 {
2936 // if there were no selected items below our node we still need to "fix" the
2937 // selection list to adjust for the changing of the row indices.
2938 // We actually do the opposite of what we are doing in Expand().
2939 for(unsigned i=0; i<m_selection.size(); ++i)
2940 {
2941 const unsigned testRow = m_selection[i];
2942 // all rows above us are not affected, so skip them
2943 if(testRow <= row)
2944 continue;
2945
2946 m_selection[i] -= rowAdjustment;
2947 }
2948
2949 // if the "current row" is being collapsed away we change it to the current row ;-)
2950 if(m_currentRow > row && m_currentRow <= maxRowToBeTested)
2951 ChangeCurrentRow(row);
2952 else if(m_currentRow > row)
2953 ChangeCurrentRow(m_currentRow - rowAdjustment);
2954 }
2955
2956 m_count = -1;
2957 UpdateDisplay();
2958 SendExpanderEvent(wxEVT_COMMAND_DATAVIEW_ITEM_COLLAPSED,node->GetItem());
2959 }
2960 }
2961
2962 wxDataViewTreeNode * wxDataViewMainWindow::FindNode( const wxDataViewItem & item )
2963 {
2964 wxDataViewModel * model = GetOwner()->GetModel();
2965 if( model == NULL )
2966 return NULL;
2967
2968 if (!item.IsOk())
2969 return m_root;
2970
2971 // Compose the a parent-chain of the finding item
2972 ItemList list;
2973 list.DeleteContents( true );
2974 wxDataViewItem it( item );
2975 while( it.IsOk() )
2976 {
2977 wxDataViewItem * pItem = new wxDataViewItem( it );
2978 list.Insert( pItem );
2979 it = model->GetParent( it );
2980 }
2981
2982 // Find the item along the parent-chain.
2983 // This algorithm is designed to speed up the node-finding method
2984 wxDataViewTreeNode * node = m_root;
2985 for( ItemList::const_iterator iter = list.begin(); iter !=list.end(); iter++ )
2986 {
2987 if( node->HasChildren() )
2988 {
2989 if( node->GetChildrenNumber() == 0 )
2990 {
2991 SortPrepare();
2992 ::BuildTreeHelper(model, node->GetItem(), node);
2993 }
2994
2995 wxDataViewTreeNodes nodes = node->GetNodes();
2996 unsigned int i;
2997 bool found = false;
2998
2999 for (i = 0; i < nodes.GetCount(); i ++)
3000 {
3001 if (nodes[i]->GetItem() == (**iter))
3002 {
3003 if (nodes[i]->GetItem() == item)
3004 return nodes[i];
3005
3006 node = nodes[i];
3007 found = true;
3008 break;
3009 }
3010 }
3011 if (!found)
3012 return NULL;
3013 }
3014 else
3015 return NULL;
3016 }
3017 return NULL;
3018 }
3019
3020 void wxDataViewMainWindow::HitTest( const wxPoint & point, wxDataViewItem & item,
3021 wxDataViewColumn* &column )
3022 {
3023 wxDataViewColumn *col = NULL;
3024 unsigned int cols = GetOwner()->GetColumnCount();
3025 unsigned int colnum = 0;
3026 int x, y;
3027 m_owner->CalcUnscrolledPosition( point.x, point.y, &x, &y );
3028 for (unsigned x_start = 0; colnum < cols; colnum++)
3029 {
3030 col = GetOwner()->GetColumnAt(colnum);
3031 if (col->IsHidden())
3032 continue; // skip it!
3033
3034 unsigned int w = col->GetWidth();
3035 if (x_start+w >= (unsigned int)x)
3036 break;
3037
3038 x_start += w;
3039 }
3040
3041 column = col;
3042 item = GetItemByRow( GetLineAt( y ) );
3043 }
3044
3045 wxRect wxDataViewMainWindow::GetItemRect( const wxDataViewItem & item,
3046 const wxDataViewColumn* column )
3047 {
3048 int xpos = 0;
3049 int width = 0;
3050
3051 unsigned int cols = GetOwner()->GetColumnCount();
3052 // If column is null the loop will compute the combined width of all columns.
3053 // Otherwise, it will compute the x position of the column we are looking for.
3054 for (unsigned int i = 0; i < cols; i++)
3055 {
3056 wxDataViewColumn* col = GetOwner()->GetColumnAt( i );
3057
3058 if (col == column)
3059 break;
3060
3061 if (col->IsHidden())
3062 continue; // skip it!
3063
3064 xpos += col->GetWidth();
3065 width += col->GetWidth();
3066 }
3067
3068 if(column != 0)
3069 {
3070 // If we have a column, we need can get its width directly.
3071 if(column->IsHidden())
3072 width = 0;
3073 else
3074 width = column->GetWidth();
3075
3076 }
3077 else
3078 {
3079 // If we have no column, we reset the x position back to zero.
3080 xpos = 0;
3081 }
3082
3083 // we have to take an expander column into account and compute its indentation
3084 // to get the correct x position where the actual text is
3085 int indent = 0;
3086 int row = GetRowByItem(item);
3087 if (!IsVirtualList() && (column == 0 || GetOwner()->GetExpanderColumn() == column) )
3088 {
3089 wxDataViewTreeNode* node = GetTreeNodeByRow(row);
3090 indent = GetOwner()->GetIndent() * node->GetIndentLevel();
3091 indent = indent + m_lineHeight; // use m_lineHeight as the width of the expander
3092
3093 if(!node->HasChildren())
3094 delete node;
3095 }
3096
3097 wxRect itemRect( xpos + indent,
3098 GetLineStart( row ),
3099 width - indent,
3100 GetLineHeight( row ) );
3101
3102 GetOwner()->CalcScrolledPosition( itemRect.x, itemRect.y,
3103 &itemRect.x, &itemRect.y );
3104
3105 return itemRect;
3106 }
3107
3108 int wxDataViewMainWindow::RecalculateCount()
3109 {
3110 if (IsVirtualList())
3111 {
3112 wxDataViewVirtualListModel *list_model =
3113 (wxDataViewVirtualListModel*) GetOwner()->GetModel();
3114
3115 return list_model->GetCount();
3116 }
3117 else
3118 {
3119 return m_root->GetSubTreeCount();
3120 }
3121 }
3122
3123 class ItemToRowJob : public DoJob
3124 {
3125 public:
3126 ItemToRowJob(const wxDataViewItem& item_, ItemList::const_iterator iter)
3127 : m_iter(iter),
3128 item(item_)
3129 {
3130 ret = -1;
3131 }
3132
3133 // Maybe binary search will help to speed up this process
3134 virtual int operator() ( wxDataViewTreeNode * node)
3135 {
3136 ret ++;
3137 if( node->GetItem() == item )
3138 {
3139 return DoJob::OK;
3140 }
3141
3142 if( node->GetItem() == **m_iter )
3143 {
3144 m_iter++;
3145 return DoJob::CONT;
3146 }
3147 else
3148 {
3149 ret += node->GetSubTreeCount();
3150 return DoJob::IGR;
3151 }
3152
3153 }
3154
3155 virtual int operator() ( void * n )
3156 {
3157 ret ++;
3158 if( n == item.GetID() )
3159 return DoJob::OK;
3160 return DoJob::CONT;
3161 }
3162
3163 // the row number is begin from zero
3164 int GetResult() const
3165 { return ret -1; }
3166
3167 private:
3168 ItemList::const_iterator m_iter;
3169 wxDataViewItem item;
3170 int ret;
3171
3172 };
3173
3174 int wxDataViewMainWindow::GetRowByItem(const wxDataViewItem & item) const
3175 {
3176 const wxDataViewModel * model = GetOwner()->GetModel();
3177 if( model == NULL )
3178 return -1;
3179
3180 if (IsVirtualList())
3181 {
3182 return wxPtrToUInt( item.GetID() ) -1;
3183 }
3184 else
3185 {
3186 if( !item.IsOk() )
3187 return -1;
3188
3189 // Compose the a parent-chain of the finding item
3190 ItemList list;
3191 wxDataViewItem * pItem;
3192 list.DeleteContents( true );
3193 wxDataViewItem it( item );
3194 while( it.IsOk() )
3195 {
3196 pItem = new wxDataViewItem( it );
3197 list.Insert( pItem );
3198 it = model->GetParent( it );
3199 }
3200 pItem = new wxDataViewItem( );
3201 list.Insert( pItem );
3202
3203 ItemToRowJob job( item, list.begin() );
3204 Walker(m_root , job );
3205 return job.GetResult();
3206 }
3207 }
3208
3209 static void BuildTreeHelper( wxDataViewModel * model, wxDataViewItem & item,
3210 wxDataViewTreeNode * node)
3211 {
3212 if( !model->IsContainer( item ) )
3213 return;
3214
3215 wxDataViewItemArray children;
3216 unsigned int num = model->GetChildren( item, children);
3217
3218 unsigned int index = 0;
3219 while( index < num )
3220 {
3221 if( model->IsContainer( children[index] ) )
3222 {
3223 wxDataViewTreeNode * n = new wxDataViewTreeNode( node );
3224 n->SetItem(children[index]);
3225 n->SetHasChildren( true );
3226 node->AddNode( n );
3227 }
3228 else
3229 {
3230 node->AddLeaf( children[index].GetID() );
3231 }
3232 index ++;
3233 }
3234 node->SetSubTreeCount( num );
3235 wxDataViewTreeNode * n = node->GetParent();
3236 if( n != NULL)
3237 n->ChangeSubTreeCount(num);
3238
3239 }
3240
3241 void wxDataViewMainWindow::BuildTree(wxDataViewModel * model)
3242 {
3243 DestroyTree();
3244
3245 if (GetOwner()->GetModel()->IsVirtualListModel())
3246 {
3247 m_count = -1;
3248 return;
3249 }
3250
3251 m_root = new wxDataViewTreeNode( NULL );
3252 m_root->SetHasChildren(true);
3253
3254 // First we define a invalid item to fetch the top-level elements
3255 wxDataViewItem item;
3256 SortPrepare();
3257 BuildTreeHelper( model, item, m_root);
3258 m_count = -1;
3259 }
3260
3261 static void DestroyTreeHelper( wxDataViewTreeNode * node )
3262 {
3263 if( node->GetNodeNumber() != 0 )
3264 {
3265 int len = node->GetNodeNumber();
3266 wxDataViewTreeNodes& nodes = node->GetNodes();
3267 for (int i = 0; i < len; i++)
3268 DestroyTreeHelper(nodes[i]);
3269 }
3270 delete node;
3271 }
3272
3273 void wxDataViewMainWindow::DestroyTree()
3274 {
3275 if (!IsVirtualList())
3276 {
3277 ::DestroyTreeHelper(m_root);
3278 m_count = 0;
3279 m_root = NULL;
3280 }
3281 }
3282
3283 void wxDataViewMainWindow::OnChar( wxKeyEvent &event )
3284 {
3285 if ( GetParent()->HandleAsNavigationKey(event) )
3286 return;
3287
3288 // no item -> nothing to do
3289 if (!HasCurrentRow())
3290 {
3291 event.Skip();
3292 return;
3293 }
3294
3295 // don't use m_linesPerPage directly as it might not be computed yet
3296 const int pageSize = GetCountPerPage();
3297 wxCHECK_RET( pageSize, wxT("should have non zero page size") );
3298
3299 switch ( event.GetKeyCode() )
3300 {
3301 case WXK_RETURN:
3302 {
3303 wxWindow *parent = GetParent();
3304 wxDataViewEvent le(wxEVT_COMMAND_DATAVIEW_ITEM_ACTIVATED,
3305 parent->GetId());
3306 le.SetItem( GetItemByRow(m_currentRow) );
3307 le.SetEventObject(parent);
3308 le.SetModel(GetOwner()->GetModel());
3309
3310 parent->GetEventHandler()->ProcessEvent(le);
3311 }
3312 break;
3313
3314 case WXK_UP:
3315 if ( m_currentRow > 0 )
3316 OnArrowChar( m_currentRow - 1, event );
3317 break;
3318
3319 case WXK_DOWN:
3320 if ( m_currentRow < GetRowCount() - 1 )
3321 OnArrowChar( m_currentRow + 1, event );
3322 break;
3323 // Add the process for tree expanding/collapsing
3324 case WXK_LEFT:
3325 {
3326 if (IsVirtualList())
3327 break;
3328
3329 wxDataViewTreeNode* node = GetTreeNodeByRow(m_currentRow);
3330 if (!node)
3331 break;
3332
3333 if (node->HasChildren() && node->IsOpen())
3334 {
3335 Collapse(m_currentRow);
3336 }
3337 else // if the node is already closed we move the selection to its parent
3338 {
3339 wxDataViewTreeNode *parent_node = node->GetParent();
3340
3341 if(!node->HasChildren())
3342 delete node;
3343
3344 if (parent_node)
3345 {
3346 int parent = GetRowByItem( parent_node->GetItem() );
3347 if ( parent >= 0 )
3348 {
3349 unsigned int row = m_currentRow;
3350 SelectRow( row, false);
3351 SelectRow( parent, true );
3352 ChangeCurrentRow( parent );
3353 GetOwner()->EnsureVisible( parent, -1 );
3354 SendSelectionChangedEvent( parent_node->GetItem() );
3355 }
3356 }
3357 }
3358 break;
3359 }
3360 case WXK_RIGHT:
3361 {
3362 if (!IsExpanded( m_currentRow ))
3363 Expand( m_currentRow );
3364 else
3365 {
3366 unsigned int row = m_currentRow;
3367 SelectRow( row, false );
3368 SelectRow( row + 1, true );
3369 ChangeCurrentRow( row + 1 );
3370 GetOwner()->EnsureVisible( row + 1, -1 );
3371 SendSelectionChangedEvent( GetItemByRow(row+1) );
3372 }
3373 break;
3374 }
3375 case WXK_END:
3376 {
3377 if (!IsEmpty())
3378 OnArrowChar( GetRowCount() - 1, event );
3379 break;
3380 }
3381 case WXK_HOME:
3382 if (!IsEmpty())
3383 OnArrowChar( 0, event );
3384 break;
3385
3386 case WXK_PAGEUP:
3387 {
3388 int steps = pageSize - 1;
3389 int index = m_currentRow - steps;
3390 if (index < 0)
3391 index = 0;
3392
3393 OnArrowChar( index, event );
3394 }
3395 break;
3396
3397 case WXK_PAGEDOWN:
3398 {
3399 int steps = pageSize - 1;
3400 unsigned int index = m_currentRow + steps;
3401 unsigned int count = GetRowCount();
3402 if ( index >= count )
3403 index = count - 1;
3404
3405 OnArrowChar( index, event );
3406 }
3407 break;
3408
3409 case WXK_F2:
3410 {
3411 if(m_selection.size() == 1)
3412 {
3413 // TODO: we need to revise that when we have a concept for a 'current column'
3414 GetOwner()->StartEditor(GetItemByRow(m_selection[0]), 0);
3415 }
3416 }
3417 break;
3418
3419 default:
3420 event.Skip();
3421 }
3422 }
3423
3424 void wxDataViewMainWindow::OnMouse( wxMouseEvent &event )
3425 {
3426 if (event.GetEventType() == wxEVT_MOUSEWHEEL)
3427 {
3428 // let the base handle mouse wheel events.
3429 event.Skip();
3430 return;
3431 }
3432
3433 int x = event.GetX();
3434 int y = event.GetY();
3435 m_owner->CalcUnscrolledPosition( x, y, &x, &y );
3436 wxDataViewColumn *col = NULL;
3437
3438 int xpos = 0;
3439 unsigned int cols = GetOwner()->GetColumnCount();
3440 unsigned int i;
3441 for (i = 0; i < cols; i++)
3442 {
3443 wxDataViewColumn *c = GetOwner()->GetColumnAt( i );
3444 if (c->IsHidden())
3445 continue; // skip it!
3446
3447 if (x < xpos + c->GetWidth())
3448 {
3449 col = c;
3450 break;
3451 }
3452 xpos += c->GetWidth();
3453 }
3454 if (!col)
3455 return;
3456
3457 wxDataViewRenderer *cell = col->GetRenderer();
3458 unsigned int current = GetLineAt( y );
3459 if ((current >= GetRowCount()) || (x > GetEndOfLastCol()))
3460 {
3461 // Unselect all if below the last row ?
3462 return;
3463 }
3464
3465 // Test whether the mouse is hovered on the tree item button
3466 bool hoverOverExpander = false;
3467 if ((!IsVirtualList()) && (GetOwner()->GetExpanderColumn() == col))
3468 {
3469 wxDataViewTreeNode * node = GetTreeNodeByRow(current);
3470 if( node!=NULL && node->HasChildren() )
3471 {
3472 int indent = node->GetIndentLevel();
3473 indent = GetOwner()->GetIndent()*indent;
3474
3475 // we make the rectangle we are looking in a bit bigger than the actual
3476 // visual expander so the user can hit that little thing reliably
3477 wxRect rect( xpos + indent,
3478 GetLineStart( current ) + (GetLineHeight(current) - m_lineHeight)/2,
3479 m_lineHeight, m_lineHeight);
3480 if( rect.Contains(x, y) )
3481 {
3482 // So the mouse is over the expander
3483 hoverOverExpander = true;
3484 if (m_underMouse && m_underMouse != node)
3485 {
3486 // wxLogMessage("Undo the row: %d", GetRowByItem(m_underMouse->GetItem()));
3487 RefreshRow(GetRowByItem(m_underMouse->GetItem()));
3488 }
3489 if (m_underMouse != node)
3490 {
3491 // wxLogMessage("Do the row: %d", current);
3492 RefreshRow(current);
3493 }
3494 m_underMouse = node;
3495 }
3496 }
3497 if (node!=NULL && !node->HasChildren())
3498 delete node;
3499 }
3500 if (!hoverOverExpander)
3501 {
3502 if (m_underMouse != NULL)
3503 {
3504 // wxLogMessage("Undo the row: %d", GetRowByItem(m_underMouse->GetItem()));
3505 RefreshRow(GetRowByItem(m_underMouse->GetItem()));
3506 m_underMouse = NULL;
3507 }
3508 }
3509
3510 wxDataViewModel *model = GetOwner()->GetModel();
3511
3512 #if wxUSE_DRAG_AND_DROP
3513 if (event.Dragging())
3514 {
3515 if (m_dragCount == 0)
3516 {
3517 // we have to report the raw, physical coords as we want to be
3518 // able to call HitTest(event.m_pointDrag) from the user code to
3519 // get the item being dragged
3520 m_dragStart = event.GetPosition();
3521 }
3522
3523 m_dragCount++;
3524
3525 if (m_dragCount != 3)
3526 return;
3527
3528 if (event.LeftIsDown())
3529 {
3530 m_owner->CalcUnscrolledPosition( m_dragStart.x, m_dragStart.y,
3531 &m_dragStart.x, &m_dragStart.y );
3532 unsigned int drag_item_row = GetLineAt( m_dragStart.y );
3533 wxDataViewItem item = GetItemByRow( drag_item_row );
3534
3535 // Notify cell about drag
3536 wxDataViewEvent event( wxEVT_COMMAND_DATAVIEW_ITEM_BEGIN_DRAG, m_owner->GetId() );
3537 event.SetEventObject( m_owner );
3538 event.SetItem( item );
3539 event.SetModel( model );
3540 if (!m_owner->HandleWindowEvent( event ))
3541 return;
3542
3543 if (!event.IsAllowed())
3544 return;
3545
3546 wxDataObject *obj = event.GetDataObject();
3547 if (!obj)
3548 return;
3549
3550 wxDataViewDropSource drag( this, drag_item_row );
3551 drag.SetData( *obj );
3552 /* wxDragResult res = */ drag.DoDragDrop();
3553 delete obj;
3554 }
3555 return;
3556 }
3557 else
3558 {
3559 m_dragCount = 0;
3560 }
3561 #endif // wxUSE_DRAG_AND_DROP
3562
3563 bool simulateClick = false;
3564
3565 if (event.ButtonDClick())
3566 {
3567 m_renameTimer->Stop();
3568 m_lastOnSame = false;
3569 }
3570
3571 wxDataViewItem item = GetItemByRow(current);
3572 bool ignore_other_columns =
3573 ((GetOwner()->GetExpanderColumn() != col) &&
3574 (model->IsContainer(item)) &&
3575 (!model->HasContainerColumns(item)));
3576
3577 if (event.LeftDClick())
3578 {
3579 if(hoverOverExpander)
3580 {
3581 // a double click on the expander will be converted into a "simulated" normal click
3582 simulateClick = true;
3583 }
3584 else if ( current == m_lineLastClicked )
3585 {
3586 if ((!ignore_other_columns) && (cell->GetMode() == wxDATAVIEW_CELL_ACTIVATABLE))
3587 {
3588 if ( wxDataViewCustomRenderer *custom = cell->WXGetAsCustom() )
3589 {
3590 wxVariant value;
3591 model->GetValue( value, item, col->GetModelColumn() );
3592 custom->SetValue( value );
3593 wxRect cell_rect( xpos, GetLineStart( current ),
3594 col->GetWidth(), GetLineHeight( current ) );
3595 custom->Activate( cell_rect, model, item, col->GetModelColumn() );
3596 }
3597 }
3598 else
3599 {
3600 wxWindow *parent = GetParent();
3601 wxDataViewEvent le(wxEVT_COMMAND_DATAVIEW_ITEM_ACTIVATED, parent->GetId());
3602 le.SetItem( item );
3603 le.SetEventObject(parent);
3604 le.SetModel(GetOwner()->GetModel());
3605
3606 parent->GetEventHandler()->ProcessEvent(le);
3607 }
3608 return;
3609 }
3610 else
3611 {
3612 // The first click was on another item, so don't interpret this as
3613 // a double click, but as a simple click instead
3614 simulateClick = true;
3615 }
3616 }
3617
3618 if (event.LeftUp() && !hoverOverExpander)
3619 {
3620 if (m_lineSelectSingleOnUp != (unsigned int)-1)
3621 {
3622 // select single line
3623 SelectAllRows( false );
3624 SelectRow( m_lineSelectSingleOnUp, true );
3625 SendSelectionChangedEvent( GetItemByRow(m_lineSelectSingleOnUp) );
3626 }
3627
3628 // If the user click the expander, we do not do editing even if the column
3629 // with expander are editable
3630 if (m_lastOnSame && !ignore_other_columns)
3631 {
3632 if ((col == m_currentCol) && (current == m_currentRow) &&
3633 (cell->GetMode() & wxDATAVIEW_CELL_EDITABLE) )
3634 {
3635 m_renameTimer->Start( 100, true );
3636 }
3637 }
3638
3639 m_lastOnSame = false;
3640 m_lineSelectSingleOnUp = (unsigned int)-1;
3641 }
3642 else if(!event.LeftUp())
3643 {
3644 // This is necessary, because after a DnD operation in
3645 // from and to ourself, the up event is swallowed by the
3646 // DnD code. So on next non-up event (which means here and
3647 // now) m_lineSelectSingleOnUp should be reset.
3648 m_lineSelectSingleOnUp = (unsigned int)-1;
3649 }
3650
3651 if (event.RightDown())
3652 {
3653 m_lineBeforeLastClicked = m_lineLastClicked;
3654 m_lineLastClicked = current;
3655
3656 // If the item is already selected, do not update the selection.
3657 // Multi-selections should not be cleared if a selected item is clicked.
3658 if (!IsRowSelected(current))
3659 {
3660 SelectAllRows(false);
3661 ChangeCurrentRow(current);
3662 SelectRow(m_currentRow,true);
3663 SendSelectionChangedEvent(GetItemByRow( m_currentRow ) );
3664 }
3665 }
3666 else if (event.RightUp())
3667 {
3668 wxVariant value;
3669 model->GetValue( value, item, col->GetModelColumn() );
3670 wxWindow *parent = GetParent();
3671 wxDataViewEvent le(wxEVT_COMMAND_DATAVIEW_ITEM_CONTEXT_MENU, parent->GetId());
3672 le.SetItem( item );
3673 le.SetEventObject(parent);
3674 le.SetModel(GetOwner()->GetModel());
3675 le.SetValue(value);
3676 parent->GetEventHandler()->ProcessEvent(le);
3677 }
3678 else if (event.MiddleDown())
3679 {
3680 }
3681
3682 if((event.LeftDown() || simulateClick) && hoverOverExpander)
3683 {
3684 wxDataViewTreeNode* node = GetTreeNodeByRow(current);
3685
3686 // hoverOverExpander being true tells us that our node must be
3687 // valid and have children.
3688 // So we don't need any extra checks.
3689 if( node->IsOpen() )
3690 Collapse(current);
3691 else
3692 Expand(current);
3693 }
3694 else if ((event.LeftDown() || simulateClick) && !hoverOverExpander)
3695 {
3696 SetFocus();
3697
3698 m_lineBeforeLastClicked = m_lineLastClicked;
3699 m_lineLastClicked = current;
3700
3701 unsigned int oldCurrentRow = m_currentRow;
3702 bool oldWasSelected = IsRowSelected(m_currentRow);
3703
3704 bool cmdModifierDown = event.CmdDown();
3705 if ( IsSingleSel() || !(cmdModifierDown || event.ShiftDown()) )
3706 {
3707 if ( IsSingleSel() || !IsRowSelected(current) )
3708 {
3709 SelectAllRows( false );
3710 ChangeCurrentRow(current);
3711 SelectRow(m_currentRow,true);
3712 SendSelectionChangedEvent(GetItemByRow( m_currentRow ) );
3713 }
3714 else // multi sel & current is highlighted & no mod keys
3715 {
3716 m_lineSelectSingleOnUp = current;
3717 ChangeCurrentRow(current); // change focus
3718 }
3719 }
3720 else // multi sel & either ctrl or shift is down
3721 {
3722 if (cmdModifierDown)
3723 {
3724 ChangeCurrentRow(current);
3725 ReverseRowSelection(m_currentRow);
3726 SendSelectionChangedEvent(GetItemByRow(m_currentRow));
3727 }
3728 else if (event.ShiftDown())
3729 {
3730 ChangeCurrentRow(current);
3731
3732 unsigned int lineFrom = oldCurrentRow,
3733 lineTo = current;
3734
3735 if ( lineTo < lineFrom )
3736 {
3737 lineTo = lineFrom;
3738 lineFrom = m_currentRow;
3739 }
3740
3741 SelectRows(lineFrom, lineTo, true);
3742 SendSelectionChangedEvent(GetItemByRow(m_selection[0]) );
3743 }
3744 else // !ctrl, !shift
3745 {
3746 // test in the enclosing if should make it impossible
3747 wxFAIL_MSG( wxT("how did we get here?") );
3748 }
3749 }
3750
3751 if (m_currentRow != oldCurrentRow)
3752 RefreshRow( oldCurrentRow );
3753
3754 wxDataViewColumn *oldCurrentCol = m_currentCol;
3755
3756 // Update selection here...
3757 m_currentCol = col;
3758
3759 m_lastOnSame = !simulateClick && ((col == oldCurrentCol) &&
3760 (current == oldCurrentRow)) && oldWasSelected;
3761
3762 // Call LeftClick after everything else as under GTK+
3763 if (cell->GetMode() & wxDATAVIEW_CELL_ACTIVATABLE)
3764 {
3765 if ( wxDataViewCustomRenderer *custom = cell->WXGetAsCustom() )
3766 {
3767 // notify cell about click
3768 wxVariant value;
3769 model->GetValue( value, item, col->GetModelColumn() );
3770 custom->SetValue( value );
3771 wxRect cell_rect( xpos, GetLineStart( current ),
3772 col->GetWidth(), GetLineHeight( current ) );
3773 /* ignore ret */ custom->LeftClick( event.GetPosition(), cell_rect,
3774 model, item, col->GetModelColumn());
3775 }
3776 }
3777 }
3778 }
3779
3780 void wxDataViewMainWindow::OnSetFocus( wxFocusEvent &event )
3781 {
3782 m_hasFocus = true;
3783
3784 if (HasCurrentRow())
3785 Refresh();
3786
3787 event.Skip();
3788 }
3789
3790 void wxDataViewMainWindow::OnKillFocus( wxFocusEvent &event )
3791 {
3792 m_hasFocus = false;
3793
3794 if (HasCurrentRow())
3795 Refresh();
3796
3797 event.Skip();
3798 }
3799
3800 wxDataViewItem wxDataViewMainWindow::GetSelection() const
3801 {
3802 if( m_selection.GetCount() != 1 )
3803 return wxDataViewItem();
3804
3805 return GetItemByRow( m_selection.Item(0));
3806 }
3807
3808 //-----------------------------------------------------------------------------
3809 // wxDataViewCtrl
3810 //-----------------------------------------------------------------------------
3811
3812 WX_DEFINE_LIST(wxDataViewColumnList)
3813
3814 IMPLEMENT_DYNAMIC_CLASS(wxDataViewCtrl, wxDataViewCtrlBase)
3815 BEGIN_EVENT_TABLE(wxDataViewCtrl, wxDataViewCtrlBase)
3816 EVT_SIZE(wxDataViewCtrl::OnSize)
3817 END_EVENT_TABLE()
3818
3819 wxDataViewCtrl::~wxDataViewCtrl()
3820 {
3821 if (m_notifier)
3822 GetModel()->RemoveNotifier( m_notifier );
3823
3824 m_cols.Clear();
3825 }
3826
3827 void wxDataViewCtrl::Init()
3828 {
3829 m_cols.DeleteContents(true);
3830 m_notifier = NULL;
3831
3832 // No sorting column at start
3833 m_sortingColumnIdx = wxNOT_FOUND;
3834
3835 m_headerArea = NULL;
3836 }
3837
3838 bool wxDataViewCtrl::Create(wxWindow *parent, wxWindowID id,
3839 const wxPoint& pos, const wxSize& size,
3840 long style, const wxValidator& validator )
3841 {
3842 // if ( (style & wxBORDER_MASK) == 0)
3843 // style |= wxBORDER_SUNKEN;
3844
3845 Init();
3846
3847 if (!wxControl::Create( parent, id, pos, size,
3848 style | wxScrolledWindowStyle, validator))
3849 return false;
3850
3851 SetInitialSize(size);
3852
3853 #ifdef __WXMAC__
3854 MacSetClipChildren( true );
3855 #endif
3856
3857 m_clientArea = new wxDataViewMainWindow( this, wxID_ANY );
3858
3859 if (HasFlag(wxDV_NO_HEADER))
3860 m_headerArea = NULL;
3861 else
3862 m_headerArea = new wxDataViewHeaderWindow(this);
3863
3864 SetTargetWindow( m_clientArea );
3865
3866 wxBoxSizer *sizer = new wxBoxSizer( wxVERTICAL );
3867 if (m_headerArea)
3868 sizer->Add( m_headerArea, 0, wxGROW );
3869 sizer->Add( m_clientArea, 1, wxGROW );
3870 SetSizer( sizer );
3871
3872 return true;
3873 }
3874
3875 wxBorder wxDataViewCtrl::GetDefaultBorder() const
3876 {
3877 return wxBORDER_THEME;
3878 }
3879
3880 #ifdef __WXMSW__
3881 WXLRESULT wxDataViewCtrl::MSWWindowProc(WXUINT nMsg,
3882 WXWPARAM wParam,
3883 WXLPARAM lParam)
3884 {
3885 WXLRESULT rc = wxDataViewCtrlBase::MSWWindowProc(nMsg, wParam, lParam);
3886
3887 #ifndef __WXWINCE__
3888 // we need to process arrows ourselves for scrolling
3889 if ( nMsg == WM_GETDLGCODE )
3890 {
3891 rc |= DLGC_WANTARROWS;
3892 }
3893 #endif
3894
3895 return rc;
3896 }
3897 #endif
3898
3899 wxSize wxDataViewCtrl::GetSizeAvailableForScrollTarget(const wxSize& size)
3900 {
3901 wxSize newsize = size;
3902 if (!HasFlag(wxDV_NO_HEADER) && (m_headerArea))
3903 newsize.y -= m_headerArea->GetSize().y;
3904
3905 return newsize;
3906 }
3907
3908 void wxDataViewCtrl::OnSize( wxSizeEvent &WXUNUSED(event) )
3909 {
3910 // We need to override OnSize so that our scrolled
3911 // window a) does call Layout() to use sizers for
3912 // positioning the controls but b) does not query
3913 // the sizer for their size and use that for setting
3914 // the scrollable area as set that ourselves by
3915 // calling SetScrollbar() further down.
3916
3917 Layout();
3918
3919 AdjustScrollbars();
3920 }
3921
3922 void wxDataViewCtrl::SetFocus()
3923 {
3924 if (m_clientArea)
3925 m_clientArea->SetFocus();
3926 }
3927
3928 bool wxDataViewCtrl::AssociateModel( wxDataViewModel *model )
3929 {
3930 if (!wxDataViewCtrlBase::AssociateModel( model ))
3931 return false;
3932
3933 m_notifier = new wxGenericDataViewModelNotifier( m_clientArea );
3934
3935 model->AddNotifier( m_notifier );
3936
3937 m_clientArea->DestroyTree();
3938
3939 m_clientArea->BuildTree(model);
3940
3941 m_clientArea->UpdateDisplay();
3942
3943 return true;
3944 }
3945
3946 #if wxUSE_DRAG_AND_DROP
3947
3948 bool wxDataViewCtrl::EnableDragSource( const wxDataFormat &format )
3949 {
3950 return m_clientArea->EnableDragSource( format );
3951 }
3952
3953 bool wxDataViewCtrl::EnableDropTarget( const wxDataFormat &format )
3954 {
3955 return m_clientArea->EnableDropTarget( format );
3956 }
3957
3958 #endif // wxUSE_DRAG_AND_DROP
3959
3960 bool wxDataViewCtrl::AppendColumn( wxDataViewColumn *col )
3961 {
3962 if (!wxDataViewCtrlBase::AppendColumn(col))
3963 return false;
3964
3965 m_cols.Append( col );
3966 OnColumnsCountChanged();
3967 return true;
3968 }
3969
3970 bool wxDataViewCtrl::PrependColumn( wxDataViewColumn *col )
3971 {
3972 if (!wxDataViewCtrlBase::PrependColumn(col))
3973 return false;
3974
3975 m_cols.Insert( col );
3976 OnColumnsCountChanged();
3977 return true;
3978 }
3979
3980 bool wxDataViewCtrl::InsertColumn( unsigned int pos, wxDataViewColumn *col )
3981 {
3982 if (!wxDataViewCtrlBase::InsertColumn(pos,col))
3983 return false;
3984
3985 m_cols.Insert( pos, col );
3986 OnColumnsCountChanged();
3987 return true;
3988 }
3989
3990 void wxDataViewCtrl::OnColumnChange(unsigned int idx)
3991 {
3992 if ( m_headerArea )
3993 m_headerArea->UpdateColumn(idx);
3994
3995 m_clientArea->UpdateDisplay();
3996 }
3997
3998 void wxDataViewCtrl::OnColumnsCountChanged()
3999 {
4000 if (m_headerArea)
4001 m_headerArea->SetColumnCount(GetColumnCount());
4002
4003 m_clientArea->UpdateDisplay();
4004 }
4005
4006 void wxDataViewCtrl::DoSetExpanderColumn()
4007 {
4008 m_clientArea->UpdateDisplay();
4009 }
4010
4011 void wxDataViewCtrl::DoSetIndent()
4012 {
4013 m_clientArea->UpdateDisplay();
4014 }
4015
4016 unsigned int wxDataViewCtrl::GetColumnCount() const
4017 {
4018 return m_cols.GetCount();
4019 }
4020
4021 wxDataViewColumn* wxDataViewCtrl::GetColumn( unsigned int idx ) const
4022 {
4023 return m_cols[idx];
4024 }
4025
4026 wxDataViewColumn *wxDataViewCtrl::GetColumnAt(unsigned int pos) const
4027 {
4028 // columns can't be reordered if there is no header window which allows
4029 // to do this
4030 const unsigned idx = m_headerArea ? m_headerArea->GetColumnsOrder()[pos]
4031 : pos;
4032
4033 return GetColumn(idx);
4034 }
4035
4036 int wxDataViewCtrl::GetColumnIndex(const wxDataViewColumn *column) const
4037 {
4038 const unsigned count = m_cols.size();
4039 for ( unsigned n = 0; n < count; n++ )
4040 {
4041 if ( m_cols[n] == column )
4042 return n;
4043 }
4044
4045 return wxNOT_FOUND;
4046 }
4047
4048 void wxDataViewCtrl::ColumnMoved(wxDataViewColumn * WXUNUSED(col),
4049 unsigned int WXUNUSED(new_pos))
4050 {
4051 // do _not_ reorder m_cols elements here, they should always be in the
4052 // order in which columns were added, we only display the columns in
4053 // different order
4054 m_clientArea->UpdateDisplay();
4055 }
4056
4057 bool wxDataViewCtrl::DeleteColumn( wxDataViewColumn *column )
4058 {
4059 wxDataViewColumnList::compatibility_iterator ret = m_cols.Find( column );
4060 if (!ret)
4061 return false;
4062
4063 m_cols.Erase(ret);
4064 OnColumnsCountChanged();
4065
4066 return true;
4067 }
4068
4069 bool wxDataViewCtrl::ClearColumns()
4070 {
4071 m_cols.Clear();
4072 OnColumnsCountChanged();
4073 return true;
4074 }
4075
4076 int wxDataViewCtrl::GetColumnPosition( const wxDataViewColumn *column ) const
4077 {
4078 #if 1
4079 unsigned int len = GetColumnCount();
4080 for ( unsigned int i = 0; i < len; i++ )
4081 {
4082 wxDataViewColumn * col = GetColumnAt(i);
4083 if (column==col)
4084 return i;
4085 }
4086
4087 return wxNOT_FOUND;
4088 #else
4089 // This returns the position in pixels which is not what we want.
4090 int ret = 0,
4091 dummy = 0;
4092 unsigned int len = GetColumnCount();
4093 for ( unsigned int i = 0; i < len; i++ )
4094 {
4095 wxDataViewColumn * col = GetColumnAt(i);
4096 if (col->IsHidden())
4097 continue;
4098 ret += col->GetWidth();
4099 if (column==col)
4100 {
4101 CalcScrolledPosition( ret, dummy, &ret, &dummy );
4102 break;
4103 }
4104 }
4105 return ret;
4106 #endif
4107 }
4108
4109 wxDataViewColumn *wxDataViewCtrl::GetSortingColumn() const
4110 {
4111 return m_sortingColumnIdx == wxNOT_FOUND ? NULL
4112 : GetColumn(m_sortingColumnIdx);
4113 }
4114
4115 // Selection code with wxDataViewItem as parameters
4116 wxDataViewItem wxDataViewCtrl::GetSelection() const
4117 {
4118 return m_clientArea->GetSelection();
4119 }
4120
4121 int wxDataViewCtrl::GetSelections( wxDataViewItemArray & sel ) const
4122 {
4123 sel.Empty();
4124 wxDataViewSelection selection = m_clientArea->GetSelections();
4125 int len = selection.GetCount();
4126 for( int i = 0; i < len; i ++)
4127 {
4128 unsigned int row = selection[i];
4129 sel.Add( m_clientArea->GetItemByRow( row ) );
4130 }
4131 return len;
4132 }
4133
4134 void wxDataViewCtrl::SetSelections( const wxDataViewItemArray & sel )
4135 {
4136 wxDataViewSelection selection(wxDataViewSelectionCmp);
4137
4138 wxDataViewItem last_parent;
4139
4140 int len = sel.GetCount();
4141 for( int i = 0; i < len; i ++ )
4142 {
4143 wxDataViewItem item = sel[i];
4144 wxDataViewItem parent = GetModel()->GetParent( item );
4145 if (parent)
4146 {
4147 if (parent != last_parent)
4148 ExpandAncestors(item);
4149 }
4150
4151 last_parent = parent;
4152 int row = m_clientArea->GetRowByItem( item );
4153 if( row >= 0 )
4154 selection.Add( static_cast<unsigned int>(row) );
4155 }
4156
4157 m_clientArea->SetSelections( selection );
4158 }
4159
4160 void wxDataViewCtrl::Select( const wxDataViewItem & item )
4161 {
4162 ExpandAncestors( item );
4163
4164 int row = m_clientArea->GetRowByItem( item );
4165 if( row >= 0 )
4166 {
4167 // Unselect all rows before select another in the single select mode
4168 if (m_clientArea->IsSingleSel())
4169 m_clientArea->SelectAllRows(false);
4170
4171 m_clientArea->SelectRow(row, true);
4172
4173 // Also set focus to the selected item
4174 m_clientArea->ChangeCurrentRow( row );
4175 }
4176 }
4177
4178 void wxDataViewCtrl::Unselect( const wxDataViewItem & item )
4179 {
4180 int row = m_clientArea->GetRowByItem( item );
4181 if( row >= 0 )
4182 m_clientArea->SelectRow(row, false);
4183 }
4184
4185 bool wxDataViewCtrl::IsSelected( const wxDataViewItem & item ) const
4186 {
4187 int row = m_clientArea->GetRowByItem( item );
4188 if( row >= 0 )
4189 {
4190 return m_clientArea->IsRowSelected(row);
4191 }
4192 return false;
4193 }
4194
4195 // Selection code with row number as parameter
4196 int wxDataViewCtrl::GetSelections( wxArrayInt & sel ) const
4197 {
4198 sel.Empty();
4199 wxDataViewSelection selection = m_clientArea->GetSelections();
4200 int len = selection.GetCount();
4201 for( int i = 0; i < len; i ++)
4202 {
4203 unsigned int row = selection[i];
4204 sel.Add( row );
4205 }
4206 return len;
4207 }
4208
4209 void wxDataViewCtrl::SetSelections( const wxArrayInt & sel )
4210 {
4211 wxDataViewSelection selection(wxDataViewSelectionCmp);
4212 int len = sel.GetCount();
4213 for( int i = 0; i < len; i ++ )
4214 {
4215 int row = sel[i];
4216 if( row >= 0 )
4217 selection.Add( static_cast<unsigned int>(row) );
4218 }
4219 m_clientArea->SetSelections( selection );
4220 }
4221
4222 void wxDataViewCtrl::Select( int row )
4223 {
4224 if( row >= 0 )
4225 {
4226 if (m_clientArea->IsSingleSel())
4227 m_clientArea->SelectAllRows(false);
4228 m_clientArea->SelectRow( row, true );
4229 }
4230 }
4231
4232 void wxDataViewCtrl::Unselect( int row )
4233 {
4234 if( row >= 0 )
4235 m_clientArea->SelectRow(row, false);
4236 }
4237
4238 bool wxDataViewCtrl::IsSelected( int row ) const
4239 {
4240 if( row >= 0 )
4241 return m_clientArea->IsRowSelected(row);
4242 return false;
4243 }
4244
4245 void wxDataViewCtrl::SelectRange( int from, int to )
4246 {
4247 wxArrayInt sel;
4248 for( int i = from; i < to; i ++ )
4249 sel.Add( i );
4250 m_clientArea->Select(sel);
4251 }
4252
4253 void wxDataViewCtrl::UnselectRange( int from, int to )
4254 {
4255 wxDataViewSelection sel = m_clientArea->GetSelections();
4256 for( int i = from; i < to; i ++ )
4257 if( sel.Index( i ) != wxNOT_FOUND )
4258 sel.Remove( i );
4259 m_clientArea->SetSelections(sel);
4260 }
4261
4262 void wxDataViewCtrl::SelectAll()
4263 {
4264 m_clientArea->SelectAllRows(true);
4265 }
4266
4267 void wxDataViewCtrl::UnselectAll()
4268 {
4269 m_clientArea->SelectAllRows(false);
4270 }
4271
4272 void wxDataViewCtrl::EnsureVisible( int row, int column )
4273 {
4274 if( row < 0 )
4275 row = 0;
4276 if( row > (int) m_clientArea->GetRowCount() )
4277 row = m_clientArea->GetRowCount();
4278
4279 int first = m_clientArea->GetFirstVisibleRow();
4280 int last = m_clientArea->GetLastVisibleRow();
4281 if( row < first )
4282 m_clientArea->ScrollTo( row, column );
4283 else if( row > last )
4284 m_clientArea->ScrollTo( row - last + first, column );
4285 else
4286 m_clientArea->ScrollTo( first, column );
4287 }
4288
4289 void wxDataViewCtrl::EnsureVisible( const wxDataViewItem & item, const wxDataViewColumn * column )
4290 {
4291 ExpandAncestors( item );
4292
4293 m_clientArea->RecalculateDisplay();
4294
4295 int row = m_clientArea->GetRowByItem(item);
4296 if( row >= 0 )
4297 {
4298 if( column == NULL )
4299 EnsureVisible(row, -1);
4300 else
4301 EnsureVisible( row, GetColumnIndex(column) );
4302 }
4303
4304 }
4305
4306 void wxDataViewCtrl::HitTest( const wxPoint & point, wxDataViewItem & item,
4307 wxDataViewColumn* &column ) const
4308 {
4309 m_clientArea->HitTest(point, item, column);
4310 }
4311
4312 wxRect wxDataViewCtrl::GetItemRect( const wxDataViewItem & item,
4313 const wxDataViewColumn* column ) const
4314 {
4315 return m_clientArea->GetItemRect(item, column);
4316 }
4317
4318 wxDataViewItem wxDataViewCtrl::GetItemByRow( unsigned int row ) const
4319 {
4320 return m_clientArea->GetItemByRow( row );
4321 }
4322
4323 int wxDataViewCtrl::GetRowByItem( const wxDataViewItem & item ) const
4324 {
4325 return m_clientArea->GetRowByItem( item );
4326 }
4327
4328 void wxDataViewCtrl::Expand( const wxDataViewItem & item )
4329 {
4330 int row = m_clientArea->GetRowByItem( item );
4331 if (row != -1)
4332 m_clientArea->Expand(row);
4333 }
4334
4335 void wxDataViewCtrl::Collapse( const wxDataViewItem & item )
4336 {
4337 int row = m_clientArea->GetRowByItem( item );
4338 if (row != -1)
4339 m_clientArea->Collapse(row);
4340 }
4341
4342 bool wxDataViewCtrl::IsExpanded( const wxDataViewItem & item ) const
4343 {
4344 int row = m_clientArea->GetRowByItem( item );
4345 if (row != -1)
4346 return m_clientArea->IsExpanded(row);
4347 return false;
4348 }
4349
4350 void wxDataViewCtrl::StartEditor( const wxDataViewItem & item, unsigned int column )
4351 {
4352 wxDataViewColumn* col = GetColumn( column );
4353 if (!col)
4354 return;
4355
4356 wxRect itemRect = GetItemRect(item, col);
4357 wxDataViewRenderer* renderer = col->GetRenderer();
4358 renderer->StartEditing(item, itemRect);
4359 }
4360
4361 #endif // !wxUSE_GENERICDATAVIEWCTRL
4362
4363 #endif // wxUSE_DATAVIEWCTRL