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