]> git.saurik.com Git - wxWidgets.git/blame - samples/dataview/mymodels.cpp
fixing reentrancy which happened in tests, bringing client coordinates origins in...
[wxWidgets.git] / samples / dataview / mymodels.cpp
CommitLineData
03dfc8f5
VZ
1/////////////////////////////////////////////////////////////////////////////
2// Name: mymodels.cpp
3// Purpose: wxDataViewCtrl wxWidgets sample
4// Author: Robert Roebling
5// Modified by: Francesco Montorsi, Bo Yang
6// Created: 06/01/06
7// RCS-ID: $Id$
8// Copyright: (c) Robert Roebling
526954c5 9// Licence: wxWindows licence
03dfc8f5
VZ
10/////////////////////////////////////////////////////////////////////////////
11
12
13// For compilers that support precompilation, includes "wx/wx.h".
14#include "wx/wxprec.h"
15
16#ifdef __BORLANDC__
17#pragma hdrstop
18#endif
19
20#ifndef WX_PRECOMP
21 #include "wx/wx.h"
22#endif
23
24#include "wx/dataview.h"
25#include "mymodels.h"
26
27// ----------------------------------------------------------------------------
28// resources
29// ----------------------------------------------------------------------------
30
31#include "null.xpm"
32#include "wx_small.xpm"
33
34
35// ----------------------------------------------------------------------------
36// MyMusicTreeModel
37// ----------------------------------------------------------------------------
38
39MyMusicTreeModel::MyMusicTreeModel()
40{
ecad59d8 41 m_root = new MyMusicTreeModelNode( NULL, "My Music" );
03dfc8f5
VZ
42
43 // setup pop music
ecad59d8 44 m_pop = new MyMusicTreeModelNode( m_root, "Pop music" );
03dfc8f5 45 m_pop->Append(
caa4f8ff 46 new MyMusicTreeModelNode( m_pop, "You are not alone", "Michael Jackson", 1995 ) );
03dfc8f5 47 m_pop->Append(
ecad59d8 48 new MyMusicTreeModelNode( m_pop, "Take a bow", "Madonna", 1994 ) );
03dfc8f5
VZ
49 m_root->Append( m_pop );
50
51 // setup classical music
ecad59d8
FM
52 m_classical = new MyMusicTreeModelNode( m_root, "Classical music" );
53 m_ninth = new MyMusicTreeModelNode( m_classical, "Ninth symphony",
54 "Ludwig van Beethoven", 1824 );
03dfc8f5 55 m_classical->Append( m_ninth );
ecad59d8
FM
56 m_classical->Append( new MyMusicTreeModelNode( m_classical, "German Requiem",
57 "Johannes Brahms", 1868 ) );
03dfc8f5
VZ
58 m_root->Append( m_classical );
59
60 m_classicalMusicIsKnownToControl = false;
61}
62
63wxString MyMusicTreeModel::GetTitle( const wxDataViewItem &item ) const
64{
65 MyMusicTreeModelNode *node = (MyMusicTreeModelNode*) item.GetID();
66 if (!node) // happens if item.IsOk()==false
67 return wxEmptyString;
68
69 return node->m_title;
70}
71
ecc32226
RR
72wxString MyMusicTreeModel::GetArtist( const wxDataViewItem &item ) const
73{
74 MyMusicTreeModelNode *node = (MyMusicTreeModelNode*) item.GetID();
75 if (!node) // happens if item.IsOk()==false
76 return wxEmptyString;
77
78 return node->m_artist;
79}
80
03dfc8f5
VZ
81int MyMusicTreeModel::GetYear( const wxDataViewItem &item ) const
82{
83 MyMusicTreeModelNode *node = (MyMusicTreeModelNode*) item.GetID();
84 if (!node) // happens if item.IsOk()==false
85 return 2000;
86
87 return node->m_year;
88}
89
90void MyMusicTreeModel::AddToClassical( const wxString &title, const wxString &artist,
91 unsigned int year )
92{
93 if (!m_classical)
94 {
95 wxASSERT(m_root);
96
97 // it was removed: restore it
ecad59d8 98 m_classical = new MyMusicTreeModelNode( m_root, "Classical music" );
03dfc8f5 99 m_root->Append( m_classical );
ecad59d8
FM
100
101 // notify control
102 wxDataViewItem child( (void*) m_classical );
103 wxDataViewItem parent( (void*) m_root );
104 ItemAdded( parent, child );
03dfc8f5
VZ
105 }
106
107 // add to the classical music node a new node:
108 MyMusicTreeModelNode *child_node =
109 new MyMusicTreeModelNode( m_classical, title, artist, year );
110 m_classical->Append( child_node );
111
ecad59d8 112 // FIXME: what's m_classicalMusicIsKnownToControl for?
03dfc8f5
VZ
113 if (m_classicalMusicIsKnownToControl)
114 {
115 // notify control
116 wxDataViewItem child( (void*) child_node );
117 wxDataViewItem parent( (void*) m_classical );
118 ItemAdded( parent, child );
119 }
120}
121
122void MyMusicTreeModel::Delete( const wxDataViewItem &item )
123{
124 MyMusicTreeModelNode *node = (MyMusicTreeModelNode*) item.GetID();
125 if (!node) // happens if item.IsOk()==false
126 return;
127
128 wxDataViewItem parent( node->GetParent() );
129 if (!parent.IsOk())
130 {
131 wxASSERT(node == m_root);
132
133 // don't make the control completely empty:
ecad59d8 134 wxLogError( "Cannot remove the root item!" );
03dfc8f5
VZ
135 return;
136 }
137
138 // is the node one of those we keep stored in special pointers?
139 if (node == m_pop)
140 m_pop = NULL;
141 else if (node == m_classical)
142 m_classical = NULL;
143 else if (node == m_ninth)
144 m_ninth = NULL;
145
146 // first remove the node from the parent's array of children;
147 // NOTE: MyMusicTreeModelNodePtrArray is only an array of _pointers_
148 // thus removing the node from it doesn't result in freeing it
149 node->GetParent()->GetChildren().Remove( node );
150
151 // free the node
152 delete node;
153
154 // notify control
155 ItemDeleted( parent, item );
156}
157
158int MyMusicTreeModel::Compare( const wxDataViewItem &item1, const wxDataViewItem &item2,
a82e28dd 159 unsigned int column, bool ascending ) const
03dfc8f5
VZ
160{
161 wxASSERT(item1.IsOk() && item2.IsOk());
162 // should never happen
163
164 if (IsContainer(item1) && IsContainer(item2))
165 {
166 wxVariant value1, value2;
167 GetValue( value1, item1, 0 );
168 GetValue( value2, item2, 0 );
169
170 wxString str1 = value1.GetString();
171 wxString str2 = value2.GetString();
172 int res = str1.Cmp( str2 );
173 if (res) return res;
174
175 // items must be different
176 wxUIntPtr litem1 = (wxUIntPtr) item1.GetID();
177 wxUIntPtr litem2 = (wxUIntPtr) item2.GetID();
178
179 return litem1-litem2;
180 }
181
182 return wxDataViewModel::Compare( item1, item2, column, ascending );
183}
184
185void MyMusicTreeModel::GetValue( wxVariant &variant,
186 const wxDataViewItem &item, unsigned int col ) const
187{
188 wxASSERT(item.IsOk());
189
190 MyMusicTreeModelNode *node = (MyMusicTreeModelNode*) item.GetID();
191 switch (col)
192 {
193 case 0:
194 variant = node->m_title;
195 break;
196 case 1:
197 variant = node->m_artist;
198 break;
199 case 2:
200 variant = (long) node->m_year;
201 break;
202 case 3:
203 variant = node->m_quality;
204 break;
205 case 4:
ef6833f9 206 variant = 80L; // all music is very 80% popular
03dfc8f5
VZ
207 break;
208 case 5:
03dfc8f5 209 if (GetYear(item) < 1900)
ef6833f9 210 variant = "old";
03dfc8f5 211 else
ef6833f9 212 variant = "new";
03dfc8f5
VZ
213 break;
214
215 default:
ecad59d8 216 wxLogError( "MyMusicTreeModel::GetValue: wrong column %d", col );
03dfc8f5
VZ
217 }
218}
219
220bool MyMusicTreeModel::SetValue( const wxVariant &variant,
221 const wxDataViewItem &item, unsigned int col )
222{
223 wxASSERT(item.IsOk());
224
225 MyMusicTreeModelNode *node = (MyMusicTreeModelNode*) item.GetID();
226 switch (col)
227 {
228 case 0:
229 node->m_title = variant.GetString();
230 return true;
231 case 1:
232 node->m_artist = variant.GetString();
233 return true;
234 case 2:
235 node->m_year = variant.GetLong();
236 return true;
237 case 3:
238 node->m_quality = variant.GetString();
239 return true;
240
241 default:
ecad59d8 242 wxLogError( "MyMusicTreeModel::SetValue: wrong column" );
03dfc8f5
VZ
243 }
244 return false;
245}
246
98f8e666
VZ
247bool MyMusicTreeModel::IsEnabled( const wxDataViewItem &item,
248 unsigned int col ) const
249{
250 wxASSERT(item.IsOk());
251
252 MyMusicTreeModelNode *node = (MyMusicTreeModelNode*) item.GetID();
253
254 // disable Beethoven's ratings, his pieces can only be good
255 return !(col == 3 && node->m_artist.EndsWith("Beethoven"));
256}
257
03dfc8f5
VZ
258wxDataViewItem MyMusicTreeModel::GetParent( const wxDataViewItem &item ) const
259{
260 // the invisible root node has no parent
261 if (!item.IsOk())
262 return wxDataViewItem(0);
263
264 MyMusicTreeModelNode *node = (MyMusicTreeModelNode*) item.GetID();
265
266 // "MyMusic" also has no parent
267 if (node == m_root)
268 return wxDataViewItem(0);
269
270 return wxDataViewItem( (void*) node->GetParent() );
271}
272
273bool MyMusicTreeModel::IsContainer( const wxDataViewItem &item ) const
274{
275 // the invisble root node can have children
276 // (in our model always "MyMusic")
277 if (!item.IsOk())
278 return true;
279
280 MyMusicTreeModelNode *node = (MyMusicTreeModelNode*) item.GetID();
281 return node->IsContainer();
282}
283
284unsigned int MyMusicTreeModel::GetChildren( const wxDataViewItem &parent,
285 wxDataViewItemArray &array ) const
286{
287 MyMusicTreeModelNode *node = (MyMusicTreeModelNode*) parent.GetID();
288 if (!node)
289 {
290 array.Add( wxDataViewItem( (void*) m_root ) );
291 return 1;
292 }
293
294 if (node == m_classical)
295 {
296 MyMusicTreeModel *model = (MyMusicTreeModel*)(const MyMusicTreeModel*) this;
297 model->m_classicalMusicIsKnownToControl = true;
298 }
299
300 if (node->GetChildCount() == 0)
301 {
302 return 0;
303 }
304
305 unsigned int count = node->GetChildren().GetCount();
306 for (unsigned int pos = 0; pos < count; pos++)
307 {
308 MyMusicTreeModelNode *child = node->GetChildren().Item( pos );
309 array.Add( wxDataViewItem( (void*) child ) );
310 }
311
312 return count;
313}
314
315
316
317// ----------------------------------------------------------------------------
318// MyListModel
319// ----------------------------------------------------------------------------
320
321static int my_sort_reverse( int *v1, int *v2 )
322{
323 return *v2-*v1;
324}
325
326static int my_sort( int *v1, int *v2 )
327{
328 return *v1-*v2;
329}
330
d8090b5e 331#define INITIAL_NUMBER_OF_ITEMS 10000
03dfc8f5
VZ
332
333MyListModel::MyListModel() :
334 wxDataViewVirtualListModel( INITIAL_NUMBER_OF_ITEMS )
335{
03dfc8f5 336 // the first 100 items are really stored in this model;
205bdf20
VZ
337 // all the others are synthesized on request
338 static const unsigned NUMBER_REAL_ITEMS = 100;
339
340 m_textColValues.reserve(NUMBER_REAL_ITEMS);
c937bcac
VZ
341 m_textColValues.push_back("first row with long label to test ellipsization");
342 for (unsigned int i = 1; i < NUMBER_REAL_ITEMS; i++)
03dfc8f5 343 {
205bdf20 344 m_textColValues.push_back(wxString::Format("real row %d", i));
03dfc8f5
VZ
345 }
346
205bdf20
VZ
347 m_iconColValues.assign(NUMBER_REAL_ITEMS, "test");
348
03dfc8f5
VZ
349 m_icon[0] = wxIcon( null_xpm );
350 m_icon[1] = wxIcon( wx_small_xpm );
351}
352
353void MyListModel::Prepend( const wxString &text )
354{
205bdf20 355 m_textColValues.Insert( text, 0 );
03dfc8f5
VZ
356 RowPrepended();
357}
358
359void MyListModel::DeleteItem( const wxDataViewItem &item )
360{
361 unsigned int row = GetRow( item );
ce00f59b 362
205bdf20 363 if (row >= m_textColValues.GetCount())
03dfc8f5
VZ
364 return;
365
205bdf20 366 m_textColValues.RemoveAt( row );
03dfc8f5
VZ
367 RowDeleted( row );
368}
369
370void MyListModel::DeleteItems( const wxDataViewItemArray &items )
371{
372 unsigned i;
373 wxArrayInt rows;
bfb9ad8b 374 for (i = 0; i < items.GetCount(); i++)
03dfc8f5
VZ
375 {
376 unsigned int row = GetRow( items[i] );
205bdf20 377 if (row < m_textColValues.GetCount())
03dfc8f5
VZ
378 rows.Add( row );
379 }
380
381 if (rows.GetCount() == 0)
382 {
383 // none of the selected items were in the range of the items
384 // which we store... for simplicity, don't allow removing them
205bdf20 385 wxLogError( "Cannot remove rows with an index greater than %d", m_textColValues.GetCount() );
03dfc8f5
VZ
386 return;
387 }
388
389 // Sort in descending order so that the last
390 // row will be deleted first. Otherwise the
391 // remaining indeces would all be wrong.
392 rows.Sort( my_sort_reverse );
393 for (i = 0; i < rows.GetCount(); i++)
205bdf20 394 m_textColValues.RemoveAt( rows[i] );
03dfc8f5
VZ
395
396 // This is just to test if wxDataViewCtrl can
397 // cope with removing rows not sorted in
398 // descending order
399 rows.Sort( my_sort );
400 RowsDeleted( rows );
401}
402
403void MyListModel::AddMany()
404{
d8090b5e 405 Reset( GetCount()+1000 );
03dfc8f5
VZ
406}
407
408void MyListModel::GetValueByRow( wxVariant &variant,
409 unsigned int row, unsigned int col ) const
410{
2746bccf 411 switch ( col )
03dfc8f5 412 {
2746bccf
VZ
413 case Col_EditableText:
414 if (row >= m_textColValues.GetCount())
415 variant = wxString::Format( "virtual row %d", row );
416 else
417 variant = m_textColValues[ row ];
418 break;
205bdf20 419
2746bccf
VZ
420 case Col_IconText:
421 {
422 wxString text;
423 if ( row >= m_iconColValues.GetCount() )
424 text = "virtual icon";
425 else
426 text = m_iconColValues[row];
427
428 variant << wxDataViewIconText(text, m_icon[row % 2]);
429 }
430 break;
431
432 case Col_TextWithAttr:
433 {
434 static const char *labels[5] =
435 {
436 "blue", "green", "red", "bold cyan", "default",
437 };
438
439 variant = labels[row % 5];
440 }
441 break;
09a0ece0 442
ef6833f9
VZ
443 case Col_Custom:
444 variant = wxString::Format("%d", row % 100);
445 break;
446
2746bccf
VZ
447 case Col_Max:
448 wxFAIL_MSG( "invalid column" );
03dfc8f5
VZ
449 }
450}
451
452bool MyListModel::GetAttrByRow( unsigned int row, unsigned int col,
7fadac88 453 wxDataViewItemAttr &attr ) const
03dfc8f5 454{
38c34918 455 switch ( col )
03dfc8f5 456 {
2746bccf 457 case Col_EditableText:
38c34918 458 return false;
09a0ece0 459
2746bccf 460 case Col_IconText:
38c34918
VZ
461 if ( !(row % 2) )
462 return false;
463 attr.SetColour(*wxLIGHT_GREY);
09a0ece0
VZ
464 break;
465
2746bccf 466 case Col_TextWithAttr:
ef6833f9
VZ
467 case Col_Custom:
468 // do what the labels defined in GetValueByRow() hint at
38c34918
VZ
469 switch ( row % 5 )
470 {
471 case 0:
472 attr.SetColour(*wxBLUE);
473 break;
474
475 case 1:
476 attr.SetColour(*wxGREEN);
477 break;
478
479 case 2:
480 attr.SetColour(*wxRED);
481 break;
482
483 case 3:
484 attr.SetColour(*wxCYAN);
485 attr.SetBold(true);
486 break;
487
488 case 4:
489 return false;
490 }
09a0ece0 491 break;
2746bccf
VZ
492
493 case Col_Max:
494 wxFAIL_MSG( "invalid column" );
03dfc8f5
VZ
495 }
496
497 return true;
498}
499
500bool MyListModel::SetValueByRow( const wxVariant &variant,
501 unsigned int row, unsigned int col )
502{
205bdf20 503 switch ( col )
03dfc8f5 504 {
2746bccf
VZ
505 case Col_EditableText:
506 case Col_IconText:
205bdf20
VZ
507 if (row >= m_textColValues.GetCount())
508 {
509 // the item is not in the range of the items
510 // which we store... for simplicity, don't allow editing it
511 wxLogError( "Cannot edit rows with an index greater than %d",
512 m_textColValues.GetCount() );
513 return false;
514 }
03dfc8f5 515
2746bccf 516 if ( col == Col_EditableText )
205bdf20
VZ
517 {
518 m_textColValues[row] = variant.GetString();
519 }
2746bccf 520 else // col == Col_IconText
205bdf20
VZ
521 {
522 wxDataViewIconText iconText;
523 iconText << variant;
524 m_iconColValues[row] = iconText.GetText();
525 }
2746bccf 526 return true;
205bdf20 527
2746bccf 528 case Col_TextWithAttr:
ef6833f9 529 case Col_Custom:
205bdf20 530 wxLogError("Cannot edit the column %d", col);
2746bccf
VZ
531 break;
532
533 case Col_Max:
534 wxFAIL_MSG( "invalid column" );
03dfc8f5
VZ
535 }
536
2746bccf 537 return false;
03dfc8f5 538}
98f8e666
VZ
539
540
541// ----------------------------------------------------------------------------
542// MyListStoreDerivedModel
543// ----------------------------------------------------------------------------
544
545bool MyListStoreDerivedModel::IsEnabledByRow(unsigned int row, unsigned int col) const
546{
547 // disabled the last two checkboxes
548 return !(col == 0 && 8 <= row && row <= 9);
549}