winecfg: Change WINE_ERR to WINE_TRACE since there is no real error.
[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     char text[256];
122     RECT rect;
123
124     if (advanced)
125     {
126         state = SW_NORMAL;
127         LoadString(GetModuleHandle(NULL), IDS_HIDE_ADVANCED, text, 256);
128     }
129     else
130     {
131         state = SW_HIDE;
132         LoadString(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     SetWindowText(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     /* removeable media properties */
431     label = current_drive->label;
432     set_text(dialog, IDC_EDIT_LABEL, label);
433
434     /* set serial edit text */
435     serial = current_drive->serial;
436     set_text(dialog, IDC_EDIT_SERIAL, serial);
437
438     /* TODO: get the device here to put into the edit box */
439     device = "Not implemented yet";
440     set_text(dialog, IDC_EDIT_DEVICE, device);
441     device = NULL;
442
443     selection = IDC_RADIO_ASSIGN;
444     if ((type == DRIVE_CDROM) || (type == DRIVE_REMOVABLE))
445     {
446         if (device)
447         {
448             selection = IDC_RADIO_AUTODETECT;
449             enable_labelserial_box(dialog, BOX_MODE_CD_AUTODETECT);
450         }
451         else
452         {
453             selection = IDC_RADIO_ASSIGN;
454             enable_labelserial_box(dialog, BOX_MODE_CD_ASSIGN);
455         }
456     }
457     else
458     {
459         enable_labelserial_box(dialog, BOX_MODE_NORMAL);
460         selection = IDC_RADIO_ASSIGN;
461     }
462
463     CheckRadioButton(dialog, IDC_RADIO_AUTODETECT, IDC_RADIO_ASSIGN, selection);
464
465     updating_ui = FALSE;
466
467     return;
468 }
469
470 static void on_edit_changed(HWND dialog, WORD id)
471 {
472     if (updating_ui) return;
473
474     WINE_TRACE("edit id %d changed\n", id);
475
476     switch (id)
477     {
478         case IDC_EDIT_LABEL:
479         {
480             char *label;
481
482             label = get_text(dialog, id);
483             HeapFree(GetProcessHeap(), 0, current_drive->label);
484             current_drive->label = label ? label :  strdupA("");
485
486             WINE_TRACE("set label to %s\n", current_drive->label);
487
488             /* enable the apply button  */
489             SendMessage(GetParent(dialog), PSM_CHANGED, (WPARAM) dialog, 0);
490             break;
491         }
492
493         case IDC_EDIT_PATH:
494         {
495             char *path;
496
497             path = get_text(dialog, id);
498             HeapFree(GetProcessHeap(), 0, current_drive->unixpath);
499             current_drive->unixpath = path ? path : strdupA("drive_c");
500
501             WINE_TRACE("set path to %s\n", current_drive->unixpath);
502
503             lv_set_item_text(dialog, lv_get_curr_select(dialog), 1,
504                              current_drive->unixpath);
505
506             /* enable the apply button  */
507             SendMessage(GetParent(dialog), PSM_CHANGED, (WPARAM) dialog, 0);
508             break;
509         }
510
511         case IDC_EDIT_SERIAL:
512         {
513             char *serial;
514
515             serial = get_text(dialog, id);
516             HeapFree(GetProcessHeap(), 0, current_drive->serial);
517             current_drive->serial = serial ? serial : strdupA("");
518
519             WINE_TRACE("set serial to %s\n", current_drive->serial);
520
521             /* enable the apply button  */
522             SendMessage(GetParent(dialog), PSM_CHANGED, (WPARAM) dialog, 0);
523             break;
524         }
525
526         case IDC_EDIT_DEVICE:
527         {
528             char *device = get_text(dialog, id);
529             /* TODO: handle device if/when it makes sense to do so.... */
530             HeapFree(GetProcessHeap(), 0, device);
531             break;
532         }
533     }
534 }
535
536 static void get_etched_rect(HWND dialog, RECT *rect)
537 {
538     GetClientRect(dialog, rect);
539
540     /* these dimensions from the labelserial static in En.rc  */
541     rect->top = 265;
542     rect->bottom = 265;
543     rect->left += 25;
544     rect->right -= 25;
545 }
546
547 /* this just draws a nice line to separate the advanced gui from the n00b gui :) */
548 static void paint(HWND dialog)
549 {
550     PAINTSTRUCT ps;
551
552     BeginPaint(dialog, &ps);
553
554     if (advanced)
555     {
556         RECT rect;
557
558         get_etched_rect(dialog, &rect);
559
560         DrawEdge(ps.hdc, &rect, EDGE_ETCHED, BF_TOP);
561     }
562
563     EndPaint(dialog, &ps);
564 }
565
566 BOOL browse_for_unix_folder(HWND dialog, char *pszPath)
567 {
568     static WCHAR wszUnixRootDisplayName[] = 
569         { ':',':','{','C','C','7','0','2','E','B','2','-','7','D','C','5','-','1','1','D','9','-',
570           'C','6','8','7','-','0','0','0','4','2','3','8','A','0','1','C','D','}', 0 };
571     char pszChoosePath[256];
572     BROWSEINFOA bi = {
573         dialog,
574         NULL,
575         NULL,
576         pszChoosePath,
577         0,
578         NULL,
579         0,
580         0
581     };
582     IShellFolder *pDesktop;
583     LPITEMIDLIST pidlUnixRoot, pidlSelectedPath;
584     HRESULT hr;
585    
586     LoadString(GetModuleHandle(NULL), IDS_CHOOSE_PATH, pszChoosePath, 256);
587     
588     hr = SHGetDesktopFolder(&pDesktop);
589     if (!SUCCEEDED(hr)) return FALSE;
590
591     hr = IShellFolder_ParseDisplayName(pDesktop, NULL, NULL, wszUnixRootDisplayName, NULL, 
592                                        &pidlUnixRoot, NULL);
593     if (!SUCCEEDED(hr)) {
594         IShellFolder_Release(pDesktop);
595         return FALSE;
596     }
597
598     bi.pidlRoot = pidlUnixRoot;
599     pidlSelectedPath = SHBrowseForFolderA(&bi);
600     SHFree(pidlUnixRoot);
601     
602     if (pidlSelectedPath) {
603         STRRET strSelectedPath;
604         char *pszSelectedPath;
605         HRESULT hr;
606         
607         hr = IShellFolder_GetDisplayNameOf(pDesktop, pidlSelectedPath, SHGDN_FORPARSING, 
608                                            &strSelectedPath);
609         IShellFolder_Release(pDesktop);
610         if (!SUCCEEDED(hr)) {
611             SHFree(pidlSelectedPath);
612             return FALSE;
613         }
614
615         hr = StrRetToStr(&strSelectedPath, pidlSelectedPath, &pszSelectedPath);
616         SHFree(pidlSelectedPath);
617         if (!SUCCEEDED(hr)) return FALSE;
618
619         lstrcpy(pszPath, pszSelectedPath);
620         
621         CoTaskMemFree(pszSelectedPath);
622         return TRUE;
623     }
624     return FALSE;
625 }
626
627 static void init_listview_columns(HWND dialog)
628 {
629     LVCOLUMNW listColumn;
630     RECT viewRect;
631     int width;
632     WCHAR column[64];
633
634     GetClientRect(GetDlgItem(dialog, IDC_LIST_DRIVES), &viewRect);
635     width = (viewRect.right - viewRect.left) / 6 - 5;
636
637     LoadStringW (GetModuleHandle (NULL), IDS_COL_DRIVELETTER, column,
638         sizeof(column)/sizeof(column[0]));
639     listColumn.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM;
640     listColumn.pszText = column;
641     listColumn.cchTextMax = lstrlenW (listColumn.pszText);
642     listColumn.cx = width;
643
644     SendDlgItemMessageW (dialog, IDC_LIST_DRIVES, LVM_INSERTCOLUMNW, 0, (LPARAM) &listColumn);
645
646     LoadStringW (GetModuleHandle (NULL), IDS_COL_DRIVEMAPPING, column,
647         sizeof(column)/sizeof(column[0]));
648     listColumn.cx = viewRect.right - viewRect.left - width;
649     listColumn.pszText = column;
650     listColumn.cchTextMax = lstrlenW (listColumn.pszText);
651
652     SendDlgItemMessageW (dialog, IDC_LIST_DRIVES, LVM_INSERTCOLUMNW, 1, (LPARAM) &listColumn);
653 }
654
655 static void load_drive_options(HWND dialog)
656 {
657     if (!strcmp(get_reg_key(config_key, "", "ShowDotFiles", "N"), "Y"))
658         CheckDlgButton(dialog, IDC_SHOW_DOT_FILES, BST_CHECKED);
659 }
660
661 INT_PTR CALLBACK
662 DriveDlgProc (HWND dialog, UINT msg, WPARAM wParam, LPARAM lParam)
663 {
664     int item;
665     struct drive *drive;
666
667     switch (msg)
668     {
669         case WM_INITDIALOG:
670             init_listview_columns(dialog);
671             load_drives();
672             load_drive_options(dialog);
673
674             if (!drives[2].in_use)
675                 driveui_msgbox (dialog, IDS_NO_DRIVE_C, MB_OK | MB_ICONEXCLAMATION);
676
677             fill_drives_list(dialog);
678             update_controls(dialog);
679             /* put in non-advanced mode by default  */
680             set_advanced(dialog);
681             break;
682
683         case WM_SHOWWINDOW:
684             set_window_title(dialog);
685             break;
686
687         case WM_PAINT:
688             paint(dialog);
689             break;
690
691         case WM_COMMAND:
692             switch (HIWORD(wParam))
693             {
694                 case EN_CHANGE:
695                     on_edit_changed(dialog, LOWORD(wParam));
696                     break;
697
698                 case BN_CLICKED:
699                     switch (LOWORD(wParam))
700                     {
701                         case IDC_SHOW_DOT_FILES:
702                             on_options_click(dialog);
703                         break;
704                     }
705                     break;
706
707                 case CBN_SELCHANGE:
708                     SendMessage(GetParent(dialog), PSM_CHANGED, 0, 0);
709                     break;
710             }
711
712             switch (LOWORD(wParam))
713             {
714                 case IDC_BUTTON_ADD:
715                     if (HIWORD(wParam) != BN_CLICKED) break;
716                     on_add_click(dialog);
717                     break;
718
719                 case IDC_BUTTON_REMOVE:
720                     if (HIWORD(wParam) != BN_CLICKED) break;
721                     on_remove_click(dialog);
722                     break;
723
724                 case IDC_BUTTON_EDIT:
725                     if (HIWORD(wParam) != BN_CLICKED) break;
726                     item = SendMessage(GetDlgItem(dialog, IDC_LIST_DRIVES),  LB_GETCURSEL, 0, 0);
727                     drive = (struct drive *) SendMessage(GetDlgItem(dialog, IDC_LIST_DRIVES), LB_GETITEMDATA, item, 0);
728                     break;
729
730                 case IDC_BUTTON_AUTODETECT:
731                     autodetect_drives();
732                     fill_drives_list(dialog);
733                     SendMessage(GetParent(dialog), PSM_CHANGED, 0, 0);
734                     break;
735
736                 case IDC_BUTTON_SHOW_HIDE_ADVANCED:
737                     advanced = !advanced;
738                     set_advanced(dialog);
739                     break;
740
741                 case IDC_BUTTON_BROWSE_PATH:
742                 {
743                     char szTargetPath[FILENAME_MAX];
744                     if (browse_for_unix_folder(dialog, szTargetPath)) 
745                         set_text(dialog, IDC_EDIT_PATH, szTargetPath);
746                     break;
747                 }
748
749                 case IDC_RADIO_ASSIGN:
750                 {
751                     char *str;
752
753                     str = get_text(dialog, IDC_EDIT_LABEL);
754                     HeapFree(GetProcessHeap(), 0, current_drive->label);
755                     current_drive->label = str ? str : strdupA("");
756
757                     str = get_text(dialog, IDC_EDIT_SERIAL);
758                     HeapFree(GetProcessHeap(), 0, current_drive->serial);
759                     current_drive->serial = str ? str : strdupA("");
760
761                     /* TODO: we don't have a device at this point */
762
763                     enable_labelserial_box(dialog, BOX_MODE_CD_ASSIGN);
764
765                     break;
766                 }
767
768
769                 case IDC_COMBO_TYPE:
770                 {
771                     int mode = BOX_MODE_NORMAL;
772                     int selection;
773
774                     if (HIWORD(wParam) != CBN_SELCHANGE) break;
775
776                     selection = SendDlgItemMessage(dialog, IDC_COMBO_TYPE, CB_GETCURSEL, 0, 0);
777
778                     if (selection >= 0 &&
779                         (type_pairs[selection].sCode == DRIVE_CDROM ||
780                          type_pairs[selection].sCode == DRIVE_REMOVABLE))
781                     {
782                         if (IsDlgButtonChecked(dialog, IDC_RADIO_AUTODETECT))
783                             mode = BOX_MODE_CD_AUTODETECT;
784                         else
785                             mode = BOX_MODE_CD_ASSIGN;
786                     }
787
788                     enable_labelserial_box(dialog, mode);
789
790                     current_drive->type = type_pairs[selection].sCode;
791                     break;
792                 }
793
794             }
795             break;
796
797         case WM_NOTIFY:
798             switch (((LPNMHDR)lParam)->code)
799             {
800                 case PSN_KILLACTIVE:
801                     WINE_TRACE("PSN_KILLACTIVE\n");
802                     SetWindowLongPtr(dialog, DWLP_MSGRESULT, FALSE);
803                     break;
804                 case PSN_APPLY:
805                     apply_drive_changes();
806                     SetWindowLongPtr(dialog, DWLP_MSGRESULT, PSNRET_NOERROR);
807                     break;
808                 case PSN_SETACTIVE:
809                     break;
810                 case LVN_ITEMCHANGED:
811                 {
812                     LPNMLISTVIEW lpnm = (LPNMLISTVIEW)lParam;
813                     if (!(lpnm->uOldState & LVIS_SELECTED) &&
814                          (lpnm->uNewState & LVIS_SELECTED))
815                     update_controls(dialog);
816                     break;
817                 }
818             }
819             break;
820     }
821
822     return FALSE;
823 }