Added RegOpenCurrentUser prototype.
[wine/multimedia.git] / programs / winecfg / drive.c
bloba22b151efbcb0a2e11e12c4d978d3e62a3415115
1 /*
2 * Drive management UI code
4 * Copyright 2003 Mark Westcott
5 * Copyright 2003 Mike Hearn
6 * Copyright 2004 Chris Morgan
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.
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.
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
24 /* TODO: */
25 /* - Support for devices(not sure what this means) */
26 /* - Various autodetections */
28 #include <assert.h>
29 #include <stdarg.h>
30 #include <stdlib.h>
31 #include <stdio.h>
32 #include <string.h>
34 #include <windef.h>
35 #include <winbase.h>
36 #include <winreg.h>
37 #include <wine/debug.h>
38 #include <shellapi.h>
39 #include <objbase.h>
40 #include <shlguid.h>
41 #include <shlwapi.h>
42 #include <shlobj.h>
44 #include "winecfg.h"
45 #include "resource.h"
48 WINE_DEFAULT_DEBUG_CHANNEL(winecfg);
50 typedef struct drive_entry_s
52 char letter;
53 char *unixpath;
54 char *label;
55 char *serial;
56 uint type;
58 BOOL in_use;
59 } drive_entry_t;
61 static BOOL updatingUI = FALSE;
62 static drive_entry_t* editDriveEntry;
63 static int lastSel = 0; /* the last drive selected in the property sheet */
64 drive_entry_t drives[26]; /* one for each drive letter */
66 int getDrive(char letter)
68 return (toupper(letter) - 'A');
71 BOOL addDrive(char letter, char *targetpath, char *label, char *serial, uint type)
73 int driveIndex = getDrive(letter);
75 if(drives[driveIndex].in_use)
76 return FALSE;
78 WINE_TRACE("letter == '%c', unixpath == '%s', label == '%s', serial == '%s', type == %d\n",
79 letter, targetpath, label, serial, type);
81 drives[driveIndex].letter = toupper(letter);
82 drives[driveIndex].unixpath = strdup(targetpath);
83 drives[driveIndex].label = strdup(label);
84 drives[driveIndex].serial = strdup(serial);
85 drives[driveIndex].type = type;
86 drives[driveIndex].in_use = TRUE;
88 return TRUE;
91 /* frees up the memory associated with a drive and returns the */
92 /* pNext of the given drive entry */
93 void freeDrive(drive_entry_t *pDrive)
95 free(pDrive->unixpath);
96 free(pDrive->label);
97 free(pDrive->serial);
99 pDrive->in_use = FALSE;
102 void setDriveLabel(drive_entry_t *pDrive, char *label)
104 WINE_TRACE("pDrive->letter '%c', label = '%s'\n", pDrive->letter, label);
105 free(pDrive->label);
106 pDrive->label = strdup(label);
109 void setDriveSerial(drive_entry_t *pDrive, char *serial)
111 WINE_TRACE("pDrive->letter '%c', serial = '%s'\n", pDrive->letter, serial);
112 free(pDrive->serial);
113 pDrive->serial = strdup(serial);
116 void setDrivePath(drive_entry_t *pDrive, char *path)
118 WINE_TRACE("pDrive->letter '%c', path = '%s'\n", pDrive->letter, path);
119 free(pDrive->unixpath);
120 pDrive->unixpath = strdup(path);
123 BOOL copyDrive(drive_entry_t *pSrc, drive_entry_t *pDst)
125 if(pDst->in_use)
127 WINE_TRACE("pDst already in use\n");
128 return FALSE;
131 if(!pSrc->unixpath) WINE_TRACE("!pSrc->unixpath\n");
132 if(!pSrc->label) WINE_TRACE("!pSrc->label\n");
133 if(!pSrc->serial) WINE_TRACE("!pSrc->serial\n");
135 pDst->unixpath = strdup(pSrc->unixpath);
136 pDst->label = strdup(pSrc->label);
137 pDst->serial = strdup(pSrc->serial);
138 pDst->type = pSrc->type;
139 pDst->in_use = TRUE;
141 return TRUE;
144 BOOL moveDrive(drive_entry_t *pSrc, drive_entry_t *pDst)
146 WINE_TRACE("pSrc->letter == %c, pDst->letter == %c\n", pSrc->letter, pDst->letter);
148 if(!copyDrive(pSrc, pDst))
150 WINE_TRACE("copyDrive failed\n");
151 return FALSE;
154 freeDrive(pSrc);
155 return TRUE;
158 int refreshDriveDlg (HWND dialog)
160 int driveCount = 0;
161 int doesDriveCExist = FALSE;
162 int i;
164 WINE_TRACE("\n");
166 updatingUI = TRUE;
168 /* Clear the listbox */
169 SendMessageA(GetDlgItem(dialog, IDC_LIST_DRIVES), LB_RESETCONTENT, 0, 0);
171 for(i = 0; i < 26; i++)
173 char *title = 0;
174 int titleLen;
175 int itemIndex;
177 /* skip over any unused drives */
178 if(!drives[i].in_use)
179 continue;
181 if(drives[i].letter == 'C')
182 doesDriveCExist = TRUE;
184 titleLen = snprintf(title, 0, "Drive %c:\\ %s", 'A' + i,
185 drives[i].unixpath);
186 titleLen++; /* add a byte for the trailing null */
188 title = malloc(titleLen);
190 /* the %s in the item label will be replaced by the drive letter, so -1, then
191 -2 for the second %s which will be expanded to the label, finally + 1 for terminating #0 */
192 snprintf(title, titleLen, "Drive %c:\\ %s", 'A' + i,
193 drives[i].unixpath);
195 WINE_TRACE("title is '%s'\n", title);
197 /* the first SendMessage call adds the string and returns the index, the second associates that index with it */
198 itemIndex = SendMessageA(GetDlgItem(dialog, IDC_LIST_DRIVES), LB_ADDSTRING ,(WPARAM) -1, (LPARAM) title);
199 SendMessageA(GetDlgItem(dialog, IDC_LIST_DRIVES), LB_SETITEMDATA, itemIndex, (LPARAM) &drives[i]);
201 free(title);
202 driveCount++;
205 WINE_TRACE("loaded %d drives\n", driveCount);
206 SendDlgItemMessage(dialog, IDC_LIST_DRIVES, LB_SETSEL, TRUE, lastSel);
208 /* show the warning if there is no Drive C */
209 if (!doesDriveCExist)
210 ShowWindow(GetDlgItem(dialog, IDS_DRIVE_NO_C), SW_NORMAL);
211 else
212 ShowWindow(GetDlgItem(dialog, IDS_DRIVE_NO_C), SW_HIDE);
214 /* disable or enable controls depending on whether we are editing global vs app specific config */
215 if (appSettings == EDITING_GLOBAL) {
216 WINE_TRACE("enabling controls\n");
217 enable(IDC_LIST_DRIVES);
218 enable(IDC_BUTTON_ADD);
219 enable(IDC_BUTTON_REMOVE);
220 enable(IDC_BUTTON_EDIT);
221 enable(IDC_BUTTON_AUTODETECT);
223 } else {
224 WINE_TRACE("disabling controls\n");
225 disable(IDC_LIST_DRIVES);
226 disable(IDC_BUTTON_ADD);
227 disable(IDC_BUTTON_REMOVE);
228 disable(IDC_BUTTON_EDIT);
229 disable(IDC_BUTTON_AUTODETECT);
233 updatingUI = FALSE;
234 return driveCount;
237 /******************************************************************************/
238 /* The Drive Editing Dialog */
239 /******************************************************************************/
240 #define DRIVE_MASK_BIT(B) 1<<(toupper(B)-'A')
242 typedef struct {
243 const uint sCode;
244 const char *sDesc;
245 } code_desc_pair;
247 static code_desc_pair type_pairs[] = {
248 {DRIVE_FIXED, "Local hard disk"},
249 {DRIVE_REMOTE, "Network share" },
250 {DRIVE_REMOVABLE, "Floppy disk"},
251 {DRIVE_CDROM, "CD-ROM"}
253 #define DRIVE_TYPE_DEFAULT 1
256 void fill_drive_droplist(long mask, char currentLetter, HWND hDlg)
258 int i;
259 int selection;
260 int count;
261 int next_letter;
262 char sName[4] = "A:";
264 for( i=0, count=0, selection=-1, next_letter=-1; i <= 'Z'-'A'; ++i ) {
265 if( mask & DRIVE_MASK_BIT('A'+i) ) {
266 int index;
268 sName[0] = 'A' + i;
269 index = SendDlgItemMessage( hDlg, IDC_COMBO_LETTER, CB_ADDSTRING, 0, (LPARAM) sName );
271 if( toupper(currentLetter) == 'A' + i ) {
272 selection = count;
275 if( i >= 2 && next_letter == -1){ /*default drive is first one of C-Z */
276 next_letter = count;
279 count++;
283 if( selection == -1 ) {
284 selection = next_letter;
287 SendDlgItemMessage( hDlg, IDC_COMBO_LETTER, CB_SETCURSEL, selection, 0 );
290 #define BOX_MODE_CD_ASSIGN 1
291 #define BOX_MODE_CD_AUTODETECT 2
292 #define BOX_MODE_NONE 3
293 #define BOX_MODE_NORMAL 4
294 void enable_labelserial_box(HWND dialog, int mode)
296 WINE_TRACE("mode=%d\n", mode);
297 switch (mode) {
298 case BOX_MODE_CD_ASSIGN:
299 /* enable(IDC_RADIO_AUTODETECT); */
300 enable(IDC_RADIO_ASSIGN);
301 disable(IDC_EDIT_DEVICE);
302 disable(IDC_BUTTON_BROWSE_DEVICE);
303 enable(IDC_EDIT_SERIAL);
304 enable(IDC_EDIT_LABEL);
305 enable(IDC_STATIC_SERIAL);
306 enable(IDC_STATIC_LABEL);
307 break;
309 case BOX_MODE_CD_AUTODETECT:
310 /* enable(IDC_RADIO_AUTODETECT); */
311 enable(IDC_RADIO_ASSIGN);
312 enable(IDC_EDIT_DEVICE);
313 enable(IDC_BUTTON_BROWSE_DEVICE);
314 disable(IDC_EDIT_SERIAL);
315 disable(IDC_EDIT_LABEL);
316 disable(IDC_STATIC_SERIAL);
317 disable(IDC_STATIC_LABEL);
318 break;
320 case BOX_MODE_NONE:
321 disable(IDC_RADIO_AUTODETECT);
322 disable(IDC_RADIO_ASSIGN);
323 disable(IDC_EDIT_DEVICE);
324 disable(IDC_BUTTON_BROWSE_DEVICE);
325 disable(IDC_EDIT_SERIAL);
326 disable(IDC_EDIT_LABEL);
327 disable(IDC_STATIC_SERIAL);
328 disable(IDC_STATIC_LABEL);
329 break;
331 case BOX_MODE_NORMAL:
332 disable(IDC_RADIO_AUTODETECT);
333 enable(IDC_RADIO_ASSIGN);
334 disable(IDC_EDIT_DEVICE);
335 disable(IDC_BUTTON_BROWSE_DEVICE);
336 enable(IDC_EDIT_SERIAL);
337 enable(IDC_EDIT_LABEL);
338 enable(IDC_STATIC_SERIAL);
339 enable(IDC_STATIC_LABEL);
340 break;
344 /* This function produces a mask for each drive letter that isn't currently used. Each bit of the long result
345 * represents a letter, with A being the least significant bit, and Z being the most significant.
347 * To calculate this, we loop over each letter, and see if we can get a drive entry for it. If so, we
348 * set the appropriate bit. At the end, we flip each bit, to give the desired result.
350 * The letter parameter is always marked as being available. This is so the edit dialog can display the
351 * currently used drive letter alongside the available ones.
353 long drive_available_mask(char letter)
355 long result = 0;
356 int i;
358 WINE_TRACE("\n");
361 for(i = 0; i < 26; i++)
363 if(!drives[i].in_use) continue;
364 result |= (1 << (toupper(drives[i].letter) - 'A'));
367 result = ~result;
368 if (letter) result |= DRIVE_MASK_BIT(letter);
370 WINE_TRACE( "finished drive letter loop with %lx\n", result );
371 return result;
374 void advancedDriveEditDialog(HWND hDlg, BOOL showAdvanced)
376 #define ADVANCED_DELTA 120
378 static RECT okpos;
379 static BOOL got_initial_ok_position = FALSE;
381 static RECT windowpos; /* we only use the height of this rectangle */
382 static BOOL got_initial_window_position = FALSE;
384 static RECT current_window;
386 INT state;
387 INT offset;
388 char *text;
390 if(!got_initial_ok_position)
392 POINT pt;
393 GetWindowRect(GetDlgItem(hDlg, ID_BUTTON_OK), &okpos);
394 pt.x = okpos.left;
395 pt.y = okpos.top;
396 ScreenToClient(hDlg, &pt);
397 okpos.right+= (pt.x - okpos.left);
398 okpos.bottom+= (pt.y - okpos.top);
399 okpos.left = pt.x;
400 okpos.top = pt.y;
401 got_initial_ok_position = TRUE;
404 if(!got_initial_window_position)
406 GetWindowRect(hDlg, &windowpos);
407 got_initial_window_position = TRUE;
410 if(showAdvanced)
412 state = SW_NORMAL;
413 offset = 0;
414 text = "Hide Advanced";
415 } else
417 state = SW_HIDE;
418 offset = ADVANCED_DELTA;
419 text = "Show Advanced";
422 ShowWindow(GetDlgItem(hDlg, IDC_STATIC_TYPE), state);
423 ShowWindow(GetDlgItem(hDlg, IDC_COMBO_TYPE), state);
424 ShowWindow(GetDlgItem(hDlg, IDC_BOX_LABELSERIAL), state);
425 ShowWindow(GetDlgItem(hDlg, IDC_RADIO_AUTODETECT), state);
426 ShowWindow(GetDlgItem(hDlg, IDC_RADIO_ASSIGN), state);
427 ShowWindow(GetDlgItem(hDlg, IDC_EDIT_LABEL), state);
428 ShowWindow(GetDlgItem(hDlg, IDC_EDIT_DEVICE), state);
429 ShowWindow(GetDlgItem(hDlg, IDC_STATIC_LABEL), state);
430 ShowWindow(GetDlgItem(hDlg, IDC_BUTTON_BROWSE_DEVICE), state);
431 SetWindowPos(GetDlgItem(hDlg, ID_BUTTON_OK),
432 HWND_TOP,
433 okpos.left, okpos.top - offset, okpos.right - okpos.left,
434 okpos.bottom - okpos.top,
437 /* resize the parent window */
438 GetWindowRect(hDlg, &current_window);
439 SetWindowPos(hDlg,
440 HWND_TOP,
441 current_window.left,
442 current_window.top,
443 windowpos.right - windowpos.left,
444 windowpos.bottom - windowpos.top - offset,
447 /* update the button text based on the state */
448 SetWindowText(GetDlgItem(hDlg, IDC_BUTTON_SHOW_HIDE_ADVANCED),
449 text);
453 void refreshDriveEditDialog(HWND dialog) {
454 char *path;
455 uint type;
456 char *label;
457 char *serial;
458 char *device;
459 int i, selection = -1;
461 updatingUI = TRUE;
463 WINE_TRACE("\n");
465 /* Drive letters */
466 fill_drive_droplist( drive_available_mask( editDriveEntry->letter ), editDriveEntry->letter, dialog );
468 /* path */
469 path = editDriveEntry->unixpath;
470 if (path) {
471 WINE_TRACE("set path control text to '%s'\n", path);
472 SetWindowText(GetDlgItem(dialog, IDC_EDIT_PATH), path);
473 } else WINE_WARN("no Path field?\n");
475 /* drive type */
476 type = editDriveEntry->type;
477 if (type) {
478 for(i = 0; i < sizeof(type_pairs)/sizeof(code_desc_pair); i++) {
479 SendDlgItemMessage(dialog, IDC_COMBO_TYPE, CB_ADDSTRING, 0,
480 (LPARAM) type_pairs[i].sDesc);
481 if(type_pairs[i].sCode == type){
482 selection = i;
486 if( selection == -1 ) selection = DRIVE_TYPE_DEFAULT;
487 SendDlgItemMessage(dialog, IDC_COMBO_TYPE, CB_SETCURSEL, selection, 0);
488 } else WINE_WARN("no Type field?\n");
491 /* removeable media properties */
492 label = editDriveEntry->label;
493 if (label) {
494 SendDlgItemMessage(dialog, IDC_EDIT_LABEL, WM_SETTEXT, 0,(LPARAM)label);
495 } else WINE_WARN("no Label field?\n");
497 /* set serial edit text */
498 serial = editDriveEntry->serial;
499 if (serial) {
500 SendDlgItemMessage(dialog, IDC_EDIT_SERIAL, WM_SETTEXT, 0,(LPARAM)serial);
501 } else WINE_WARN("no Serial field?\n");
503 /* TODO: get the device here to put into the edit box */
504 device = "Not implemented yet";
505 if (device) {
506 SendDlgItemMessage(dialog, IDC_EDIT_DEVICE, WM_SETTEXT, 0,(LPARAM)device);
507 } else WINE_WARN("no Device field?\n");
509 selection = IDC_RADIO_ASSIGN;
510 if ((type == DRIVE_CDROM) || (type == DRIVE_REMOVABLE)) {
511 #if 0
512 if (device) {
513 selection = IDC_RADIO_AUTODETECT;
514 enable_labelserial_box(dialog, BOX_MODE_CD_AUTODETECT);
515 } else {
516 #endif
517 selection = IDC_RADIO_ASSIGN;
518 enable_labelserial_box(dialog, BOX_MODE_CD_ASSIGN);
519 #if 0
521 #endif
522 } else {
523 enable_labelserial_box(dialog, BOX_MODE_NORMAL);
524 selection = IDC_RADIO_ASSIGN;
527 CheckRadioButton( dialog, IDC_RADIO_AUTODETECT, IDC_RADIO_ASSIGN, selection );
529 updatingUI = FALSE;
531 return;
534 /* storing the drive propsheet HWND here is a bit ugly, but the simplest solution for now */
535 static HWND driveDlgHandle;
537 void onEditChanged(HWND hDlg, WORD controlID) {
538 WINE_TRACE("controlID=%d\n", controlID);
539 switch (controlID) {
540 case IDC_EDIT_LABEL: {
541 char *label = getDialogItemText(hDlg, controlID);
542 if(!label) label = strdup("");
543 setDriveLabel(editDriveEntry, label);
544 refreshDriveDlg(driveDlgHandle);
545 if (label) free(label);
546 break;
548 case IDC_EDIT_PATH: {
549 char *path = getDialogItemText(hDlg, controlID);
550 if (!path) path = strdup("fake_windows"); /* default to assuming fake_windows in the .wine directory */
551 WINE_TRACE("got path from control of '%s'\n", path);
552 setDrivePath(editDriveEntry, path);
553 if(path) free(path);
554 break;
556 case IDC_EDIT_SERIAL: {
557 char *serial = getDialogItemText(hDlg, controlID);
558 if(!serial) serial = strdup("");
559 setDriveSerial(editDriveEntry, serial);
560 if (serial) free (serial);
561 break;
563 case IDC_EDIT_DEVICE: {
564 char *device = getDialogItemText(hDlg,controlID);
565 /* TODO: handle device if/when it makes sense to do so.... */
566 if (device) free(device);
567 refreshDriveDlg(driveDlgHandle);
568 break;
573 /* edit a drive entry */
574 INT_PTR CALLBACK DriveEditDlgProc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
576 int selection;
577 static BOOL advanced = FALSE;
579 switch (uMsg) {
580 case WM_CLOSE:
581 EndDialog(hDlg, wParam);
582 return TRUE;
584 case WM_INITDIALOG: {
585 enable_labelserial_box(hDlg, BOX_MODE_NORMAL);
586 advancedDriveEditDialog(hDlg, advanced);
587 editDriveEntry = (drive_entry_t*)lParam;
588 refreshDriveEditDialog(hDlg);
591 case WM_COMMAND:
592 switch (LOWORD(wParam)) {
593 case IDC_COMBO_TYPE:
594 if (HIWORD(wParam) != CBN_SELCHANGE) break;
595 selection = SendDlgItemMessage( hDlg, IDC_COMBO_TYPE, CB_GETCURSEL, 0, 0);
596 if( selection == 2 || selection == 3 ) { /* cdrom or floppy */
597 if (IsDlgButtonChecked(hDlg, IDC_RADIO_AUTODETECT))
598 enable_labelserial_box(hDlg, BOX_MODE_CD_AUTODETECT);
599 else
600 enable_labelserial_box(hDlg, BOX_MODE_CD_ASSIGN);
602 else {
603 enable_labelserial_box( hDlg, BOX_MODE_NORMAL );
605 editDriveEntry->type = type_pairs[selection].sCode;
606 break;
608 case IDC_COMBO_LETTER: {
609 int item = SendDlgItemMessage(hDlg, IDC_COMBO_LETTER, CB_GETCURSEL, 0, 0);
610 char newLetter[4];
611 SendDlgItemMessage(hDlg, IDC_COMBO_LETTER, CB_GETLBTEXT, item, (LPARAM) newLetter);
613 if (HIWORD(wParam) != CBN_SELCHANGE) break;
614 if (newLetter[0] == editDriveEntry->letter) break;
616 WINE_TRACE("changing drive letter to %c\n", newLetter[0]);
617 moveDrive(editDriveEntry, &drives[getDrive(newLetter[0])]);
618 editDriveEntry = &drives[getDrive(newLetter[0])];
619 refreshDriveDlg(driveDlgHandle);
620 break;
623 case IDC_BUTTON_BROWSE_PATH:
624 WRITEME(hDlg);
625 break;
627 case IDC_RADIO_AUTODETECT: {
628 /* TODO: */
629 WINE_FIXME("Implement autodetection\n");
630 enable_labelserial_box(hDlg, BOX_MODE_CD_AUTODETECT);
631 refreshDriveDlg(driveDlgHandle);
632 break;
635 case IDC_RADIO_ASSIGN:
637 char *edit, *serial;
638 edit = getDialogItemText(hDlg, IDC_EDIT_LABEL);
639 if(!edit) edit = strdup("");
640 setDriveLabel(editDriveEntry, edit);
641 free(edit);
643 serial = getDialogItemText(hDlg, IDC_EDIT_SERIAL);
644 if(!serial) serial = strdup("");
645 setDriveSerial(editDriveEntry, serial);
646 free(serial);
648 /* TODO: we don't have a device at this point */
649 /* setDriveValue(editWindowLetter, "Device", NULL); */
650 enable_labelserial_box(hDlg, BOX_MODE_CD_ASSIGN);
651 refreshDriveDlg(driveDlgHandle);
652 break;
655 case IDC_BUTTON_SHOW_HIDE_ADVANCED:
656 advanced = (advanced == TRUE) ? FALSE : TRUE; /* toggle state */
657 advancedDriveEditDialog(hDlg, advanced);
658 break;
660 case ID_BUTTON_OK:
661 EndDialog(hDlg, wParam);
662 return TRUE;
665 if (HIWORD(wParam) == EN_CHANGE) onEditChanged(hDlg, LOWORD(wParam));
666 break;
668 return FALSE;
671 void onAddDriveClicked(HWND hDlg) {
672 /* we should allocate a drive letter automatically. We also need some way to let the user choose the mapping point,
673 for now we will just force them to enter a path automatically, with / being the default. In future we should
674 be able to temporarily map / then invoke the directory chooser dialog. */
676 char newLetter = 'C'; /* we skip A and B, they are historically floppy drives */
677 long mask = ~drive_available_mask(0); /* the mask is now which drives aren't available */
679 while (mask & (1 << (newLetter - 'A'))) {
680 newLetter++;
681 if (newLetter > 'Z') {
682 MessageBox(NULL, "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);
683 return;
686 WINE_TRACE("allocating drive letter %c\n", newLetter);
688 if(newLetter == 'C') {
689 addDrive(newLetter, "fake_windows", "System Drive", "", DRIVE_FIXED);
690 } else {
691 addDrive(newLetter, "/", "", "", DRIVE_FIXED);
694 refreshDriveDlg(driveDlgHandle);
696 DialogBoxParam(NULL, MAKEINTRESOURCE(IDD_DRIVE_EDIT), NULL, (DLGPROC) DriveEditDlgProc, (LPARAM) &(drives[getDrive(newLetter)]));
699 void onDriveInitDialog(void)
701 char *pDevices, *pDev;
702 int ret;
703 int i;
704 int retval;
706 WINE_TRACE("\n");
708 /* setup the drives array */
709 pDev = pDevices = malloc(512);
710 ret = GetLogicalDriveStrings(512, pDevices);
712 /* make all devices unused */
713 for(i = 0; i < 26; i++)
715 drives[i].letter = 'A' + i;
716 drives[i].in_use = FALSE;
719 i = 0;
721 while(ret)
723 CHAR volumeNameBuffer[512];
724 DWORD serialNumber;
725 CHAR serialNumberString[256];
726 DWORD maxComponentLength;
727 DWORD fileSystemFlags;
728 CHAR fileSystemName[128];
729 char rootpath[256];
730 char simplepath[3];
731 int pathlen;
732 char targetpath[256];
734 *pDevices = toupper(*pDevices);
736 WINE_TRACE("pDevices == '%s'\n", pDevices);
738 volumeNameBuffer[0] = 0;
740 retval = GetVolumeInformation(pDevices,
741 volumeNameBuffer,
742 sizeof(volumeNameBuffer),
743 &serialNumber,
744 &maxComponentLength,
745 &fileSystemFlags,
746 fileSystemName,
747 sizeof(fileSystemName));
748 if(!retval)
750 WINE_TRACE("GetVolumeInformation() for '%s' failed, setting serialNumber to 0\n", pDevices);
751 PRINTERROR();
752 serialNumber = 0;
755 WINE_TRACE("serialNumber: '0x%lX'\n", serialNumber);
757 /* build rootpath for GetDriveType() */
758 strncpy(rootpath, pDevices, sizeof(rootpath));
759 pathlen = strlen(rootpath);
760 /* ensure that we have a backslash on the root path */
761 if((rootpath[pathlen - 1] != '\\') &&
762 (pathlen < sizeof(rootpath)))
764 rootpath[pathlen] = '\\';
765 rootpath[pathlen + 1] = 0;
768 strncpy(simplepath, pDevices, 2); /* QueryDosDevice() requires no trailing backslash */
769 simplepath[2] = 0;
770 QueryDosDevice(simplepath, targetpath, sizeof(targetpath));
772 snprintf(serialNumberString, sizeof(serialNumberString), "%lX", serialNumber);
773 WINE_TRACE("serialNumberString: '%s'\n", serialNumberString);
774 addDrive(*pDevices, targetpath, volumeNameBuffer, serialNumberString, GetDriveType(rootpath));
776 ret-=strlen(pDevices);
777 pDevices+=strlen(pDevices);
779 /* skip over any nulls */
780 while((*pDevices == 0) && (ret))
782 ret--;
783 pDevices++;
786 i++;
789 WINE_TRACE("found %d drives\n", i);
791 free(pDev);
795 void applyDriveChanges(void)
797 int i;
798 CHAR devicename[4];
799 CHAR targetpath[256];
800 BOOL foundDrive;
801 CHAR volumeNameBuffer[512];
802 DWORD serialNumber;
803 DWORD maxComponentLength;
804 DWORD fileSystemFlags;
805 CHAR fileSystemName[128];
806 int retval;
807 BOOL defineDevice;
809 WINE_TRACE("\n");
811 /* add each drive and remove as we go */
812 for(i = 0; i < 26; i++)
814 defineDevice = FALSE;
815 foundDrive = FALSE;
816 snprintf(devicename, sizeof(devicename), "%c:", 'A' + i);
818 /* get a drive */
819 if(QueryDosDevice(devicename, targetpath, sizeof(targetpath)))
821 foundDrive = TRUE;
824 /* if we found a drive and have a drive then compare things */
825 if(foundDrive && drives[i].in_use)
827 char newSerialNumberText[256];
829 volumeNameBuffer[0] = 0;
831 WINE_TRACE("drives[i].letter: '%c'\n", drives[i].letter);
833 snprintf(devicename, sizeof(devicename), "%c:\\", 'A' + i);
834 retval = GetVolumeInformation(devicename,
835 volumeNameBuffer,
836 sizeof(volumeNameBuffer),
837 &serialNumber,
838 &maxComponentLength,
839 &fileSystemFlags,
840 fileSystemName,
841 sizeof(fileSystemName));
842 if(!retval)
844 WINE_TRACE(" GetVolumeInformation() for '%s' failed\n", devicename);
845 WINE_TRACE(" Skipping this drive\n");
846 PRINTERROR();
847 continue; /* skip this drive */
850 snprintf(newSerialNumberText, sizeof(newSerialNumberText), "%lX", serialNumber);
852 WINE_TRACE(" current path: '%s', new path: '%s'\n",
853 targetpath, drives[i].unixpath);
854 WINE_TRACE(" current label: '%s', new label: '%s'\n",
855 volumeNameBuffer, drives[i].label);
856 WINE_TRACE(" current serial: '%s', new serial: '%s'\n",
857 newSerialNumberText, drives[i].serial);
859 /* compare to what we have */
860 /* do we have the same targetpath? */
861 if(strcmp(drives[i].unixpath, targetpath) ||
862 strcmp(drives[i].label, volumeNameBuffer) ||
863 strcmp(drives[i].serial, newSerialNumberText))
865 defineDevice = TRUE;
866 WINE_TRACE(" making changes to drive '%s'\n", devicename);
867 } else
869 WINE_TRACE(" no changes to drive '%s'\n", devicename);
871 } else if(foundDrive && !drives[i].in_use)
873 /* remove this drive */
874 if(!DefineDosDevice(DDD_REMOVE_DEFINITION, devicename, drives[i].unixpath))
876 WINE_ERR("unable to remove devicename of '%s', targetpath of '%s'\n",
877 devicename, drives[i].unixpath);
878 PRINTERROR();
879 } else
881 WINE_TRACE("removed devicename of '%s', targetpath of '%s'\n",
882 devicename, drives[i].unixpath);
884 } else if(drives[i].in_use) /* foundDrive must be false from the above check */
886 defineDevice = TRUE;
889 /* adding and modifying are the same steps */
890 if(defineDevice)
892 char filename[256];
893 HANDLE hFile;
895 HKEY hKey;
896 char *typeText;
897 char driveValue[256];
899 /* define this drive */
900 /* DefineDosDevice() requires that NO trailing slash be present */
901 snprintf(devicename, sizeof(devicename), "%c:", 'A' + i);
902 if(!DefineDosDevice(DDD_RAW_TARGET_PATH, devicename, drives[i].unixpath))
904 WINE_ERR(" unable to define devicename of '%s', targetpath of '%s'\n",
905 devicename, drives[i].unixpath);
906 PRINTERROR();
907 } else
909 WINE_TRACE(" added devicename of '%s', targetpath of '%s'\n",
910 devicename, drives[i].unixpath);
912 /* SetVolumeLabel() requires a trailing slash */
913 snprintf(devicename, sizeof(devicename), "%c:\\", 'A' + i);
914 if(!SetVolumeLabel(devicename, drives[i].label))
916 WINE_ERR("unable to set volume label for devicename of '%s', label of '%s'\n",
917 devicename, drives[i].label);
918 PRINTERROR();
919 } else
921 WINE_TRACE(" set volume label for devicename of '%s', label of '%s'\n",
922 devicename, drives[i].label);
926 /* Set the drive type in the registry */
927 if(drives[i].type == DRIVE_FIXED)
928 typeText = "hd";
929 else if(drives[i].type == DRIVE_REMOTE)
930 typeText = "network";
931 else if(drives[i].type == DRIVE_REMOVABLE)
932 typeText = "floppy";
933 else /* must be DRIVE_CDROM */
934 typeText = "cdrom";
937 snprintf(driveValue, sizeof(driveValue), "%c:", toupper(drives[i].letter));
939 retval = RegOpenKey(HKEY_LOCAL_MACHINE,
940 "Software\\Wine\\Drives",
941 &hKey);
943 if(retval != ERROR_SUCCESS)
945 WINE_TRACE(" Unable to open '%s'\n", "Software\\Wine\\Drives");
946 } else
948 retval = RegSetValueEx(
949 hKey,
950 driveValue,
952 REG_SZ,
953 typeText,
954 strlen(typeText) + 1);
955 if(retval != ERROR_SUCCESS)
957 WINE_TRACE(" Unable to set value of '%s' to '%s'\n",
958 driveValue, typeText);
959 } else
961 WINE_TRACE(" Finished setting value of '%s' to '%s'\n",
962 driveValue, typeText);
963 RegCloseKey(hKey);
967 /* Set the drive serial number via a .windows-serial file in */
968 /* the targetpath directory */
969 snprintf(filename, sizeof(filename), "%c:\\.windows-serial", drives[i].letter);
970 WINE_TRACE(" Putting serial number of '%ld' into file '%s'\n",
971 serialNumber, filename);
972 hFile = CreateFile(filename,
973 GENERIC_WRITE,
974 FILE_SHARE_READ,
975 NULL,
976 CREATE_ALWAYS,
977 FILE_ATTRIBUTE_NORMAL,
978 NULL);
979 if(hFile)
981 WINE_TRACE(" writing serial number of '%s'\n", drives[i].serial);
982 WriteFile(hFile,
983 drives[i].serial,
984 strlen(drives[i].serial),
985 NULL,
986 NULL);
987 WriteFile(hFile,
988 "\n",
989 strlen("\n"),
990 NULL,
991 NULL);
992 CloseHandle(hFile);
993 } else
995 WINE_TRACE(" CreateFile() error with file '%s'\n", filename);
999 /* if this drive is in use we should free it up */
1000 if(drives[i].in_use)
1002 freeDrive(&drives[i]); /* free up the string memory */
1008 INT_PTR CALLBACK
1009 DriveDlgProc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
1011 int nItem;
1012 drive_entry_t *pDrive;
1014 switch (uMsg) {
1015 case WM_INITDIALOG:
1016 onDriveInitDialog();
1017 break;
1018 case WM_COMMAND:
1019 switch (LOWORD(wParam)) {
1020 case IDC_LIST_DRIVES:
1021 /* double click should open the edit window for the chosen drive */
1022 if (HIWORD(wParam) == LBN_DBLCLK)
1023 SendMessageA(hDlg, WM_COMMAND, IDC_BUTTON_EDIT, 0);
1025 if (HIWORD(wParam) == LBN_SELCHANGE) lastSel = SendDlgItemMessage(hDlg, IDC_LIST_DRIVES, LB_GETCURSEL, 0, 0);
1026 break;
1028 case IDC_BUTTON_ADD:
1029 onAddDriveClicked(hDlg);
1030 break;
1032 case IDC_BUTTON_REMOVE:
1033 if (HIWORD(wParam) != BN_CLICKED) break;
1034 nItem = SendDlgItemMessage(hDlg, IDC_LIST_DRIVES, LB_GETCURSEL, 0, 0);
1035 pDrive = (drive_entry_t*)SendDlgItemMessage(hDlg, IDC_LIST_DRIVES, LB_GETITEMDATA, nItem, 0);
1036 freeDrive(pDrive);
1037 refreshDriveDlg(driveDlgHandle);
1038 break;
1040 case IDC_BUTTON_EDIT:
1041 if (HIWORD(wParam) != BN_CLICKED) break;
1042 nItem = SendMessage(GetDlgItem(hDlg, IDC_LIST_DRIVES), LB_GETCURSEL, 0, 0);
1043 pDrive = (drive_entry_t*)SendMessage(GetDlgItem(hDlg, IDC_LIST_DRIVES), LB_GETITEMDATA, nItem, 0);
1044 DialogBoxParam(NULL, MAKEINTRESOURCE(IDD_DRIVE_EDIT), NULL, (DLGPROC) DriveEditDlgProc, (LPARAM) pDrive);
1045 break;
1047 case IDC_BUTTON_AUTODETECT:
1048 WRITEME(hDlg);
1049 break;
1051 break;
1053 case WM_NOTIFY: switch(((LPNMHDR)lParam)->code) {
1054 case PSN_KILLACTIVE:
1055 WINE_TRACE("PSN_KILLACTIVE\n");
1056 SetWindowLong(hDlg, DWL_MSGRESULT, FALSE);
1057 break;
1058 case PSN_APPLY:
1059 applyDriveChanges();
1060 SetWindowLong(hDlg, DWL_MSGRESULT, PSNRET_NOERROR);
1061 break;
1062 case PSN_SETACTIVE:
1063 driveDlgHandle = hDlg;
1064 refreshDriveDlg (driveDlgHandle);
1065 break;
1067 break;
1070 return FALSE;