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