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 int 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 int iterChildren(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.stamp     = stamp;
234 		iter.userData  = rows[0];
235 
236 		return true;
237 	}
238 
239 
240 	/*
241 	 * Returns TRUE or FALSE depending on whether
242 	 * the row specified by 'iter' has any children.
243 	 * We only have a list and thus no children.
244 	 */
245 	override int iterHasChild(TreeIter iter)
246 	{
247 		return false;
248 	}
249 
250 
251 	/*
252 	 * Returns the number of children the row
253 	 * specified by 'iter' has. This is usually 0,
254 	 * as we only have a list and thus do not have
255 	 * any children to any rows. A special case is
256 	 * when 'iter' is NULL, in which case we need
257 	 * to return the number of top-level nodes,
258 	 * ie. the number of rows in our list.
259 	 */
260 	override int iterNChildren(TreeIter iter)
261 	{
262 		/* special case: if iter == NULL, return number of top-level rows */
263 		if ( iter is null )
264 			return numRows;
265 
266 		return 0; /* otherwise, this is easy again for a list */
267 	}
268 
269 
270 	/*
271 	 * If the row specified by 'parent' has any
272 	 * children, set 'iter' to the n-th child and
273 	 * return TRUE if it exists, otherwise FALSE.
274 	 * A special case is when 'parent' is NULL, in
275 	 * which case we need to set 'iter' to the n-th
276 	 * row if it exists.
277 	 */
278 	override int iterNthChild(TreeIter iter, TreeIter parent, int n)
279 	{
280 		CustomRecord  *record;
281 
282 		/* a list has only top-level rows */
283 		if( parent !is null )
284 			return false;
285 
286 		if( n >= numRows )
287 			return false;
288 
289 		record = rows[n];
290 
291 		if ( record == null || record.pos != n )
292 			throw new Exception("Invalid record");
293 
294 		iter.stamp     = stamp;
295 		iter.userData  = record;
296 
297 		return true;
298 	}
299 
300 
301 	/*
302 	 * Point 'iter' to the parent node of 'child'. As
303 	 * we have a list and thus no children and no
304 	 * parents of children, we can just return FALSE.
305 	 */
306 	override int iterParent(TreeIter iter, TreeIter child)
307 	{
308 		return false;
309 	}
310 
311 	/*
312 	 * Empty lists are boring. This function can
313 	 * be used in your own code to add rows to the
314 	 * list. Note how we emit the "row-inserted"
315 	 * signal after we have appended the row
316 	 * internally, so the tree view and other
317 	 * interested objects know about the new row.
318 	 */
319 	void appendRecord(string name, uint yearBorn)
320 	{
321 		TreeIter      iter;
322 		TreePath      path;
323 		CustomRecord* newrecord;
324 		uint          pos;
325 
326 		if ( name is null )
327 			return;
328 
329 		pos = numRows;
330 		numRows++;
331 
332 		newrecord = new CustomRecord;
333 
334 		newrecord.name = name;
335 		newrecord.yearBorn = yearBorn;
336 
337 		rows ~= newrecord;
338 		newrecord.pos = pos;
339 
340 		/* inform the tree view and other interested objects
341 		 *  (e.g. tree row references) that we have inserted
342 		 *  a new row, and where it was inserted */
343 
344 		path = new TreePath(pos);
345 
346 		iter = new TreeIter();
347 		getIter(iter, path);
348 
349 		rowInserted(path, iter);
350 	}
351 }