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