← Back to team overview

linuxdcpp-team team mailing list archive

[Branch ~dcplusplus-team/dcplusplus/trunk] Rev 2187: tabs

 

------------------------------------------------------------
revno: 2187
committer: poy <poy@xxxxxxxxxx>
branch nick: repo
timestamp: Fri 2010-08-06 23:02:04 +0200
message:
  tabs
modified:
  changelog.txt
  dcpp/SettingsManager.cpp
  dcpp/SettingsManager.h
  dwt/include/dwt/widgets/TabView.h
  dwt/src/Dispatcher.cpp
  dwt/src/widgets/Slider.cpp
  dwt/src/widgets/TabView.cpp
  help/settings_tabs.html
  win32/FinishedFrameBase.h
  win32/MainWindow.cpp
  win32/TabsPage.cpp
  win32/TabsPage.h
  win32/TransferView.cpp
  win32/WinUtil.cpp
  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 'changelog.txt'
--- changelog.txt	2010-07-23 21:33:17 +0000
+++ changelog.txt	2010-08-06 21:02:04 +0000
@@ -2,7 +2,8 @@
 * Update Boost to version 1.43
 * Prevent hub-provided cmds from "open own list" from going to the wrong hub
 * Remove ADC empty token workaround targeting 0.698 (cologic)
-* Enable Data Execution Prevention on WinXP SP3/Vista/Win7/Win 2k8 Server (cologic)
+* Enable Data Execution Prevention (cologic)
+* Improved tabs, new settings (poy)
 
 -- 0.770 2010-07-05 --
 * [L#550300] Catch more potential file corruptions (thanks bigmuscle)

=== modified file 'dcpp/SettingsManager.cpp'
--- dcpp/SettingsManager.cpp	2010-07-10 15:49:05 +0000
+++ dcpp/SettingsManager.cpp	2010-08-06 21:02:04 +0000
@@ -81,9 +81,9 @@
 	"UseTLS", "AutoSearchLimit", "AltSortOrder", "AutoKickNoFavs", "PromptPassword", "SpyFrameIgnoreTthSearches",
 	"DontDlAlreadyQueued", "MaxCommandLength", "AllowUntrustedHubs", "AllowUntrustedClients",
 	"TLSPort", "FastHash", "SortFavUsersFirst", "SegmentedDL", "FollowLinks",
-	"SendBloom", "OwnerDrawnMenus", "Coral", "SearchFilterShared", "MaxTabChars", "FinishedDLOnlyFull",
+	"SendBloom", "OwnerDrawnMenus", "Coral", "SearchFilterShared", "FinishedDLOnlyFull",
 	"ConfirmExit", "ConfirmHubClosing", "ConfirmHubRemoval", "ConfirmUserRemoval", "ConfirmItemRemoval", "ConfirmADLSRemoval",
-	"SearchMerge", "ToolbarSize",
+	"SearchMerge", "ToolbarSize", "TabWidth", "TabStyle",
 	"KeepFinishedFiles",
 	"MinMessageLines", "MaxMessageLines",
 	"BandwidthLimitStart", "BandwidthLimitEnd", "TimeDependentThrottle", "MaxDownloadSpeedRealTime",
@@ -279,7 +279,6 @@
 	setDefault(SEND_BLOOM, true);
 	setDefault(OWNER_DRAWN_MENUS, true);
 	setDefault(CORAL, true);
-	setDefault(MAX_TAB_CHARS, 20);
 	setDefault(FINISHED_DL_ONLY_FULL, true);
 	setDefault(CONFIRM_EXIT, true);
 	setDefault(CONFIRM_HUB_CLOSING, true);
@@ -289,6 +288,7 @@
 	setDefault(CONFIRM_ADLS_REMOVAL, true);
 	setDefault(SEARCH_MERGE, true);
 	setDefault(TOOLBAR_SIZE, 20);
+	setDefault(TAB_WIDTH, 150);
 	setDefault(TRANSFERS_PANED_POS, .7);
 	setDefault(QUEUE_PANED_POS, .3);
 	setDefault(SEARCH_PANED_POS, .2);

=== modified file 'dcpp/SettingsManager.h'
--- dcpp/SettingsManager.h	2010-07-10 14:36:48 +0000
+++ dcpp/SettingsManager.h	2010-08-06 21:02:04 +0000
@@ -101,9 +101,9 @@
 		USE_TLS, AUTO_SEARCH_LIMIT, ALT_SORT_ORDER, AUTO_KICK_NO_FAVS, PROMPT_PASSWORD, SPY_FRAME_IGNORE_TTH_SEARCHES,
 		DONT_DL_ALREADY_QUEUED, MAX_COMMAND_LENGTH, ALLOW_UNTRUSTED_HUBS, ALLOW_UNTRUSTED_CLIENTS,
 		TLS_PORT, FAST_HASH, SORT_FAVUSERS_FIRST, SEGMENTED_DL, FOLLOW_LINKS,
-		SEND_BLOOM, OWNER_DRAWN_MENUS, CORAL, SEARCH_FILTER_SHARED, MAX_TAB_CHARS, FINISHED_DL_ONLY_FULL,
+		SEND_BLOOM, OWNER_DRAWN_MENUS, CORAL, SEARCH_FILTER_SHARED, FINISHED_DL_ONLY_FULL,
 		CONFIRM_EXIT, CONFIRM_HUB_CLOSING, CONFIRM_HUB_REMOVAL, CONFIRM_USER_REMOVAL, CONFIRM_ITEM_REMOVAL, CONFIRM_ADLS_REMOVAL,
-		SEARCH_MERGE, TOOLBAR_SIZE,
+		SEARCH_MERGE, TOOLBAR_SIZE, TAB_WIDTH, TAB_STYLE,
 		KEEP_FINISHED_FILES,
 		MIN_MESSAGE_LINES, MAX_MESSAGE_LINES,
 		BANDWIDTH_LIMIT_START, BANDWIDTH_LIMIT_END, TIME_DEPENDENT_THROTTLE, MAX_DOWNLOAD_SPEED_ALTERNATE,

=== modified file 'dwt/include/dwt/widgets/TabView.h'
--- dwt/include/dwt/widgets/TabView.h	2010-07-10 14:36:48 +0000
+++ dwt/include/dwt/widgets/TabView.h	2010-08-06 21:02:04 +0000
@@ -76,22 +76,15 @@
 
 		FontPtr font;
 
-		enum TabStyles {
-			/** use the default Windows tab style. multiline tabs re-organize so that the selected
-			tab is always at the bottom. */
-			WinDefault = TCS_TABS | TCS_HOTTRACK,
-
-			/** use a button-like style and have dwt draw tabs by itself, allowing for more
-			customization. multiline tabs always stay in the same position. */
-			dwtCustom = TCS_BUTTONS | TCS_OWNERDRAWFIXED
-		} tabStyle;
-
-		unsigned maxLength; /// max chars per tab; any value <= 3 means infinite
+		/** for owner-drawn tabs (that have the TCS_OWNERDRAWFIXED style), defines the width of
+		each tab. otherwise, sets the max chars per tab (any value <= 3 means infinite). */
+		unsigned widthConfig;
+
 		bool toggleActive; /// switch the active tab when clicking on the current active tab
 		bool ctrlTab; /// handle Ctrl+Tab and Ctrl+Shift+Tab
 
 		/// Fills with default parameters
-		Seed(unsigned maxLength_ = 20, bool toggleActive_ = false, bool ctrlTab_ = false);
+		Seed(unsigned widthConfig_ = 150, bool toggleActive_ = false, bool ctrlTab_ = false);
 	};
 
 	void add(ContainerPtr w, const IconPtr& icon = IconPtr());
@@ -158,7 +151,7 @@
 	TitleChangedFunction titleChangedFunction;
 
 	// these can be set through the Seed
-	unsigned maxLength; // max chars per tab; either 0 (infinite) or > 3
+	unsigned widthConfig;
 	bool toggleActive;
 	FontPtr font;
 	FontPtr boldFont;
@@ -195,8 +188,8 @@
 	bool handleXMouseUp(const MouseEvent& mouseEvent);
 	bool handleMouseMove(const MouseEvent& mouseEvent);
 	void handleMouseLeave();
-
 	bool handlePainting(LPDRAWITEMSTRUCT info, TabInfo* ti);
+	void handlePainting(PaintCanvas& canvas);
 
 	tstring formatTitle(tstring title);
 	void layout();
@@ -208,6 +201,7 @@
 	tstring getText(unsigned idx) const;
 	void setText(unsigned idx, const tstring& text);
 	void redraw(unsigned index);
+	void draw(Canvas& canvas, unsigned index, Rectangle&& rect, bool isSelected);
 
 	// AspectCollection
 	void eraseImpl( int row );

=== modified file 'dwt/src/Dispatcher.cpp'
--- dwt/src/Dispatcher.cpp	2010-07-10 14:36:48 +0000
+++ dwt/src/Dispatcher.cpp	2010-08-06 21:02:04 +0000
@@ -124,6 +124,8 @@
 		}
 
 	case WM_COMMAND:
+	case WM_HSCROLL:
+	case WM_VSCROLL:
 		{
 			if(msg.lParam != 0) {
 				handler = reinterpret_cast<HWND>(msg.lParam);

=== modified file 'dwt/src/widgets/Slider.cpp'
--- dwt/src/widgets/Slider.cpp	2010-02-11 21:44:13 +0000
+++ dwt/src/widgets/Slider.cpp	2010-08-06 21:02:04 +0000
@@ -33,8 +33,10 @@
 
 namespace dwt {
 
+const TCHAR Slider::windowClass[] = TRACKBAR_CLASS;
+
 Slider::Seed::Seed() :
-	BaseType::Seed(WS_CHILD | WS_TABSTOP)
+BaseType::Seed(WS_CHILD | WS_TABSTOP | TBS_NOTICKS | TBS_TOOLTIPS)
 {
 }
 

=== modified file 'dwt/src/widgets/TabView.cpp'
--- dwt/src/widgets/TabView.cpp	2010-07-10 14:36:48 +0000
+++ dwt/src/widgets/TabView.cpp	2010-08-06 21:02:04 +0000
@@ -45,16 +45,14 @@
 
 const TCHAR TabView::windowClass[] = WC_TABCONTROL;
 
-TabView::Seed::Seed(unsigned maxLength_, bool toggleActive_, bool ctrlTab_) :
+TabView::Seed::Seed(unsigned widthConfig_, bool toggleActive_, bool ctrlTab_) :
 BaseType::Seed(WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_VISIBLE |
-	TCS_FOCUSNEVER | TCS_MULTILINE | TCS_RAGGEDRIGHT | TCS_TOOLTIPS),
+	TCS_FOCUSNEVER | TCS_HOTTRACK | TCS_MULTILINE | TCS_OWNERDRAWFIXED | TCS_RAGGEDRIGHT | TCS_TOOLTIPS),
 font(new Font(DefaultGuiFont)),
-tabStyle(WinDefault),
-maxLength(maxLength_),
+widthConfig(widthConfig_),
 toggleActive(toggleActive_),
 ctrlTab(ctrlTab_)
 {
-	style |= tabStyle;
 }
 
 TabView::TabView(Widget* w) :
@@ -79,9 +77,7 @@
 
 	BaseType::create(cs);
 
-	maxLength = cs.maxLength;
-	if(maxLength <= 3)
-		maxLength = 0;
+	widthConfig = cs.widthConfig;
 	toggleActive = cs.toggleActive;
 
 	if(cs.font)
@@ -90,20 +86,29 @@
 		font = new Font(DefaultGuiFont);
 
 	if(cs.style & TCS_OWNERDRAWFIXED) {
-		dwtassert(dynamic_cast<Control*>(getParent()), _T("Owner-drawn tabs must have a parent derived from dwt::Control"));
+		TabCtrl_SetMinTabWidth(handle(), widthConfig);
 
 		LOGFONT lf;
 		::GetObject(font->handle(), sizeof(lf), &lf);
 		lf.lfWeight = FW_BOLD;
 		boldFont = FontPtr(new Font(::CreateFontIndirect(&lf), true));
-		setFont(boldFont); // so the control adjusts its size per the bold font
 
 		loadTheme(VSCLASS_TAB);
 
+		// in button-style, use classic owner-draw callbacks; in tab style, fully take over painting.
+		if(cs.style & TCS_BUTTONS) {
+			dwtassert(dynamic_cast<Control*>(getParent()), _T("Owner-drawn tabs must have a parent derived from dwt::Control"));
+		} else {
+			onPainting(std::bind((void (TabView::*)(PaintCanvas&))(&TabView::handlePainting), this, _1));
+		}
+
 		// TCS_HOTTRACK seems to have no effect in owner-drawn tabs, so do the tracking ourselves.
 		onMouseMove(std::bind(&TabView::handleMouseMove, this, _1));
 
 	} else {
+		if(widthConfig <= 3)
+			widthConfig = 0;
+
 		setFont(font);
 	}
 
@@ -127,15 +132,18 @@
 
 void TabView::add(ContainerPtr w, const IconPtr& icon) {
 	int image = addIcon(icon);
+
 	size_t tabs = size();
+
 	TabInfo* ti = new TabInfo(this, w);
-	tstring title = formatTitle(w->getText());
-
-	TCITEM item = { 0 };
-	item.mask = TCIF_TEXT | TCIF_PARAM;
-	item.pszText = const_cast < TCHAR * >( title.c_str() );
+	TCITEM item = {  TCIF_PARAM };
 	item.lParam = reinterpret_cast<LPARAM>(ti);
 
+	if(!hasStyle(TCS_OWNERDRAWFIXED)) {
+		item.mask |= TCIF_TEXT;
+		item.pszText = const_cast<LPTSTR>(formatTitle(w->getText()).c_str());
+	}
+
 	if(image != -1) {
 		item.mask |= TCIF_IMAGE;
 		item.iImage = image;
@@ -339,8 +347,12 @@
 void TabView::handleTextChanging(ContainerPtr w, const tstring& newText) {
 	int i = findTab(w);
 	if(i != -1) {
-		setText(i, formatTitle(newText));
-		layout();
+		if(hasStyle(TCS_OWNERDRAWFIXED)) {
+			redraw(i);
+		} else {
+			setText(i, formatTitle(newText));
+			layout();
+		}
 
 		if((i == active) && titleChangedFunction)
 			titleChangedFunction(newText);
@@ -348,8 +360,8 @@
 }
 
 tstring TabView::formatTitle(tstring title) {
-	if(maxLength && title.length() > maxLength)
-		title = title.substr(0, maxLength - 3) + _T("...");
+	if(widthConfig && title.length() > widthConfig)
+		title = title.substr(0, widthConfig - 3) + _T("...");
 	return util::escapeMenu(title);
 }
 
@@ -563,24 +575,68 @@
 }
 
 bool TabView::handlePainting(LPDRAWITEMSTRUCT info, TabInfo* ti) {
-	bool isSelected = (info->itemState & ODS_SELECTED) == ODS_SELECTED;
-	bool isHighlighted = static_cast<int>(info->itemID) == highlighted || ti->marked;
-
 	FreeCanvas canvas(info->hDC);
 	bool oldMode = canvas.setBkMode(true);
 
 	Rectangle rect(info->rcItem);
-
-	int part, state;
 	if(theme) {
-		part = TABP_TABITEM;
-		state = isSelected ? TIS_SELECTED : isHighlighted ? TIS_HOT : TIS_NORMAL;
-
 		// remove some borders
 		rect.pos.x -= 1;
 		rect.pos.y -= 1;
 		rect.size.x += 2;
 		rect.size.y += 1;
+	}
+
+	draw(canvas, info->itemID, std::move(rect), (info->itemState & ODS_SELECTED) == ODS_SELECTED);
+
+	canvas.setBkMode(oldMode);
+	return true;
+}
+
+void TabView::handlePainting(PaintCanvas& canvas) {
+	bool oldMode = canvas.setBkMode(true);
+
+	Rectangle rect(canvas.getPaintRect());
+
+	int sel = getSelected();
+	Rectangle selRect;
+
+	for(size_t i = 0; i < size(); ++i) {
+		RECT rc;
+		if(TabCtrl_GetItemRect(handle(), i, &rc) &&
+			(rc.right >= rect.left() || rc.left <= rect.right()) &&
+			(rc.bottom >= rect.top() || rc.top <= rect.bottom()))
+		{
+			if(static_cast<int>(i) == sel) {
+				rc.top -= 2;
+				rc.bottom += 1;
+				rc.left -= 1;
+				rc.right += 1;
+				selRect = Rectangle(rc);
+			} else {
+				draw(canvas, i, Rectangle(rc), false);
+			}
+		}
+	}
+
+	// draw the selected tab last because it might need to step on others
+	if(selRect.height() > 0)
+		draw(canvas, sel, std::move(selRect), true);
+
+	canvas.setBkMode(oldMode);
+}
+
+void TabView::draw(Canvas& canvas, unsigned index, Rectangle&& rect, bool isSelected) {
+	TabInfo* ti = getTabInfo(index);
+	if(!ti)
+		return;
+
+	bool isHighlighted = static_cast<int>(index) == highlighted || ti->marked;
+
+	int part, state;
+	if(theme) {
+		part = TABP_TABITEM;
+		state = isSelected ? TIS_SELECTED : isHighlighted ? TIS_HOT : TIS_NORMAL;
 
 		drawThemeBackground(canvas, part, state, rect);
 
@@ -588,32 +644,37 @@
 		canvas.fill(rect, Brush(isSelected ? Brush::Window : isHighlighted ? Brush::HighLight : Brush::BtnFace));
 	}
 
-	const Point margin(2, 1);
+	if(isSelected && !hasStyle(TCS_BUTTONS)) {
+		rect.pos.y += 2;
+		rect.size.y -= 2;
+	}
+
+	const Point margin(4, 1);
 	rect.pos += margin;
 	rect.size -= margin + margin;
 
-	IconPtr icon = getIcon(info->itemID);
+	IconPtr icon = getIcon(index);
 	if(icon) {
 		Point size = icon->getSize();
-		canvas.drawIcon(icon, Rectangle(rect.pos, size));
+		Point pos = rect.pos;
+		if(size.y < rect.size.y)
+			pos.y += (rect.size.y - size.y) / 2; // center the icon vertically
+		canvas.drawIcon(icon, Rectangle(pos, size));
 
 		size.x += margin.x;
 		rect.pos.x += size.x;
 		rect.size.x -= size.x;
 	}
 
-	const tstring text = getText(info->itemID);
+	const tstring text = ti->w->getText();
+	const unsigned dtFormat = DT_CENTER | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX | DT_WORD_ELLIPSIS;
 	Canvas::Selector select(canvas, *((isSelected || ti->marked) ? boldFont : font));
 	if(theme) {
-		drawThemeText(canvas, part, state, text, DT_CENTER | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX, rect);
+		drawThemeText(canvas, part, state, text, dtFormat, rect);
 	} else {
 		canvas.setTextColor(::GetSysColor(isSelected ? COLOR_WINDOWTEXT : isHighlighted ? COLOR_HIGHLIGHTTEXT : COLOR_BTNTEXT));
-		canvas.drawText(text, rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX);
+		canvas.drawText(text, rect, dtFormat);
 	}
-
-	canvas.setBkMode(oldMode);
-
-	return true;
 }
 
 void TabView::helpImpl(unsigned& id) {
@@ -663,11 +724,10 @@
 	return false;
 }
 
-void TabView::setText( unsigned index, const tstring& text )
-{
+void TabView::setText(unsigned index, const tstring& text) {
 	TCITEM item = { TCIF_TEXT };
-	item.pszText = const_cast < TCHAR * >( text.c_str() );
-	TabCtrl_SetItem(this->handle(), index, &item);
+	item.pszText = const_cast<LPTSTR>(text.c_str());
+	TabCtrl_SetItem(handle(), index, &item);
 }
 
 tstring TabView::getText(unsigned idx) const

=== modified file 'help/settings_tabs.html'
--- help/settings_tabs.html	2010-06-18 10:09:23 +0000
+++ help/settings_tabs.html	2010-08-06 21:02:04 +0000
@@ -8,6 +8,34 @@
 <body>
 <h1>Tab-related options</h1>
 Tabs appear right below toolbar icons; they give you quick access to currently opened windows.
+<h2>First set of choices</h2>
+<p cshelp="IDH_SETTINGS_TABS_DRAW">
+<b>DC++ draws tabs</b>: Select this option to let DC++ draw tabs, which allows it to add more
+customization. (default)<br/>
+<b>Windows draws tabs</b>: Select this option to use standard Windows tabs - DC++ won't be able to
+customize them.<br/>
+<i>DC++ needs to be restarted for this setting to take effect.</i>
+</p>
+<h2>Second set of choices</h2>
+<p cshelp="IDH_SETTINGS_TABS_STYLE">
+2 modes are exposed here with various differences; select the one you prefer.<br/>
+One particular difference is that in tab style mode, multiline tabs re-organize so that the
+selected tab is always at the bottom. That isn't the case in button style mode.<br/>
+<i>DC++ needs to be restarted for this setting to take effect.</i>
+</p>
+<h2>Tab width</h2>
+<p cshelp="IDH_SETTINGS_TAB_WIDTH">
+Use this slider to define the width occupied by each tab. Move to the left for a lower width, and
+to the right for a higher width.<br/>
+When the title of a tab exceeds the available width, it will be cut accordingly and appended with
+&quot;...&quot;.<br/>
+<i>DC++ needs to be restarted for this setting to take effect.</i>
+</p>
+<h2>Preview</h2>
+<p cshelp="IDH_SETTINGS_TAB_PREVIEW">
+This fake tab control shows how the main tab control would display after a restart of DC++, were
+the settings defined above be validated.
+</p>
 <h2>Tab highlight on content change</h2>
 <dl style="margin-left: 40px;">
   <dt>Hub</dt>
@@ -41,13 +69,5 @@
   <dd cshelp="IDH_SETTINGS_TABS_BOLD_FINISHED_UPLOADS">When an upload completes, the
 <a href="window_finished_uploads.html">Finished Uploads</a> tab will be highlighted if this option is selected.</dd>
 </dl>
-<h2>Max characters per tab (0 = infinite)</h2>
-<p cshelp="IDH_SETTINGS_MAX_TAB_CHARS">
-This value defines the maximum number of characters allowed to appear on each tab. When the title
-of a tab exceeds that value, it is cut accordingly and appended with &quot;...&quot;. A value of 0
-means infinite. (default: 20)<br/>
-Note; any value lower than or equal to 3 also means infinite.<br/>
-Note; DC++ needs to be restarted for this setting to take effect.
-</p>
 </body>
 </html>

=== modified file 'win32/FinishedFrameBase.h'
--- win32/FinishedFrameBase.h	2010-07-10 14:36:48 +0000
+++ win32/FinishedFrameBase.h	2010-08-06 21:02:04 +0000
@@ -68,10 +68,11 @@
 		bOnlyFull(false)
 	{
 		{
-			dwt::TabView::Seed cs = WinUtil::Seeds::tabs;
-			cs.location = dwt::Rectangle(this->getClientSize());
-			cs.maxLength = 0;
-			tabs = this->addChild(cs);
+			dwt::TabView::Seed seed = WinUtil::Seeds::tabs;
+			seed.style &= ~TCS_OWNERDRAWFIXED;
+			seed.location = dwt::Rectangle(this->getClientSize());
+			seed.widthConfig = 0;
+			tabs = this->addChild(seed);
 		}
 
 		{

=== modified file 'win32/MainWindow.cpp'
--- win32/MainWindow.cpp	2010-07-10 14:36:48 +0000
+++ win32/MainWindow.cpp	2010-08-06 21:02:04 +0000
@@ -449,8 +449,15 @@
 
 void MainWindow::initTabs() {
 	dcdebug("initTabs\n");
-	dwt::TabView::Seed seed = WinUtil::Seeds::tabs;
-	seed.maxLength = SETTING(MAX_TAB_CHARS);
+	SettingsManager::getInstance()->setDefault(SettingsManager::TAB_STYLE, TCS_OWNERDRAWFIXED);
+	TabView::Seed seed = WinUtil::Seeds::tabs;
+	seed.widthConfig = SETTING(TAB_WIDTH);
+	if(!(SETTING(TAB_STYLE) & TCS_OWNERDRAWFIXED)) {
+		seed.style &= ~TCS_OWNERDRAWFIXED;
+		seed.widthConfig -= 100; // max width to max chars
+	}
+	if(SETTING(TAB_STYLE) & TCS_BUTTONS)
+		seed.style |= TCS_BUTTONS;
 	seed.toggleActive = BOOLSETTING(TOGGLE_ACTIVE_WINDOW);
 	seed.ctrlTab = true;
 	tabs = addChild(seed);

=== modified file 'win32/TabsPage.cpp'
--- win32/TabsPage.cpp	2010-06-18 10:09:23 +0000
+++ win32/TabsPage.cpp	2010-08-06 21:02:04 +0000
@@ -22,7 +22,7 @@
 
 #include "TabsPage.h"
 
-#include <dwt/widgets/Spinner.h>
+#include <dwt/widgets/Slider.h>
 
 #include <dcpp/SettingsManager.h>
 #include "WinUtil.h"
@@ -44,34 +44,70 @@
 TabsPage::TabsPage(dwt::Widget* parent) :
 PropPage(parent),
 grid(0),
+dcppDraw(0),
+buttonStyle(0),
+tabWidth(0),
+previewGroup(0),
 options(0)
 {
 	setHelpId(IDH_TABSPAGE);
 
-	grid = addChild(Grid::Seed(2, 1));
+	grid = addChild(Grid::Seed(4, 1));
 	grid->column(0).mode = GridInfo::FILL;
-	grid->row(0).mode = GridInfo::FILL;
-	grid->row(0).align = GridInfo::STRETCH;
+	grid->row(3).mode = GridInfo::FILL;
+	grid->row(3).align = GridInfo::STRETCH;
+	grid->setSpacing(10);
+
+	{
+		GridPtr cur = grid->addChild(Grid::Seed(1, 2));
+
+		GroupBoxPtr group = cur->addChild(GroupBox::Seed());
+		group->setHelpId(IDH_SETTINGS_TABS_DRAW);
+		GridPtr cur2 = group->addChild(Grid::Seed(2, 1));
+		dcppDraw = cur2->addChild(RadioButton::Seed(T_("DC++ draws tabs")));
+		dcppDraw->onClicked(std::bind(&TabsPage::createPreview, this));
+		RadioButtonPtr button = cur2->addChild(RadioButton::Seed(T_("Windows draws tabs")));
+		button->onClicked(std::bind(&TabsPage::createPreview, this));
+		if(SETTING(TAB_STYLE) & TCS_OWNERDRAWFIXED)
+			dcppDraw->setChecked();
+		else
+			button->setChecked();
+
+		group = cur->addChild(GroupBox::Seed());
+		group->setHelpId(IDH_SETTINGS_TABS_STYLE);
+		cur2 = group->addChild(Grid::Seed(2, 1));
+		button = cur2->addChild(RadioButton::Seed(T_("Tab style")));
+		button->onClicked(std::bind(&TabsPage::createPreview, this));
+		buttonStyle = cur2->addChild(RadioButton::Seed(T_("Button style")));
+		buttonStyle->onClicked(std::bind(&TabsPage::createPreview, this));
+		if(SETTING(TAB_STYLE) & TCS_BUTTONS)
+			buttonStyle->setChecked();
+		else
+			button->setChecked();
+	}
+
+	{
+		GroupBoxPtr group = grid->addChild(GroupBox::Seed(T_("Tab width")));
+		group->setHelpId(IDH_SETTINGS_TAB_WIDTH);
+
+		GridPtr cur = group->addChild(Grid::Seed(1, 1));
+		cur->column(0).mode = GridInfo::FILL;
+		cur->row(0).size = 30;
+		cur->row(0).mode = GridInfo::STATIC;
+		cur->row(0).align = GridInfo::STRETCH;
+
+		tabWidth = cur->addChild(Slider::Seed());
+		tabWidth->setRange(100, 1000);
+		tabWidth->setPosition(SETTING(TAB_WIDTH));
+		tabWidth->onScrollHorz(std::bind(&TabsPage::createPreview, this));
+	}
+
+	previewGroup = grid->addChild(GroupBox::Seed(T_("Preview")));
+	previewGroup->setHelpId(IDH_SETTINGS_TAB_PREVIEW);
+	createPreview();
 
 	options = grid->addChild(GroupBox::Seed(T_("Tab highlight on content change")))->addChild(WinUtil::Seeds::Dialog::optionsTable);
 
-	{
-		GridPtr cur = grid->addChild(Grid::Seed(1, 2));
-		cur->column(1).size = 40;
-		cur->column(1).mode = GridInfo::STATIC;
-
-		cur->addChild(Label::Seed(T_("Max characters per tab (0 = infinite)")))->setHelpId(IDH_SETTINGS_MAX_TAB_CHARS);
-
-		TextBoxPtr box = cur->addChild(WinUtil::Seeds::Dialog::intTextBox);
-		items.push_back(Item(box, SettingsManager::MAX_TAB_CHARS, PropPage::T_INT_WITH_SPIN));
-		box->setHelpId(IDH_SETTINGS_MAX_TAB_CHARS);
-
-		SpinnerPtr spin = cur->addChild(Spinner::Seed(0, UD_MAXVAL, box));
-		cur->setWidget(spin);
-		spin->setHelpId(IDH_SETTINGS_MAX_TAB_CHARS);
-	}
-
-	PropPage::read(items);
 	PropPage::read(listItems, options);
 }
 
@@ -86,6 +122,56 @@
 }
 
 void TabsPage::write() {
-	PropPage::write(items);
+	int tabStyle = 0;
+	if(dcppDraw->getChecked())
+		tabStyle |= TCS_OWNERDRAWFIXED;
+	if(buttonStyle->getChecked())
+		tabStyle |= TCS_BUTTONS;
+	SettingsManager::getInstance()->set(SettingsManager::TAB_STYLE, tabStyle);
+
+	SettingsManager::getInstance()->set(SettingsManager::TAB_WIDTH, tabWidth->getPosition());
+
 	PropPage::write(options);
 }
+
+void TabsPage::createPreview() {
+	GridPtr cur = previewGroup->addChild(Grid::Seed(1, 1));
+	cur->column(0).mode = GridInfo::FILL;
+	cur->row(0).size = 100;
+	cur->row(0).mode = GridInfo::STATIC;
+	cur->row(0).align = GridInfo::STRETCH;
+
+	TabView::Seed seed = WinUtil::Seeds::tabs;
+	seed.widthConfig = tabWidth->getPosition();
+	seed.style |= WS_DISABLED;
+	if(!dcppDraw->getChecked()) {
+		seed.style &= ~TCS_OWNERDRAWFIXED;
+		seed.widthConfig -= 100; // max width to max chars
+	}
+	if(buttonStyle->getChecked())
+		seed.style |= TCS_BUTTONS;
+	TabViewPtr tabs = cur->addChild(seed);
+
+	auto makeTab = [&tabs](const tstring& text) {
+		Container::Seed cs;
+		cs.caption = text;
+		ContainerPtr ret = dwt::WidgetCreator<Container>::create(tabs, cs);
+		// the tab control sends WM_ACTIVATE messages; catch them, otherwise the dialog gets messed up.
+		ret->onRaw([](WPARAM, LPARAM) { return 0; }, dwt::Message(WM_ACTIVATE));
+		return ret;
+	};
+
+	tabs->add(makeTab(T_("Example hub tab")), WinUtil::tabIcon(IDI_HUB));
+	dwt::IconPtr icon = WinUtil::tabIcon(IDI_DCPP);
+	tabs->add(makeTab(T_("Selected tab")), icon);
+	ContainerPtr highlighted = makeTab(T_("Highlighted tab"));
+	tabs->add(highlighted, icon);
+	tabs->add(makeTab(T_("Yet another tab")), icon);
+	tabs->setSelected(1);
+	tabs->mark(highlighted);
+
+	// refresh
+	dwt::Rectangle rect = previewGroup->getWindowRect();
+	rect.pos -= grid->getWindowRect().pos; // screen->client coords
+	previewGroup->layout(rect);
+}

=== modified file 'win32/TabsPage.h'
--- win32/TabsPage.h	2010-02-11 21:44:13 +0000
+++ win32/TabsPage.h	2010-08-06 21:02:04 +0000
@@ -31,12 +31,16 @@
 	virtual void write();
 
 private:
-	ItemList items;
-
 	GridPtr grid;
+	RadioButtonPtr dcppDraw;
+	RadioButtonPtr buttonStyle;
+	SliderPtr tabWidth;
+	GroupBoxPtr previewGroup;
 
 	static ListItem listItems[];
 	TablePtr options;
+
+	void createPreview();
 };
 
 #endif // !defined(DCPLUSPLUS_WIN32_TABS_PAGE_H)

=== modified file 'win32/TransferView.cpp'
--- win32/TransferView.cpp	2010-07-10 14:36:48 +0000
+++ win32/TransferView.cpp	2010-08-06 21:02:04 +0000
@@ -81,11 +81,12 @@
 	create();
 
 	{
-		TabView::Seed cs = WinUtil::Seeds::tabs;
-		cs.location = dwt::Rectangle(getClientSize());
-		cs.maxLength = 0;
-		cs.ctrlTab = true;
-		tabs = addChild(cs);
+		TabView::Seed seed = WinUtil::Seeds::tabs;
+		seed.style &= ~TCS_OWNERDRAWFIXED;
+		seed.location = dwt::Rectangle(getClientSize());
+		seed.widthConfig = 0;
+		seed.ctrlTab = true;
+		tabs = addChild(seed);
 		tabs->onHelp(std::bind(&WinUtil::help, _1, _2));
 	}
 

=== modified file 'win32/WinUtil.cpp'
--- win32/WinUtil.cpp	2010-07-22 15:31:17 +0000
+++ win32/WinUtil.cpp	2010-08-06 21:02:04 +0000
@@ -264,7 +264,7 @@
 
 void WinUtil::enableDEP() {
 	dwt::LibraryLoader kernel(_T("kernel32.dll"));
-	typedef WINBASEAPI BOOL WINAPI (*SPDP)(DWORD);
+	typedef BOOL (WINAPI *SPDP)(DWORD);
 	SPDP spdp = (SPDP)kernel.getProcAddress(_T("SetProcessDEPPolicy"));
 	if (spdp)
 		dcdebug("SetProcessDEPPolicy %s\n", (*spdp)(1)?"succeeded":"failed");

=== modified file 'win32/stdafx.h'
--- win32/stdafx.h	2010-07-10 14:36:48 +0000
+++ win32/stdafx.h	2010-08-06 21:02:04 +0000
@@ -80,6 +80,8 @@
 using dwt::RadioButton;
 using dwt::RadioButtonPtr;
 using dwt::SaveDialog;
+using dwt::Slider;
+using dwt::SliderPtr;
 using dwt::Spinner;
 using dwt::SpinnerPtr;
 using dwt::TabView;