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  // pane.cpp
24  //
25  // Martin Fuchs, 23.07.2003
26  //
27 
28 
29 #include <precomp.h>
30 
31 enum IMAGE {
32 	IMG_NONE=-1,	IMG_FILE=0, 		IMG_DOCUMENT,	IMG_EXECUTABLE,
33 	IMG_FOLDER, 	IMG_OPEN_FOLDER,	IMG_FOLDER_PLUS,IMG_OPEN_PLUS,	IMG_OPEN_MINUS,
34 	IMG_FOLDER_UP,	IMG_FOLDER_CUR
35 };
36 
37 
38 #define IMAGE_WIDTH 		16
39 #define IMAGE_HEIGHT		13
40 
41 
42 static const TCHAR* g_pos_names[COLUMNS] = {
43 	TEXT(""),			/* symbol */
44 	TEXT("Name"),
45 	TEXT("Type"),
46 	TEXT("Size"),
47 	TEXT("CDate"),
48 	TEXT("ADate"),
49 	TEXT("MDate"),
50 	TEXT("Index/Inode"),
51 	TEXT("Links"),
52 	TEXT("Attributes"),
53 	TEXT("Security"),
54 	TEXT("Content")
55 };
56 
57 static const int g_pos_align[] = {
58 	0,
59 	HDF_LEFT,	/* Name */
60 	HDF_LEFT,	/* Type */
61 	HDF_RIGHT,	/* Size */
62 	HDF_LEFT,	/* CDate */
63 	HDF_LEFT,	/* ADate */
64 	HDF_LEFT,	/* MDate */
65 	HDF_LEFT,	/* Index */
66 	HDF_RIGHT,	/* Links */
67 	HDF_CENTER,	/* Attributes */
68 	HDF_LEFT,	/* Security */
69 	HDF_LEFT	/* Content / Description */
70 };
71 
72 
73 Pane::Pane(HWND hparent, int id, int id_header, Entry* root, bool treePane, int visible_cols)
74  :	super(CreateWindow(TEXT("ListBox"), TEXT(""), WS_CHILD|WS_VISIBLE|WS_HSCROLL|WS_VSCROLL|
75 			LBS_DISABLENOSCROLL|LBS_NOINTEGRALHEIGHT|LBS_OWNERDRAWFIXED|LBS_NOTIFY,
76 			0, 0, 0, 0, hparent, (HMENU)id, g_Globals._hInstance, 0)),
77 	_root(root),
78 	_visible_cols(visible_cols),
79 	_treePane(treePane)
80 {
81 	 // insert entries into listbox
82 	Entry* entry = _root;
83 
84 	if (entry)
85 		insert_entries(entry);
86 
87 	init();
88 
89 	create_header(hparent, id_header);
90 }
91 
92 Pane::~Pane()
93 {
94 	ImageList_Destroy(_himl);
95 }
96 
97 
98 LRESULT Pane::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
99 {
100 	switch(nmsg) {
101 	  case WM_HSCROLL:
102 		set_header();
103 		break;
104 
105 	  case WM_SETFOCUS: {
106 		FileChildWindow* child = (FileChildWindow*) SendMessage(GetParent(_hwnd), PM_GET_FILEWND_PTR, 0, 0);
107 
108 		child->set_focus_pane(this);
109 		ListBox_SetSel(_hwnd, TRUE, 1);
110 		/*@todo check menu items */
111 		break;}
112 
113 	  case WM_KEYDOWN: {
114 		FileChildWindow* child = (FileChildWindow*) SendMessage(GetParent(_hwnd), PM_GET_FILEWND_PTR, 0, 0);
115 
116 		if (wparam == VK_TAB) {
117 			/*@todo SetFocus(g_Globals.hdrivebar) */
118 			child->switch_focus_pane();
119 		}
120 		break;}
121 	}
122 
123 	return super::WndProc(nmsg, wparam, lparam);
124 }
125 
126 
127 bool Pane::create_header(HWND hparent, int id)
128 {
129 	HWND hwnd = CreateWindow(WC_HEADER, 0, WS_CHILD|WS_VISIBLE|HDS_HORZ/*@todo |HDS_BUTTONS + sort orders*/,
130 								0, 0, 0, 0, hparent, (HMENU)id, g_Globals._hInstance, 0);
131 	if (!hwnd)
132 		return false;
133 
134 	SetWindowFont(hwnd, GetStockFont(DEFAULT_GUI_FONT), FALSE);
135 
136 	HD_ITEM hdi;
137 
138 	hdi.mask = HDI_TEXT|HDI_WIDTH|HDI_FORMAT;
139 
140 	for(int idx=0; idx<COLUMNS; idx++) {
141 		hdi.pszText = (TCHAR*)g_pos_names[idx];
142 		hdi.fmt = HDF_STRING | g_pos_align[idx];
143 		hdi.cxy = _widths[idx];
144 		Header_InsertItem(hwnd, idx, &hdi);
145 	}
146 
147 	_hwndHeader = hwnd;
148 
149 	return true;
150 }
151 
152 
153 void Pane::init()
154 {
155 	_himl = ImageList_LoadBitmap(g_Globals._hInstance, MAKEINTRESOURCE(IDB_IMAGES), 16, 0, RGB(0,255,0));
156 
157 	SetWindowFont(_hwnd, _out_wrkr._hfont, FALSE);
158 
159 	 // read the color for compressed files from registry
160 	HKEY hkeyExplorer = 0;
161 	DWORD len = sizeof(_clrCompressed);
162 
163 	if (RegOpenKey(HKEY_CURRENT_USER, TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer"), &hkeyExplorer) ||
164 		RegQueryValueEx(hkeyExplorer, TEXT("AltColor"), 0, NULL, (LPBYTE)&_clrCompressed, &len) || len!=sizeof(_clrCompressed))
165 		_clrCompressed = RGB(0,0,255);
166 
167 	if (hkeyExplorer)
168 		RegCloseKey(hkeyExplorer);
169 
170 	 // calculate column widths
171 	_out_wrkr.init_output(_hwnd);
172 	calc_widths(true);
173 }
174 
175 
176  // calculate prefered width for all visible columns
177 
178 bool Pane::calc_widths(bool anyway)
179 {
180 	int col, x, cx, spc=3*_out_wrkr._spaceSize.cx;
181 	int entries = ListBox_GetCount(_hwnd);
182 	int orgWidths[COLUMNS];
183 	int orgPositions[COLUMNS+1];
184 	HFONT hfontOld;
185 	HDC hdc;
186 	int cnt;
187 
188 	if (!anyway) {
189 		memcpy(orgWidths, _widths, sizeof(orgWidths));
190 		memcpy(orgPositions, _positions, sizeof(orgPositions));
191 	}
192 
193 	for(col=0; col<COLUMNS; col++)
194 		_widths[col] = 0;
195 
196 	hdc = GetDC(_hwnd);
197 	hfontOld = SelectFont(hdc, _out_wrkr._hfont);
198 
199 	for(cnt=0; cnt<entries; cnt++) {
200 		Entry* entry = (Entry*) ListBox_GetItemData(_hwnd, cnt);
201 
202 		DRAWITEMSTRUCT dis;
203 
204 		dis.CtlType		  = 0;
205 		dis.CtlID		  = 0;
206 		dis.itemID		  = 0;
207 		dis.itemAction	  = 0;
208 		dis.itemState	  = 0;
209 		dis.hwndItem	  = _hwnd;
210 		dis.hDC			  = hdc;
211 		dis.rcItem.left	  = 0;
212 		dis.rcItem.top    = 0;
213 		dis.rcItem.right  = 0;
214 		dis.rcItem.bottom = 0;
215 		/*dis.itemData	  = 0; */
216 
217 		draw_item(&dis, entry, COLUMNS);
218 	}
219 
220 	SelectObject(hdc, hfontOld);
221 	ReleaseDC(_hwnd, hdc);
222 
223 	x = 0;
224 	for(col=0; col<COLUMNS; col++) {
225 		_positions[col] = x;
226 		cx = _widths[col];
227 
228 		if (cx) {
229 			cx += spc;
230 
231 			if (cx < IMAGE_WIDTH)
232 				cx = IMAGE_WIDTH;
233 
234 			_widths[col] = cx;
235 		}
236 
237 		x += cx;
238 	}
239 
240 	_positions[COLUMNS] = x;
241 
242 	ListBox_SetHorizontalExtent(_hwnd, x);
243 
244 	 // no change?
245 	if (!memcmp(orgWidths, _widths, sizeof(orgWidths)))
246 		return FALSE;
247 
248 	 // don't move, if only collapsing an entry
249 	if (!anyway && _widths[0]<orgWidths[0] &&
250 		!memcmp(orgWidths+1, _widths+1, sizeof(orgWidths)-sizeof(int))) {
251 		_widths[0] = orgWidths[0];
252 		memcpy(_positions, orgPositions, sizeof(orgPositions));
253 
254 		return FALSE;
255 	}
256 
257 	InvalidateRect(_hwnd, 0, TRUE);
258 
259 	return TRUE;
260 }
261 
262 
263 static void format_date(const FILETIME* ft, TCHAR* buffer, int visible_cols)
264 {
265 	SYSTEMTIME systime;
266 	FILETIME lft;
267 	int len = 0;
268 
269 	*buffer = TEXT('\0');
270 
271 	if (!ft->dwLowDateTime && !ft->dwHighDateTime)
272 		return;
273 
274 	if (!FileTimeToLocalFileTime(ft, &lft))
275 		{err: lstrcpy(buffer,TEXT("???")); return;}
276 
277 	if (!FileTimeToSystemTime(&lft, &systime))
278 		goto err;
279 
280 	if (visible_cols & COL_DATE) {
281 		len = GetDateFormat(LOCALE_USER_DEFAULT, 0, &systime, 0, buffer, BUFFER_LEN);
282 		if (!len)
283 			goto err;
284 	}
285 
286 	if (visible_cols & COL_TIME) {
287 		if (len)
288 			buffer[len-1] = ' ';
289 
290 		buffer[len++] = ' ';
291 
292 		if (!GetTimeFormat(LOCALE_USER_DEFAULT, 0, &systime, 0, buffer+len, BUFFER_LEN-len))
293 			buffer[len] = TEXT('\0');
294 	}
295 }
296 
297 
298 void Pane::draw_item(LPDRAWITEMSTRUCT dis, Entry* entry, int calcWidthCol)
299 {
300 	TCHAR buffer[BUFFER_LEN];
301 	DWORD attrs;
302 	int visible_cols = _visible_cols;
303 	COLORREF bkcolor, textcolor;
304 	RECT focusRect = dis->rcItem;
305 	enum IMAGE img;
306 	int img_pos, cx;
307 	int col = 0;
308 
309 	if (entry) {
310 		attrs = entry->_data.dwFileAttributes;
311 
312 		if (attrs & FILE_ATTRIBUTE_DIRECTORY) {
313 			if (entry->_data.cFileName[0]==TEXT('.') && entry->_data.cFileName[1]==TEXT('.')
314 					&& entry->_data.cFileName[2]==TEXT('\0'))
315 				img = IMG_FOLDER_UP;
316 			else if (entry->_data.cFileName[0]==TEXT('.') && entry->_data.cFileName[1]==TEXT('\0'))
317 				img = IMG_FOLDER_CUR;
318 			else if ((_treePane && (dis->itemState&ODS_FOCUS)))
319 				img = IMG_OPEN_FOLDER;
320 			else
321 				img = IMG_FOLDER;
322 		} else {
323 			if (attrs & ATTRIBUTE_EXECUTABLE)
324 				img = IMG_EXECUTABLE;
325 			else if (entry->_type_name)
326 				img = IMG_DOCUMENT;
327 			else
328 				img = IMG_FILE;
329 		}
330 	} else {
331 		attrs = 0;
332 		img = IMG_NONE;
333 	}
334 
335 	if (_treePane) {
336 		if (entry) {
337 			img_pos = dis->rcItem.left + entry->_level*(IMAGE_WIDTH+_out_wrkr._spaceSize.cx);
338 
339 			if (calcWidthCol == -1) {
340 				int x;
341 				int y = dis->rcItem.top + IMAGE_HEIGHT/2;
342 				Entry* up;
343 				RECT rt_clip;
344 				HRGN hrgn_org = CreateRectRgn(0, 0, 0, 0);
345 				HRGN hrgn;
346 
347 				rt_clip.left   = dis->rcItem.left;
348 				rt_clip.top    = dis->rcItem.top;
349 				rt_clip.right  = dis->rcItem.left+_widths[col];
350 				rt_clip.bottom = dis->rcItem.bottom;
351 
352 				hrgn = CreateRectRgnIndirect(&rt_clip);
353 
354 				if (!GetClipRgn(dis->hDC, hrgn_org)) {
355 					DeleteObject(hrgn_org);
356 					hrgn_org = 0;
357 				}
358 
359 				//HGDIOBJ holdPen = SelectObject(dis->hDC, GetStockObject(BLACK_PEN));
360 				ExtSelectClipRgn(dis->hDC, hrgn, RGN_AND);
361 				DeleteObject(hrgn);
362 
363 				if ((up=entry->_up) != NULL) {
364 					MoveToEx(dis->hDC, img_pos-IMAGE_WIDTH/2, y, 0);
365 					LineTo(dis->hDC, img_pos-2, y);
366 
367 					x = img_pos - IMAGE_WIDTH/2;
368 
369 					do {
370 						x -= IMAGE_WIDTH+_out_wrkr._spaceSize.cx;
371 
372 						if (up->_next) {
373 #ifndef _LEFT_FILES
374 							bool following_child = (up->_next->_data.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)!=0;	// a directory?
375 
376 							if (!following_child)
377 							{
378 								for(Entry*n=up->_next; n; n=n->_next)
379 									if (n->_down) {	// any file with NTFS sub-streams?
380 										following_child = true;
381 										break;
382 									}
383 							}
384 							if (following_child)
385 #endif
386 							{
387 								MoveToEx(dis->hDC, x, dis->rcItem.top, 0);
388 								LineTo(dis->hDC, x, dis->rcItem.bottom);
389 							}
390 						}
391 					} while((up=up->_up) != NULL);
392 				}
393 
394 				x = img_pos - IMAGE_WIDTH/2;
395 
396 				MoveToEx(dis->hDC, x, dis->rcItem.top, 0);
397 				LineTo(dis->hDC, x, y);
398 
399 				if (entry->_next) {
400 #ifndef _LEFT_FILES
401 					bool following_child = (entry->_next->_data.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)!=0;	// a directory?
402 
403 					if (!following_child)
404 					{
405 						for(Entry*n=entry->_next; n; n=n->_next)
406 							if (n->_down) {	// any file with NTFS sub-streams?
407 								following_child = true;
408 								break;
409 							}
410 					}
411 					if (following_child)
412 #endif
413 						LineTo(dis->hDC, x, dis->rcItem.bottom);
414 				}
415 
416 				if (entry->_down && entry->_expanded) {
417 					x += IMAGE_WIDTH + _out_wrkr._spaceSize.cx;
418 					MoveToEx(dis->hDC, x, dis->rcItem.top+IMAGE_HEIGHT, 0);
419 					LineTo(dis->hDC, x, dis->rcItem.bottom);
420 				}
421 
422 				SelectClipRgn(dis->hDC, hrgn_org);
423 				if (hrgn_org) DeleteObject(hrgn_org);
424 				//SelectObject(dis->hDC, holdPen);
425 			} else if (calcWidthCol==col || calcWidthCol==COLUMNS) {
426 				int right = img_pos + IMAGE_WIDTH - _out_wrkr._spaceSize.cx;
427 
428 				if (right > _widths[col])
429 					_widths[col] = right;
430 			}
431 		} else	{
432 			img_pos = dis->rcItem.left;
433 		}
434 	} else {
435 		img_pos = dis->rcItem.left;
436 
437 		if (calcWidthCol==col || calcWidthCol==COLUMNS)
438 			_widths[col] = IMAGE_WIDTH;
439 	}
440 
441 	if (calcWidthCol == -1) {
442 		focusRect.left = img_pos -2;
443 
444 		if (attrs & FILE_ATTRIBUTE_COMPRESSED)
445 			textcolor = _clrCompressed;
446 		else
447 			textcolor = RGB(0,0,0);
448 
449 		if (dis->itemState & ODS_FOCUS) {
450 			textcolor = GetSysColor(COLOR_HIGHLIGHTTEXT);
451 			bkcolor = GetSysColor(COLOR_HIGHLIGHT);
452 		} else {
453 			bkcolor = GetSysColor(COLOR_WINDOW);
454 		}
455 
456 		HBRUSH hbrush = CreateSolidBrush(bkcolor);
457 		FillRect(dis->hDC, &focusRect, hbrush);
458 		DeleteObject(hbrush);
459 
460 		SetBkMode(dis->hDC, TRANSPARENT);
461 		SetTextColor(dis->hDC, textcolor);
462 
463 		cx = _widths[col];
464 
465 		if (cx && img!=IMG_NONE) {
466 			if (cx > IMAGE_WIDTH)
467 				cx = IMAGE_WIDTH;
468 
469 			if (entry->_icon_id > ICID_NONE)
470 				g_Globals._icon_cache.get_icon(entry->_icon_id).draw(dis->hDC, img_pos, dis->rcItem.top, cx, GetSystemMetrics(SM_CYSMICON), bkcolor, 0);
471 			else
472 				ImageList_DrawEx(_himl, img, dis->hDC,
473 								 img_pos, dis->rcItem.top, cx,
474 								 IMAGE_HEIGHT, bkcolor, CLR_DEFAULT, ILD_NORMAL);
475 		}
476 	}
477 
478 	if (!entry)
479 		return;
480 
481 	++col;
482 
483 	 // output file name
484 	if (calcWidthCol == -1)
485 		_out_wrkr.output_text(dis, _positions, col, entry->_display_name, 0);
486 	else if (calcWidthCol==col || calcWidthCol==COLUMNS)
487 		calc_width(dis, col, entry->_display_name);
488 	++col;
489 
490 	 // output type/class name
491 	if (visible_cols & COL_TYPE) {
492 		if (calcWidthCol == -1)
493 			_out_wrkr.output_text(dis, _positions, col, entry->_type_name? entry->_type_name: TEXT(""), 0);
494 		else if (calcWidthCol==col || calcWidthCol==COLUMNS)
495 			calc_width(dis, col, entry->_type_name? entry->_type_name: TEXT(""));
496 	}
497 	++col;
498 
499 	 // display file size
500 	if (visible_cols & COL_SIZE) {
501 		ULONGLONG size = ((ULONGLONG)entry->_data.nFileSizeHigh << 32) | entry->_data.nFileSizeLow;
502 
503 		_stprintf(buffer, TEXT("%") LONGLONGARG TEXT("d"), size);
504 
505 		if (calcWidthCol == -1)
506 			_out_wrkr.output_number(dis, _positions, col, buffer);
507 		else if (calcWidthCol==col || calcWidthCol==COLUMNS)
508 			calc_width(dis, col, buffer);	///@todo not in every case time enough
509 	}
510 	++col;
511 
512 	 // display file date
513 	if (visible_cols & (COL_DATE|COL_TIME)) {
514 		format_date(&entry->_data.ftCreationTime, buffer, visible_cols);
515 		if (calcWidthCol == -1)
516 			_out_wrkr.output_text(dis, _positions, col, buffer, 0);
517 		else if (calcWidthCol==col || calcWidthCol==COLUMNS)
518 			calc_width(dis, col, buffer);
519 		++col;
520 
521 		format_date(&entry->_data.ftLastAccessTime, buffer, visible_cols);
522 		if (calcWidthCol == -1)
523 			_out_wrkr.output_text(dis,_positions,  col, buffer, 0);
524 		else if (calcWidthCol==col || calcWidthCol==COLUMNS)
525 			calc_width(dis, col, buffer);
526 		++col;
527 
528 		format_date(&entry->_data.ftLastWriteTime, buffer, visible_cols);
529 		if (calcWidthCol == -1)
530 			_out_wrkr.output_text(dis, _positions, col, buffer, 0);
531 		else if (calcWidthCol==col || calcWidthCol==COLUMNS)
532 			calc_width(dis, col, buffer);
533 		++col;
534 	} else
535 		col += 3;
536 
537 	if (entry->_bhfi_valid) {
538 		ULONGLONG index = ((ULONGLONG)entry->_bhfi.nFileIndexHigh << 32) | entry->_bhfi.nFileIndexLow;
539 
540 		if (visible_cols & COL_INDEX) {
541 			_stprintf(buffer, TEXT("%") LONGLONGARG TEXT("X"), index);
542 
543 			if (calcWidthCol == -1)
544 				_out_wrkr.output_text(dis, _positions, col, buffer, DT_RIGHT);
545 			else if (calcWidthCol==col || calcWidthCol==COLUMNS)
546 				calc_width(dis, col, buffer);
547 
548 			++col;
549 		}
550 
551 		if (visible_cols & COL_LINKS) {
552 			wsprintf(buffer, TEXT("%d"), entry->_bhfi.nNumberOfLinks);
553 
554 			if (calcWidthCol == -1)
555 				_out_wrkr.output_text(dis, _positions, col, buffer, DT_RIGHT);
556 			else if (calcWidthCol==col || calcWidthCol==COLUMNS)
557 				calc_width(dis, col, buffer);
558 
559 			++col;
560 		}
561 	} else
562 		col += 2;
563 
564 	 // show file attributes
565 	if (visible_cols & COL_ATTRIBUTES) {
566 		lstrcpy(buffer, TEXT(" \t \t \t \t \t \t \t \t \t \t \t \t \t \t "));
567 
568 		if (attrs & FILE_ATTRIBUTE_NORMAL)					buffer[ 0] = 'N';
569 		else {
570 			if (attrs & FILE_ATTRIBUTE_READONLY)			buffer[ 2] = 'R';
571 			if (attrs & FILE_ATTRIBUTE_HIDDEN)				buffer[ 4] = 'H';
572 			if (attrs & FILE_ATTRIBUTE_SYSTEM)				buffer[ 6] = 'S';
573 			if (attrs & FILE_ATTRIBUTE_ARCHIVE) 			buffer[ 8] = 'A';
574 			if (attrs & FILE_ATTRIBUTE_COMPRESSED)			buffer[10] = 'C';
575 			if (attrs & FILE_ATTRIBUTE_DIRECTORY)			buffer[12] = 'D';
576 			if (attrs & FILE_ATTRIBUTE_ENCRYPTED)			buffer[14] = 'E';
577 			if (attrs & FILE_ATTRIBUTE_TEMPORARY)			buffer[16] = 'T';
578 			if (attrs & FILE_ATTRIBUTE_SPARSE_FILE) 		buffer[18] = 'P';
579 			if (attrs & FILE_ATTRIBUTE_REPARSE_POINT)		buffer[20] = 'Q';
580 			if (attrs & FILE_ATTRIBUTE_OFFLINE) 			buffer[22] = 'O';
581 			if (attrs & FILE_ATTRIBUTE_NOT_CONTENT_INDEXED) buffer[24] = 'X';
582 			if (attrs & ATTRIBUTE_EXECUTABLE)				buffer[26] = 'x';
583 			if (attrs & ATTRIBUTE_SYMBOLIC_LINK)			buffer[28] = 'L';
584 		}
585 
586 		if (calcWidthCol == -1)
587 			_out_wrkr.output_tabbed_text(dis, _positions, col, buffer);
588 		else if (calcWidthCol==col || calcWidthCol==COLUMNS)
589 			calc_tabbed_width(dis, col, buffer);
590 	}
591 	++col;
592 
593 /*TODO
594 	if (flags.security) {
595 		DWORD rights = get_access_mask();
596 
597 		tcscpy(buffer, TEXT(" \t \t \t  \t  \t \t \t  \t  \t \t \t "));
598 
599 		if (rights & FILE_READ_DATA)			buffer[ 0] = 'R';
600 		if (rights & FILE_WRITE_DATA)			buffer[ 2] = 'W';
601 		if (rights & FILE_APPEND_DATA)			buffer[ 4] = 'A';
602 		if (rights & FILE_READ_EA)				{buffer[6] = 'entry'; buffer[ 7] = 'R';}
603 		if (rights & FILE_WRITE_EA) 			{buffer[9] = 'entry'; buffer[10] = 'W';}
604 		if (rights & FILE_EXECUTE)				buffer[12] = 'X';
605 		if (rights & FILE_DELETE_CHILD) 		buffer[14] = 'D';
606 		if (rights & FILE_READ_ATTRIBUTES)		{buffer[16] = 'a'; buffer[17] = 'R';}
607 		if (rights & FILE_WRITE_ATTRIBUTES) 	{buffer[19] = 'a'; buffer[20] = 'W';}
608 		if (rights & WRITE_DAC) 				buffer[22] = 'C';
609 		if (rights & WRITE_OWNER)				buffer[24] = 'O';
610 		if (rights & SYNCHRONIZE)				buffer[26] = 'S';
611 
612 		output_text(dis, col++, buffer, DT_LEFT, 3, psize);
613 	}
614 
615 	if (flags.description) {
616 		get_description(buffer);
617 		output_text(dis, col++, buffer, 0, psize);
618 	}
619 */
620 	++col;
621 
622 	 // output content / symbolic link target / comment
623 	if (visible_cols & COL_CONTENT) {
624 		if (calcWidthCol == -1)
625 			_out_wrkr.output_text(dis, _positions, col, entry->_content? entry->_content: TEXT(""), 0);
626 		else if (calcWidthCol==col || calcWidthCol==COLUMNS)
627 			calc_width(dis, col, entry->_content? entry->_content: TEXT(""));
628 	}
629 }
630 
631 
632 void Pane::calc_width(LPDRAWITEMSTRUCT dis, int col, LPCTSTR str)
633 {
634 	RECT rt = {0, 0, 0, 0};
635 
636 	DrawText(dis->hDC, (LPTSTR)str, -1, &rt, DT_CALCRECT|DT_SINGLELINE|DT_NOPREFIX);
637 
638 	if (rt.right > _widths[col])
639 		_widths[col] = rt.right;
640 }
641 
642 void Pane::calc_tabbed_width(LPDRAWITEMSTRUCT dis, int col, LPCTSTR str)
643 {
644 	RECT rt = {0, 0, 0, 0};
645 
646 /*	DRAWTEXTPARAMS dtp = {sizeof(DRAWTEXTPARAMS), 2};
647 	DrawTextEx(dis->hDC, (LPTSTR)str, -1, &rt, DT_CALCRECT|DT_SINGLELINE|DT_NOPREFIX|DT_EXPANDTABS|DT_TABSTOP, &dtp);*/
648 
649 	DrawText(dis->hDC, (LPTSTR)str, -1, &rt, DT_CALCRECT|DT_SINGLELINE|DT_EXPANDTABS|DT_TABSTOP|(2<<8));
650 
651 	if (rt.right > _widths[col])
652 		_widths[col] = rt.right;
653 }
654 
655 
656  // insert listbox entries after index idx
657 
658 int Pane::insert_entries(Entry* dir, int idx)
659 {
660 	Entry* entry = dir;
661 
662 	if (!entry)
663 		return idx;
664 
665 	for(; entry; entry=entry->_next) {
666 #ifndef _LEFT_FILES
667 		if (_treePane &&
668 			!(entry->_data.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY) &&	// not a directory?
669 			!entry->_down)	// not a file with NTFS sub-streams?
670 			continue;
671 #endif
672 
673 		 // don't display entries "." and ".." in the left pane
674 		if (_treePane && (entry->_data.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)
675 				&& entry->_data.cFileName[0]==TEXT('.'))
676 			if (entry->_data.cFileName[1]==TEXT('\0') ||
677 				(entry->_data.cFileName[1]==TEXT('.') && entry->_data.cFileName[2]==TEXT('\0')))
678 				continue;
679 
680 		if (idx != -1)
681 			++idx;
682 
683 		ListBox_InsertItemData(_hwnd, idx, entry);
684 
685 		if (_treePane && entry->_expanded)
686 			idx = insert_entries(entry->_down, idx);
687 	}
688 
689 	return idx;
690 }
691 
692 
693 void Pane::set_header()
694 {
695 	HD_ITEM item;
696 	int scroll_pos = GetScrollPos(_hwnd, SB_HORZ);
697 	int i=0, x=0;
698 
699 	item.mask = HDI_WIDTH;
700 	item.cxy = 0;
701 
702 	for(; i<COLUMNS; i++) {
703 		if (x + _widths[i] >= scroll_pos)
704 			break;
705 
706 		x += _widths[i];
707 		Header_SetItem(_hwndHeader, i, &item);
708 	}
709 
710 	if (i < COLUMNS) {
711 		x += _widths[i];
712 		item.cxy = x - scroll_pos;
713 		Header_SetItem(_hwndHeader, i++, &item);
714 
715 		for(; i<COLUMNS; i++) {
716 			item.cxy = _widths[i];
717 			x += _widths[i];
718 			Header_SetItem(_hwndHeader, i, &item);
719 		}
720 	}
721 }
722 
723 
724  // calculate one prefered column width
725 
726 void Pane::calc_single_width(int col)
727 {
728 	HFONT hfontOld;
729 	int x, cx;
730 	int cnt;
731 	HDC hdc;
732 
733 	int entries = ListBox_GetCount(_hwnd);
734 
735 	_widths[col] = 0;
736 
737 	hdc = GetDC(_hwnd);
738 	hfontOld = SelectFont(hdc, _out_wrkr._hfont);
739 
740 	for(cnt=0; cnt<entries; cnt++) {
741 		Entry* entry = (Entry*) ListBox_GetItemData(_hwnd, cnt);
742 
743 		DRAWITEMSTRUCT dis;
744 
745 		dis.CtlType		  = 0;
746 		dis.CtlID		  = 0;
747 		dis.itemID		  = 0;
748 		dis.itemAction	  = 0;
749 		dis.itemState	  = 0;
750 		dis.hwndItem	  = _hwnd;
751 		dis.hDC			  = hdc;
752 		dis.rcItem.left	  = 0;
753 		dis.rcItem.top    = 0;
754 		dis.rcItem.right  = 0;
755 		dis.rcItem.bottom = 0;
756 		/*dis.itemData	  = 0; */
757 
758 		draw_item(&dis, entry, col);
759 	}
760 
761 	SelectObject(hdc, hfontOld);
762 	ReleaseDC(_hwnd, hdc);
763 
764 	cx = _widths[col];
765 
766 	if (cx) {
767 		cx += 3*_out_wrkr._spaceSize.cx;
768 
769 		if (cx < IMAGE_WIDTH)
770 			cx = IMAGE_WIDTH;
771 	}
772 
773 	_widths[col] = cx;
774 
775 	x = _positions[col] + cx;
776 
777 	for(; col<COLUMNS; col++) {
778 		_positions[col+1] = x;
779 		x += _widths[col];
780 	}
781 
782 	ListBox_SetHorizontalExtent(_hwnd, x);
783 }
784 
785 
786 int Pane::Notify(int id, NMHDR* pnmh)
787 {
788 	switch(pnmh->code) {
789 		case HDN_TRACK:
790 		case HDN_ENDTRACK: {
791 			HD_NOTIFY* phdn = (HD_NOTIFY*) pnmh;
792 			int idx = phdn->iItem;
793 			int dx = phdn->pitem->cxy - _widths[idx];
794 			int i;
795 
796 			ClientRect clnt(_hwnd);
797 
798 			 // move immediate to simulate HDS_FULLDRAG (for now [04/2000] not realy needed with WINELIB)
799 			Header_SetItem(_hwndHeader, idx, phdn->pitem);
800 
801 			_widths[idx] += dx;
802 
803 			for(i=idx; ++i<=COLUMNS; )
804 				_positions[i] += dx;
805 
806 			{
807 				int scroll_pos = GetScrollPos(_hwnd, SB_HORZ);
808 				RECT rt_scr;
809 				RECT rt_clip;
810 
811 				rt_scr.left   = _positions[idx+1]-scroll_pos;
812 				rt_scr.top    = 0;
813 				rt_scr.right  = clnt.right;
814 				rt_scr.bottom = clnt.bottom;
815 
816 				rt_clip.left   = _positions[idx]-scroll_pos;
817 				rt_clip.top    = 0;
818 				rt_clip.right  = clnt.right;
819 				rt_clip.bottom = clnt.bottom;
820 
821 				if (rt_scr.left < 0) rt_scr.left = 0;
822 				if (rt_clip.left < 0) rt_clip.left = 0;
823 
824 				ScrollWindowEx(_hwnd, dx, 0, &rt_scr, &rt_clip, 0, 0, SW_INVALIDATE);
825 
826 				rt_clip.right = _positions[idx+1];
827 				RedrawWindow(_hwnd, &rt_clip, 0, RDW_INVALIDATE|RDW_UPDATENOW);
828 
829 				if (pnmh->code == HDN_ENDTRACK) {
830 					ListBox_SetHorizontalExtent(_hwnd, _positions[COLUMNS]);
831 
832 					if (GetScrollPos(_hwnd, SB_HORZ) != scroll_pos)
833 						set_header();
834 				}
835 			}
836 
837 			return 0;
838 		}
839 
840 		case HDN_DIVIDERDBLCLICK: {
841 			HD_NOTIFY* phdn = (HD_NOTIFY*) pnmh;
842 			HD_ITEM item;
843 
844 			calc_single_width(phdn->iItem);
845 			item.mask = HDI_WIDTH;
846 			item.cxy = _widths[phdn->iItem];
847 
848 			Header_SetItem(_hwndHeader, phdn->iItem, &item);
849 			InvalidateRect(_hwnd, 0, TRUE);
850 			break;}
851 
852 		default:
853 			return super::Notify(id, pnmh);
854 	}
855 
856 	return 0;
857 }
858 
859 
860 OutputWorker::OutputWorker()
861 {
862 	_hfont = CreateFont(-MulDiv(8,GetDeviceCaps(WindowCanvas(0),LOGPIXELSY),72), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, TEXT("MS Sans Serif"));
863 }
864 
865 void OutputWorker::init_output(HWND hwnd)
866 {
867 	TCHAR b[16];
868 
869 	WindowCanvas canvas(hwnd);
870 
871 	if (GetNumberFormat(LOCALE_USER_DEFAULT, 0, TEXT("1000"), 0, b, 16) > 4)
872 		_num_sep = b[1];
873 	else
874 		_num_sep = TEXT('.');
875 
876 	FontSelection font(canvas, _hfont);
877 	GetTextExtentPoint32(canvas, TEXT(" "), 1, &_spaceSize);
878 }
879 
880 
881 void OutputWorker::output_text(LPDRAWITEMSTRUCT dis, int* positions, int col, LPCTSTR str, DWORD flags)
882 {
883 	int x = dis->rcItem.left;
884 	RECT rt;
885 
886 	rt.left   = x+positions[col]+_spaceSize.cx;
887 	rt.top	  = dis->rcItem.top;
888 	rt.right  = x+positions[col+1]-_spaceSize.cx;
889 	rt.bottom = dis->rcItem.bottom;
890 
891 	DrawText(dis->hDC, (LPTSTR)str, -1, &rt, DT_SINGLELINE|DT_NOPREFIX|flags);
892 }
893 
894 void OutputWorker::output_tabbed_text(LPDRAWITEMSTRUCT dis, int* positions, int col, LPCTSTR str)
895 {
896 	int x = dis->rcItem.left;
897 	RECT rt;
898 
899 	rt.left   = x+positions[col]+_spaceSize.cx;
900 	rt.top	  = dis->rcItem.top;
901 	rt.right  = x+positions[col+1]-_spaceSize.cx;
902 	rt.bottom = dis->rcItem.bottom;
903 
904 /*	DRAWTEXTPARAMS dtp = {sizeof(DRAWTEXTPARAMS), 2};
905 	DrawTextEx(dis->hDC, (LPTSTR)str, -1, &rt, DT_SINGLELINE|DT_NOPREFIX|DT_EXPANDTABS|DT_TABSTOP, &dtp);*/
906 
907 	DrawText(dis->hDC, (LPTSTR)str, -1, &rt, DT_SINGLELINE|DT_EXPANDTABS|DT_TABSTOP|(2<<8));
908 }
909 
910 void OutputWorker::output_number(LPDRAWITEMSTRUCT dis, int* positions, int col, LPCTSTR str)
911 {
912 	int x = dis->rcItem.left;
913 	RECT rt;
914 	LPCTSTR s = str;
915 	TCHAR b[128];
916 	LPTSTR d = b;
917 	int pos;
918 
919 	rt.left   = x+positions[col]+_spaceSize.cx;
920 	rt.top	  = dis->rcItem.top;
921 	rt.right  = x+positions[col+1]-_spaceSize.cx;
922 	rt.bottom = dis->rcItem.bottom;
923 
924 	if (*s)
925 		*d++ = *s++;
926 
927 	 // insert number separator characters
928 	pos = lstrlen(s) % 3;
929 
930 	while(*s)
931 		if (pos--)
932 			*d++ = *s++;
933 		else {
934 			*d++ = _num_sep;
935 			pos = 3;
936 		}
937 
938 	DrawText(dis->hDC, b, d-b, &rt, DT_RIGHT|DT_SINGLELINE|DT_NOPREFIX|DT_END_ELLIPSIS);
939 }
940 
941 
942 BOOL Pane::command(UINT cmd)
943 {
944 	switch(cmd) {
945 		case ID_VIEW_NAME:
946 			if (_visible_cols) {
947 				_visible_cols = 0;
948 				calc_widths(true);
949 				set_header();
950 				InvalidateRect(_hwnd, 0, TRUE);
951 				MenuInfo* menu_info = Frame_GetMenuInfo(GetParent(_hwnd));
952 				if (menu_info) {
953 					CheckMenuItem(menu_info->_hMenuView, ID_VIEW_NAME, MF_BYCOMMAND|MF_CHECKED);
954 					CheckMenuItem(menu_info->_hMenuView, ID_VIEW_ALL_ATTRIBUTES, MF_BYCOMMAND);
955 					CheckMenuItem(menu_info->_hMenuView, ID_VIEW_SELECTED_ATTRIBUTES, MF_BYCOMMAND);
956 				}
957 			}
958 			break;
959 
960 		case ID_VIEW_ALL_ATTRIBUTES:
961 			if (_visible_cols != COL_ALL) {
962 				_visible_cols = COL_ALL;
963 				calc_widths(true);
964 				set_header();
965 				InvalidateRect(_hwnd, 0, TRUE);
966 				MenuInfo* menu_info = Frame_GetMenuInfo(GetParent(_hwnd));
967 				if (menu_info) {
968 					CheckMenuItem(menu_info->_hMenuView, ID_VIEW_NAME, MF_BYCOMMAND);
969 					CheckMenuItem(menu_info->_hMenuView, ID_VIEW_ALL_ATTRIBUTES, MF_BYCOMMAND|MF_CHECKED);
970 					CheckMenuItem(menu_info->_hMenuView, ID_VIEW_SELECTED_ATTRIBUTES, MF_BYCOMMAND);
971 				}
972 			}
973 			break;
974 
975 		case ID_PREFERED_SIZES: {
976 			calc_widths(true);
977 			set_header();
978 			InvalidateRect(_hwnd, 0, TRUE);
979 			break;}
980 
981 		        /*@todo more command ids... */
982 
983 		default:
984 			return FALSE;
985 	}
986 
987 	return TRUE;
988 }
989 
990 MainFrameBase* Pane::get_frame()
991 {
992 	HWND owner = GetParent(_hwnd);
993 
994 	return (MainFrameBase*)owner;
995 }
996