mymodels.cpp 15 KB

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