Add custom renderer column to the list model example in dataview sample.
[wxWidgets.git] / samples / dataview / mymodels.cpp
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
9 // Licence: wxWindows license
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
39 MyMusicTreeModel::MyMusicTreeModel()
40 {
41 m_root = new MyMusicTreeModelNode( NULL, "My Music" );
42
43 // setup pop music
44 m_pop = new MyMusicTreeModelNode( m_root, "Pop music" );
45 m_pop->Append(
46 new MyMusicTreeModelNode( m_pop, "You are not alone", "Michael Jackson", 1995 ) );
47 m_pop->Append(
48 new MyMusicTreeModelNode( m_pop, "Take a bow", "Madonna", 1994 ) );
49 m_root->Append( m_pop );
50
51 // setup classical music
52 m_classical = new MyMusicTreeModelNode( m_root, "Classical music" );
53 m_ninth = new MyMusicTreeModelNode( m_classical, "Ninth symphony",
54 "Ludwig van Beethoven", 1824 );
55 m_classical->Append( m_ninth );
56 m_classical->Append( new MyMusicTreeModelNode( m_classical, "German Requiem",
57 "Johannes Brahms", 1868 ) );
58 m_root->Append( m_classical );
59
60 m_classicalMusicIsKnownToControl = false;
61 }
62
63 wxString 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
72 wxString 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
81 int 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
90 void 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
98 m_classical = new MyMusicTreeModelNode( m_root, "Classical music" );
99 m_root->Append( m_classical );
100
101 // notify control
102 wxDataViewItem child( (void*) m_classical );
103 wxDataViewItem parent( (void*) m_root );
104 ItemAdded( parent, child );
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
112 // FIXME: what's m_classicalMusicIsKnownToControl for?
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
122 void 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:
134 wxLogError( "Cannot remove the root item!" );
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
158 int MyMusicTreeModel::Compare( const wxDataViewItem &item1, const wxDataViewItem &item2,
159 unsigned int column, bool ascending ) const
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
185 void 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:
206 variant = 80L; // all music is very 80% popular
207 break;
208 case 5:
209 if (GetYear(item) < 1900)
210 variant = "old";
211 else
212 variant = "new";
213 break;
214
215 default:
216 wxLogError( "MyMusicTreeModel::GetValue: wrong column %d", col );
217 }
218 }
219
220 bool 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:
242 wxLogError( "MyMusicTreeModel::SetValue: wrong column" );
243 }
244 return false;
245 }
246
247 wxDataViewItem MyMusicTreeModel::GetParent( const wxDataViewItem &item ) const
248 {
249 // the invisible root node has no parent
250 if (!item.IsOk())
251 return wxDataViewItem(0);
252
253 MyMusicTreeModelNode *node = (MyMusicTreeModelNode*) item.GetID();
254
255 // "MyMusic" also has no parent
256 if (node == m_root)
257 return wxDataViewItem(0);
258
259 return wxDataViewItem( (void*) node->GetParent() );
260 }
261
262 bool MyMusicTreeModel::IsContainer( const wxDataViewItem &item ) const
263 {
264 // the invisble root node can have children
265 // (in our model always "MyMusic")
266 if (!item.IsOk())
267 return true;
268
269 MyMusicTreeModelNode *node = (MyMusicTreeModelNode*) item.GetID();
270 return node->IsContainer();
271 }
272
273 unsigned int MyMusicTreeModel::GetChildren( const wxDataViewItem &parent,
274 wxDataViewItemArray &array ) const
275 {
276 MyMusicTreeModelNode *node = (MyMusicTreeModelNode*) parent.GetID();
277 if (!node)
278 {
279 array.Add( wxDataViewItem( (void*) m_root ) );
280 return 1;
281 }
282
283 if (node == m_classical)
284 {
285 MyMusicTreeModel *model = (MyMusicTreeModel*)(const MyMusicTreeModel*) this;
286 model->m_classicalMusicIsKnownToControl = true;
287 }
288
289 if (node->GetChildCount() == 0)
290 {
291 return 0;
292 }
293
294 unsigned int count = node->GetChildren().GetCount();
295 for (unsigned int pos = 0; pos < count; pos++)
296 {
297 MyMusicTreeModelNode *child = node->GetChildren().Item( pos );
298 array.Add( wxDataViewItem( (void*) child ) );
299 }
300
301 return count;
302 }
303
304
305
306 // ----------------------------------------------------------------------------
307 // MyListModel
308 // ----------------------------------------------------------------------------
309
310 static int my_sort_reverse( int *v1, int *v2 )
311 {
312 return *v2-*v1;
313 }
314
315 static int my_sort( int *v1, int *v2 )
316 {
317 return *v1-*v2;
318 }
319
320 #define INITIAL_NUMBER_OF_ITEMS 100000
321
322 MyListModel::MyListModel() :
323 wxDataViewVirtualListModel( INITIAL_NUMBER_OF_ITEMS )
324 {
325 m_virtualItems = INITIAL_NUMBER_OF_ITEMS;
326
327 // the first 100 items are really stored in this model;
328 // all the others are synthesized on request
329 static const unsigned NUMBER_REAL_ITEMS = 100;
330
331 m_textColValues.reserve(NUMBER_REAL_ITEMS);
332 m_textColValues.push_back("first row with long label to test ellipsization");
333 for (unsigned int i = 1; i < NUMBER_REAL_ITEMS; i++)
334 {
335 m_textColValues.push_back(wxString::Format("real row %d", i));
336 }
337
338 m_iconColValues.assign(NUMBER_REAL_ITEMS, "test");
339
340 m_icon[0] = wxIcon( null_xpm );
341 m_icon[1] = wxIcon( wx_small_xpm );
342 }
343
344 void MyListModel::Prepend( const wxString &text )
345 {
346 m_textColValues.Insert( text, 0 );
347 RowPrepended();
348 }
349
350 void MyListModel::DeleteItem( const wxDataViewItem &item )
351 {
352 unsigned int row = GetRow( item );
353 if (row >= m_textColValues.GetCount())
354 return;
355
356 m_textColValues.RemoveAt( row );
357 RowDeleted( row );
358 }
359
360 void MyListModel::DeleteItems( const wxDataViewItemArray &items )
361 {
362 unsigned i;
363 wxArrayInt rows;
364 for (i = 0; i < items.GetCount(); i++)
365 {
366 unsigned int row = GetRow( items[i] );
367 if (row < m_textColValues.GetCount())
368 rows.Add( row );
369 }
370
371 if (rows.GetCount() == 0)
372 {
373 // none of the selected items were in the range of the items
374 // which we store... for simplicity, don't allow removing them
375 wxLogError( "Cannot remove rows with an index greater than %d", m_textColValues.GetCount() );
376 return;
377 }
378
379 // Sort in descending order so that the last
380 // row will be deleted first. Otherwise the
381 // remaining indeces would all be wrong.
382 rows.Sort( my_sort_reverse );
383 for (i = 0; i < rows.GetCount(); i++)
384 m_textColValues.RemoveAt( rows[i] );
385
386 // This is just to test if wxDataViewCtrl can
387 // cope with removing rows not sorted in
388 // descending order
389 rows.Sort( my_sort );
390 RowsDeleted( rows );
391 }
392
393 void MyListModel::AddMany()
394 {
395 m_virtualItems += 1000;
396 Reset( m_textColValues.GetCount() + m_virtualItems );
397 }
398
399 void MyListModel::GetValueByRow( wxVariant &variant,
400 unsigned int row, unsigned int col ) const
401 {
402 switch ( col )
403 {
404 case Col_EditableText:
405 if (row >= m_textColValues.GetCount())
406 variant = wxString::Format( "virtual row %d", row );
407 else
408 variant = m_textColValues[ row ];
409 break;
410
411 case Col_IconText:
412 {
413 wxString text;
414 if ( row >= m_iconColValues.GetCount() )
415 text = "virtual icon";
416 else
417 text = m_iconColValues[row];
418
419 variant << wxDataViewIconText(text, m_icon[row % 2]);
420 }
421 break;
422
423 case Col_TextWithAttr:
424 {
425 static const char *labels[5] =
426 {
427 "blue", "green", "red", "bold cyan", "default",
428 };
429
430 variant = labels[row % 5];
431 }
432 break;
433
434 case Col_Custom:
435 variant = wxString::Format("%d", row % 100);
436 break;
437
438 case Col_Max:
439 wxFAIL_MSG( "invalid column" );
440 }
441 }
442
443 bool MyListModel::GetAttrByRow( unsigned int row, unsigned int col,
444 wxDataViewItemAttr &attr ) const
445 {
446 switch ( col )
447 {
448 case Col_EditableText:
449 return false;
450
451 case Col_IconText:
452 if ( !(row % 2) )
453 return false;
454 attr.SetColour(*wxLIGHT_GREY);
455 break;
456
457 case Col_TextWithAttr:
458 case Col_Custom:
459 // do what the labels defined in GetValueByRow() hint at
460 switch ( row % 5 )
461 {
462 case 0:
463 attr.SetColour(*wxBLUE);
464 break;
465
466 case 1:
467 attr.SetColour(*wxGREEN);
468 break;
469
470 case 2:
471 attr.SetColour(*wxRED);
472 break;
473
474 case 3:
475 attr.SetColour(*wxCYAN);
476 attr.SetBold(true);
477 break;
478
479 case 4:
480 return false;
481 }
482 break;
483
484 case Col_Max:
485 wxFAIL_MSG( "invalid column" );
486 }
487
488 return true;
489 }
490
491 bool MyListModel::SetValueByRow( const wxVariant &variant,
492 unsigned int row, unsigned int col )
493 {
494 switch ( col )
495 {
496 case Col_EditableText:
497 case Col_IconText:
498 if (row >= m_textColValues.GetCount())
499 {
500 // the item is not in the range of the items
501 // which we store... for simplicity, don't allow editing it
502 wxLogError( "Cannot edit rows with an index greater than %d",
503 m_textColValues.GetCount() );
504 return false;
505 }
506
507 if ( col == Col_EditableText )
508 {
509 m_textColValues[row] = variant.GetString();
510 }
511 else // col == Col_IconText
512 {
513 wxDataViewIconText iconText;
514 iconText << variant;
515 m_iconColValues[row] = iconText.GetText();
516 }
517 return true;
518
519 case Col_TextWithAttr:
520 case Col_Custom:
521 wxLogError("Cannot edit the column %d", col);
522 break;
523
524 case Col_Max:
525 wxFAIL_MSG( "invalid column" );
526 }
527
528 return false;
529 }