1 module treemodel;
2 
3 import dqml.qabstractitemmodel;
4 import dqml.qmodelindex;
5 import dqml.qvariant;
6 import dqml.qobject;
7 import dqml.qt;
8 
9 import treeitem;
10 import custom_type : CustomType;
11 
12 class TreeModel : QAbstractItemModel
13 {
14 public:
15 	
16 	enum TreeModelRoles
17 	{
18 		Name = UserRole + 1,
19 		Description
20 	}
21 
22 	alias flags = QAbstractItemModel.flags;
23 
24 	this(const string data, QObject parent = null)
25 	{
26 		super();
27 		m_roleNameMapping[TreeModelRoles.Name]        = "title";
28 		m_roleNameMapping[TreeModelRoles.Description] = "summary";
29 
30 		QVariant[] rootData;
31 		rootData ~= new QVariant("Title" ~ "Summary");
32 		rootItem = new TreeItem(rootData);
33 		import std.string : split;
34 		setupModelData(data.split("\n"), rootItem);
35 	}
36 
37 	~this()
38 	{
39 		destroy(rootItem);
40 	}
41 
42 	/* QAbstractItemModel interface */
43 	override QVariant data(QModelIndex index, int role) const
44 	{
45 		if (!index.isValid())
46 			return new QVariant();
47 
48 		if (role != TreeModelRoles.Name && role != TreeModelRoles.Description)
49 			return new QVariant();
50 
51 		TreeItem item = cast(TreeItem)(index.internalPointer());
52 
53 		return item.data(role - UserRole - 1);
54 	}
55 
56 	override int flags(QModelIndex index)
57 	{
58 		if (!index.isValid())
59 			return 0;
60 
61 		return super.flags(index);
62 	}
63 
64 	override QModelIndex index(int row, int column,
65 					  QModelIndex parent = new QModelIndex())
66 	{
67 		if (!hasIndex(row, column, parent))
68 			return new QModelIndex();
69 
70 		TreeItem parentItem;
71 
72 		if (!parent.isValid())
73 			parentItem = rootItem;
74 		else
75 			parentItem = cast(TreeItem)(parent.internalPointer());
76 
77 		TreeItem childItem = parentItem.child(row);
78 		if (childItem)
79 			return createIndex(row, column, cast(void*) childItem);
80 		else
81 			return new QModelIndex();
82 	}
83 
84 	override QModelIndex parent(QModelIndex index)
85 	{
86 		if (!index.isValid())
87 			return new QModelIndex();
88 
89 		TreeItem childItem = cast(TreeItem)(index.internalPointer());
90 		TreeItem parentItem = childItem.parentItem();
91 
92 		if (parentItem == rootItem)
93 			return new QModelIndex();
94 
95 		return createIndex(parentItem.row(), 0, cast(void*) parentItem);
96 	}
97 
98 	override int rowCount(QModelIndex parent = new QModelIndex())
99 	{
100 		TreeItem parentItem;
101 		if (parent.column() > 0)
102 			return 0;
103 
104 		if (!parent.isValid())
105 			parentItem = rootItem;
106 		else
107 			parentItem = cast(TreeItem)(parent.internalPointer());
108 
109 		return parentItem.childCount();
110 	}
111 
112 	override int columnCount(QModelIndex parent = new QModelIndex())
113 	{
114 		if (parent.isValid())
115 			return (cast(TreeItem)(parent.internalPointer())).columnCount();
116 		else
117 			return rootItem.columnCount();
118 	}
119 
120 	override string[int] roleNames()
121 	{
122 		return m_roleNameMapping;
123 	}
124 
125 private:
126 
127 	QVariant newCustomType(const string text, int position)
128 	{
129 		CustomType t = new CustomType(this);
130 		t.setText(text);
131 		t.setIndentation(position);
132 		auto v = new QVariant();
133 		v.setValue(t);
134 		return v;
135 	}
136 
137 	void setupModelData(const string[] lines, TreeItem parent)
138 	{
139 		TreeItem[] parents;
140 		int[] indentations;
141 		parents ~= parent;
142 		indentations ~= 0;
143 
144 		int number = 0;
145 
146 		while (number < lines.length) {
147 			int position = 0;
148 			while (position < lines[number].length) {
149 				if (lines[number][position] != ' ')
150 					break;
151 				position++;
152 			}
153 
154 			import std.string : split, strip, squeeze;
155 			string lineData = lines[number][position..$].strip.squeeze;
156 
157 			import std.array : empty, back, popBack;
158 			if (!lineData.empty) {
159 				// Read the column data from the rest of the line.
160 				string[] columnStrings = lineData.split("\t");
161 				QVariant[] columnData;
162 				for (int column = 0; column < columnStrings.length; ++column)
163 					columnData ~= newCustomType(columnStrings[column], position);
164 
165 				if (position > indentations.back) {
166 					// The last child of the current parent is now the new parent
167 					// unless the current parent has no children.
168 					if (parents.back.childCount() > 0) {
169 						parents ~= parents.back.child(parents.back.childCount()-1);
170 						indentations ~= position;
171 					}
172 				} else {
173 					while (position < indentations.back && parents.length > 0) {
174 						parents.popBack;
175 						indentations.popBack;
176 					}
177 				}
178 
179 				// Append a new item to the current parent's list of children.
180 				parents.back.appendChild(new TreeItem(columnData, parents.back));
181 			}
182 
183 			++number;
184 		}
185 	}
186 
187 	TreeItem    rootItem;
188 	string[int] m_roleNameMapping;
189 }