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 }