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