dsound: Clean up DSPROPERTY_EnumerateW.
[wine] / programs / winecfg / driveui.c
1 /*
2  * Drive management UI code
3  *
4  * Copyright 2003 Mark Westcott
5  * Copyright 2004 Chris Morgan
6  * Copyright 2003-2004 Mike Hearn
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21  *
22  */
23
24 #include <stdio.h>
25
26 #define WIN32_LEAN_AND_MEAN
27 #define COBJMACROS
28
29 #include <windows.h>
30 #include <shellapi.h>
31 #include <objbase.h>
32 #include <shlguid.h>
33 #include <shlwapi.h>
34 #include <shlobj.h>
35
36 #include <wine/unicode.h>
37 #include <wine/debug.h>
38
39 #include "winecfg.h"
40 #include "resource.h"
41
42 WINE_DEFAULT_DEBUG_CHANNEL(winecfg);
43
44 #define BOX_MODE_CD_ASSIGN 1
45 #define BOX_MODE_CD_AUTODETECT 2
46 #define BOX_MODE_NONE 3
47 #define BOX_MODE_NORMAL 4
48
49 static BOOL advanced = FALSE;
50 static BOOL updating_ui = FALSE;
51 static struct drive* current_drive;
52
53 static void get_etched_rect(HWND dialog, RECT *rect);
54 static void update_controls(HWND dialog);
55
56 static DWORD driveui_msgbox (HWND parent, UINT messageId, DWORD flags)
57 {
58   WCHAR* caption = load_string (IDS_WINECFG_TITLE);
59   WCHAR* text = load_string (messageId);
60   DWORD result = MessageBoxW (parent, text, caption, flags);
61   HeapFree (GetProcessHeap(), 0, caption);
62   HeapFree (GetProcessHeap(), 0, text);
63   return result;
64 }
65
66 /**** listview helper functions ****/
67
68 /* clears the item at index in the listview */
69 static void lv_clear_curr_select(HWND dialog, int index)
70 {
71     ListView_SetItemState(GetDlgItem(dialog, IDC_LIST_DRIVES), index, 0, LVIS_SELECTED);
72 }
73
74 /* selects the item at index in the listview */
75 static void lv_set_curr_select(HWND dialog, int index)
76 {
77     /* no more than one item can be selected in our listview */
78     lv_clear_curr_select(dialog, -1);
79     ListView_SetItemState(GetDlgItem(dialog, IDC_LIST_DRIVES), index, LVIS_SELECTED, LVIS_SELECTED);
80 }
81
82 /* returns the currently selected item in the listview */
83 static int lv_get_curr_select(HWND dialog)
84 {
85     return SendDlgItemMessage(dialog, IDC_LIST_DRIVES, LVM_GETNEXTITEM, -1, LVNI_SELECTED);
86 }
87
88 /* sets the item in the listview at item->iIndex */
89 static void lv_set_item(HWND dialog, LVITEMW *item)
90 {
91     SendDlgItemMessageW(dialog, IDC_LIST_DRIVES, LVM_SETITEMW, 0, (LPARAM) item);
92 }
93
94 /* sets specified item's text */
95 static void lv_set_item_text(HWND dialog, int item, int subItem, WCHAR *text)
96 {
97     LVITEMW lvItem;
98     if (item < 0 || subItem < 0) return;
99     lvItem.mask = LVIF_TEXT;
100     lvItem.iItem = item;
101     lvItem.iSubItem = subItem;
102     lvItem.pszText = text;
103     lvItem.cchTextMax = lstrlenW(lvItem.pszText);
104     lv_set_item(dialog, &lvItem);
105 }
106
107 /* inserts an item into the listview */
108 static void lv_insert_item(HWND dialog, LVITEMW *item)
109 {
110     SendDlgItemMessageW(dialog, IDC_LIST_DRIVES, LVM_INSERTITEMW, 0, (LPARAM) item);
111 }
112
113 /* retrieve the item at index item->iIndex */
114 static void lv_get_item(HWND dialog, LVITEMW *item)
115 {
116     SendDlgItemMessageW(dialog, IDC_LIST_DRIVES, LVM_GETITEMW, 0, (LPARAM) item);
117 }
118
119 static void set_advanced(HWND dialog)
120 {
121     int state;
122     WCHAR text[256];
123     RECT rect;
124
125     if (advanced)
126     {
127         state = SW_NORMAL;
128         LoadStringW(GetModuleHandle(NULL), IDS_HIDE_ADVANCED, text, 256);
129     }
130     else
131     {
132         state = SW_HIDE;
133         LoadStringW(GetModuleHandle(NULL), IDS_SHOW_ADVANCED, text, 256);
134     }
135
136     ShowWindow(GetDlgItem(dialog, IDC_RADIO_AUTODETECT), state);
137     ShowWindow(GetDlgItem(dialog, IDC_RADIO_ASSIGN), state);
138     ShowWindow(GetDlgItem(dialog, IDC_EDIT_LABEL), state);
139     ShowWindow(GetDlgItem(dialog, IDC_EDIT_DEVICE), state);
140     ShowWindow(GetDlgItem(dialog, IDC_STATIC_LABEL), state);
141     ShowWindow(GetDlgItem(dialog, IDC_BUTTON_BROWSE_DEVICE), state);
142     ShowWindow(GetDlgItem(dialog, IDC_EDIT_SERIAL), state);
143     ShowWindow(GetDlgItem(dialog, IDC_STATIC_SERIAL), state);
144     ShowWindow(GetDlgItem(dialog, IDC_LABELSERIAL_STATIC), state);
145     ShowWindow(GetDlgItem(dialog, IDC_COMBO_TYPE), state);
146     ShowWindow(GetDlgItem(dialog, IDC_STATIC_TYPE), state);
147
148     /* update the button text based on the state */
149     SetWindowTextW(GetDlgItem(dialog, IDC_BUTTON_SHOW_HIDE_ADVANCED), text);
150
151     /* redraw for the etched line */
152     get_etched_rect(dialog, &rect);
153     InflateRect(&rect, 5, 5);
154     InvalidateRect(dialog, &rect, TRUE);
155 }
156
157 struct drive_typemap {
158     unsigned int sCode;
159     UINT idDesc;
160 };
161
162 static const struct drive_typemap type_pairs[] = {
163   { DRIVE_UNKNOWN,    IDS_DRIVE_UNKNOWN   },
164   { DRIVE_FIXED,      IDS_DRIVE_FIXED     },
165   { DRIVE_REMOTE,     IDS_DRIVE_REMOTE    },
166   { DRIVE_REMOVABLE,  IDS_DRIVE_REMOVABLE },
167   { DRIVE_CDROM,      IDS_DRIVE_CDROM     }
168 };
169
170 #define DRIVE_TYPE_DEFAULT 0
171
172 static void enable_labelserial_box(HWND dialog, int mode)
173 {
174     WINE_TRACE("mode=%d\n", mode);
175
176     switch (mode)
177     {
178         case BOX_MODE_CD_ASSIGN:
179             enable(IDC_RADIO_ASSIGN);
180             disable(IDC_EDIT_DEVICE);
181             disable(IDC_BUTTON_BROWSE_DEVICE);
182             enable(IDC_EDIT_SERIAL);
183             enable(IDC_EDIT_LABEL);
184             enable(IDC_STATIC_SERIAL);
185             enable(IDC_STATIC_LABEL);
186             break;
187
188         case BOX_MODE_CD_AUTODETECT:
189             enable(IDC_RADIO_ASSIGN);
190             enable(IDC_EDIT_DEVICE);
191             enable(IDC_BUTTON_BROWSE_DEVICE);
192             disable(IDC_EDIT_SERIAL);
193             disable(IDC_EDIT_LABEL);
194             disable(IDC_STATIC_SERIAL);
195             disable(IDC_STATIC_LABEL);
196             break;
197
198         case BOX_MODE_NONE:
199             disable(IDC_RADIO_ASSIGN);
200             disable(IDC_EDIT_DEVICE);
201             disable(IDC_BUTTON_BROWSE_DEVICE);
202             disable(IDC_EDIT_SERIAL);
203             disable(IDC_EDIT_LABEL);
204             disable(IDC_STATIC_SERIAL);
205             disable(IDC_STATIC_LABEL);
206             break;
207
208         case BOX_MODE_NORMAL:
209             enable(IDC_RADIO_ASSIGN);
210             disable(IDC_EDIT_DEVICE);
211             disable(IDC_BUTTON_BROWSE_DEVICE);
212             enable(IDC_EDIT_SERIAL);
213             enable(IDC_EDIT_LABEL);
214             enable(IDC_STATIC_SERIAL);
215             enable(IDC_STATIC_LABEL);
216             break;
217     }
218 }
219
220 static int fill_drives_list(HWND dialog)
221 {
222     int count = 0;
223     BOOL drivec_present = FALSE;
224     int i;
225     int prevsel = -1;
226
227     WINE_TRACE("\n");
228
229     updating_ui = TRUE;
230
231     prevsel = lv_get_curr_select(dialog); 
232
233     /* Clear the listbox */
234     SendDlgItemMessage(dialog, IDC_LIST_DRIVES, LVM_DELETEALLITEMS, 0, 0);
235
236     for(i = 0; i < 26; i++)
237     {
238         LVITEMW item;
239         WCHAR *path;
240         char letter[4];
241
242         /* skip over any unused drives */
243         if (!drives[i].in_use)
244             continue;
245
246         if (drives[i].letter == 'C')
247             drivec_present = TRUE;
248
249         letter[0] = 'A' + i;
250         letter[1] = ':';
251         letter[2] = 0;
252
253         item.mask = LVIF_TEXT | LVIF_PARAM;
254         item.iItem = count;
255         item.iSubItem = 0;
256         item.pszText = strdupU2W(letter);
257         item.cchTextMax = lstrlenW(item.pszText);
258         item.lParam = (LPARAM) &drives[i];
259
260         lv_insert_item(dialog, &item);
261         HeapFree(GetProcessHeap(), 0, item.pszText);
262
263         path = strdupU2W(drives[i].unixpath);
264         lv_set_item_text(dialog, count, 1, path);
265         HeapFree(GetProcessHeap(), 0, path);
266
267         count++;
268     }
269
270     WINE_TRACE("loaded %d drives\n", count);
271
272     /* show the warning if there is no Drive C */
273     if (!drivec_present)
274         ShowWindow(GetDlgItem(dialog, IDS_DRIVE_NO_C), SW_NORMAL);
275     else
276         ShowWindow(GetDlgItem(dialog, IDS_DRIVE_NO_C), SW_HIDE);
277
278     lv_set_curr_select(dialog, prevsel == -1 ? 0 : prevsel);
279
280     updating_ui = FALSE;
281     return count;
282 }
283
284 static void on_options_click(HWND dialog)
285 {
286     if (IsDlgButtonChecked(dialog, IDC_SHOW_DOT_FILES) == BST_CHECKED)
287         set_reg_key(config_key, "", "ShowDotFiles", "Y");
288     else
289         set_reg_key(config_key, "", "ShowDotFiles", "N");
290
291     SendMessage(GetParent(dialog), PSM_CHANGED, 0, 0);
292 }
293
294 static INT_PTR CALLBACK drivechoose_dlgproc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
295 {
296     static int i, sel;
297     char c;
298     char drive[] = "X:";
299
300     switch(uMsg)
301     {
302     case WM_INITDIALOG:
303         {
304         ULONG mask = ~drive_available_mask(0); /* the mask is now which drives aren't available */
305         for( c = 'A'; c<= 'Z'; c++){
306             drive[0] = c;
307             if(!( mask & (1 << (c - 'A'))))
308                 SendDlgItemMessageA( hwndDlg, IDC_DRIVESA2Z, CB_ADDSTRING, 0, (LPARAM) drive);
309         }
310         drive[0] = lParam;
311         SendDlgItemMessageA( hwndDlg, IDC_DRIVESA2Z, CB_SELECTSTRING, 0, (LPARAM) drive);
312         return TRUE;
313         }
314     case WM_COMMAND:
315         if(HIWORD(wParam) != BN_CLICKED) break;
316         switch (LOWORD(wParam))
317         {
318         case IDOK:
319             i = SendDlgItemMessageA( hwndDlg, IDC_DRIVESA2Z, CB_GETCURSEL, 0, 0);
320             if( i != CB_ERR){
321                 SendDlgItemMessageA( hwndDlg, IDC_DRIVESA2Z, CB_GETLBTEXT, i, (LPARAM) drive);
322                 sel = drive[0];
323             } else
324                 sel = -1;
325             EndDialog(hwndDlg, sel);
326             return TRUE;
327         case IDCANCEL:
328             EndDialog(hwndDlg, -1);
329             return TRUE;
330         }
331     }
332     return FALSE;
333 }
334
335 static void on_add_click(HWND dialog)
336 {
337     /* we should allocate a drive letter automatically. We also need
338        some way to let the user choose the mapping point, for now we
339        will just force them to enter a path automatically, with / being
340        the default. In future we should be able to temporarily map /
341        then invoke the directory chooser dialog. */
342
343     char new = 'C'; /* we skip A and B, they are historically floppy drives */
344     ULONG mask = ~drive_available_mask(0); /* the mask is now which drives aren't available */
345     int i, c;
346
347     while (mask & (1 << (new - 'A')))
348     {
349         new++;
350         if (new > 'Z')
351         {
352             driveui_msgbox (dialog, IDS_DRIVE_LETTERS_EXCEEDED, MB_OK | MB_ICONEXCLAMATION);
353             return;
354         }
355     }
356
357
358     new = DialogBoxParam(0, MAKEINTRESOURCE(IDD_DRIVECHOOSE), dialog, drivechoose_dlgproc, new);
359
360     if( new == -1) return;
361
362     WINE_TRACE("selected drive letter %c\n", new);
363
364     if (new == 'C')
365     {
366         WCHAR label[64];
367         LoadStringW (GetModuleHandle (NULL), IDS_SYSTEM_DRIVE_LABEL, label,
368                      sizeof(label)/sizeof(label[0]));
369         add_drive(new, "../drive_c", NULL, label, 0, DRIVE_FIXED);
370     }
371     else add_drive(new, "/", NULL, NULL, 0, DRIVE_UNKNOWN);
372
373     fill_drives_list(dialog);
374
375     /* select the newly created drive */
376     mask = ~drive_available_mask(0);
377     c = 0;
378     for (i = 0; i < 26; i++)
379     {
380         if ('A' + i == new) break;
381         if ((1 << i) & mask) c++;
382     }
383     lv_set_curr_select(dialog, c);
384
385     SetFocus(GetDlgItem(dialog, IDC_LIST_DRIVES));
386
387     update_controls(dialog);
388     SendMessage(GetParent(dialog), PSM_CHANGED, (WPARAM) dialog, 0);
389 }
390
391 static void on_remove_click(HWND dialog)
392 {
393     int itemIndex;
394     struct drive *drive;
395     LVITEMW item;
396
397     itemIndex = lv_get_curr_select(dialog);
398     if (itemIndex == -1) return; /* no selection */
399
400     item.mask = LVIF_PARAM;
401     item.iItem = itemIndex;
402     item.iSubItem = 0;
403
404     lv_get_item(dialog, &item);
405
406     drive = (struct drive *) item.lParam;
407
408     WINE_TRACE("unixpath: %s\n", drive->unixpath);
409
410     if (drive->letter == 'C')
411     {
412         DWORD result = driveui_msgbox (dialog, IDS_CONFIRM_DELETE_C, MB_YESNO | MB_ICONEXCLAMATION);
413         if (result == IDNO) return;
414     }
415
416     delete_drive(drive);
417
418     fill_drives_list(dialog);
419
420     itemIndex = itemIndex - 1;
421     if (itemIndex < 0) itemIndex = 0;
422     lv_set_curr_select(dialog, itemIndex);   /* previous item */
423
424     SetFocus(GetDlgItem(dialog, IDC_LIST_DRIVES));
425
426     update_controls(dialog);
427     SendMessage(GetParent(dialog), PSM_CHANGED, (WPARAM) dialog, 0);
428 }
429
430 static void update_controls(HWND dialog)
431 {
432     static const WCHAR emptyW[1];
433     WCHAR *path;
434     unsigned int type;
435     char serial[16];
436     const char *device;
437     int i, selection = -1;
438     LVITEMW item;
439
440     updating_ui = TRUE;
441
442     i = lv_get_curr_select(dialog);
443     if (i == -1)
444     {
445         /* no selection? let's select something for the user. this will re-enter */
446         lv_set_curr_select(dialog, i);
447         return;
448     }
449
450     item.mask = LVIF_PARAM;
451     item.iItem = i;
452     item.iSubItem = 0;
453
454     lv_get_item(dialog, &item);
455     current_drive = (struct drive *) item.lParam;
456
457     WINE_TRACE("Updating sheet for drive %c\n", current_drive->letter);
458
459     /* path */
460     WINE_TRACE("set path control text to '%s'\n", current_drive->unixpath);
461     path = strdupU2W(current_drive->unixpath);
462     set_textW(dialog, IDC_EDIT_PATH, path);
463     HeapFree(GetProcessHeap(), 0, path);
464
465     /* drive type */
466     type = current_drive->type;
467     SendDlgItemMessage(dialog, IDC_COMBO_TYPE, CB_RESETCONTENT, 0, 0);
468
469     for (i = 0; i < sizeof(type_pairs) / sizeof(struct drive_typemap); i++)
470     {
471         WCHAR driveDesc[64];
472         LoadStringW (GetModuleHandle (NULL), type_pairs[i].idDesc, driveDesc,
473             sizeof(driveDesc)/sizeof(driveDesc[0]));
474         SendDlgItemMessageW (dialog, IDC_COMBO_TYPE, CB_ADDSTRING, 0, (LPARAM)driveDesc);
475
476         if (type_pairs[i].sCode ==  type)
477         {
478             selection = i;
479         }
480     }
481
482     if (selection == -1) selection = DRIVE_TYPE_DEFAULT;
483     SendDlgItemMessage(dialog, IDC_COMBO_TYPE, CB_SETCURSEL, selection, 0);
484
485     EnableWindow( GetDlgItem( dialog, IDC_BUTTON_REMOVE ), (current_drive->letter != 'C') );
486     EnableWindow( GetDlgItem( dialog, IDC_EDIT_PATH ), (current_drive->letter != 'C') );
487     EnableWindow( GetDlgItem( dialog, IDC_BUTTON_BROWSE_PATH ), (current_drive->letter != 'C') );
488     EnableWindow( GetDlgItem( dialog, IDC_COMBO_TYPE ), (current_drive->letter != 'C') );
489
490     /* removeable media properties */
491     set_textW(dialog, IDC_EDIT_LABEL, current_drive->label ? current_drive->label : emptyW);
492
493     /* set serial edit text */
494     sprintf( serial, "%X", current_drive->serial );
495     set_text(dialog, IDC_EDIT_SERIAL, serial);
496
497     /* TODO: get the device here to put into the edit box */
498     device = "Not implemented yet";
499     set_text(dialog, IDC_EDIT_DEVICE, device);
500     device = NULL;
501
502     selection = IDC_RADIO_ASSIGN;
503     if ((type == DRIVE_CDROM) || (type == DRIVE_REMOVABLE))
504     {
505         if (device)
506         {
507             selection = IDC_RADIO_AUTODETECT;
508             enable_labelserial_box(dialog, BOX_MODE_CD_AUTODETECT);
509         }
510         else
511         {
512             selection = IDC_RADIO_ASSIGN;
513             enable_labelserial_box(dialog, BOX_MODE_CD_ASSIGN);
514         }
515     }
516     else
517     {
518         enable_labelserial_box(dialog, BOX_MODE_NORMAL);
519         selection = IDC_RADIO_ASSIGN;
520     }
521
522     CheckRadioButton(dialog, IDC_RADIO_AUTODETECT, IDC_RADIO_ASSIGN, selection);
523
524     updating_ui = FALSE;
525
526     return;
527 }
528
529 static void on_edit_changed(HWND dialog, WORD id)
530 {
531     if (updating_ui) return;
532
533     WINE_TRACE("edit id %d changed\n", id);
534
535     switch (id)
536     {
537         case IDC_EDIT_LABEL:
538         {
539             WCHAR *label = get_textW(dialog, id);
540             HeapFree(GetProcessHeap(), 0, current_drive->label);
541             current_drive->label = label;
542             current_drive->modified = TRUE;
543
544             WINE_TRACE("set label to %s\n", wine_dbgstr_w(current_drive->label));
545
546             /* enable the apply button  */
547             SendMessage(GetParent(dialog), PSM_CHANGED, (WPARAM) dialog, 0);
548             break;
549         }
550
551         case IDC_EDIT_PATH:
552         {
553             WCHAR *wpath;
554             char *path;
555             int lenW;
556
557             wpath = get_textW(dialog, id);
558             if( (lenW = WideCharToMultiByte(CP_UNIXCP, 0, wpath, -1, NULL, 0, NULL, NULL)) )
559             {
560                 path = HeapAlloc(GetProcessHeap(), 0, lenW);
561                 WideCharToMultiByte(CP_UNIXCP, 0, wpath, -1, path, lenW, NULL, NULL);
562             }
563             else
564             {
565                 path = NULL;
566                 wpath = strdupU2W("drive_c");
567             }
568
569             HeapFree(GetProcessHeap(), 0, current_drive->unixpath);
570             current_drive->unixpath = path ? path : strdupA("drive_c");
571             current_drive->modified = TRUE;
572
573             WINE_TRACE("set path to %s\n", current_drive->unixpath);
574
575             lv_set_item_text(dialog, lv_get_curr_select(dialog), 1,
576                              wpath);
577             HeapFree(GetProcessHeap(), 0, wpath);
578
579             /* enable the apply button  */
580             SendMessage(GetParent(dialog), PSM_CHANGED, (WPARAM) dialog, 0);
581             break;
582         }
583
584         case IDC_EDIT_SERIAL:
585         {
586             char *serial;
587
588             serial = get_text(dialog, id);
589             current_drive->serial = serial ? strtoul( serial, NULL, 16 ) : 0;
590             HeapFree(GetProcessHeap(), 0, serial);
591             current_drive->modified = TRUE;
592
593             WINE_TRACE("set serial to %08X\n", current_drive->serial);
594
595             /* enable the apply button  */
596             SendMessage(GetParent(dialog), PSM_CHANGED, (WPARAM) dialog, 0);
597             break;
598         }
599
600         case IDC_EDIT_DEVICE:
601         {
602             char *device = get_text(dialog, id);
603             /* TODO: handle device if/when it makes sense to do so.... */
604             HeapFree(GetProcessHeap(), 0, device);
605             break;
606         }
607     }
608 }
609
610 static void get_etched_rect(HWND dialog, RECT *rect)
611 {
612     GetClientRect(dialog, rect);
613
614     /* these dimensions from the labelserial static in En.rc  */
615     rect->top = 265;
616     rect->bottom = 265;
617     rect->left += 25;
618     rect->right -= 25;
619 }
620
621 /* this just draws a nice line to separate the advanced gui from the n00b gui :) */
622 static void paint(HWND dialog)
623 {
624     PAINTSTRUCT ps;
625
626     BeginPaint(dialog, &ps);
627
628     if (advanced)
629     {
630         RECT rect;
631
632         get_etched_rect(dialog, &rect);
633
634         DrawEdge(ps.hdc, &rect, EDGE_ETCHED, BF_TOP);
635     }
636
637     EndPaint(dialog, &ps);
638 }
639
640 BOOL browse_for_unix_folder(HWND dialog, WCHAR *pszPath)
641 {
642     static WCHAR wszUnixRootDisplayName[] = 
643         { ':',':','{','C','C','7','0','2','E','B','2','-','7','D','C','5','-','1','1','D','9','-',
644           'C','6','8','7','-','0','0','0','4','2','3','8','A','0','1','C','D','}', 0 };
645     WCHAR pszChoosePath[FILENAME_MAX];
646     BROWSEINFOW bi = {
647         dialog,
648         NULL,
649         NULL,
650         pszChoosePath,
651         0,
652         NULL,
653         0,
654         0
655     };
656     IShellFolder *pDesktop;
657     LPITEMIDLIST pidlUnixRoot, pidlSelectedPath;
658     HRESULT hr;
659    
660     LoadStringW(GetModuleHandle(NULL), IDS_CHOOSE_PATH, pszChoosePath, FILENAME_MAX);
661     
662     hr = SHGetDesktopFolder(&pDesktop);
663     if (FAILED(hr)) return FALSE;
664
665     hr = IShellFolder_ParseDisplayName(pDesktop, NULL, NULL, wszUnixRootDisplayName, NULL, 
666                                        &pidlUnixRoot, NULL);
667     if (FAILED(hr)) {
668         IShellFolder_Release(pDesktop);
669         return FALSE;
670     }
671
672     bi.pidlRoot = pidlUnixRoot;
673     pidlSelectedPath = SHBrowseForFolderW(&bi);
674     SHFree(pidlUnixRoot);
675     
676     if (pidlSelectedPath) {
677         STRRET strSelectedPath;
678         WCHAR *pszSelectedPath;
679         HRESULT hr;
680         
681         hr = IShellFolder_GetDisplayNameOf(pDesktop, pidlSelectedPath, SHGDN_FORPARSING, 
682                                            &strSelectedPath);
683         IShellFolder_Release(pDesktop);
684         if (FAILED(hr)) {
685             SHFree(pidlSelectedPath);
686             return FALSE;
687         }
688
689         hr = StrRetToStrW(&strSelectedPath, pidlSelectedPath, &pszSelectedPath);
690         SHFree(pidlSelectedPath);
691         if (FAILED(hr)) return FALSE;
692
693         lstrcpyW(pszPath, pszSelectedPath);
694         
695         CoTaskMemFree(pszSelectedPath);
696         return TRUE;
697     }
698     return FALSE;
699 }
700
701 static void init_listview_columns(HWND dialog)
702 {
703     LVCOLUMNW listColumn;
704     RECT viewRect;
705     int width;
706     WCHAR column[64];
707
708     GetClientRect(GetDlgItem(dialog, IDC_LIST_DRIVES), &viewRect);
709     width = (viewRect.right - viewRect.left) / 6 - 5;
710
711     LoadStringW (GetModuleHandle (NULL), IDS_COL_DRIVELETTER, column,
712         sizeof(column)/sizeof(column[0]));
713     listColumn.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM;
714     listColumn.pszText = column;
715     listColumn.cchTextMax = lstrlenW (listColumn.pszText);
716     listColumn.cx = width;
717
718     SendDlgItemMessageW (dialog, IDC_LIST_DRIVES, LVM_INSERTCOLUMNW, 0, (LPARAM) &listColumn);
719
720     LoadStringW (GetModuleHandle (NULL), IDS_COL_DRIVEMAPPING, column,
721         sizeof(column)/sizeof(column[0]));
722     listColumn.cx = viewRect.right - viewRect.left - width;
723     listColumn.pszText = column;
724     listColumn.cchTextMax = lstrlenW (listColumn.pszText);
725
726     SendDlgItemMessageW (dialog, IDC_LIST_DRIVES, LVM_INSERTCOLUMNW, 1, (LPARAM) &listColumn);
727 }
728
729 static void load_drive_options(HWND dialog)
730 {
731     if (!strcmp(get_reg_key(config_key, "", "ShowDotFiles", "N"), "Y"))
732         CheckDlgButton(dialog, IDC_SHOW_DOT_FILES, BST_CHECKED);
733 }
734
735 INT_PTR CALLBACK
736 DriveDlgProc (HWND dialog, UINT msg, WPARAM wParam, LPARAM lParam)
737 {
738     int item;
739     struct drive *drive;
740
741     switch (msg)
742     {
743         case WM_INITDIALOG:
744             init_listview_columns(dialog);
745             if (!load_drives())
746             {
747                 ShowWindow( GetDlgItem( dialog, IDC_STATIC_MOUNTMGR_ERROR ), SW_SHOW );
748                 ShowWindow( GetDlgItem( dialog, IDC_LIST_DRIVES ), SW_HIDE );
749                 ShowWindow( GetDlgItem( dialog, IDC_BUTTON_ADD ), SW_HIDE );
750                 ShowWindow( GetDlgItem( dialog, IDC_BUTTON_REMOVE ), SW_HIDE );
751                 ShowWindow( GetDlgItem( dialog, IDC_BUTTON_AUTODETECT ), SW_HIDE );
752                 ShowWindow( GetDlgItem( dialog, IDC_STATIC_PATH ), SW_HIDE );
753                 ShowWindow( GetDlgItem( dialog, IDC_EDIT_PATH ), SW_HIDE );
754                 ShowWindow( GetDlgItem( dialog, IDC_BUTTON_BROWSE_PATH ), SW_HIDE );
755                 ShowWindow( GetDlgItem( dialog, IDC_COMBO_TYPE ), SW_HIDE );
756                 ShowWindow( GetDlgItem( dialog, IDC_BUTTON_SHOW_HIDE_ADVANCED ), SW_HIDE );
757                 set_advanced(dialog);
758                 break;
759             }
760             ShowWindow( GetDlgItem( dialog, IDC_STATIC_MOUNTMGR_ERROR ), SW_HIDE );
761             load_drive_options(dialog);
762
763             if (!drives[2].in_use)
764                 driveui_msgbox (dialog, IDS_NO_DRIVE_C, MB_OK | MB_ICONEXCLAMATION);
765
766             fill_drives_list(dialog);
767             update_controls(dialog);
768             /* put in non-advanced mode by default  */
769             set_advanced(dialog);
770             break;
771
772         case WM_SHOWWINDOW:
773             set_window_title(dialog);
774             break;
775
776         case WM_PAINT:
777             paint(dialog);
778             break;
779
780         case WM_COMMAND:
781             switch (HIWORD(wParam))
782             {
783                 case EN_CHANGE:
784                     on_edit_changed(dialog, LOWORD(wParam));
785                     break;
786
787                 case BN_CLICKED:
788                     switch (LOWORD(wParam))
789                     {
790                         case IDC_SHOW_DOT_FILES:
791                             on_options_click(dialog);
792                         break;
793                     }
794                     break;
795
796                 case CBN_SELCHANGE:
797                     SendMessage(GetParent(dialog), PSM_CHANGED, 0, 0);
798                     break;
799             }
800
801             switch (LOWORD(wParam))
802             {
803                 case IDC_BUTTON_ADD:
804                     if (HIWORD(wParam) != BN_CLICKED) break;
805                     on_add_click(dialog);
806                     break;
807
808                 case IDC_BUTTON_REMOVE:
809                     if (HIWORD(wParam) != BN_CLICKED) break;
810                     on_remove_click(dialog);
811                     break;
812
813                 case IDC_BUTTON_EDIT:
814                     if (HIWORD(wParam) != BN_CLICKED) break;
815                     item = SendMessage(GetDlgItem(dialog, IDC_LIST_DRIVES),  LB_GETCURSEL, 0, 0);
816                     drive = (struct drive *) SendMessage(GetDlgItem(dialog, IDC_LIST_DRIVES), LB_GETITEMDATA, item, 0);
817                     break;
818
819                 case IDC_BUTTON_AUTODETECT:
820                     autodetect_drives();
821                     fill_drives_list(dialog);
822                     SendMessage(GetParent(dialog), PSM_CHANGED, 0, 0);
823                     break;
824
825                 case IDC_BUTTON_SHOW_HIDE_ADVANCED:
826                     advanced = !advanced;
827                     set_advanced(dialog);
828                     break;
829
830                 case IDC_BUTTON_BROWSE_PATH:
831                 {
832                     WCHAR szTargetPath[FILENAME_MAX];
833                     if (browse_for_unix_folder(dialog, szTargetPath)) 
834                         set_textW(dialog, IDC_EDIT_PATH, szTargetPath);
835                     break;
836                 }
837
838                 case IDC_RADIO_ASSIGN:
839                 {
840                     WCHAR *str = get_textW(dialog, IDC_EDIT_LABEL);
841                     HeapFree(GetProcessHeap(), 0, current_drive->label);
842                     current_drive->label = str;
843
844                     str = get_textW(dialog, IDC_EDIT_SERIAL);
845                     current_drive->serial = str ? strtoulW( str, NULL, 16 ) : 0;
846                     HeapFree(GetProcessHeap(), 0, str);
847                     current_drive->modified = TRUE;
848
849                     /* TODO: we don't have a device at this point */
850
851                     enable_labelserial_box(dialog, BOX_MODE_CD_ASSIGN);
852
853                     break;
854                 }
855
856
857                 case IDC_COMBO_TYPE:
858                 {
859                     int mode = BOX_MODE_NORMAL;
860                     int selection;
861
862                     if (HIWORD(wParam) != CBN_SELCHANGE) break;
863
864                     selection = SendDlgItemMessage(dialog, IDC_COMBO_TYPE, CB_GETCURSEL, 0, 0);
865
866                     if (selection >= 0 &&
867                         (type_pairs[selection].sCode == DRIVE_CDROM ||
868                          type_pairs[selection].sCode == DRIVE_REMOVABLE))
869                     {
870                         if (IsDlgButtonChecked(dialog, IDC_RADIO_AUTODETECT))
871                             mode = BOX_MODE_CD_AUTODETECT;
872                         else
873                             mode = BOX_MODE_CD_ASSIGN;
874                     }
875
876                     enable_labelserial_box(dialog, mode);
877
878                     current_drive->type = type_pairs[selection].sCode;
879                     current_drive->modified = TRUE;
880                     break;
881                 }
882
883             }
884             break;
885
886         case WM_NOTIFY:
887             switch (((LPNMHDR)lParam)->code)
888             {
889                 case PSN_KILLACTIVE:
890                     WINE_TRACE("PSN_KILLACTIVE\n");
891                     SetWindowLongPtr(dialog, DWLP_MSGRESULT, FALSE);
892                     break;
893                 case PSN_APPLY:
894                     apply_drive_changes();
895                     SetWindowLongPtr(dialog, DWLP_MSGRESULT, PSNRET_NOERROR);
896                     break;
897                 case PSN_SETACTIVE:
898                     break;
899                 case LVN_ITEMCHANGED:
900                 {
901                     LPNMLISTVIEW lpnm = (LPNMLISTVIEW)lParam;
902                     if (!(lpnm->uOldState & LVIS_SELECTED) &&
903                          (lpnm->uNewState & LVIS_SELECTED))
904                     update_controls(dialog);
905                     break;
906                 }
907             }
908             break;
909     }
910
911     return FALSE;
912 }