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