changeset 153:ea0861a37bf6

add editabletreemodel, still buggy
author mandel
date Tue, 16 Jun 2009 00:51:38 +0000
parents 4d1c5d1d1bbf
children 00d84545551e
files examples/itemviews/editabletreemodel/CMakeLists.txt examples/itemviews/editabletreemodel/build.bat examples/itemviews/editabletreemodel/build.sh examples/itemviews/editabletreemodel/default.txt examples/itemviews/editabletreemodel/editabletreemodel.qrc examples/itemviews/editabletreemodel/main.d examples/itemviews/editabletreemodel/mainwindow.d examples/itemviews/editabletreemodel/mainwindow.ui examples/itemviews/editabletreemodel/treeitem.d examples/itemviews/editabletreemodel/treemodel.d
diffstat 10 files changed, 953 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/itemviews/editabletreemodel/CMakeLists.txt	Tue Jun 16 00:51:38 2009 +0000
@@ -0,0 +1,1 @@
+build_example(editabletreemodel main.d mainwindow.d treeitem.d treemodel.d ui_mainwindow.d qrc_editabletreemodel.d)
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/itemviews/editabletreemodel/build.bat	Tue Jun 16 00:51:38 2009 +0000
@@ -0,0 +1,3 @@
+../../../tools/drcc/drcc editabletreemodel.qrc -o qrc_editabletreemodel.d
+../../../tools/duic/duic mainwindow.ui -o ui_mainwindow.d
+dmd main.d mainwindow.d treeitem.d treemodel.d ui_mainwindow.d qrc_editabletreemodel.d libqtdcore.lib libqtdgui.lib -I../../../
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/itemviews/editabletreemodel/build.sh	Tue Jun 16 00:51:38 2009 +0000
@@ -0,0 +1,5 @@
+#! /bin/bash
+
+../../../tools/drcc/drcc editabletreemodel.qrc -o qrc_editabletreemodel.d
+../../../tools/duic/duic mainwindow.ui -o ui_mainwindow.d
+ldc main.d mainwindow.d treeitem.d treemodel.d ui_mainwindow.d qrc_editabletreemodel.d -I../../../ -I../../../qt/d1 -L-L../../../lib -L-lqtdgui -L-lqtdcore -L-lQtCore -L-lQtGui -ofeditabletreemodel
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/itemviews/editabletreemodel/default.txt	Tue Jun 16 00:51:38 2009 +0000
@@ -0,0 +1,40 @@
+Getting Started				How to familiarize yourself with Qt Designer
+    Launching Designer			Running the Qt Designer application
+    The User Interface			How to interact with Qt Designer
+
+Designing a Component			Creating a GUI for your application
+    Creating a Dialog			How to create a dialog
+    Composing the Dialog		Putting widgets into the dialog example
+    Creating a Layout			Arranging widgets on a form
+    Signal and Slot Connections		Making widget communicate with each other
+
+Using a Component in Your Application	Generating code from forms
+    The Direct Approach			Using a form without any adjustments
+    The Single Inheritance Approach	Subclassing a form's base class
+    The Multiple Inheritance Approach	Subclassing the form itself
+    Automatic Connections		Connecting widgets using a naming scheme
+        A Dialog Without Auto-Connect	How to connect widgets without a naming scheme
+        A Dialog With Auto-Connect	Using automatic connections
+
+Form Editing Mode			How to edit a form in Qt Designer
+    Managing Forms			Loading and saving forms
+    Editing a Form			Basic editing techniques
+    The Property Editor			Changing widget properties
+    The Object Inspector		Examining the hierarchy of objects on a form
+    Layouts				Objects that arrange widgets on a form
+        Applying and Breaking Layouts	Managing widgets in layouts 
+        Horizontal and Vertical Layouts	Standard row and column layouts
+        The Grid Layout			Arranging widgets in a matrix
+    Previewing Forms			Checking that the design works
+
+Using Containers			How to group widgets together
+    General Features			Common container features
+    Frames				QFrame
+    Group Boxes				QGroupBox
+    Stacked Widgets			QStackedWidget
+    Tab Widgets				QTabWidget
+    Toolbox Widgets			QToolBox
+
+Connection Editing Mode			Connecting widgets together with signals and slots
+    Connecting Objects			Making connections in Qt Designer
+    Editing Connections			Changing existing connections
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/itemviews/editabletreemodel/editabletreemodel.qrc	Tue Jun 16 00:51:38 2009 +0000
@@ -0,0 +1,5 @@
+<RCC>
+    <qresource prefix="/" >
+        <file>default.txt</file>
+    </qresource>
+</RCC>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/itemviews/editabletreemodel/main.d	Tue Jun 16 00:51:38 2009 +0000
@@ -0,0 +1,55 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial Usage
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+module main;
+
+
+import qt.gui.QApplication;
+
+import mainwindow;
+
+
+int main(string[] args)
+{
+	scope app = new QApplication(args);
+	scope window = new MainWindow;
+	window.show();
+	return app.exec();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/itemviews/editabletreemodel/mainwindow.d	Tue Jun 16 00:51:38 2009 +0000
@@ -0,0 +1,202 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial Usage
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+module mainwindow;
+
+
+import qt.core.QModelIndex;
+import qt.core.QFile;
+import qt.gui.QMainWindow;
+import qt.gui.QAction;
+import qt.gui.QTreeView;
+import qt.gui.QWidget;
+
+import tango.text.convert.Format;
+
+import ui_mainwindow;
+import mainwindow;
+import treemodel;
+
+
+class MainWindow : public QMainWindow
+{
+
+mixin Ui_MainWindow;
+
+public:
+
+	this(QWidget parent = null)
+	{
+		super(parent);
+		setupUi(this);
+
+		string[] headers;
+		headers ~= tr("Title");
+		headers ~= tr("Description");
+
+		auto file = new QFile(":/default.txt");
+		file.open(QIODevice.ReadOnly);
+		TreeModel model = new TreeModel(headers, file.readAll().data()[0..file.size()]);
+		file.close();
+
+		view.setModel(model);
+		for (int column = 0; column < model.columnCount(); ++column)
+			view.resizeColumnToContents(column);
+
+		exitAction.triggered.connect(&QApplication.quit);
+
+		view.selectionModel().selectionChanged.connect(&this.updateActions);
+
+		actionsMenu.aboutToShow.connect(&this.updateActions);
+		insertRowAction.triggered.connect(&this.insertRow);
+		insertColumnAction.triggered.connect(&this.insertColumn);
+		removeRowAction.triggered.connect(&this.removeRow);
+		removeColumnAction.triggered.connect(&this.removeColumn);
+		insertChildAction.triggered.connect(&this.insertChild);
+
+		updateActions();
+	}
+
+public:
+
+	void updateActions()
+	{
+		bool hasSelection = !view.selectionModel().selection().isEmpty();
+		removeRowAction.setEnabled(hasSelection);
+		removeColumnAction.setEnabled(hasSelection);
+
+		bool hasCurrent = view.selectionModel().currentIndex().isValid();
+		insertRowAction.setEnabled(hasCurrent);
+		insertColumnAction.setEnabled(hasCurrent);
+
+		if (hasCurrent) {
+			view.closePersistentEditor(view.selectionModel().currentIndex());
+
+			int row = view.selectionModel().currentIndex().row();
+			int column = view.selectionModel().currentIndex().column();
+			if (view.selectionModel().currentIndex().parent().isValid())
+				statusBar().showMessage(Format(tr("Position: ({},{})"), row, column));
+			else
+				statusBar().showMessage(Format(tr("Position: ({},{}) in top level"), row, column));
+		}
+	}
+
+private:
+
+	void insertChild()
+	{
+		QModelIndex index = view.selectionModel().currentIndex();
+		QAbstractItemModel model = view.model();
+
+		if (model.columnCount(index) == 0) {
+			if (!model.insertColumn(0, index))
+				return;
+		}
+
+		if (!model.insertRow(0, index))
+			return;
+
+		for (int column = 0; column < model.columnCount(index); ++column) {
+			QModelIndex child = model.index(0, column, index);
+			model.setData(child, QVariant("[No data]"), Qt.EditRole);
+			if (!model.headerData(column, Qt.Horizontal).isValid())
+				model.setHeaderData(column, Qt.Horizontal, QVariant("[No header]"), Qt.EditRole);
+		}
+
+		view.selectionModel().setCurrentIndex(model.index(0, 0, index), QItemSelectionModel.ClearAndSelect);
+		updateActions();
+	}
+
+	bool insertColumn()
+	{
+		QModelIndex parent = QModelIndex(); //moved from method head
+		QAbstractItemModel model = view.model();
+		int column = view.selectionModel().currentIndex().column();
+
+		// Insert a column in the parent item.
+		bool changed = model.insertColumn(column + 1, parent);
+		if (changed)
+			model.setHeaderData(column + 1, Qt.Horizontal, QVariant("[No header]"), Qt.EditRole);
+
+		updateActions();
+
+		return changed;
+	}
+
+	void insertRow()
+	{
+		QModelIndex index = view.selectionModel().currentIndex();
+		QAbstractItemModel model = view.model();
+
+		if (!model.insertRow(index.row()+1, index.parent()))
+			return;
+
+		updateActions();
+
+		for (int column = 0; column < model.columnCount(index.parent()); ++column) {
+			QModelIndex child = model.index(index.row()+1, column, index.parent());
+			model.setData(child, QVariant("[No data]"), Qt.EditRole);
+		}
+	}
+
+	bool removeColumn()
+	{
+		QModelIndex parent = QModelIndex(); //moved from method head
+		QAbstractItemModel model = view.model();
+		int column = view.selectionModel().currentIndex().column();
+
+		// Insert columns in each child of the parent item.
+		bool changed = model.removeColumn(column, parent);
+
+		if (!parent.isValid() && changed)
+			updateActions();
+
+		return changed;
+	}
+
+	void removeRow()
+	{
+		QModelIndex index = view.selectionModel().currentIndex();
+		QAbstractItemModel model = view.model();
+		if (model.removeRow(index.row(), index.parent()))
+			updateActions();
+	}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/itemviews/editabletreemodel/mainwindow.ui	Tue Jun 16 00:51:38 2009 +0000
@@ -0,0 +1,128 @@
+<ui version="4.0" >
+ <class>MainWindow</class>
+ <widget class="QMainWindow" name="MainWindow" >
+  <property name="geometry" >
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>573</width>
+    <height>468</height>
+   </rect>
+  </property>
+  <property name="windowTitle" >
+   <string>Editable Tree Model</string>
+  </property>
+  <widget class="QWidget" name="centralwidget" >
+   <layout class="QVBoxLayout" >
+    <property name="margin" >
+     <number>0</number>
+    </property>
+    <property name="spacing" >
+     <number>0</number>
+    </property>
+    <item>
+     <widget class="QTreeView" name="view" >
+      <property name="alternatingRowColors" >
+       <bool>true</bool>
+      </property>
+      <property name="selectionBehavior" >
+       <enum>QAbstractItemView::SelectItems</enum>
+      </property>
+      <property name="horizontalScrollMode" >
+       <enum>QAbstractItemView::ScrollPerPixel</enum>
+      </property>
+      <property name="animated" >
+       <bool>false</bool>
+      </property>
+      <property name="allColumnsShowFocus" >
+       <bool>true</bool>
+      </property>
+     </widget>
+    </item>
+   </layout>
+  </widget>
+  <widget class="QMenuBar" name="menubar" >
+   <property name="geometry" >
+    <rect>
+     <x>0</x>
+     <y>0</y>
+     <width>573</width>
+     <height>31</height>
+    </rect>
+   </property>
+   <widget class="QMenu" name="fileMenu" >
+    <property name="title" >
+     <string>&amp;File</string>
+    </property>
+    <addaction name="exitAction" />
+   </widget>
+   <widget class="QMenu" name="actionsMenu" >
+    <property name="title" >
+     <string>&amp;Actions</string>
+    </property>
+    <addaction name="insertRowAction" />
+    <addaction name="insertColumnAction" />
+    <addaction name="separator" />
+    <addaction name="removeRowAction" />
+    <addaction name="removeColumnAction" />
+    <addaction name="separator" />
+    <addaction name="insertChildAction" />
+   </widget>
+   <addaction name="fileMenu" />
+   <addaction name="actionsMenu" />
+  </widget>
+  <widget class="QStatusBar" name="statusbar" />
+  <action name="exitAction" >
+   <property name="text" >
+    <string>E&amp;xit</string>
+   </property>
+   <property name="shortcut" >
+    <string>Ctrl+Q</string>
+   </property>
+  </action>
+  <action name="insertRowAction" >
+   <property name="text" >
+    <string>Insert Row</string>
+   </property>
+   <property name="shortcut" >
+    <string>Ctrl+I, R</string>
+   </property>
+  </action>
+  <action name="removeRowAction" >
+   <property name="text" >
+    <string>Remove Row</string>
+   </property>
+   <property name="shortcut" >
+    <string>Ctrl+R, R</string>
+   </property>
+  </action>
+  <action name="insertColumnAction" >
+   <property name="text" >
+    <string>Insert Column</string>
+   </property>
+   <property name="shortcut" >
+    <string>Ctrl+I, C</string>
+   </property>
+  </action>
+  <action name="removeColumnAction" >
+   <property name="text" >
+    <string>Remove Column</string>
+   </property>
+   <property name="shortcut" >
+    <string>Ctrl+R, C</string>
+   </property>
+  </action>
+  <action name="insertChildAction" >
+   <property name="text" >
+    <string>Insert Child</string>
+   </property>
+   <property name="shortcut" >
+    <string>Ctrl+N</string>
+   </property>
+  </action>
+ </widget>
+ <resources>
+  <include location="editabletreemodel.qrc" />
+ </resources>
+ <connections/>
+</ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/itemviews/editabletreemodel/treeitem.d	Tue Jun 16 00:51:38 2009 +0000
@@ -0,0 +1,197 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial Usage
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+module treeitem;
+
+
+import qt.core.QVariant;
+
+import tango.core.Array;
+
+
+//substitute for QList methods
+void insert(T)(ref T[] items, int pos, T item)
+{
+	items = items[0..pos] ~ [item] ~ items[pos..$];
+}
+
+T takeAt(T)(ref T[] items, int pos)
+{
+	T item = items[pos];
+	items = items[0..pos] ~ items[pos+1..$];
+	return item;
+}
+
+int indexOf(T)(T[] items, T item, int from = -1)
+{
+	assert(from < cast(int) items.length);
+	auto beg = (from < 0) ? 0 : from;
+	auto pos = beg + find(items[beg..$], item);
+	return (pos == items.length) ? -1 : pos;
+}
+
+void remove(T)(ref T[] items, uint pos)
+{
+	items = items[0..pos] ~ items[pos+1..$];
+}
+
+
+class TreeItem
+{
+public:
+
+	this(QVariant[] data, TreeItem parent = null)
+	{
+		parentItem = parent;
+		itemData = data;
+	}
+	
+	~this()
+	{
+		delete childItems;
+	}
+
+	TreeItem child(int number)
+	{
+		if(number < 0 || number >= childItems.length)
+			return null;
+		return childItems[number];
+	}
+
+	int childCount()
+	{
+		return childItems.length;
+	}
+
+	int columnCount()
+	{
+		return itemData.length;
+	}
+
+	QVariant data(int column)
+	{
+		if(column < 0 || column >= itemData.length)
+			return null;
+		return itemData[column];
+	}
+
+	bool insertChildren(int position, int count, int columns)
+	{
+		if (position < 0 || position > childItems.length)
+			return false;
+
+		for (int row = 0; row < count; ++row) {
+			auto data = new QVariant[](columns);
+			TreeItem item = new TreeItem(data, this);
+			insert(childItems, position, item);
+		}
+
+		return true;
+	}
+
+	bool insertColumns(int position, int columns)
+	{
+		if (position < 0 || position > itemData.length)
+			return false;
+
+		for (int column = 0; column < columns; ++column)
+			insert(itemData, position, new QVariant());
+
+		foreach (TreeItem child; childItems)
+			child.insertColumns(position, columns);
+
+		return true;
+	}
+
+	TreeItem parent()
+	{
+		return parentItem;
+	}
+
+	bool removeChildren(int position, int count)
+	{
+		if (position < 0 || position + count > childItems.length)
+			return false;
+
+		for (int row = 0; row < count; ++row)
+		{
+			auto tmp = takeAt(childItems, position);
+			delete tmp;
+		}
+		return true;
+	}
+
+	bool removeColumns(int position, int columns)
+	{
+		if (position < 0 || position + columns > itemData.length)
+			return false;
+
+		for (int column = 0; column < columns; ++column)
+			remove(itemData, position);
+
+		foreach (TreeItem child; childItems)
+			child.removeColumns(position, columns);
+
+		return true;
+	}
+
+	int childNumber()
+	{
+		if (parentItem)
+			return indexOf(parentItem.childItems, cast(TreeItem) this);
+
+		return 0;
+	}
+
+	bool setData(int column, QVariant value)
+	{
+		if (column < 0 || column >= itemData.length)
+			return false;
+
+		itemData[column] = value;
+		return true;
+	}
+
+private:
+
+	TreeItem[] childItems;
+	QVariant[] itemData;
+	TreeItem parentItem;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/itemviews/editabletreemodel/treemodel.d	Tue Jun 16 00:51:38 2009 +0000
@@ -0,0 +1,317 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial Usage
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+module treemodel;
+
+
+import qt.core.QAbstractItemModel;
+import qt.core.QModelIndex;
+import qt.core.QVariant;
+
+import treeitem;
+
+import tango.core.Array : find;
+import tango.text.Util : trim;
+
+
+//replacement for QString.split
+T[][] split(T)(T[] str, T[] match, bool keep_empty = true)
+{
+	uint pos, start = 0;
+	
+	T[][] values;
+	if(str.length == 0)
+		return values;
+	
+	while(true)
+	{
+		pos = start + find(str[start..$], match);
+		if(keep_empty)
+		{
+			if((pos - start) >= 0)
+				values ~= str[start..pos];
+		}
+		else
+		{
+			if((pos - start) != 0)
+				values ~= str[start..pos];
+		}
+		
+		if(pos == str.length)
+			break;
+		
+		start = pos + 1;
+	}
+	
+	return values;	
+}
+
+
+class TreeModel : public QAbstractItemModel
+{
+public:
+
+	this(string[] headers, string data, QObject parent = null)
+	{
+		super(parent);
+		QVariant[] rootData;
+		foreach(string header; headers)
+			rootData ~= new QVariant(header);
+
+		rootItem = new TreeItem(rootData);
+		setupModelData(split(data, "\n"), rootItem);
+	}
+
+	~this()
+	{
+		delete rootItem;
+	}
+
+	QVariant data(QModelIndex index, int role)
+	{
+		if (!index.isValid())
+			return new QVariant();
+
+		if (role != Qt.DisplayRole && role != Qt.EditRole)
+			return new QVariant();
+
+		TreeItem item = getItem(index);
+		return item.data(index.column());
+	}
+
+	QVariant headerData(int section, Qt.Orientation orientation, int role = Qt.DisplayRole)
+	{
+		if (orientation == Qt.Horizontal && role == Qt.DisplayRole)
+			return rootItem.data(section);
+
+		return new QVariant();
+	}
+
+	QModelIndex index(int row, int column, QModelIndex parent = QModelIndex())
+	{
+		if (parent.isValid() && parent.column() != 0)
+			return QModelIndex();
+
+		TreeItem parentItem = getItem(parent);
+
+		TreeItem childItem = parentItem.child(row);
+		if (childItem)
+			return createIndex(row, column, cast(void*) childItem);
+		else
+			return QModelIndex();
+	}
+
+	QModelIndex parent(QModelIndex index)
+	{
+		if (!index.isValid())
+			return QModelIndex();
+
+		TreeItem childItem = getItem(index);
+		TreeItem parentItem = childItem.parent();
+
+		if (parentItem == rootItem)
+			return QModelIndex();
+
+		return createIndex(parentItem.childNumber(), 0, cast(void*) parentItem);
+	}
+
+	int rowCount(QModelIndex parent = QModelIndex())
+	{
+		TreeItem parentItem = getItem(parent);
+		return parentItem.childCount();
+	}
+	
+	int columnCount(QModelIndex parent = QModelIndex())
+	{
+		return rootItem.columnCount();
+	}
+
+	int flags(QModelIndex index)
+	{
+		if (!index.isValid())
+			return 0;
+
+		return Qt.ItemIsEditable | Qt.ItemIsEnabled | Qt.ItemIsSelectable;
+	}
+
+
+	bool setData(QModelIndex index, QVariant value, int role = Qt.EditRole)
+	{
+		if (role != Qt.EditRole)
+			return false;
+
+		TreeItem item = getItem(index);
+		bool result = item.setData(index.column(), value);
+
+		if (result)
+			dataChanged.emit(index, index);
+
+		return result;
+	}
+
+	bool setHeaderData(int section, Qt.Orientation orientation, QVariant value, int role = Qt.EditRole)
+	{
+		if (role != Qt.EditRole || orientation != Qt.Horizontal)
+			return false;
+
+		bool result = rootItem.setData(section, value);
+
+		if (result)
+			headerDataChanged.emit(orientation, section, section);
+
+		return result;
+	}
+
+	bool insertColumns(int position, int columns, QModelIndex parent = QModelIndex())
+	{
+		bool success;
+
+		beginInsertColumns(parent, position, position + columns - 1);
+		success = rootItem.insertColumns(position, columns);
+		endInsertColumns();
+
+		return success;
+	}
+
+
+	bool removeColumns(int position, int columns, QModelIndex parent = QModelIndex())
+	{
+		bool success;
+
+		beginRemoveColumns(parent, position, position + columns - 1);
+		success = rootItem.removeColumns(position, columns);
+		endRemoveColumns();
+
+		if (rootItem.columnCount() == 0)
+			removeRows(0, rowCount());
+
+		return success;
+	}
+
+
+	bool insertRows(int position, int rows, QModelIndex parent = QModelIndex())
+	{
+		TreeItem parentItem = getItem(parent);
+		bool success;
+
+		beginInsertRows(parent, position, position + rows - 1);
+		success = parentItem.insertChildren(position, rows, rootItem.columnCount());
+		endInsertRows();
+
+		return success;
+	}
+
+	bool removeRows(int position, int rows, QModelIndex parent = QModelIndex())
+	{
+		TreeItem parentItem = getItem(parent);
+		bool success = true;
+
+		beginRemoveRows(parent, position, position + rows - 1);
+		success = parentItem.removeChildren(position, rows);
+		endRemoveRows();
+
+		return success;
+	}
+
+private:
+
+	void setupModelData(string[] lines, TreeItem parent)
+	{
+		TreeItem[] parents;
+		int[] indentations;
+		parents ~= parent;
+		indentations ~= 0;
+
+		int number = 0;
+
+		while (number < lines.length) {
+			int position = 0;
+			while (position < lines[number].length) {
+				if (lines[number][position] != ' ')
+					break;
+				position++;
+			}
+
+			string lineData = trim(lines[number][position..$]);
+
+			if (lineData.length) {
+				// Read the column data from the rest of the line.
+				string[] columnStrings = split(lineData, "\t", false);
+				QVariant[] columnData;
+				for (int column = 0; column < columnStrings.length; ++column)
+					columnData ~= new QVariant(columnStrings[column]);
+
+				if (position > indentations[$-1]) {
+					// The last child of the current parent is now the new parent
+					// unless the current parent has no children.
+
+					if (parents[$-1].childCount() > 0) {
+						parents ~= parents[$-1].child(parents[$-1].childCount()-1);
+						indentations ~= position;
+					}
+				} else {
+					while (position < indentations[$-1] && parents.length > 0) {
+						parents = parents[0..$-1];
+						indentations = indentations[0..$-1];
+					}
+				}
+
+				// Append a new item to the current parent's list of children.
+				parent = parents[$-1];
+				parent.insertChildren(parent.childCount(), 1, rootItem.columnCount());
+				for (int column = 0; column < columnData.length; ++column)
+					parent.child(parent.childCount() - 1).setData(column, columnData[column]);
+			}
+
+			number++;
+		}
+	}
+
+	TreeItem getItem(QModelIndex index)
+	{
+		if (index.isValid()) {
+			TreeItem item = cast(TreeItem) index.internalPointer();
+			if (item) return item;
+		}
+		return rootItem;
+	}
+
+	TreeItem rootItem;
+}