← Back to team overview

linuxdcpp-team team mailing list archive

[Branch ~dcplusplus-team/dcplusplus/trunk] Rev 2717: add icons next to list-view group headers

 

------------------------------------------------------------
revno: 2717
committer: poy <poy@xxxxxxxxxx>
branch nick: trunk
timestamp: Tue 2011-12-13 22:15:23 +0100
message:
  add icons next to list-view group headers
modified:
  dwt/include/dwt/widgets/Table.h
  dwt/src/widgets/Table.cpp
  win32/FavHubsFrame.cpp
  win32/StylesPage.cpp


--
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 'dwt/include/dwt/widgets/Table.h'
--- dwt/include/dwt/widgets/Table.h	2011-11-19 00:10:54 +0000
+++ dwt/include/dwt/widgets/Table.h	2011-12-13 21:15:23 +0000
@@ -281,17 +281,13 @@
 	  */
 	void setColumnWidth( unsigned columnNo, int width );
 
-	/** Enable group support (only for ComCtrl 6), and insert each given group. The group id used
-	will be the position of the group in the vector. */
+	/** Enable group support, and insert each given group. The group id used as the "index" param
+	of the insert function will be the position of the group in the vector given here.
+	Once a table has been set into grouped mode, it cannot be switched back to non-grouped mode. */
 	void setGroups(const std::vector<tstring>& groups);
-	tstring getGroup(unsigned id) const;
 
 	bool isGrouped() const { return grouped; }
 
-	/** tell dwt to take over painting of group headers in order to allow custom colors that match
-	the background. the theme will be respected. */
-	void handleGroupDraw();
-
 	/// Returns the checked state of the given row
 	/** A list view can have checkboxes in each row, if the checkbox for the given
 	  * row is CHECKED this funtion returns true.
@@ -397,19 +393,22 @@
 	/** normalImageList is the image list that contains the images
 	  * for the data grid icons in Icon View (big icons).
 	  */
-	void setNormalImageList( ImageListPtr normalImageList );
+	void setNormalImageList(ImageListPtr imageList);
 
 	/// Set the small image list for the Data Grid.
 	/** smallImageList is the image list that contains the images
 	  * for the data grid icons in Report, List & Small Icon Views.
 	  */
-	void setSmallImageList( ImageListPtr smallImageList );
+	void setSmallImageList(ImageListPtr imageList);
 
 	/// Set the state image list for the Data Grid.
 	/** stateImageList is the image list that contains the images
 	  * for the data grid icons states.
 	  */
-	void setStateImageList( ImageListPtr stateImageList );
+	void setStateImageList(ImageListPtr imageList);
+
+	/** Set the image list to find icons from when adding groups. Only available on >= Visa. */
+	void setGroupImageList(ImageListPtr imageList);
 
 	/// Change the view for the Data Grid.
 	/** The view parameter can be one of LVS_ICON, LVS_SMALLICON, LVS_LIST or
@@ -486,6 +485,7 @@
 	ImageListPtr itsNormalImageList;
 	ImageListPtr itsSmallImageList;
 	ImageListPtr itsStateImageList;
+	ImageListPtr groupImageList;
 
 	// If true the grid is in "read only mode" meaning that cell values cannot be edited.
 	// A simpler version of defining a beenValidate always returning false
@@ -501,6 +501,7 @@
 	static int CALLBACK compareFuncCallback( LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort );
 
 	void setIndex(LVITEM& item, int index) const;
+	void initGroupSupport();
 	void updateArrow();
 #ifdef PORT_ME
 	// Private validate function, this ones returns the "read only" property of the list

=== modified file 'dwt/src/widgets/Table.cpp'
--- dwt/src/widgets/Table.cpp	2011-11-16 18:12:07 +0000
+++ dwt/src/widgets/Table.cpp	2011-12-13 21:15:23 +0000
@@ -45,6 +45,38 @@
 
 const TCHAR Table::windowClass[] = WC_LISTVIEW;
 
+/* the following dance adds Vista members to LVGROUP (notably iTitleImage to have group icons)
+without requiring a global switch of WINVER / _WIN32_WINNT / etc to Vista values. */
+typedef LVGROUP legacyLVGROUP;
+#if(_WIN32_WINNT < 0x600)
+struct LVGROUP_ : LVGROUP {
+	LPWSTR  pszSubtitle;
+    UINT    cchSubtitle;
+    LPWSTR  pszTask;
+    UINT    cchTask;
+    LPWSTR  pszDescriptionTop;
+    UINT    cchDescriptionTop;
+    LPWSTR  pszDescriptionBottom;
+    UINT    cchDescriptionBottom;
+    int     iTitleImage;
+    int     iExtendedImage;
+    int     iFirstItem;
+    UINT    cItems;
+    LPWSTR  pszSubsetTitle;
+    UINT    cchSubsetTitle;
+	LVGROUP_(const LVGROUP& lvg) : LVGROUP(lvg) { }
+};
+#define LVGROUP LVGROUP_
+#define LVGF_TITLEIMAGE 0x00001000
+#define ListView_SetGroupHeaderImageList(hwnd, himl) \
+    (HIMAGELIST)SNDMSG((hwnd), LVM_SETIMAGELIST, (WPARAM)LVSIL_GROUPHEADER, (LPARAM)(HIMAGELIST)(himl))
+#endif
+
+namespace { legacyLVGROUP makeLVGROUP() {
+	legacyLVGROUP lvg = { util::win32::ensureVersion(util::win32::VISTA) ? sizeof(LVGROUP) : sizeof(legacyLVGROUP) };
+	return lvg;
+} }
+
 Table::Seed::Seed() :
 	BaseType::Seed(WS_CHILD | WS_TABSTOP | LVS_REPORT),
 	font(0),
@@ -294,12 +326,16 @@
 }
 
 void Table::setGroups(const std::vector<tstring>& groups) {
-	grouped = ListView_EnableGroupView(handle(), TRUE) >= 0;
-	if(!grouped)
-		return;
-
-	LVGROUP group = { sizeof(LVGROUP) };
-	for(std::vector<tstring>::const_iterator i = groups.begin(), iend = groups.end(); i != iend; ++i) {
+	if(!grouped) {
+		grouped = ListView_EnableGroupView(handle(), TRUE) >= 0;
+		if(!grouped)
+			return;
+
+		initGroupSupport();
+	}
+
+	LVGROUP group = makeLVGROUP();
+	for(auto i = groups.cbegin(), iend = groups.cend(); i != iend; ++i) {
 		if(i->empty()) {
 			group.mask = LVGF_GROUPID;
 			group.pszHeader = 0;
@@ -307,6 +343,10 @@
 			group.mask = LVGF_GROUPID | LVGF_HEADER;
 			group.pszHeader = const_cast<LPWSTR>(i->c_str());
 		}
+		if(groupImageList) {
+			group.mask |= LVGF_TITLEIMAGE;
+			group.iTitleImage = group.iGroupId;
+		}
 		if(ListView_InsertGroup(handle(), -1, &group) == -1) {
 			throw DWTException("Group insertion failed in Table::setGroups");
 		}
@@ -316,22 +356,16 @@
 	grouped = true;
 }
 
-tstring Table::getGroup(unsigned id) const {
-	if(grouped) {
-		LVGROUP group = { sizeof(LVGROUP), LVGF_HEADER };
-		if(ListView_GetGroupInfo(handle(), id, &group) == static_cast<int>(id)) {
-			return tstring(group.pszHeader, group.cchHeader);
-		}
-	}
-	return tstring();
-}
+void Table::initGroupSupport() {
+	/* fiddle with the painting of group headers to allow custom colors that match the background (the
+	theme will be respected). */
 
-void Table::handleGroupDraw() {
 	theme.load(VSCLASS_LISTVIEW, this);
 
 	onCustomDraw([this](NMLVCUSTOMDRAW& data) -> LRESULT {
-		if(!grouped || data.dwItemType != LVCDI_GROUP)
+		if(data.dwItemType != LVCDI_GROUP)
 			return CDRF_DODEFAULT;
+
 		switch(data.nmcd.dwDrawStage) {
 		case CDDS_PREPAINT:
 			{
@@ -346,20 +380,57 @@
 					/* the theme color and the bg color are too close to each other; start by
 					filling the canvas with an invert of the bg, then invert the whole canvas after
 					everything has been drawn (after CDDS_POSTPAINT). */
+					FreeCanvas canvas(data.nmcd.hdc);
+					Brush brush(0xFFFFFF - bgColor);
+
 					Rectangle rect(data.rcText);
 					if(!theme && util::win32::ensureVersion(util::win32::VISTA))
 						rect.size.y += 6;
-					FreeCanvas(data.nmcd.hdc).fill(rect, Brush(0xFFFFFF - bgColor));
+
+					LONG iconPos = 0;
+
+					if(groupImageList) {
+						// don't invert the icon. let's find out where it is placed...
+						if(theme) {
+							auto temp = rect;
+							theme.formatRect(canvas, LVP_GROUPHEADER, LVGH_OPEN, temp);
+							iconPos = temp.left() - rect.left();
+						}
+						if(iconPos <= 0)
+							iconPos = 10; // assume a 10px margin for unthemed visual styles
+
+						rect.size.x = iconPos;
+						canvas.fill(rect, brush);
+
+						rect.pos.x = rect.right() + 16;
+						rect.size.x = data.rcText.right - rect.pos.x;
+					}
+
+					canvas.fill(rect, brush);
 
 					// set a flag so we don't have to re-compare colors on CDDS_POSTPAINT.
-					data.nmcd.lItemlParam = 1;
+					data.nmcd.lItemlParam = std::max(iconPos, 0L) + 1;
 				}
 				break;
 			}
+
 		case CDDS_POSTPAINT:
 			{
 				if(data.nmcd.lItemlParam) {
-					FreeCanvas(data.nmcd.hdc).invert(Region(Rectangle(data.rcText)));
+					LONG iconPos = data.nmcd.lItemlParam - 1;
+
+					FreeCanvas canvas(data.nmcd.hdc);
+					Rectangle rect(data.rcText);
+
+					if(iconPos > 0) {
+						rect.size.x = iconPos;
+						canvas.invert(Region(rect));
+
+						rect.pos.x = rect.right() + 16;
+						rect.size.x = data.rcText.right - rect.pos.x;
+					}
+
+					canvas.invert(Region(rect));
 				}
 				break;
 			}
@@ -415,6 +486,13 @@
 	  ListView_SetImageList( handle(), imageList->getImageList(), LVSIL_STATE );
 }
 
+void Table::setGroupImageList(ImageListPtr imageList) {
+	if(util::win32::ensureVersion(util::win32::VISTA)) {
+		groupImageList = imageList;
+		ListView_SetGroupHeaderImageList(handle(), groupImageList->handle());
+	}
+}
+
 void Table::setView( int view ) {
 	if ( ( view & LVS_TYPEMASK ) != view )
 	{

=== modified file 'win32/FavHubsFrame.cpp'
--- win32/FavHubsFrame.cpp	2011-11-16 18:12:07 +0000
+++ win32/FavHubsFrame.cpp	2011-12-13 21:15:23 +0000
@@ -80,7 +80,6 @@
 		hubs->onDblClicked([this] { handleDoubleClick(); });
 		hubs->onKeyDown([this](int c) { return handleKeyDown(c); });
 		hubs->onContextMenu([this](const dwt::ScreenCoordinate &sc) { return handleContextMenu(sc); });
-		hubs->handleGroupDraw();
 	}
 
 	{

=== modified file 'win32/StylesPage.cpp'
--- win32/StylesPage.cpp	2011-12-13 17:16:36 +0000
+++ win32/StylesPage.cpp	2011-12-13 21:15:23 +0000
@@ -92,7 +92,12 @@
 			seed.style &= ~LVS_SHOWSELALWAYS;
 			seed.style |= LVS_SINGLESEL | LVS_NOCOLUMNHEADER;
 			table = cur->addChild(Table::Seed(seed));
-			table->handleGroupDraw();
+
+			dwt::ImageListPtr images(new dwt::ImageList(dwt::Point(16, 16)));
+			images->add(*WinUtil::createIcon(IDI_DCPP, 16));
+			images->add(*WinUtil::createIcon(IDI_NET_STATS, 16)); /// @todo better icon for the "transfers" group?
+			images->add(*WinUtil::createIcon(IDI_USERS, 16));
+			table->setGroupImageList(images);
 		}
 
 		{
@@ -418,7 +423,9 @@
 void StylesPage::handleCustomTextColor() {
 	auto data = table->getSelectedData();
 	data->customTextColor = customTextColor->getChecked();
-	data->textColor = getTextColor(data);
+	if(data->customTextColor) {
+		data->textColor = getTextColor(data);
+	}
 	update(data);
 	handleSelectionChanged();
 }
@@ -435,7 +442,9 @@
 void StylesPage::handleCustomBgColor() {
 	auto data = table->getSelectedData();
 	data->customBgColor = customBgColor->getChecked();
-	data->bgColor = getBgColor(data);
+	if(data->customBgColor) {
+		data->bgColor = getBgColor(data);
+	}
 	update(data);
 	handleSelectionChanged();
 }