← Back to team overview

linuxdcpp-team team mailing list archive

[Branch ~dcplusplus-team/dcplusplus/trunk] Rev 2990: sort file list dirs before adding them to the tree (good perf improvement over letting the tree h...

 

------------------------------------------------------------
revno: 2990
committer: poy <poy@xxxxxxxxxx>
branch nick: trunk
timestamp: Wed 2012-07-11 19:13:42 +0200
message:
  sort file list dirs before adding them to the tree (good perf improvement over letting the tree handle sorting)
modified:
  dcpp/DirectoryListing.cpp
  dcpp/DirectoryListing.h
  dcpp/Util.cpp
  dcpp/Util.h
  dwt/include/dwt/forward.h
  dwt/include/dwt/widgets/Table.h
  dwt/include/dwt/widgets/Tree.h
  dwt/src/widgets/Table.cpp
  dwt/src/widgets/Tree.cpp
  dwt/test/TreeTest.cpp
  win32/DirectoryListingFrame.cpp
  win32/DirectoryListingFrame.h
  win32/FavHubGroupsDlg.cpp
  win32/FinishedFrameBase.h
  win32/HubFrame.cpp
  win32/PublicHubsFrame.cpp
  win32/QueueFrame.cpp
  win32/QueueFrame.h
  win32/SearchFrame.cpp
  win32/SearchFrame.h
  win32/SearchTypesPage.cpp
  win32/SettingsDialog.cpp
  win32/SettingsDialog.h
  win32/SpyFrame.cpp
  win32/TransferView.cpp
  win32/TransferView.h
  win32/TypedTable.h
  win32/TypedTree.h
  win32/UsersFrame.h
  win32/forward.h
  win32/stdafx.h


--
lp:dcplusplus
https://code.launchpad.net/~dcplusplus-team/dcplusplus/trunk

Your team Dcplusplus-team is subscribed to branch lp:dcplusplus.
To unsubscribe from this branch go to https://code.launchpad.net/~dcplusplus-team/dcplusplus/trunk/+edit-subscription
=== modified file 'dcpp/DirectoryListing.cpp'
--- dcpp/DirectoryListing.cpp	2012-04-07 18:05:24 +0000
+++ dcpp/DirectoryListing.cpp	2012-07-11 17:13:42 +0000
@@ -335,6 +335,25 @@
 	stream.write(LIT("\"/>\r\n"));
 }
 
+void DirectoryListing::sortDirs() {
+	root->sortDirs();
+}
+
+void DirectoryListing::Directory::sortDirs() {
+	for(auto d: directories) {
+		d->sortDirs();
+	}
+	sort(directories.begin(), directories.end(), Directory::Sort());
+}
+
+bool DirectoryListing::Directory::Sort::operator()(const Ptr& a, const Ptr& b) const {
+	return compare(a->getName(), b->getName()) < 0;
+}
+
+bool DirectoryListing::File::Sort::operator()(const Ptr& a, const Ptr& b) const {
+	return compare(a->getName(), b->getName()) < 0;
+}
+
 string DirectoryListing::getPath(const Directory* d) const {
 	if(d == root)
 		return "";
@@ -373,13 +392,13 @@
 	string target = (aDir == getRoot()) ? aTarget : aTarget + aDir->getName() + PATH_SEPARATOR;
 	// First, recurse over the directories
 	Directory::List& lst = aDir->directories;
-	sort(lst.begin(), lst.end(), Directory::DirSort());
+	sort(lst.begin(), lst.end(), Directory::Sort());
 	for(auto& j: lst) {
 		download(j, target, highPrio);
 	}
 	// Then add the files
 	File::List& l = aDir->files;
-	sort(l.begin(), l.end(), File::FileSort());
+	sort(l.begin(), l.end(), File::Sort());
 	for(auto file: aDir->files) {
 		try {
 			download(file, target + file->getName(), false, highPrio);

=== modified file 'dcpp/DirectoryListing.h'
--- dcpp/DirectoryListing.h	2012-03-03 19:33:45 +0000
+++ dcpp/DirectoryListing.h	2012-07-11 17:13:42 +0000
@@ -40,11 +40,6 @@
 	class File : public FastAlloc<File> {
 	public:
 		typedef File* Ptr;
-		struct FileSort {
-			bool operator()(const Ptr& a, const Ptr& b) const {
-				return Util::stricmp(a->getName().c_str(), b->getName().c_str()) < 0;
-			}
-		};
 		typedef vector<Ptr> List;
 		typedef List::iterator Iter;
 
@@ -66,6 +61,8 @@
 
 		void save(OutputStream& stream, string& indent, string& tmp) const;
 
+		struct Sort { bool operator()(const Ptr& a, const Ptr& b) const; };
+
 		GETSET(string, name, Name);
 		GETSET(int64_t, size, Size);
 		GETSET(Directory*, parent, Parent);
@@ -76,11 +73,6 @@
 	class Directory : public FastAlloc<Directory>, boost::noncopyable {
 	public:
 		typedef Directory* Ptr;
-		struct DirSort {
-			bool operator()(const Ptr& a, const Ptr& b) const {
-				return Util::stricmp(a->getName().c_str(), b->getName().c_str()) < 0;
-			}
-		};
 		typedef vector<Ptr> List;
 		typedef List::iterator Iter;
 
@@ -100,6 +92,7 @@
 		void filterList(TTHSet& l);
 		void getHashList(TTHSet& l);
 		void save(OutputStream& stream, string& indent, string& tmp) const;
+		void sortDirs();
 
 		size_t getFileCount() const { return files.size(); }
 
@@ -111,6 +104,8 @@
 			return x;
 		}
 
+		struct Sort { bool operator()(const Ptr& a, const Ptr& b) const; };
+
 		GETSET(string, name, Name);
 		GETSET(Directory*, parent, Parent);
 		GETSET(bool, adls, Adls);
@@ -134,6 +129,8 @@
 
 	/** write an XML representation of this file list to the specified file. */
 	void save(const string& path) const;
+	/** sort directories and sub-directories recursively (case-insensitive). */
+	void sortDirs();
 
 	void download(const string& aDir, const string& aTarget, bool highPrio);
 	void download(Directory* aDir, const string& aTarget, bool highPrio);

=== modified file 'dcpp/Util.cpp'
--- dcpp/Util.cpp	2012-03-11 18:06:18 +0000
+++ dcpp/Util.cpp	2012-07-11 17:13:42 +0000
@@ -717,8 +717,9 @@
 }
 
 int Util::stricmp(const char* a, const char* b) {
+	wchar_t ca = 0, cb = 0;
 	while(*a) {
-		wchar_t ca = 0, cb = 0;
+		ca = cb = 0;
 		int na = Text::utf8ToWc(a, ca);
 		int nb = Text::utf8ToWc(b, cb);
 		ca = Text::toLower(ca);
@@ -729,17 +730,17 @@
 		a += abs(na);
 		b += abs(nb);
 	}
-	wchar_t ca = 0, cb = 0;
+	ca = cb = 0;
 	Text::utf8ToWc(a, ca);
 	Text::utf8ToWc(b, cb);
-
 	return (int)Text::toLower(ca) - (int)Text::toLower(cb);
 }
 
 int Util::strnicmp(const char* a, const char* b, size_t n) {
 	const char* end = a + n;
+	wchar_t ca = 0, cb = 0;
 	while(*a && a < end) {
-		wchar_t ca = 0, cb = 0;
+		ca = cb = 0;
 		int na = Text::utf8ToWc(a, ca);
 		int nb = Text::utf8ToWc(b, cb);
 		ca = Text::toLower(ca);
@@ -750,12 +751,41 @@
 		a += abs(na);
 		b += abs(nb);
 	}
-	wchar_t ca = 0, cb = 0;
+	ca = cb = 0;
 	Text::utf8ToWc(a, ca);
 	Text::utf8ToWc(b, cb);
 	return (a >= end) ? 0 : ((int)Text::toLower(ca) - (int)Text::toLower(cb));
 }
 
+int compare(const std::string& a, const std::string& b) {
+	return compare(a.c_str(), b.c_str());
+}
+int compare(const std::wstring& a, const std::wstring& b) {
+	return compare(a.c_str(), b.c_str());
+}
+int compare(const char* a, const char* b) {
+	// compare wide chars because the locale is usually not *.utf8 (never on Win)
+	wchar_t ca[2] = { 0 }, cb[2] = { 0 };
+	while(*a) {
+		ca[0] = cb[0] = 0;
+		int na = Text::utf8ToWc(a, ca[0]);
+		int nb = Text::utf8ToWc(b, cb[0]);
+		auto comp = compare(const_cast<const wchar_t*>(ca), const_cast<const wchar_t*>(cb));
+		if(comp) {
+			return comp;
+		}
+		a += abs(na);
+		b += abs(nb);
+	}
+	ca[0] = cb[0] = 0;
+	Text::utf8ToWc(a, ca[0]);
+	Text::utf8ToWc(b, cb[0]);
+	return compare(const_cast<const wchar_t*>(ca), const_cast<const wchar_t*>(cb));
+}
+int compare(const wchar_t* a, const wchar_t* b) {
+	return wcscoll(a, b);
+}
+
 string Util::cssColor(int color) {
 #ifdef _WIN32
 	// assume it's a COLORREF.

=== modified file 'dcpp/Util.h'
--- dcpp/Util.h	2012-06-28 17:44:32 +0000
+++ dcpp/Util.h	2012-07-11 17:13:42 +0000
@@ -80,13 +80,16 @@
  * @return -1 if v1 < v2, 0 if v1 == v2 and 1 if v1 > v2
  */
 template<typename T>
-inline int compare(const T& v1, const T& v2) {
-	static_assert(!std::is_same<T, string>::value && !std::is_same<T, wstring>::value, "trying to numerically compare strings");
-
+int compare(const T& v1, const T& v2) {
 	return (v1 < v2) ? -1 : ((v1 == v2) ? 0 : 1);
 }
+/** Locale-aware string comparison, to be used when sorting. */
+int compare(const std::string& a, const std::string& b);
+int compare(const std::wstring& a, const std::wstring& b);
+int compare(const char* a, const char* b);
+int compare(const wchar_t* a, const wchar_t* b);
 
-template<typename T> inline double fraction(T a, T b) { return static_cast<double>(a) / b; }
+template<typename T> double fraction(T a, T b) { return static_cast<double>(a) / b; }
 
 /** Uses SFINAE to determine whether a type provides a function; stores the result in "value".
 Inspired by <http://stackoverflow.com/a/8752988>. */

=== modified file 'dwt/include/dwt/forward.h'
--- dwt/include/dwt/forward.h	2012-07-03 19:15:39 +0000
+++ dwt/include/dwt/forward.h	2012-07-11 17:13:42 +0000
@@ -185,6 +185,9 @@
 class Tree;
 typedef Tree* TreePtr;
 
+class VirtualTree;
+typedef VirtualTree* VirtualTreePtr;
+
 class Window;
 typedef Window* WindowPtr;
 

=== modified file 'dwt/include/dwt/widgets/Table.h'
--- dwt/include/dwt/widgets/Table.h	2012-06-08 15:27:48 +0000
+++ dwt/include/dwt/widgets/Table.h	2012-07-11 17:13:42 +0000
@@ -114,9 +114,9 @@
 	};
 
 	enum SortType {
-		SORT_CALLBACK,
- 		SORT_STRING,
-		SORT_STRING_NOCASE,
+		SORT_CALLBACK, /// Call a custom callback function to sort items.
+ 		SORT_STRING, /// Sort alphabetically, respecting international characters in the user's locale.
+		SORT_STRING_SIMPLE, /// Sort alphabetically; ignore the user's locale.
 		SORT_INT,
 		SORT_FLOAT
 	};

=== modified file 'dwt/include/dwt/widgets/Tree.h'
--- dwt/include/dwt/widgets/Tree.h	2012-01-29 19:25:56 +0000
+++ dwt/include/dwt/widgets/Tree.h	2012-07-11 17:13:42 +0000
@@ -47,22 +47,6 @@
 
 namespace dwt {
 
-class Tree;
-
-class TreeItem : boost::noncopyable {
-public:
-
-private:
-	friend class Tree;
-
-	TreeItem(HTREEITEM handle);
-
-	HTREEITEM handle;
-};
-
- /** \ingroup WidgetControls
-   * \WidgetUsageInfo
-   */
 class Tree :
 	public Control,
 	public aspects::Clickable<Tree>,
@@ -127,8 +111,12 @@
 		Seed();
 	};
 
-	/// Inserts a "node" into the TreeView
-	HTREEITEM insert(const tstring& text, HTREEITEM parent = NULL, LPARAM param = 0, bool expanded = false, int iconIndex = - 1, int selectedIconIndex = - 1);
+	/** Inserts a node into the tree control.
+	@param insertAfter One of TVI_FIRST, TVI_LAST, TVI_SORT; or an existing HTREEITEM. Be careful,
+	TVI_SORT can have an important performance impact. */
+	HTREEITEM insert(const tstring& text, HTREEITEM parent, HTREEITEM insertAfter = TVI_LAST,
+		LPARAM param = 0, bool expanded = false, int iconIndex = -1, int selectedIconIndex = -1);
+	HTREEITEM insert(TVINSERTSTRUCT& tvis);
 
 	HTREEITEM getNext(HTREEITEM node, unsigned flag);
 

=== modified file 'dwt/src/widgets/Table.cpp'
--- dwt/src/widgets/Table.cpp	2012-06-08 15:27:48 +0000
+++ dwt/src/widgets/Table.cpp	2012-07-11 17:13:42 +0000
@@ -576,9 +576,9 @@
 		ListView_GetItemText(p->handle(), nb, p->sortColumn, buf2, BUF_SIZE);
 
 		if(type == SORT_STRING) {
-			result = lstrcmp(buf, buf2);
-		} else if(type == SORT_STRING_NOCASE) {
-			result = lstrcmpi(buf, buf2);
+			result = wcscoll(buf, buf2);
+		} else if(type == SORT_STRING_SIMPLE) {
+			result = wcscmp(buf, buf2);
 		} else if(type == SORT_INT) {
 			result = compare(_ttoi(buf), _ttoi(buf2));
 		} else if(type == SORT_FLOAT) {

=== modified file 'dwt/src/widgets/Tree.cpp'
--- dwt/src/widgets/Tree.cpp	2012-06-29 19:50:24 +0000
+++ dwt/src/widgets/Tree.cpp	2012-07-11 17:13:42 +0000
@@ -85,10 +85,13 @@
 	layout();
 }
 
-HTREEITEM Tree::insert(const tstring& text, HTREEITEM parent, LPARAM param, bool expanded, int iconIndex, int selectedIconIndex) {
-	TVITEMEX t = { TVIF_TEXT };
-	if ( param != 0 )
-	{
+HTREEITEM Tree::insert(const tstring& text, HTREEITEM parent, HTREEITEM insertAfter,
+					   LPARAM param, bool expanded, int iconIndex, int selectedIconIndex)
+{
+	TVINSERTSTRUCT item = { parent, insertAfter, { { TVIF_TEXT } } };
+	auto& t = item.itemex;
+	t.pszText = const_cast<TCHAR*>(text.c_str());
+	if(param) {
 		t.mask |= TVIF_PARAM;
 		t.lParam = param;
 	}
@@ -97,21 +100,16 @@
 		t.state = TVIS_EXPANDED;
 		t.stateMask = TVIS_EXPANDED;
 	}
-	if ( itsNormalImageList )
-	{
+	if(itsNormalImageList) {
 		t.mask |= TVIF_IMAGE | TVIF_SELECTEDIMAGE;
-		t.iImage = ( iconIndex == - 1 ? I_IMAGECALLBACK : iconIndex );
-		t.iSelectedImage = ( selectedIconIndex == - 1 ? t.iImage : selectedIconIndex );
+		t.iImage = (iconIndex == -1) ? I_IMAGECALLBACK : iconIndex;
+		t.iSelectedImage = (selectedIconIndex == - 1) ? t.iImage : selectedIconIndex;
 	}
-	t.pszText = const_cast < TCHAR * >( text.c_str() );
+	return insert(item);
+}
 
-	TVINSERTSTRUCT tv = { parent, TVI_LAST };
-#ifdef WINCE
-	tv.item = t;
-#else
-	tv.itemex = t;
-#endif
-	return TreeView_InsertItem(treeHandle(), &tv);
+HTREEITEM Tree::insert(TVINSERTSTRUCT& tvis) {
+	return TreeView_InsertItem(treeHandle(), &tvis);
 }
 
 tstring Tree::getSelectedText() {

=== modified file 'dwt/test/TreeTest.cpp'
--- dwt/test/TreeTest.cpp	2012-01-23 20:45:16 +0000
+++ dwt/test/TreeTest.cpp	2012-07-11 17:13:42 +0000
@@ -27,7 +27,7 @@
 		tstring name(_T("item 1"));
 		name.back() += i;
 
-		auto item = tree->insert(name, NULL, 1);
+		auto item = tree->insert(name, nullptr, TVI_LAST, 1);
 		tree->setText(item, 1, _T("sub") + name);
 
 		assert(tree->getData(item) == 1);

=== modified file 'win32/DirectoryListingFrame.cpp'
--- win32/DirectoryListingFrame.cpp	2012-07-02 18:13:18 +0000
+++ win32/DirectoryListingFrame.cpp	2012-07-11 17:13:42 +0000
@@ -37,6 +37,7 @@
 #include <dwt/widgets/SaveDialog.h>
 #include <dwt/widgets/SplitterContainer.h>
 #include <dwt/widgets/ToolBar.h>
+#include <dwt/widgets/Tree.h>//#include <dwt/widgets/VirtualTree.h>
 
 #include "TypedTable.h"
 #include "TypedTree.h"
@@ -88,7 +89,7 @@
 			switch(col) {
 			case COLUMN_EXACTSIZE: return compare(a->dir->getTotalSize(), b->dir->getTotalSize());
 			case COLUMN_SIZE: return compare(a->dir->getTotalSize(), b->dir->getTotalSize());
-			default: return lstrcmpi(a->columns[col].c_str(), b->columns[col].c_str());
+			default: return compare(a->columns[col], b->columns[col]);
 			}
 		} else {
 			return -1;
@@ -99,7 +100,7 @@
 		switch(col) {
 		case COLUMN_EXACTSIZE: return compare(a->file->getSize(), b->file->getSize());
 		case COLUMN_SIZE: return compare(a->file->getSize(), b->file->getSize());
-		default: return lstrcmp(a->columns[col].c_str(), b->columns[col].c_str());
+		default: return compare(a->columns[col], b->columns[col]);
 		}
 	}
 }
@@ -390,7 +391,7 @@
 
 	dirs->setFocus();
 
-	treeRoot = dirs->insert(NULL, new ItemInfo(true, dl->getRoot()));
+	treeRoot = dirs->insert(new ItemInfo(true, dl->getRoot()), nullptr);
 
 	ClientManager::getInstance()->addListener(this);
 	updateTitle();
@@ -423,6 +424,7 @@
 	int run() {
 		try {
 			dl->loadFile(path);
+			dl->sortDirs();
 			ADLSearchManager::getInstance()->matchListing(*dl);
 			successF();
 		} catch(const Exception& e) {
@@ -510,6 +512,7 @@
 		}
 
 		auto base = dl->updateXML(txt);
+		dl->sortDirs();
 		dl->save(path);
 
 		// remove previous ADLS matches.
@@ -1057,7 +1060,7 @@
 }
 
 void DirectoryListingFrame::addDir(DirectoryListing::Directory* d, HTREEITEM parent) {
-	auto item = dirs->insert(parent, new ItemInfo(d));
+	auto item = dirs->insert(new ItemInfo(d), parent);
 	if(d->getAdls())
 		dirs->setItemState(item, TVIS_BOLD, TVIS_BOLD);
 	updateDir(d, item);

=== modified file 'win32/DirectoryListingFrame.h'
--- win32/DirectoryListingFrame.h	2012-02-04 18:32:29 +0000
+++ win32/DirectoryListingFrame.h	2012-07-11 17:13:42 +0000
@@ -165,7 +165,7 @@
 	ComboBoxPtr searchBox;
 	ComboBoxPtr filterMethod;
 
-	typedef TypedTree<ItemInfo> WidgetDirs;
+	typedef TypedTree<ItemInfo, true/*, dwt::VirtualTree*/> WidgetDirs;
 	typedef WidgetDirs* WidgetDirsPtr;
 	WidgetDirsPtr dirs;
 

=== modified file 'win32/FavHubGroupsDlg.cpp'
--- win32/FavHubGroupsDlg.cpp	2012-06-18 15:56:01 +0000
+++ win32/FavHubGroupsDlg.cpp	2012-07-11 17:13:42 +0000
@@ -76,7 +76,7 @@
 }
 
 int FavHubGroupsDlg::GroupInfo::compareItems(const GroupInfo* a, const GroupInfo* b, int col) {
-	return lstrcmpi(a->columns[col].c_str(), b->columns[col].c_str());
+	return compare(a->columns[col], b->columns[col]);
 }
 
 bool FavHubGroupsDlg::handleInitDialog() {

=== modified file 'win32/FinishedFrameBase.h'
--- win32/FinishedFrameBase.h	2012-06-21 18:52:47 +0000
+++ win32/FinishedFrameBase.h	2012-07-11 17:13:42 +0000
@@ -275,7 +275,7 @@
 				case FILES_COLUMN_PERCENTAGE: return compare(a->entry->getTransferredPercentage(), b->entry->getTransferredPercentage());
 				case FILES_COLUMN_SPEED: return compare(a->entry->getAverageSpeed(), b->entry->getAverageSpeed());
 				case FILES_COLUMN_ELAPSED: return compare(a->entry->getMilliSeconds(), b->entry->getMilliSeconds());
-				default: return lstrcmpi(a->columns[col].c_str(), b->columns[col].c_str());
+				default: return compare(a->columns[col], b->columns[col]);
 			}
 		}
 
@@ -356,7 +356,7 @@
 				case USERS_COLUMN_TRANSFERRED: return compare(a->entry->getTransferred(), b->entry->getTransferred());
 				case USERS_COLUMN_SPEED: return compare(a->entry->getAverageSpeed(), b->entry->getAverageSpeed());
 				case USERS_COLUMN_ELAPSED: return compare(a->entry->getMilliSeconds(), b->entry->getMilliSeconds());
-				default: return lstrcmpi(a->columns[col].c_str(), b->columns[col].c_str());
+				default: return compare(a->columns[col], b->columns[col]);
 			}
 		}
 

=== modified file 'win32/HubFrame.cpp'
--- win32/HubFrame.cpp	2012-07-01 18:41:13 +0000
+++ win32/HubFrame.cpp	2012-07-11 17:13:42 +0000
@@ -897,7 +897,7 @@
 	if(col == COLUMN_SHARED) {
 		return compare(a->identity.getBytesShared(), b->identity.getBytesShared());;
 	}
-	return lstrcmpi(a->columns[col].c_str(), b->columns[col].c_str());
+	return compare(a->columns[col], b->columns[col]);
 }
 
 void HubFrame::on(Connecting, Client*) noexcept {

=== modified file 'win32/PublicHubsFrame.cpp'
--- win32/PublicHubsFrame.cpp	2012-04-15 22:19:38 +0000
+++ win32/PublicHubsFrame.cpp	2012-07-11 17:13:42 +0000
@@ -76,7 +76,7 @@
 	case COLUMN_MAXHUBS: return compare(a->entry->getMaxHubs(), b->entry->getMaxHubs());
 	case COLUMN_MAXUSERS: return compare(a->entry->getMaxUsers(), b->entry->getMaxUsers());
 	case COLUMN_RELIABILITY: return compare(a->entry->getReliability(), b->entry->getReliability());
-	default: return lstrcmpi(a->columns[col].c_str(), b->columns[col].c_str());
+	default: return compare(a->columns[col], b->columns[col]);
 	}
 }
 

=== modified file 'win32/QueueFrame.cpp'
--- win32/QueueFrame.cpp	2012-07-01 19:24:30 +0000
+++ win32/QueueFrame.cpp	2012-07-11 17:13:42 +0000
@@ -26,6 +26,7 @@
 #include <dwt/widgets/FolderDialog.h>
 #include <dwt/widgets/MessageBox.h>
 #include <dwt/widgets/SplitterContainer.h>
+#include <dwt/widgets/Tree.h>
 
 #include "WinUtil.h"
 #include "resource.h"
@@ -438,7 +439,7 @@
 		// We assume we haven't added it yet, and that all filelists go to the same
 		// directory...
 		dcassert(fileLists == NULL);
-		fileLists = dirs->insert(NULL, new DirItemInfo(dir, T_("File Lists")));
+		fileLists = dirs->insert(new DirItemInfo(dir, T_("File Lists")), nullptr, TVI_SORT);
 		return fileLists;
 	}
 
@@ -468,10 +469,10 @@
 			// First addition, set commonStart to the dir minus the last part...
 			i = dir.rfind('\\', dir.length()-2);
 			if(i != string::npos) {
-				next = dirs->insert(NULL, new DirItemInfo(dir.substr(0, i+1)));
+				next = dirs->insert(new DirItemInfo(dir.substr(0, i+1)), nullptr, TVI_SORT);
 			} else {
 				dcassert(dir.length() == 3);
-				next = dirs->insert(NULL, new DirItemInfo(dir, Text::toT(dir)));
+				next = dirs->insert(new DirItemInfo(dir, Text::toT(dir)), nullptr, TVI_SORT);
 			}
 		}
 
@@ -494,7 +495,7 @@
 			HTREEITEM oldRoot = next;
 
 			// Create a new root
-			HTREEITEM newRoot = dirs->insert(NULL, new DirItemInfo(rootStr.substr(0, i)));
+			HTREEITEM newRoot = dirs->insert(new DirItemInfo(rootStr.substr(0, i)), nullptr, TVI_SORT);
 
 			parent = addDirectory(rootStr, false, newRoot);
 
@@ -537,7 +538,7 @@
 			// We didn't find it, add...
 			j = dir.find('\\', i);
 			dcassert(j != string::npos);
-			parent = dirs->insert(parent, new DirItemInfo(dir.substr(0, j+1), Text::toT(dir.substr(i, j-i))));
+			parent = dirs->insert(new DirItemInfo(dir.substr(0, j+1), Text::toT(dir.substr(i, j-i))), parent, TVI_SORT);
 			i = j + 1;
 		}
 	}
@@ -808,7 +809,7 @@
 	dirs->getItem(&tvis.itemex);
 	tvis.hInsertAfter =	TVI_SORT;
 	tvis.hParent = parent;
-	HTREEITEM ht = dirs->insert(&tvis);
+	HTREEITEM ht = dirs->insert(tvis);
 	HTREEITEM next = dirs->getChild(item);
 	while(next != NULL) {
 		moveNode(next, ht);

=== modified file 'win32/QueueFrame.h'
--- win32/QueueFrame.h	2012-06-18 14:50:09 +0000
+++ win32/QueueFrame.h	2012-07-11 17:13:42 +0000
@@ -141,7 +141,7 @@
 				case COLUMN_PRIORITY: return compare((int)a->getPriority(), (int)b->getPriority());
 				case COLUMN_DOWNLOADED: return compare(a->getDownloadedBytes(), b->getDownloadedBytes());
 				case COLUMN_ADDED: return compare(a->getAdded(), b->getAdded());
-				default: return lstrcmpi(a->getDisplay()->columns[col].c_str(), b->getDisplay()->columns[col].c_str());
+				default: return compare(a->getDisplay()->columns[col], b->getDisplay()->columns[col]);
 			}
 		}
 

=== modified file 'win32/SearchFrame.cpp'
--- win32/SearchFrame.cpp	2012-06-21 18:52:47 +0000
+++ win32/SearchFrame.cpp	2012-07-11 17:13:42 +0000
@@ -82,10 +82,10 @@
 		else if (a->srs.size() > 1 || b->srs.size() > 1)
 			return(a->srs.size() > 1) ? -1 : 1;
 		else
-			return lstrcmpi(a->columns[COLUMN_NICK].c_str(), b->columns[COLUMN_NICK].c_str());
+			return compare(a->columns[COLUMN_NICK], b->columns[COLUMN_NICK]);
 	case COLUMN_TYPE:
 		if(a->srs[0]->getType() == b->srs[0]->getType())
-			return lstrcmpi(a->columns[COLUMN_TYPE].c_str(), b->columns[COLUMN_TYPE].c_str());
+			return compare(a->columns[COLUMN_TYPE], b->columns[COLUMN_TYPE]);
 		else
 			return(a->srs[0]->getType() == SearchResult::TYPE_DIRECTORY) ? -1 : 1;
 	case COLUMN_SLOTS:
@@ -95,7 +95,7 @@
 			return compare(a->srs[0]->getFreeSlots(), b->srs[0]->getFreeSlots());
 	case COLUMN_SIZE: // Fall through
 	case COLUMN_EXACT_SIZE: return compare(a->srs[0]->getSize(), b->srs[0]->getSize());
-	default: return lstrcmpi(a->getText(col).c_str(), b->getText(col).c_str());
+	default: return compare(a->getText(col), b->getText(col));
 	}
 }
 

=== modified file 'win32/SearchFrame.h'
--- win32/SearchFrame.h	2012-01-22 20:27:14 +0000
+++ win32/SearchFrame.h	2012-07-11 17:13:42 +0000
@@ -149,7 +149,7 @@
 			return 0;
 		}
 		static int compareItems(const HubInfo* a, const HubInfo* b, int col) {
-			return (col == 0) ? lstrcmpi(a->name.c_str(), b->name.c_str()) : 0;
+			return (col == 0) ? compare(a->name, b->name) : 0;
 		}
 		tstring url;
 		tstring name;

=== modified file 'win32/SearchTypesPage.cpp'
--- win32/SearchTypesPage.cpp	2012-03-03 19:33:45 +0000
+++ win32/SearchTypesPage.cpp	2012-07-11 17:13:42 +0000
@@ -104,7 +104,7 @@
 	}
 
 	WinUtil::makeColumns(types, columns, 3);
-	types->setSort(0, dwt::Table::SORT_STRING_NOCASE);
+	types->setSort(0, dwt::Table::SORT_STRING);
 
 	fillList();
 

=== modified file 'win32/SettingsDialog.cpp'
--- win32/SettingsDialog.cpp	2012-07-02 18:13:18 +0000
+++ win32/SettingsDialog.cpp	2012-07-11 17:13:42 +0000
@@ -144,7 +144,7 @@
 				setSmallIcon(WinUtil::createIcon(icon, 16));
 				setLargeIcon(WinUtil::createIcon(icon, 32));
 			} });
-			auto item = tree->insert(title, parent, 0, true, index);
+			auto item = tree->insert(title, parent, TVI_LAST, 0, true, index);
 			if(index == setting)
 				callAsync([=] { tree->setSelected(item); tree->ensureVisible(item); });
 			pages.emplace_back(page, item);

=== modified file 'win32/SettingsDialog.h'
--- win32/SettingsDialog.h	2012-03-03 19:33:45 +0000
+++ win32/SettingsDialog.h	2012-07-11 17:13:42 +0000
@@ -27,6 +27,8 @@
 #include "forward.h"
 #include "PropPage.h"
 
+using dwt::TreePtr;
+
 class SettingsDialog : public dwt::ModalDialog
 {
 public:

=== modified file 'win32/SpyFrame.cpp'
--- win32/SpyFrame.cpp	2012-06-21 18:52:47 +0000
+++ win32/SpyFrame.cpp	2012-07-11 17:13:42 +0000
@@ -142,7 +142,7 @@
 		if(column == COLUMN_COUNT) {
 			searches->setSort(column, dwt::Table::SORT_INT);
 		} else {
-			searches->setSort(column, dwt::Table::SORT_STRING_NOCASE);
+			searches->setSort(column, dwt::Table::SORT_STRING);
 		}
 	}
 }

=== modified file 'win32/TransferView.cpp'
--- win32/TransferView.cpp	2012-06-21 18:52:47 +0000
+++ win32/TransferView.cpp	2012-07-11 17:13:42 +0000
@@ -463,7 +463,7 @@
 	case CONNECTION_COLUMN_TRANSFERED: return compare(a->transfered, b->transfered);
 	case CONNECTION_COLUMN_QUEUED: return compare(a->queued, b->queued);
 	case CONNECTION_COLUMN_CHUNK: return compare(a->chunk, b->chunk);
-	default: return lstrcmpi(a->columns[col].c_str(), b->columns[col].c_str());
+	default: return compare(a->columns[col], b->columns[col]);
 	}
 }
 

=== modified file 'win32/TransferView.h'
--- win32/TransferView.h	2012-02-17 23:34:33 +0000
+++ win32/TransferView.h	2012-07-11 17:13:42 +0000
@@ -212,7 +212,7 @@
 			case DOWNLOAD_COLUMN_SPEED: return compare(a->bps, b->bps);
 			case DOWNLOAD_COLUMN_SIZE: return compare(a->size, b->size);
 			case DOWNLOAD_COLUMN_DONE: return compare(a->done, b->done);
-			default: return lstrcmpi(a->columns[col].c_str(), b->columns[col].c_str());
+			default: return compare(a->columns[col], b->columns[col]);
 			}
 		}
 

=== modified file 'win32/TypedTable.h'
--- win32/TypedTable.h	2012-02-17 23:34:33 +0000
+++ win32/TypedTable.h	2012-07-11 17:13:42 +0000
@@ -51,11 +51,11 @@
 @note Support for tooltips:
 The ContentType class must provide a tstring getTooltip() [const] function. Note that tooltips are
 only for the first column. */
-template<typename ContentType, bool managed>
-class TypedTable : public Table
+template<typename ContentType, bool managed, typename TableType>
+class TypedTable : public TableType
 {
-	typedef Table BaseType;
-	typedef TypedTable<ContentType, managed> ThisType;
+	typedef TableType BaseType;
+	typedef TypedTable<ContentType, managed, TableType> ThisType;
 
 public:
 	typedef ThisType* ObjectType;
@@ -67,7 +67,7 @@
 	struct Seed : public BaseType::Seed {
 		typedef ThisType WidgetType;
 
-		Seed(const BaseType::Seed& seed) : BaseType::Seed(seed) {
+		Seed(const typename BaseType::Seed& seed) : BaseType::Seed(seed) {
 		}
 	};
 
@@ -87,7 +87,7 @@
 		addTooltipEvent<ContentType>();
 
 		if(managed) {
-			onDestroy([this] { this->clear(); });
+			this->onDestroy([this] { this->clear(); });
 		}
 	}
 
@@ -119,7 +119,7 @@
 	using BaseType::find;
 
 	int find(ContentType* item) {
-		return findData(reinterpret_cast<LPARAM>(item));
+		return this->findData(reinterpret_cast<LPARAM>(item));
 	}
 
 	struct CompFirst {
@@ -154,7 +154,7 @@
 	}
 
 	void update(int i) {
-		redraw(i, i);
+		this->redraw(i, i);
 	}
 
 	void update(ContentType* item) { int i = find(item); if(i != -1) update(i); }

=== modified file 'win32/TypedTree.h'
--- win32/TypedTree.h	2012-05-04 21:20:44 +0000
+++ win32/TypedTree.h	2012-07-11 17:13:42 +0000
@@ -27,11 +27,11 @@
 @tparam ContentType Type of the objects associated to each item.
 
 @tparam managed Whether this class should handle deleting associated objects. */
-template<typename ContentType, bool managed>
-class TypedTree : public dwt::Tree
+template<typename ContentType, bool managed, typename TreeType>
+class TypedTree : public TreeType
 {
-	typedef typename dwt::Tree BaseType;
-	typedef TypedTree<ContentType, managed> ThisType;
+	typedef TreeType BaseType;
+	typedef TypedTree<ContentType, managed, TreeType> ThisType;
 
 public:
 	typedef ThisType* ObjectType;
@@ -41,7 +41,7 @@
 	struct Seed : public BaseType::Seed {
 		typedef ThisType WidgetType;
 
-		Seed(const BaseType::Seed& seed) : BaseType::Seed(seed) {
+		Seed(const typename BaseType::Seed& seed) : BaseType::Seed(seed) {
 		}
 	};
 
@@ -58,21 +58,22 @@
 		}, dwt::Message(WM_NOTIFY, TVN_GETDISPINFO));
 
 		if(managed) {
-			onDestroy([this] { this->clear(); });
+			this->onDestroy([this] { this->clear(); });
 		}
 	}
 
-	HTREEITEM insert(HTREEITEM parent, ContentType* data) {
-		TVINSERTSTRUCT item = { parent, TVI_SORT, { { TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM | TVIF_TEXT } } };
+	using BaseType::insert;
+
+	/** Inserts a node into the tree control.
+	@param insertAfter One of TVI_FIRST, TVI_LAST, TVI_SORT; or an existing HTREEITEM. Be careful,
+	TVI_SORT can have an important performance impact. */
+	HTREEITEM insert(ContentType* data, HTREEITEM parent, HTREEITEM insertAfter = TVI_LAST) {
+		TVINSERTSTRUCT item = { parent, insertAfter, { { TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM | TVIF_TEXT } } };
 		item.itemex.pszText = LPSTR_TEXTCALLBACK;
 		item.itemex.iImage = I_IMAGECALLBACK;
 		item.itemex.iSelectedImage = I_IMAGECALLBACK;
 		item.itemex.lParam = reinterpret_cast<LPARAM>(data);
-		return this->insert(&item);
-	}
-
-	HTREEITEM insert(TVINSERTSTRUCT* tvis) {
-		return TreeView_InsertItem(this->treeHandle(), tvis);
+		return insert(item);
 	}
 
 	ContentType* getData(HTREEITEM item) {

=== modified file 'win32/UsersFrame.h'
--- win32/UsersFrame.h	2012-06-23 12:31:10 +0000
+++ win32/UsersFrame.h	2012-07-11 17:13:42 +0000
@@ -100,7 +100,7 @@
 			switch(col) {
 			case COLUMN_FAVORITE: return compare(a->isFavorite, b->isFavorite);
 			case COLUMN_SLOT: return compare(a->grantSlot, b->grantSlot);
-			default: return lstrcmpi(a->columns[col].c_str(), b->columns[col].c_str());
+			default: return compare(a->columns[col], b->columns[col]);
 			}
 		}
 

=== modified file 'win32/forward.h'
--- win32/forward.h	2012-01-22 20:27:14 +0000
+++ win32/forward.h	2012-07-11 17:13:42 +0000
@@ -37,7 +37,6 @@
 using dwt::SplitterContainerPtr;
 using dwt::TabViewPtr;
 using dwt::TextBoxPtr;
-using dwt::TreePtr;
 using dwt::ToolBarPtr;
 using dwt::ToolTipPtr;
 
@@ -57,10 +56,10 @@
 
 class TransferView;
 
-template<typename ContentType, bool managed = true>
+template<typename ContentType, bool managed = true, typename TableType = Table>
 class TypedTable;
 
-template<typename ContentType, bool managed = true>
+template<typename ContentType, bool managed = true, typename TreeType = dwt::Tree>
 class TypedTree;
 
 #endif /* FORWARD_H_ */

=== modified file 'win32/stdafx.h'
--- win32/stdafx.h	2012-01-13 20:55:20 +0000
+++ win32/stdafx.h	2012-07-11 17:13:42 +0000
@@ -30,7 +30,6 @@
 
 #ifdef HAS_PCH
 
-
 #include <dwt/forward.h>
 #include <dwt/util/StringUtils.h>
 #include <dwt/widgets/Button.h>
@@ -49,7 +48,6 @@
 #include <dwt/widgets/Table.h>
 #include <dwt/widgets/TabView.h>
 #include <dwt/widgets/TextBox.h>
-#include <dwt/widgets/Tree.h>
 #include <dwt/widgets/Window.h>
 
 #include "ComboBox.h"