| /* | |
| * Drive management UI code | |
| * | |
| * Copyright 2003 Mark Westcott | |
| * Copyright 2004 Chris Morgan | |
| * Copyright 2003-2004 Mike Hearn | |
| * | |
| * This library is free software; you can redistribute it and/or | |
| * modify it under the terms of the GNU Lesser General Public | |
| * License as published by the Free Software Foundation; either | |
| * version 2.1 of the License, or (at your option) any later version. | |
| * | |
| * This library is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| * Lesser General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU Lesser General Public | |
| * License along with this library; if not, write to the Free Software | |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA | |
| * | |
| */ | |
| #include <stdio.h> | |
| #define WIN32_LEAN_AND_MEAN | |
| #define COBJMACROS | |
| #include <windows.h> | |
| #include <shellapi.h> | |
| #include <objbase.h> | |
| #include <shlguid.h> | |
| #include <shlwapi.h> | |
| #include <shlobj.h> | |
| #include <wine/unicode.h> | |
| #include <wine/debug.h> | |
| #include "winecfg.h" | |
| #include "resource.h" | |
| WINE_DEFAULT_DEBUG_CHANNEL(winecfg); | |
| #define BOX_MODE_CD_ASSIGN 1 | |
| #define BOX_MODE_CD_AUTODETECT 2 | |
| #define BOX_MODE_NONE 3 | |
| #define BOX_MODE_NORMAL 4 | |
| static BOOL advanced = FALSE; | |
| static BOOL updating_ui = FALSE; | |
| static struct drive* current_drive; | |
| static void get_etched_rect(HWND dialog, RECT *rect); | |
| static void update_controls(HWND dialog); | |
| static DWORD driveui_msgbox (HWND parent, UINT messageId, DWORD flags) | |
| { | |
| WCHAR* caption = load_string (IDS_WINECFG_TITLE); | |
| WCHAR* text = load_string (messageId); | |
| DWORD result = MessageBoxW (parent, text, caption, flags); | |
| HeapFree (GetProcessHeap(), 0, caption); | |
| HeapFree (GetProcessHeap(), 0, text); | |
| return result; | |
| } | |
| /**** listview helper functions ****/ | |
| /* clears the item at index in the listview */ | |
| static void lv_clear_curr_select(HWND dialog, int index) | |
| { | |
| ListView_SetItemState(GetDlgItem(dialog, IDC_LIST_DRIVES), index, 0, LVIS_SELECTED); | |
| } | |
| /* selects the item at index in the listview */ | |
| static void lv_set_curr_select(HWND dialog, int index) | |
| { | |
| /* no more than one item can be selected in our listview */ | |
| lv_clear_curr_select(dialog, -1); | |
| ListView_SetItemState(GetDlgItem(dialog, IDC_LIST_DRIVES), index, LVIS_SELECTED, LVIS_SELECTED); | |
| } | |
| /* returns the currently selected item in the listview */ | |
| static int lv_get_curr_select(HWND dialog) | |
| { | |
| return SendDlgItemMessage(dialog, IDC_LIST_DRIVES, LVM_GETNEXTITEM, -1, LVNI_SELECTED); | |
| } | |
| /* sets the item in the listview at item->iIndex */ | |
| static void lv_set_item(HWND dialog, LVITEMW *item) | |
| { | |
| SendDlgItemMessageW(dialog, IDC_LIST_DRIVES, LVM_SETITEMW, 0, (LPARAM) item); | |
| } | |
| /* sets specified item's text */ | |
| static void lv_set_item_text(HWND dialog, int item, int subItem, WCHAR *text) | |
| { | |
| LVITEMW lvItem; | |
| if (item < 0 || subItem < 0) return; | |
| lvItem.mask = LVIF_TEXT; | |
| lvItem.iItem = item; | |
| lvItem.iSubItem = subItem; | |
| lvItem.pszText = text; | |
| lvItem.cchTextMax = lstrlenW(lvItem.pszText); | |
| lv_set_item(dialog, &lvItem); | |
| } | |
| /* inserts an item into the listview */ | |
| static void lv_insert_item(HWND dialog, LVITEMW *item) | |
| { | |
| SendDlgItemMessageW(dialog, IDC_LIST_DRIVES, LVM_INSERTITEMW, 0, (LPARAM) item); | |
| } | |
| /* retrieve the item at index item->iIndex */ | |
| static void lv_get_item(HWND dialog, LVITEMW *item) | |
| { | |
| SendDlgItemMessageW(dialog, IDC_LIST_DRIVES, LVM_GETITEMW, 0, (LPARAM) item); | |
| } | |
| static void set_advanced(HWND dialog) | |
| { | |
| int state; | |
| WCHAR text[256]; | |
| RECT rect; | |
| if (advanced) | |
| { | |
| state = SW_NORMAL; | |
| LoadStringW(GetModuleHandle(NULL), IDS_HIDE_ADVANCED, text, 256); | |
| } | |
| else | |
| { | |
| state = SW_HIDE; | |
| LoadStringW(GetModuleHandle(NULL), IDS_SHOW_ADVANCED, text, 256); | |
| } | |
| ShowWindow(GetDlgItem(dialog, IDC_RADIO_AUTODETECT), state); | |
| ShowWindow(GetDlgItem(dialog, IDC_RADIO_ASSIGN), state); | |
| ShowWindow(GetDlgItem(dialog, IDC_EDIT_LABEL), state); | |
| ShowWindow(GetDlgItem(dialog, IDC_EDIT_DEVICE), state); | |
| ShowWindow(GetDlgItem(dialog, IDC_STATIC_LABEL), state); | |
| ShowWindow(GetDlgItem(dialog, IDC_BUTTON_BROWSE_DEVICE), state); | |
| ShowWindow(GetDlgItem(dialog, IDC_EDIT_SERIAL), state); | |
| ShowWindow(GetDlgItem(dialog, IDC_STATIC_SERIAL), state); | |
| ShowWindow(GetDlgItem(dialog, IDC_LABELSERIAL_STATIC), state); | |
| ShowWindow(GetDlgItem(dialog, IDC_COMBO_TYPE), state); | |
| ShowWindow(GetDlgItem(dialog, IDC_STATIC_TYPE), state); | |
| /* update the button text based on the state */ | |
| SetWindowTextW(GetDlgItem(dialog, IDC_BUTTON_SHOW_HIDE_ADVANCED), text); | |
| /* redraw for the etched line */ | |
| get_etched_rect(dialog, &rect); | |
| InflateRect(&rect, 5, 5); | |
| InvalidateRect(dialog, &rect, TRUE); | |
| } | |
| struct drive_typemap { | |
| unsigned int sCode; | |
| UINT idDesc; | |
| }; | |
| static const struct drive_typemap type_pairs[] = { | |
| { DRIVE_UNKNOWN, IDS_DRIVE_UNKNOWN }, | |
| { DRIVE_FIXED, IDS_DRIVE_FIXED }, | |
| { DRIVE_REMOTE, IDS_DRIVE_REMOTE }, | |
| { DRIVE_REMOVABLE, IDS_DRIVE_REMOVABLE }, | |
| { DRIVE_CDROM, IDS_DRIVE_CDROM } | |
| }; | |
| #define DRIVE_TYPE_DEFAULT 0 | |
| static void enable_labelserial_box(HWND dialog, int mode) | |
| { | |
| WINE_TRACE("mode=%d\n", mode); | |
| switch (mode) | |
| { | |
| case BOX_MODE_CD_ASSIGN: | |
| enable(IDC_RADIO_ASSIGN); | |
| disable(IDC_EDIT_DEVICE); | |
| disable(IDC_BUTTON_BROWSE_DEVICE); | |
| enable(IDC_EDIT_SERIAL); | |
| enable(IDC_EDIT_LABEL); | |
| enable(IDC_STATIC_SERIAL); | |
| enable(IDC_STATIC_LABEL); | |
| break; | |
| case BOX_MODE_CD_AUTODETECT: | |
| enable(IDC_RADIO_ASSIGN); | |
| enable(IDC_EDIT_DEVICE); | |
| enable(IDC_BUTTON_BROWSE_DEVICE); | |
| disable(IDC_EDIT_SERIAL); | |
| disable(IDC_EDIT_LABEL); | |
| disable(IDC_STATIC_SERIAL); | |
| disable(IDC_STATIC_LABEL); | |
| break; | |
| case BOX_MODE_NONE: | |
| disable(IDC_RADIO_ASSIGN); | |
| disable(IDC_EDIT_DEVICE); | |
| disable(IDC_BUTTON_BROWSE_DEVICE); | |
| disable(IDC_EDIT_SERIAL); | |
| disable(IDC_EDIT_LABEL); | |
| disable(IDC_STATIC_SERIAL); | |
| disable(IDC_STATIC_LABEL); | |
| break; | |
| case BOX_MODE_NORMAL: | |
| enable(IDC_RADIO_ASSIGN); | |
| disable(IDC_EDIT_DEVICE); | |
| disable(IDC_BUTTON_BROWSE_DEVICE); | |
| enable(IDC_EDIT_SERIAL); | |
| enable(IDC_EDIT_LABEL); | |
| enable(IDC_STATIC_SERIAL); | |
| enable(IDC_STATIC_LABEL); | |
| break; | |
| } | |
| } | |
| static int fill_drives_list(HWND dialog) | |
| { | |
| int count = 0; | |
| BOOL drivec_present = FALSE; | |
| int i; | |
| int prevsel = -1; | |
| WINE_TRACE("\n"); | |
| updating_ui = TRUE; | |
| prevsel = lv_get_curr_select(dialog); | |
| /* Clear the listbox */ | |
| SendDlgItemMessage(dialog, IDC_LIST_DRIVES, LVM_DELETEALLITEMS, 0, 0); | |
| for(i = 0; i < 26; i++) | |
| { | |
| LVITEMW item; | |
| WCHAR *path; | |
| char letter[4]; | |
| /* skip over any unused drives */ | |
| if (!drives[i].in_use) | |
| continue; | |
| if (drives[i].letter == 'C') | |
| drivec_present = TRUE; | |
| letter[0] = 'A' + i; | |
| letter[1] = ':'; | |
| letter[2] = 0; | |
| item.mask = LVIF_TEXT | LVIF_PARAM; | |
| item.iItem = count; | |
| item.iSubItem = 0; | |
| item.pszText = strdupU2W(letter); | |
| item.cchTextMax = lstrlenW(item.pszText); | |
| item.lParam = (LPARAM) &drives[i]; | |
| lv_insert_item(dialog, &item); | |
| HeapFree(GetProcessHeap(), 0, item.pszText); | |
| path = strdupU2W(drives[i].unixpath); | |
| lv_set_item_text(dialog, count, 1, path); | |
| HeapFree(GetProcessHeap(), 0, path); | |
| count++; | |
| } | |
| WINE_TRACE("loaded %d drives\n", count); | |
| /* show the warning if there is no Drive C */ | |
| if (!drivec_present) | |
| ShowWindow(GetDlgItem(dialog, IDS_DRIVE_NO_C), SW_NORMAL); | |
| else | |
| ShowWindow(GetDlgItem(dialog, IDS_DRIVE_NO_C), SW_HIDE); | |
| lv_set_curr_select(dialog, prevsel == -1 ? 0 : prevsel); | |
| updating_ui = FALSE; | |
| return count; | |
| } | |
| static void on_options_click(HWND dialog) | |
| { | |
| if (IsDlgButtonChecked(dialog, IDC_SHOW_DOT_FILES) == BST_CHECKED) | |
| set_reg_key(config_key, "", "ShowDotFiles", "Y"); | |
| else | |
| set_reg_key(config_key, "", "ShowDotFiles", "N"); | |
| SendMessage(GetParent(dialog), PSM_CHANGED, 0, 0); | |
| } | |
| static INT_PTR CALLBACK drivechoose_dlgproc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) | |
| { | |
| static int i, sel; | |
| char c; | |
| char drive[] = "X:"; | |
| switch(uMsg) | |
| { | |
| case WM_INITDIALOG: | |
| { | |
| ULONG mask = ~drive_available_mask(0); /* the mask is now which drives aren't available */ | |
| for( c = 'A'; c<= 'Z'; c++){ | |
| drive[0] = c; | |
| if(!( mask & (1 << (c - 'A')))) | |
| SendDlgItemMessageA( hwndDlg, IDC_DRIVESA2Z, CB_ADDSTRING, 0, (LPARAM) drive); | |
| } | |
| drive[0] = lParam; | |
| SendDlgItemMessageA( hwndDlg, IDC_DRIVESA2Z, CB_SELECTSTRING, 0, (LPARAM) drive); | |
| return TRUE; | |
| } | |
| case WM_COMMAND: | |
| if(HIWORD(wParam) != BN_CLICKED) break; | |
| switch (LOWORD(wParam)) | |
| { | |
| case IDOK: | |
| i = SendDlgItemMessageA( hwndDlg, IDC_DRIVESA2Z, CB_GETCURSEL, 0, 0); | |
| if( i != CB_ERR){ | |
| SendDlgItemMessageA( hwndDlg, IDC_DRIVESA2Z, CB_GETLBTEXT, i, (LPARAM) drive); | |
| sel = drive[0]; | |
| } else | |
| sel = -1; | |
| EndDialog(hwndDlg, sel); | |
| return TRUE; | |
| case IDCANCEL: | |
| EndDialog(hwndDlg, -1); | |
| return TRUE; | |
| } | |
| } | |
| return FALSE; | |
| } | |
| static void on_add_click(HWND dialog) | |
| { | |
| /* we should allocate a drive letter automatically. We also need | |
| some way to let the user choose the mapping point, for now we | |
| will just force them to enter a path automatically, with / being | |
| the default. In future we should be able to temporarily map / | |
| then invoke the directory chooser dialog. */ | |
| char new = 'C'; /* we skip A and B, they are historically floppy drives */ | |
| ULONG mask = ~drive_available_mask(0); /* the mask is now which drives aren't available */ | |
| int i, c; | |
| while (mask & (1 << (new - 'A'))) | |
| { | |
| new++; | |
| if (new > 'Z') | |
| { | |
| driveui_msgbox (dialog, IDS_DRIVE_LETTERS_EXCEEDED, MB_OK | MB_ICONEXCLAMATION); | |
| return; | |
| } | |
| } | |
| new = DialogBoxParam(0, MAKEINTRESOURCE(IDD_DRIVECHOOSE), dialog, drivechoose_dlgproc, new); | |
| if( new == -1) return; | |
| WINE_TRACE("selected drive letter %c\n", new); | |
| if (new == 'C') | |
| { | |
| WCHAR label[64]; | |
| LoadStringW (GetModuleHandle (NULL), IDS_SYSTEM_DRIVE_LABEL, label, | |
| sizeof(label)/sizeof(label[0])); | |
| add_drive(new, "../drive_c", NULL, label, 0, DRIVE_FIXED); | |
| } | |
| else add_drive(new, "/", NULL, NULL, 0, DRIVE_UNKNOWN); | |
| fill_drives_list(dialog); | |
| /* select the newly created drive */ | |
| mask = ~drive_available_mask(0); | |
| c = 0; | |
| for (i = 0; i < 26; i++) | |
| { | |
| if ('A' + i == new) break; | |
| if ((1 << i) & mask) c++; | |
| } | |
| lv_set_curr_select(dialog, c); | |
| SetFocus(GetDlgItem(dialog, IDC_LIST_DRIVES)); | |
| update_controls(dialog); | |
| SendMessage(GetParent(dialog), PSM_CHANGED, (WPARAM) dialog, 0); | |
| } | |
| static void on_remove_click(HWND dialog) | |
| { | |
| int itemIndex; | |
| struct drive *drive; | |
| LVITEMW item; | |
| itemIndex = lv_get_curr_select(dialog); | |
| if (itemIndex == -1) return; /* no selection */ | |
| item.mask = LVIF_PARAM; | |
| item.iItem = itemIndex; | |
| item.iSubItem = 0; | |
| lv_get_item(dialog, &item); | |
| drive = (struct drive *) item.lParam; | |
| WINE_TRACE("unixpath: %s\n", drive->unixpath); | |
| if (drive->letter == 'C') | |
| { | |
| DWORD result = driveui_msgbox (dialog, IDS_CONFIRM_DELETE_C, MB_YESNO | MB_ICONEXCLAMATION); | |
| if (result == IDNO) return; | |
| } | |
| delete_drive(drive); | |
| fill_drives_list(dialog); | |
| itemIndex = itemIndex - 1; | |
| if (itemIndex < 0) itemIndex = 0; | |
| lv_set_curr_select(dialog, itemIndex); /* previous item */ | |
| SetFocus(GetDlgItem(dialog, IDC_LIST_DRIVES)); | |
| update_controls(dialog); | |
| SendMessage(GetParent(dialog), PSM_CHANGED, (WPARAM) dialog, 0); | |
| } | |
| static void update_controls(HWND dialog) | |
| { | |
| static const WCHAR emptyW[1]; | |
| WCHAR *path; | |
| unsigned int type; | |
| char serial[16]; | |
| const char *device; | |
| int i, selection = -1; | |
| LVITEMW item; | |
| updating_ui = TRUE; | |
| i = lv_get_curr_select(dialog); | |
| if (i == -1) | |
| { | |
| /* no selection? let's select something for the user. this will re-enter */ | |
| lv_set_curr_select(dialog, i); | |
| return; | |
| } | |
| item.mask = LVIF_PARAM; | |
| item.iItem = i; | |
| item.iSubItem = 0; | |
| lv_get_item(dialog, &item); | |
| current_drive = (struct drive *) item.lParam; | |
| WINE_TRACE("Updating sheet for drive %c\n", current_drive->letter); | |
| /* path */ | |
| WINE_TRACE("set path control text to '%s'\n", current_drive->unixpath); | |
| path = strdupU2W(current_drive->unixpath); | |
| set_textW(dialog, IDC_EDIT_PATH, path); | |
| HeapFree(GetProcessHeap(), 0, path); | |
| /* drive type */ | |
| type = current_drive->type; | |
| SendDlgItemMessage(dialog, IDC_COMBO_TYPE, CB_RESETCONTENT, 0, 0); | |
| for (i = 0; i < sizeof(type_pairs) / sizeof(struct drive_typemap); i++) | |
| { | |
| WCHAR driveDesc[64]; | |
| LoadStringW (GetModuleHandle (NULL), type_pairs[i].idDesc, driveDesc, | |
| sizeof(driveDesc)/sizeof(driveDesc[0])); | |
| SendDlgItemMessageW (dialog, IDC_COMBO_TYPE, CB_ADDSTRING, 0, (LPARAM)driveDesc); | |
| if (type_pairs[i].sCode == type) | |
| { | |
| selection = i; | |
| } | |
| } | |
| if (selection == -1) selection = DRIVE_TYPE_DEFAULT; | |
| SendDlgItemMessage(dialog, IDC_COMBO_TYPE, CB_SETCURSEL, selection, 0); | |
| EnableWindow( GetDlgItem( dialog, IDC_BUTTON_REMOVE ), (current_drive->letter != 'C') ); | |
| EnableWindow( GetDlgItem( dialog, IDC_EDIT_PATH ), (current_drive->letter != 'C') ); | |
| EnableWindow( GetDlgItem( dialog, IDC_BUTTON_BROWSE_PATH ), (current_drive->letter != 'C') ); | |
| EnableWindow( GetDlgItem( dialog, IDC_COMBO_TYPE ), (current_drive->letter != 'C') ); | |
| /* removeable media properties */ | |
| set_textW(dialog, IDC_EDIT_LABEL, current_drive->label ? current_drive->label : emptyW); | |
| /* set serial edit text */ | |
| sprintf( serial, "%X", current_drive->serial ); | |
| set_text(dialog, IDC_EDIT_SERIAL, serial); | |
| /* TODO: get the device here to put into the edit box */ | |
| device = "Not implemented yet"; | |
| set_text(dialog, IDC_EDIT_DEVICE, device); | |
| device = NULL; | |
| selection = IDC_RADIO_ASSIGN; | |
| if ((type == DRIVE_CDROM) || (type == DRIVE_REMOVABLE)) | |
| { | |
| if (device) | |
| { | |
| selection = IDC_RADIO_AUTODETECT; | |
| enable_labelserial_box(dialog, BOX_MODE_CD_AUTODETECT); | |
| } | |
| else | |
| { | |
| selection = IDC_RADIO_ASSIGN; | |
| enable_labelserial_box(dialog, BOX_MODE_CD_ASSIGN); | |
| } | |
| } | |
| else | |
| { | |
| enable_labelserial_box(dialog, BOX_MODE_NORMAL); | |
| selection = IDC_RADIO_ASSIGN; | |
| } | |
| CheckRadioButton(dialog, IDC_RADIO_AUTODETECT, IDC_RADIO_ASSIGN, selection); | |
| updating_ui = FALSE; | |
| return; | |
| } | |
| static void on_edit_changed(HWND dialog, WORD id) | |
| { | |
| if (updating_ui) return; | |
| WINE_TRACE("edit id %d changed\n", id); | |
| switch (id) | |
| { | |
| case IDC_EDIT_LABEL: | |
| { | |
| WCHAR *label = get_textW(dialog, id); | |
| HeapFree(GetProcessHeap(), 0, current_drive->label); | |
| current_drive->label = label; | |
| current_drive->modified = TRUE; | |
| WINE_TRACE("set label to %s\n", wine_dbgstr_w(current_drive->label)); | |
| /* enable the apply button */ | |
| SendMessage(GetParent(dialog), PSM_CHANGED, (WPARAM) dialog, 0); | |
| break; | |
| } | |
| case IDC_EDIT_PATH: | |
| { | |
| WCHAR *wpath; | |
| char *path; | |
| int lenW; | |
| wpath = get_textW(dialog, id); | |
| if( (lenW = WideCharToMultiByte(CP_UNIXCP, 0, wpath, -1, NULL, 0, NULL, NULL)) ) | |
| { | |
| path = HeapAlloc(GetProcessHeap(), 0, lenW); | |
| WideCharToMultiByte(CP_UNIXCP, 0, wpath, -1, path, lenW, NULL, NULL); | |
| } | |
| else | |
| { | |
| path = NULL; | |
| wpath = strdupU2W("drive_c"); | |
| } | |
| HeapFree(GetProcessHeap(), 0, current_drive->unixpath); | |
| current_drive->unixpath = path ? path : strdupA("drive_c"); | |
| current_drive->modified = TRUE; | |
| WINE_TRACE("set path to %s\n", current_drive->unixpath); | |
| lv_set_item_text(dialog, lv_get_curr_select(dialog), 1, | |
| wpath); | |
| HeapFree(GetProcessHeap(), 0, wpath); | |
| /* enable the apply button */ | |
| SendMessage(GetParent(dialog), PSM_CHANGED, (WPARAM) dialog, 0); | |
| break; | |
| } | |
| case IDC_EDIT_SERIAL: | |
| { | |
| char *serial; | |
| serial = get_text(dialog, id); | |
| current_drive->serial = serial ? strtoul( serial, NULL, 16 ) : 0; | |
| HeapFree(GetProcessHeap(), 0, serial); | |
| current_drive->modified = TRUE; | |
| WINE_TRACE("set serial to %08X\n", current_drive->serial); | |
| /* enable the apply button */ | |
| SendMessage(GetParent(dialog), PSM_CHANGED, (WPARAM) dialog, 0); | |
| break; | |
| } | |
| case IDC_EDIT_DEVICE: | |
| { | |
| char *device = get_text(dialog, id); | |
| /* TODO: handle device if/when it makes sense to do so.... */ | |
| HeapFree(GetProcessHeap(), 0, device); | |
| break; | |
| } | |
| } | |
| } | |
| static void get_etched_rect(HWND dialog, RECT *rect) | |
| { | |
| GetClientRect(dialog, rect); | |
| /* these dimensions from the labelserial static in En.rc */ | |
| rect->top = 265; | |
| rect->bottom = 265; | |
| rect->left += 25; | |
| rect->right -= 25; | |
| } | |
| /* this just draws a nice line to separate the advanced gui from the n00b gui :) */ | |
| static void paint(HWND dialog) | |
| { | |
| PAINTSTRUCT ps; | |
| BeginPaint(dialog, &ps); | |
| if (advanced) | |
| { | |
| RECT rect; | |
| get_etched_rect(dialog, &rect); | |
| DrawEdge(ps.hdc, &rect, EDGE_ETCHED, BF_TOP); | |
| } | |
| EndPaint(dialog, &ps); | |
| } | |
| BOOL browse_for_unix_folder(HWND dialog, WCHAR *pszPath) | |
| { | |
| static WCHAR wszUnixRootDisplayName[] = | |
| { ':',':','{','C','C','7','0','2','E','B','2','-','7','D','C','5','-','1','1','D','9','-', | |
| 'C','6','8','7','-','0','0','0','4','2','3','8','A','0','1','C','D','}', 0 }; | |
| WCHAR pszChoosePath[FILENAME_MAX]; | |
| BROWSEINFOW bi = { | |
| dialog, | |
| NULL, | |
| NULL, | |
| pszChoosePath, | |
| 0, | |
| NULL, | |
| 0, | |
| 0 | |
| }; | |
| IShellFolder *pDesktop; | |
| LPITEMIDLIST pidlUnixRoot, pidlSelectedPath; | |
| HRESULT hr; | |
| LoadStringW(GetModuleHandle(NULL), IDS_CHOOSE_PATH, pszChoosePath, FILENAME_MAX); | |
| hr = SHGetDesktopFolder(&pDesktop); | |
| if (FAILED(hr)) return FALSE; | |
| hr = IShellFolder_ParseDisplayName(pDesktop, NULL, NULL, wszUnixRootDisplayName, NULL, | |
| &pidlUnixRoot, NULL); | |
| if (FAILED(hr)) { | |
| IShellFolder_Release(pDesktop); | |
| return FALSE; | |
| } | |
| bi.pidlRoot = pidlUnixRoot; | |
| pidlSelectedPath = SHBrowseForFolderW(&bi); | |
| SHFree(pidlUnixRoot); | |
| if (pidlSelectedPath) { | |
| STRRET strSelectedPath; | |
| WCHAR *pszSelectedPath; | |
| HRESULT hr; | |
| hr = IShellFolder_GetDisplayNameOf(pDesktop, pidlSelectedPath, SHGDN_FORPARSING, | |
| &strSelectedPath); | |
| IShellFolder_Release(pDesktop); | |
| if (FAILED(hr)) { | |
| SHFree(pidlSelectedPath); | |
| return FALSE; | |
| } | |
| hr = StrRetToStrW(&strSelectedPath, pidlSelectedPath, &pszSelectedPath); | |
| SHFree(pidlSelectedPath); | |
| if (FAILED(hr)) return FALSE; | |
| lstrcpyW(pszPath, pszSelectedPath); | |
| CoTaskMemFree(pszSelectedPath); | |
| return TRUE; | |
| } | |
| return FALSE; | |
| } | |
| static void init_listview_columns(HWND dialog) | |
| { | |
| LVCOLUMNW listColumn; | |
| RECT viewRect; | |
| int width; | |
| WCHAR column[64]; | |
| GetClientRect(GetDlgItem(dialog, IDC_LIST_DRIVES), &viewRect); | |
| width = (viewRect.right - viewRect.left) / 6 - 5; | |
| LoadStringW (GetModuleHandle (NULL), IDS_COL_DRIVELETTER, column, | |
| sizeof(column)/sizeof(column[0])); | |
| listColumn.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM; | |
| listColumn.pszText = column; | |
| listColumn.cchTextMax = lstrlenW (listColumn.pszText); | |
| listColumn.cx = width; | |
| SendDlgItemMessageW (dialog, IDC_LIST_DRIVES, LVM_INSERTCOLUMNW, 0, (LPARAM) &listColumn); | |
| LoadStringW (GetModuleHandle (NULL), IDS_COL_DRIVEMAPPING, column, | |
| sizeof(column)/sizeof(column[0])); | |
| listColumn.cx = viewRect.right - viewRect.left - width; | |
| listColumn.pszText = column; | |
| listColumn.cchTextMax = lstrlenW (listColumn.pszText); | |
| SendDlgItemMessageW (dialog, IDC_LIST_DRIVES, LVM_INSERTCOLUMNW, 1, (LPARAM) &listColumn); | |
| } | |
| static void load_drive_options(HWND dialog) | |
| { | |
| if (!strcmp(get_reg_key(config_key, "", "ShowDotFiles", "N"), "Y")) | |
| CheckDlgButton(dialog, IDC_SHOW_DOT_FILES, BST_CHECKED); | |
| } | |
| INT_PTR CALLBACK | |
| DriveDlgProc (HWND dialog, UINT msg, WPARAM wParam, LPARAM lParam) | |
| { | |
| int item; | |
| struct drive *drive; | |
| switch (msg) | |
| { | |
| case WM_INITDIALOG: | |
| init_listview_columns(dialog); | |
| if (!load_drives()) | |
| { | |
| ShowWindow( GetDlgItem( dialog, IDC_STATIC_MOUNTMGR_ERROR ), SW_SHOW ); | |
| ShowWindow( GetDlgItem( dialog, IDC_LIST_DRIVES ), SW_HIDE ); | |
| ShowWindow( GetDlgItem( dialog, IDC_BUTTON_ADD ), SW_HIDE ); | |
| ShowWindow( GetDlgItem( dialog, IDC_BUTTON_REMOVE ), SW_HIDE ); | |
| ShowWindow( GetDlgItem( dialog, IDC_BUTTON_AUTODETECT ), SW_HIDE ); | |
| ShowWindow( GetDlgItem( dialog, IDC_STATIC_PATH ), SW_HIDE ); | |
| ShowWindow( GetDlgItem( dialog, IDC_EDIT_PATH ), SW_HIDE ); | |
| ShowWindow( GetDlgItem( dialog, IDC_BUTTON_BROWSE_PATH ), SW_HIDE ); | |
| ShowWindow( GetDlgItem( dialog, IDC_COMBO_TYPE ), SW_HIDE ); | |
| ShowWindow( GetDlgItem( dialog, IDC_BUTTON_SHOW_HIDE_ADVANCED ), SW_HIDE ); | |
| set_advanced(dialog); | |
| break; | |
| } | |
| ShowWindow( GetDlgItem( dialog, IDC_STATIC_MOUNTMGR_ERROR ), SW_HIDE ); | |
| load_drive_options(dialog); | |
| if (!drives[2].in_use) | |
| driveui_msgbox (dialog, IDS_NO_DRIVE_C, MB_OK | MB_ICONEXCLAMATION); | |
| fill_drives_list(dialog); | |
| update_controls(dialog); | |
| /* put in non-advanced mode by default */ | |
| set_advanced(dialog); | |
| break; | |
| case WM_SHOWWINDOW: | |
| set_window_title(dialog); | |
| break; | |
| case WM_PAINT: | |
| paint(dialog); | |
| break; | |
| case WM_COMMAND: | |
| switch (HIWORD(wParam)) | |
| { | |
| case EN_CHANGE: | |
| on_edit_changed(dialog, LOWORD(wParam)); | |
| break; | |
| case BN_CLICKED: | |
| switch (LOWORD(wParam)) | |
| { | |
| case IDC_SHOW_DOT_FILES: | |
| on_options_click(dialog); | |
| break; | |
| } | |
| break; | |
| case CBN_SELCHANGE: | |
| SendMessage(GetParent(dialog), PSM_CHANGED, 0, 0); | |
| break; | |
| } | |
| switch (LOWORD(wParam)) | |
| { | |
| case IDC_BUTTON_ADD: | |
| if (HIWORD(wParam) != BN_CLICKED) break; | |
| on_add_click(dialog); | |
| break; | |
| case IDC_BUTTON_REMOVE: | |
| if (HIWORD(wParam) != BN_CLICKED) break; | |
| on_remove_click(dialog); | |
| break; | |
| case IDC_BUTTON_EDIT: | |
| if (HIWORD(wParam) != BN_CLICKED) break; | |
| item = SendMessage(GetDlgItem(dialog, IDC_LIST_DRIVES), LB_GETCURSEL, 0, 0); | |
| drive = (struct drive *) SendMessage(GetDlgItem(dialog, IDC_LIST_DRIVES), LB_GETITEMDATA, item, 0); | |
| break; | |
| case IDC_BUTTON_AUTODETECT: | |
| autodetect_drives(); | |
| fill_drives_list(dialog); | |
| SendMessage(GetParent(dialog), PSM_CHANGED, 0, 0); | |
| break; | |
| case IDC_BUTTON_SHOW_HIDE_ADVANCED: | |
| advanced = !advanced; | |
| set_advanced(dialog); | |
| break; | |
| case IDC_BUTTON_BROWSE_PATH: | |
| { | |
| WCHAR szTargetPath[FILENAME_MAX]; | |
| if (browse_for_unix_folder(dialog, szTargetPath)) | |
| set_textW(dialog, IDC_EDIT_PATH, szTargetPath); | |
| break; | |
| } | |
| case IDC_RADIO_ASSIGN: | |
| { | |
| WCHAR *str = get_textW(dialog, IDC_EDIT_LABEL); | |
| HeapFree(GetProcessHeap(), 0, current_drive->label); | |
| current_drive->label = str; | |
| str = get_textW(dialog, IDC_EDIT_SERIAL); | |
| current_drive->serial = str ? strtoulW( str, NULL, 16 ) : 0; | |
| HeapFree(GetProcessHeap(), 0, str); | |
| current_drive->modified = TRUE; | |
| /* TODO: we don't have a device at this point */ | |
| enable_labelserial_box(dialog, BOX_MODE_CD_ASSIGN); | |
| break; | |
| } | |
| case IDC_COMBO_TYPE: | |
| { | |
| int mode = BOX_MODE_NORMAL; | |
| int selection; | |
| if (HIWORD(wParam) != CBN_SELCHANGE) break; | |
| selection = SendDlgItemMessage(dialog, IDC_COMBO_TYPE, CB_GETCURSEL, 0, 0); | |
| if (selection >= 0 && | |
| (type_pairs[selection].sCode == DRIVE_CDROM || | |
| type_pairs[selection].sCode == DRIVE_REMOVABLE)) | |
| { | |
| if (IsDlgButtonChecked(dialog, IDC_RADIO_AUTODETECT)) | |
| mode = BOX_MODE_CD_AUTODETECT; | |
| else | |
| mode = BOX_MODE_CD_ASSIGN; | |
| } | |
| enable_labelserial_box(dialog, mode); | |
| current_drive->type = type_pairs[selection].sCode; | |
| current_drive->modified = TRUE; | |
| break; | |
| } | |
| } | |
| break; | |
| case WM_NOTIFY: | |
| switch (((LPNMHDR)lParam)->code) | |
| { | |
| case PSN_KILLACTIVE: | |
| WINE_TRACE("PSN_KILLACTIVE\n"); | |
| SetWindowLongPtr(dialog, DWLP_MSGRESULT, FALSE); | |
| break; | |
| case PSN_APPLY: | |
| apply_drive_changes(); | |
| SetWindowLongPtr(dialog, DWLP_MSGRESULT, PSNRET_NOERROR); | |
| break; | |
| case PSN_SETACTIVE: | |
| break; | |
| case LVN_ITEMCHANGED: | |
| { | |
| LPNMLISTVIEW lpnm = (LPNMLISTVIEW)lParam; | |
| if (!(lpnm->uOldState & LVIS_SELECTED) && | |
| (lpnm->uNewState & LVIS_SELECTED)) | |
| update_controls(dialog); | |
| break; | |
| } | |
| } | |
| break; | |
| } | |
| return FALSE; | |
| } |