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