1 /*
2  * Copyright 2003, 2004, 2005 Martin Fuchs
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
17  */
18 
19 
20  //
21  // Explorer clone
22  //
23  // quicklaunch.cpp
24  //
25  // Martin Fuchs, 22.08.2003
26  //
27 
28 
29 #include <precomp.h>
30 
31 #include "quicklaunch.h"
32 
33 
QuickLaunchEntry()34 QuickLaunchEntry::QuickLaunchEntry()
35 {
36 	_hbmp = 0;
37 }
38 
~QuickLaunchMap()39 QuickLaunchMap::~QuickLaunchMap()
40 {
41 	while(!empty()) {
42 		iterator it = begin();
43 		DeleteBitmap(it->second._hbmp);
44 		erase(it);
45 	}
46 }
47 
48 
QuickLaunchBar(HWND hwnd)49 QuickLaunchBar::QuickLaunchBar(HWND hwnd)
50  :	super(hwnd)
51 {
52 	CONTEXT("QuickLaunchBar::QuickLaunchBar()");
53 
54 	_dir = NULL;
55 	_next_id = IDC_FIRST_QUICK_ID;
56 	_btn_dist = 20;
57 	_size = 0;
58 
59 	HWND hwndToolTip = (HWND) SendMessage(hwnd, TB_GETTOOLTIPS, 0, 0);
60 
61 	SetWindowStyle(hwndToolTip, GetWindowStyle(hwndToolTip)|TTS_ALWAYSTIP);
62 
63 	 // delay refresh to some time later
64 	PostMessage(hwnd, PM_REFRESH, 0, 0);
65 }
66 
~QuickLaunchBar()67 QuickLaunchBar::~QuickLaunchBar()
68 {
69 	delete _dir;
70 }
71 
Create(HWND hwndParent)72 HWND QuickLaunchBar::Create(HWND hwndParent)
73 {
74 	CONTEXT("QuickLaunchBar::Create()");
75 
76 	ClientRect clnt(hwndParent);
77 
78 	HWND hwnd = CreateToolbarEx(hwndParent,
79 								WS_CHILD|WS_VISIBLE|WS_CLIPSIBLINGS|WS_CLIPCHILDREN|
80 								CCS_TOP|CCS_NODIVIDER|CCS_NOPARENTALIGN|CCS_NORESIZE|
81 								TBSTYLE_TOOLTIPS|TBSTYLE_WRAPABLE|TBSTYLE_FLAT,
82 								IDW_QUICKLAUNCHBAR, 0, 0, 0, NULL, 0, 0, 0, 16, 16, sizeof(TBBUTTON));
83 
84 	if (hwnd)
85 		new QuickLaunchBar(hwnd);
86 
87 	return hwnd;
88 }
89 
AddShortcuts()90 void QuickLaunchBar::AddShortcuts()
91 {
92 	CONTEXT("QuickLaunchBar::AddShortcuts()");
93 
94 	WaitCursor wait;
95 
96 	try {
97 		TCHAR path[MAX_PATH];
98 
99 		SpecialFolderFSPath app_data(CSIDL_APPDATA, _hwnd);	///@todo perhaps also look into CSIDL_COMMON_APPDATA ?
100 
101 		_stprintf(path, TEXT("%s\\")QUICKLAUNCH_FOLDER, (LPCTSTR)app_data);
102 
103 		RecursiveCreateDirectory(path);
104 		_dir = new ShellDirectory(GetDesktopFolder(), path, _hwnd);
105 
106 		_dir->smart_scan(SORT_NAME);
107 
108 		 // immediatelly extract the shortcut icons
109 		for(Entry*entry=_dir->_down; entry; entry=entry->_next)
110 			entry->_icon_id = entry->safe_extract_icon(ICF_NORMAL);
111 	} catch(COMException&) {
112 		return;
113 	}
114 
115 
116 	ShellFolder desktop_folder;
117 	WindowCanvas canvas(_hwnd);
118 
119 	COLORREF bk_color = GetSysColor(COLOR_BTNFACE);
120 	HBRUSH bk_brush = GetSysColorBrush(COLOR_BTNFACE);
121 
122 	AddButton(ID_MINIMIZE_ALL, g_Globals._icon_cache.get_icon(ICID_MINIMIZE).create_bitmap(bk_color, bk_brush, canvas), ResString(IDS_MINIMIZE_ALL), NULL);
123 	AddButton(ID_EXPLORE, g_Globals._icon_cache.get_icon(ICID_EXPLORER).create_bitmap(bk_color, bk_brush, canvas), ResString(IDS_TITLE), NULL);
124 
125 	TBBUTTON sep = {0, -1, TBSTATE_ENABLED, BTNS_SEP, {0, 0}, 0, 0};
126 	SendMessage(_hwnd, TB_INSERTBUTTON, INT_MAX, (LPARAM)&sep);
127 
128 	int cur_desktop = g_Globals._desktops._current_desktop;
129 	ResString desktop_fmt(IDS_DESKTOP_NUM);
130 
131 	HDC hdc = CreateCompatibleDC(canvas);
132 	DWORD size = SendMessage(_hwnd, TB_GETBUTTONSIZE, 0, 0);
133 	int cx = LOWORD(size);
134 	int cy = HIWORD(size);
135 	RECT rect = {0, 0, cx, cy};
136 	RECT textRect = {0, 0, cx-7, cy-7};
137 
138 	for(int i=0; i<DESKTOP_COUNT; ++i) {
139 		HBITMAP hbmp = CreateCompatibleBitmap(canvas, cx, cy);
140 		HBITMAP hbmp_old = SelectBitmap(hdc, hbmp);
141 
142 		FontSelection font(hdc, GetStockFont(ANSI_VAR_FONT));
143 		FmtString num_txt(TEXT("%d"), i+1);
144 		TextColor color(hdc, RGB(64,64,64));
145 		BkMode mode(hdc, TRANSPARENT);
146 		FillRect(hdc, &rect, GetSysColorBrush(COLOR_BTNFACE));
147 		DrawText(hdc, num_txt, num_txt.length(), &textRect, DT_CENTER|DT_VCENTER|DT_SINGLELINE);
148 
149 		SelectBitmap(hdc, hbmp_old);
150 
151 		AddButton(ID_SWITCH_DESKTOP_1+i, hbmp, FmtString(desktop_fmt, i+1), NULL, cur_desktop==i?TBSTATE_ENABLED|TBSTATE_PRESSED:TBSTATE_ENABLED);
152 	}
153 	DeleteDC(hdc);
154 
155 	for(Entry*entry=_dir->_down; entry; entry=entry->_next) {
156 		 // hide files like "desktop.ini"
157 		if (entry->_data.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
158 			continue;
159 
160 		 // hide subfolders
161 		if (!(entry->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
162 			HBITMAP hbmp = g_Globals._icon_cache.get_icon(entry->_icon_id).create_bitmap(bk_color, bk_brush, canvas);
163 
164 			AddButton(_next_id++, hbmp, entry->_display_name, entry);	//entry->_etype==ET_SHELL? desktop_folder.get_name(static_cast<ShellEntry*>(entry)->_pidl): entry->_display_name);
165 		}
166 	}
167 
168 	_btn_dist = LOWORD(SendMessage(_hwnd, TB_GETBUTTONSIZE, 0, 0));
169 	_size = _entries.size() * _btn_dist;
170 
171 	SendMessage(GetParent(_hwnd), PM_RESIZE_CHILDREN, 0, 0);
172 }
173 
AddButton(int id,HBITMAP hbmp,LPCTSTR name,Entry * entry,int flags)174 void QuickLaunchBar::AddButton(int id, HBITMAP hbmp, LPCTSTR name, Entry* entry, int flags)
175 {
176 	TBADDBITMAP ab = {0, (UINT_PTR)hbmp};
177 	int bmp_idx = SendMessage(_hwnd, TB_ADDBITMAP, 1, (LPARAM)&ab);
178 
179 	QuickLaunchEntry qle;
180 
181 	qle._hbmp = hbmp;
182 	qle._title = name;
183 	qle._entry = entry;
184 
185 	_entries[id] = qle;
186 
187 	TBBUTTON btn = {0, 0, (BYTE)flags, BTNS_BUTTON|BTNS_NOPREFIX, {0, 0}, 0, 0};
188 
189 	btn.idCommand = id;
190 	btn.iBitmap = bmp_idx;
191 
192 	SendMessage(_hwnd, TB_INSERTBUTTON, INT_MAX, (LPARAM)&btn);
193 }
194 
UpdateDesktopButtons(int desktop_idx)195 void QuickLaunchBar::UpdateDesktopButtons(int desktop_idx)
196 {
197 	for(int i=0; i<DESKTOP_COUNT; ++i) {
198 		TBBUTTONINFO tbi = {sizeof(TBBUTTONINFO), TBIF_STATE, 0, 0, (BYTE)(desktop_idx==i? TBSTATE_ENABLED|TBSTATE_PRESSED: TBSTATE_ENABLED)};
199 
200 		SendMessage(_hwnd, TB_SETBUTTONINFO, ID_SWITCH_DESKTOP_1+i, (LPARAM)&tbi);
201 	}
202 }
203 
WndProc(UINT nmsg,WPARAM wparam,LPARAM lparam)204 LRESULT QuickLaunchBar::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
205 {
206 	switch(nmsg) {
207 	  case PM_REFRESH:
208 		AddShortcuts();
209 		break;
210 
211 	  case PM_GET_WIDTH: {
212 		 // take line wrapping into account
213 		int btns = SendMessage(_hwnd, TB_BUTTONCOUNT, 0, 0);
214 		int rows = SendMessage(_hwnd, TB_GETROWS, 0, 0);
215 
216 		if (rows<2 || rows==btns)
217 			return _size;
218 
219 		RECT rect;
220 		int max_cx = 0;
221 
222 		for(QuickLaunchMap::const_iterator it=_entries.begin(); it!=_entries.end(); ++it) {
223 			SendMessage(_hwnd, TB_GETRECT, it->first, (LPARAM)&rect);
224 
225 			if (rect.right > max_cx)
226 				max_cx = rect.right;
227 		}
228 
229 		return max_cx;}
230 
231 	  case PM_UPDATE_DESKTOP:
232 		UpdateDesktopButtons(wparam);
233 		break;
234 
235 	  case WM_CONTEXTMENU: {
236 		TBBUTTON btn;
237 		QuickLaunchMap::iterator it;
238 		Point screen_pt(lparam), clnt_pt=screen_pt;
239 		ScreenToClient(_hwnd, &clnt_pt);
240 
241 		Entry* entry = NULL;
242 		int idx = SendMessage(_hwnd, TB_HITTEST, 0, (LPARAM)&clnt_pt);
243 
244 		if (idx>=0 &&
245 			SendMessage(_hwnd, TB_GETBUTTON, idx, (LPARAM)&btn)!=-1 &&
246 			(it=_entries.find(btn.idCommand))!=_entries.end()) {
247 			entry = it->second._entry;
248 		}
249 
250 		if (entry) {	// entry is NULL for desktop switch buttons
251 			HRESULT hr = entry->do_context_menu(_hwnd, screen_pt, _cm_ifs);
252 
253 			if (!SUCCEEDED(hr))
254 				CHECKERROR(hr);
255 		} else
256 			goto def;
257 		break;}
258 
259 	  default: def:
260 		return super::WndProc(nmsg, wparam, lparam);
261 	}
262 
263 	return 0;
264 }
265 
Command(int id,int code)266 int QuickLaunchBar::Command(int id, int code)
267 {
268 	CONTEXT("QuickLaunchBar::Command()");
269 
270 	if ((id&~0xFF) == IDC_FIRST_QUICK_ID) {
271 		QuickLaunchEntry& qle = _entries[id];
272 
273 		if (qle._entry) {
274 			qle._entry->launch_entry(_hwnd);
275 			return 0;
276 		}
277 	}
278 
279 	return 0; // Don't return 1 to avoid recursion with DesktopBar::Command()
280 }
281 
Notify(int id,NMHDR * pnmh)282 int QuickLaunchBar::Notify(int id, NMHDR* pnmh)
283 {
284 	switch(pnmh->code) {
285 	  case TTN_GETDISPINFO: {
286 		NMTTDISPINFO* ttdi = (NMTTDISPINFO*) pnmh;
287 
288 		int id = ttdi->hdr.idFrom;
289 		ttdi->lpszText = _entries[id]._title.str();
290 #ifdef TTF_DI_SETITEM
291 		ttdi->uFlags |= TTF_DI_SETITEM;
292 #endif
293 
294 		 // enable multiline tooltips (break at CR/LF and for very long one-line strings)
295 		SendMessage(pnmh->hwndFrom, TTM_SETMAXTIPWIDTH, 0, 400);
296 
297 		break;}
298 
299 		return super::Notify(id, pnmh);
300 	}
301 
302 	return 0;
303 }
304