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 }