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