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();
135 		path.appendIndex(record.pos);
136 
137 		return path;
138 	}
139 
140 
141 	/*
142 	 * Returns a row's exported data columns
143 	 * (_get_value is what gtk_tree_model_get uses)
144 	 */
145 
146 	override Value getValue(TreeIter iter, int column, Value value = null)
147 	{
148 		CustomRecord  *record;
149 
150 		if ( value is null )
151 			value = new Value();
152 
153 		if ( iter is null || column >= nColumns || iter.stamp != stamp )
154 			return null;
155 
156 		value.init(columnTypes[column]);
157 
158 		record = cast(CustomRecord*) iter.userData;
159 
160 		if ( record is null || record.pos >= numRows )
161 			return null;
162 
163 		switch(column)
164 		{
165 			case CustomListColumn.Record:
166 				value.setPointer(record);
167 				break;
168 
169 			case CustomListColumn.Name:
170 				value.setString(record.name);
171 				break;
172 
173 			case CustomListColumn.YearBorn:
174 				value.setUint(record.yearBorn);
175 				break;
176 
177 			default:
178 				break;
179 		}
180 
181 		return value;
182 	}
183 
184 
185 	/*
186 	 * Takes an iter structure and sets it to point
187 	 * to the next row.
188 	 */
189 	override int iterNext(TreeIter iter)
190 	{
191 		CustomRecord* record, nextrecord;
192 	  
193 		if ( iter is null || iter.userData is null || iter.stamp != stamp )
194 			return false;
195 
196 		record = cast(CustomRecord*) iter.userData;
197 
198 		/* Is this the last record in the list? */
199 		if ( (record.pos + 1) >= numRows)
200 			return false;
201 
202 		nextrecord = rows[(record.pos + 1)];
203 
204 		if ( nextrecord is null || nextrecord.pos != record.pos + 1 )
205 			throw new Exception("Invalid next record");
206 
207 		iter.stamp     = stamp;
208 		iter.userData  = nextrecord;
209 
210 		return true;
211 	}
212 
213 
214 	/*
215 	 * Returns TRUE or FALSE depending on whether
216 	 * the row specified by 'parent' has any children.
217 	 * If it has children, then 'iter' is set to
218 	 * point to the first child. Special case: if
219 	 * 'parent' is NULL, then the first top-level
220 	 * row should be returned if it exists.
221 	 */
222 
223 	override int iterChildren(TreeIter iter, TreeIter parent)
224 	{
225 		/* this is a list, nodes have no children */
226 		if ( parent !is null )
227 			return false;
228 
229 		/* No rows => no first row */
230 		if ( numRows == 0 )
231 			return false;
232 
233 		/* Set iter to first item in list */
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 int 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 int iterNthChild(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.stamp     = stamp;
296 		iter.userData  = record;
297 
298 		return true;
299 	}
300 
301 
302 	/*
303 	 * Point 'iter' to the parent node of 'child'. As
304 	 * we have a list and thus no children and no
305 	 * parents of children, we can just return FALSE.
306 	 */
307 	override int iterParent(TreeIter iter, TreeIter child)
308 	{
309 		return false;
310 	}
311 
312 	/*
313 	 * Empty lists are boring. This function can
314 	 * be used in your own code to add rows to the
315 	 * list. Note how we emit the "row-inserted"
316 	 * signal after we have appended the row
317 	 * internally, so the tree view and other
318 	 * interested objects know about the new row.
319 	 */
320 	void appendRecord(string name, uint yearBorn)
321 	{
322 		TreeIter      iter;
323 		TreePath      path;
324 		CustomRecord* newrecord;
325 		uint          pos;
326 
327 		if ( name is null )
328 			return;
329 
330 		pos = numRows;
331 		numRows++;
332 
333 		newrecord = new CustomRecord;
334 
335 		newrecord.name = name;
336 		newrecord.yearBorn = yearBorn;
337 
338 		rows ~= newrecord;
339 		newrecord.pos = pos;
340 
341 		/* inform the tree view and other interested objects
342 		 *  (e.g. tree row references) that we have inserted
343 		 *  a new row, and where it was inserted */
344 
345 		path = new TreePath();
346 		path.appendIndex(pos);
347 
348 		iter = new TreeIter();
349 		getIter(iter, path);
350 
351 		rowInserted(path, iter);
352 	}
353 }