1 module CustomList; 2 3 import glib.RandG; 4 import gobject.Value; 5 import gtk.TreeIter; 6 import gtk.TreePath; 7 import gtk.TreeModel; 8 9 struct CustomRecord 10 { 11 /* data - you can extend this */ 12 string name; 13 uint yearBorn; 14 15 /* admin stuff used by the custom list model */ 16 uint pos; /* pos within the array */ 17 } 18 19 enum CustomListColumn 20 { 21 Record = 0, 22 Name, 23 YearBorn, 24 NColumns, 25 } 26 27 class CustomList : TreeModel 28 { 29 uint numRows; 30 int nColumns; 31 int stamp; 32 GType[3] columnTypes; 33 CustomRecord*[] rows; 34 35 public this() 36 { 37 nColumns = columnTypes.length; 38 columnTypes[0] = GType.POINTER; 39 columnTypes[1] = GType.STRING; 40 columnTypes[2] = GType.UINT; 41 42 stamp = RandG.randomInt(); 43 } 44 45 /* 46 * tells the rest of the world whether our tree model 47 * has any special characteristics. In our case, 48 * we have a list model (instead of a tree), and each 49 * tree iter is valid as long as the row in question 50 * exists, as it only contains a pointer to our struct. 51 */ 52 override GtkTreeModelFlags getFlags() 53 { 54 return (GtkTreeModelFlags.LIST_ONLY | GtkTreeModelFlags.ITERS_PERSIST); 55 } 56 57 58 /* 59 * tells the rest of the world how many data 60 * columns we export via the tree model interface 61 */ 62 63 override int getNColumns() 64 { 65 return nColumns; 66 } 67 68 /* 69 * tells the rest of the world which type of 70 * data an exported model column contains 71 */ 72 override GType getColumnType(int index) 73 { 74 if ( index >= nColumns || index < 0 ) 75 return GType.INVALID; 76 77 return columnTypes[index]; 78 } 79 80 /* 81 * converts a tree path (physical position) into a 82 * tree iter structure (the content of the iter 83 * fields will only be used internally by our model). 84 * We simply store a pointer to our CustomRecord 85 * structure that represents that row in the tree iter. 86 */ 87 override int getIter(TreeIter iter, TreePath path) 88 { 89 CustomRecord* record; 90 int[] indices; 91 int n, depth; 92 93 indices = path.getIndices(); 94 depth = path.getDepth(); 95 96 /* we do not allow children */ 97 if (depth != 1) 98 return false;//throw new Exception("We only except lists"); 99 100 n = indices[0]; /* the n-th top level row */ 101 102 if ( n >= numRows || n < 0 ) 103 return false; 104 105 record = rows[n]; 106 107 if ( record is null ) 108 throw new Exception("Not Exsisting record requested"); 109 if ( record.pos != n ) 110 throw new Exception("record.pos != TreePath.getIndices()[0]"); 111 112 /* We simply store a pointer to our custom record in the iter */ 113 iter.stamp = stamp; 114 iter.userData = record; 115 116 return true; 117 } 118 119 120 /* 121 * converts a tree iter into a tree path (ie. the 122 * physical position of that row in the list). 123 */ 124 override TreePath getPath(TreeIter iter) 125 { 126 TreePath path; 127 CustomRecord* record; 128 129 if ( iter is null || iter.userData is null || iter.stamp != stamp ) 130 return null; 131 132 record = cast(CustomRecord*) iter.userData; 133 134 path = new TreePath(record.pos); 135 136 return path; 137 } 138 139 140 /* 141 * Returns a row's exported data columns 142 * (_get_value is what gtk_tree_model_get uses) 143 */ 144 145 override Value getValue(TreeIter iter, int column, Value value = null) 146 { 147 CustomRecord *record; 148 149 if ( value is null ) 150 value = new Value(); 151 152 if ( iter is null || column >= nColumns || iter.stamp != stamp ) 153 return null; 154 155 value.init(columnTypes[column]); 156 157 record = cast(CustomRecord*) iter.userData; 158 159 if ( record is null || record.pos >= numRows ) 160 return null; 161 162 switch(column) 163 { 164 case CustomListColumn.Record: 165 value.setPointer(record); 166 break; 167 168 case CustomListColumn.Name: 169 value.setString(record.name); 170 break; 171 172 case CustomListColumn.YearBorn: 173 value.setUint(record.yearBorn); 174 break; 175 176 default: 177 break; 178 } 179 180 return value; 181 } 182 183 184 /* 185 * Takes an iter structure and sets it to point 186 * to the next row. 187 */ 188 override bool iterNext(TreeIter iter) 189 { 190 CustomRecord* record, nextrecord; 191 192 if ( iter is null || iter.userData is null || iter.stamp != stamp ) 193 return false; 194 195 record = cast(CustomRecord*) iter.userData; 196 197 /* Is this the last record in the list? */ 198 if ( (record.pos + 1) >= numRows) 199 return false; 200 201 nextrecord = rows[(record.pos + 1)]; 202 203 if ( nextrecord is null || nextrecord.pos != record.pos + 1 ) 204 throw new Exception("Invalid next record"); 205 206 iter.stamp = stamp; 207 iter.userData = nextrecord; 208 209 return true; 210 } 211 212 213 /* 214 * Returns TRUE or FALSE depending on whether 215 * the row specified by 'parent' has any children. 216 * If it has children, then 'iter' is set to 217 * point to the first child. Special case: if 218 * 'parent' is NULL, then the first top-level 219 * row should be returned if it exists. 220 */ 221 222 override bool iterChildren(out TreeIter iter, TreeIter parent) 223 { 224 /* this is a list, nodes have no children */ 225 if ( parent !is null ) 226 return false; 227 228 /* No rows => no first row */ 229 if ( numRows == 0 ) 230 return false; 231 232 /* Set iter to first item in list */ 233 iter = new TreeIter(); 234 iter.stamp = stamp; 235 iter.userData = rows[0]; 236 237 return true; 238 } 239 240 241 /* 242 * Returns TRUE or FALSE depending on whether 243 * the row specified by 'iter' has any children. 244 * We only have a list and thus no children. 245 */ 246 override bool iterHasChild(TreeIter iter) 247 { 248 return false; 249 } 250 251 252 /* 253 * Returns the number of children the row 254 * specified by 'iter' has. This is usually 0, 255 * as we only have a list and thus do not have 256 * any children to any rows. A special case is 257 * when 'iter' is NULL, in which case we need 258 * to return the number of top-level nodes, 259 * ie. the number of rows in our list. 260 */ 261 override int iterNChildren(TreeIter iter) 262 { 263 /* special case: if iter == NULL, return number of top-level rows */ 264 if ( iter is null ) 265 return numRows; 266 267 return 0; /* otherwise, this is easy again for a list */ 268 } 269 270 271 /* 272 * If the row specified by 'parent' has any 273 * children, set 'iter' to the n-th child and 274 * return TRUE if it exists, otherwise FALSE. 275 * A special case is when 'parent' is NULL, in 276 * which case we need to set 'iter' to the n-th 277 * row if it exists. 278 */ 279 override bool iterNthChild(out TreeIter iter, TreeIter parent, int n) 280 { 281 CustomRecord *record; 282 283 /* a list has only top-level rows */ 284 if( parent !is null ) 285 return false; 286 287 if( n >= numRows ) 288 return false; 289 290 record = rows[n]; 291 292 if ( record == null || record.pos != n ) 293 throw new Exception("Invalid record"); 294 295 iter = new TreeIter(); 296 iter.stamp = stamp; 297 iter.userData = record; 298 299 return true; 300 } 301 302 303 /* 304 * Point 'iter' to the parent node of 'child'. As 305 * we have a list and thus no children and no 306 * parents of children, we can just return FALSE. 307 */ 308 override bool iterParent(out TreeIter iter, TreeIter child) 309 { 310 return false; 311 } 312 313 /* 314 * Empty lists are boring. This function can 315 * be used in your own code to add rows to the 316 * list. Note how we emit the "row-inserted" 317 * signal after we have appended the row 318 * internally, so the tree view and other 319 * interested objects know about the new row. 320 */ 321 void appendRecord(string name, uint yearBorn) 322 { 323 TreeIter iter; 324 TreePath path; 325 CustomRecord* newrecord; 326 uint pos; 327 328 if ( name is null ) 329 return; 330 331 pos = numRows; 332 numRows++; 333 334 newrecord = new CustomRecord; 335 336 newrecord.name = name; 337 newrecord.yearBorn = yearBorn; 338 339 rows ~= newrecord; 340 newrecord.pos = pos; 341 342 /* inform the tree view and other interested objects 343 * (e.g. tree row references) that we have inserted 344 * a new row, and where it was inserted */ 345 346 path = new TreePath(pos); 347 348 iter = new TreeIter(); 349 getIter(iter, path); 350 351 rowInserted(path, iter); 352 } 353 }