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 }