/*++ Copyright (c) 1990 Microsoft Corporation Module Name: fdlistbx.c Abstract: Routines for handling the subclassed owner-draw listbox used by NT fdisk to display the state of attached disks. Author: Ted Miller (tedm) 7-Jan-1992 --*/ #include "fdisk.h" // constants used when listbox or its focus rectangle is // scrolled/moved. #define DIR_NONE 0 #define DIR_UP 1 #define DIR_DN 2 // original window procedure for our subclassed listbox WNDPROC OldListBoxProc; // item which has focus DWORD LBCursorListBoxItem,LBCursorRegion; BOOL LBCursorOn = FALSE; VOID ToggleLBCursor( IN HDC hdc ); VOID ToggleRegion( IN PDISKSTATE DiskState, IN DWORD RegionIndex, IN HDC hdc ); LONG ListBoxSubProc( IN HWND hwnd, IN UINT msg, IN DWORD wParam, IN LONG lParam ) /*++ Routine Description: This routine is the window procedure used for our subclassed listbox. We subclass the listbox so that we can handle keyboard input processing. All other messages are passed through to the original listbox procedure. Significant keys are arrows, pageup/dn, tab, space, return, home, and end. Control may be used to modify space and return. Shift may be used to modify tab. Arguments: hwnd - window handle of listbox msg - message # wParam - user param # 1 lParam - user param # 2 Return Value: see below --*/ { int focusDir = DIR_NONE; USHORT vKey; DWORD maxRegion; PDISKSTATE diskState; LONG topItem, bottomWholeItem, visibleItems; RECT rc; switch (msg) { case WM_CHAR: break; case WM_KEYDOWN: switch (vKey = LOWORD(wParam)) { case VK_LEFT: case VK_RIGHT: case VK_UP: case VK_DOWN: ToggleLBCursor(NULL); switch (vKey) { case VK_LEFT: LBCursorRegion = LBCursorRegion ? LBCursorRegion-1 : 0; break; case VK_RIGHT: maxRegion = Disks[LBCursorListBoxItem]->RegionCount - 1; if (LBCursorRegion < maxRegion) { LBCursorRegion++; } break; case VK_UP: if (LBCursorListBoxItem) { LBCursorListBoxItem--; LBCursorRegion = 0; focusDir = DIR_UP; } break; case VK_DOWN: if (LBCursorListBoxItem < DiskCount-1) { LBCursorListBoxItem++; LBCursorRegion = 0; focusDir = DIR_DN; } break; } // don't allow list box cursor to fall on extended partition diskState = Disks[LBCursorListBoxItem]; maxRegion = diskState->RegionCount - 1; if (IsExtended(diskState->RegionArray[LBCursorRegion].SysID)) { if (LBCursorRegion && ((vKey == VK_LEFT) || (LBCursorRegion == maxRegion))) { LBCursorRegion--; } else { LBCursorRegion++; } } ToggleLBCursor(NULL); break; case VK_TAB: ToggleLBCursor(NULL); if (GetKeyState(VK_SHIFT) & ~1) { // shift-tab LBCursorListBoxItem--; focusDir = DIR_UP; } else { LBCursorListBoxItem++; focusDir = DIR_DN; } if (LBCursorListBoxItem == (DWORD)(-1)) { LBCursorListBoxItem = DiskCount-1; focusDir = DIR_DN; } else if (LBCursorListBoxItem == DiskCount) { LBCursorListBoxItem = 0; focusDir = DIR_UP; } ResetLBCursorRegion(); ToggleLBCursor(NULL); break; case VK_HOME: case VK_END: ToggleLBCursor(NULL); topItem = (vKey == VK_HOME) ? 0 : DiskCount-1; SendMessage(hwndList, LB_SETTOPINDEX, (DWORD)topItem, 0); LBCursorListBoxItem = topItem; ResetLBCursorRegion(); ToggleLBCursor(NULL); break; case VK_PRIOR: case VK_NEXT: ToggleLBCursor(NULL); topItem = SendMessage(hwndList, LB_GETTOPINDEX, 0, 0); GetClientRect(hwndList,&rc); visibleItems = (rc.bottom - rc.top) / GraphHeight; if (!visibleItems) { visibleItems = 1; } topItem = (vKey == VK_PRIOR) ? max(topItem - visibleItems, 0) : min(topItem + visibleItems, (LONG)DiskCount-1); SendMessage(hwndList, LB_SETTOPINDEX, (DWORD)topItem, 0); LBCursorListBoxItem = SendMessage(hwndList, LB_GETTOPINDEX, 0, 0); ResetLBCursorRegion(); ToggleLBCursor(NULL); break; case VK_RETURN: case VK_SPACE: // Select the region that currently has the list box selection cursor. if (!Disks[LBCursorListBoxItem]->OffLine) { Selection(GetKeyState(VK_CONTROL) & ~1, // strip toggle bit Disks[LBCursorListBoxItem], LBCursorRegion); } break; } // now scroll the newly focused item into view if necessary switch (focusDir) { case DIR_UP: if (LBCursorListBoxItem < (DWORD)SendMessage(hwndList, LB_GETTOPINDEX, 0, 0)) { SendMessage(hwndList, LB_SETTOPINDEX, LBCursorListBoxItem, 0); } break; case DIR_DN: GetClientRect(hwndList, &rc); topItem = SendMessage(hwndList, LB_GETTOPINDEX, 0, 0); bottomWholeItem = topItem + ((rc.bottom - rc.top) / GraphHeight) - 1; if (bottomWholeItem < topItem) { bottomWholeItem = topItem; } if ((DWORD)bottomWholeItem > DiskCount-1) { bottomWholeItem = DiskCount-1; } if (LBCursorListBoxItem > (DWORD)bottomWholeItem) { SendMessage(hwndList, LB_SETTOPINDEX, topItem + LBCursorListBoxItem - bottomWholeItem, 0); } break; } break; default: return CallWindowProc(OldListBoxProc, hwnd, msg, wParam, lParam); } return 0; } VOID SubclassListBox( IN HWND hwnd ) { OldListBoxProc = (WNDPROC)GetWindowLong(hwnd, GWL_WNDPROC); SetWindowLong(hwnd, GWL_WNDPROC, (LONG)ListBoxSubProc); // There is a scantily documented 'feature' of a listbox wherein it will // use its parent's DC. This means that drawing is not always clipped to // the client area of the listbox. Seeing as we're subclassing listboxes // anyway, take care of this here. SetClassLong(hwnd, GCL_STYLE, GetClassLong(hwnd, GCL_STYLE) & ~CS_PARENTDC); } VOID DeselectSelectedRegions( VOID ) /*++ Routine Description: This routine visually unselects all selected regions. The selection state is also updated in the master disk structures. Arguments: None. Return Value: None. --*/ { DWORD i, j; PDISKSTATE diskState; for (i=0; iRegionCount; j++) { if (diskState->Selected[j]) { diskState->Selected[j] = FALSE; ToggleRegion(diskState, j, NULL); } } } } VOID Selection( IN BOOL MultipleSel, IN PDISKSTATE DiskState, IN DWORD RegionIndex ) /*++ Routine Description: This routine handles a user selection of a disk region. It is called directly for a keyboard selection or indirectly for a mouse selection. If not a multiple selection, all selected regions are deselected. The focus rectangle is moved to the selected region, which is then visually selected. Arguments: MultipleSel - whether the user has made a multiple selection (ie, control-clicked). DiskState - master disk structure for disk containing selected region RegionIndex - index of selected region on the disk Return Value: None. --*/ { PFT_OBJECT ftObject, ftObj; PFT_OBJECT_SET ftSet; ULONG disk, r; if (!MultipleSel) { // need to deselect all selected regions first. DeselectSelectedRegions(); } // remove the list box selection cursor from its previous region ToggleLBCursor(NULL); // The selected region might be part of an ft object set. If it is, // scan each region in each disk and select each item in the set. if (ftObject = GET_FT_OBJECT(&DiskState->RegionArray[RegionIndex])) { ftSet = ftObject->Set; for (disk=0; diskRegionCount; r++) { PREGION_DESCRIPTOR regionDescriptor = &diskState->RegionArray[r]; if (DmSignificantRegion(regionDescriptor)) { if (ftObj = GET_FT_OBJECT(regionDescriptor)) { if (ftObj->Set == ftSet) { diskState->Selected[r] = (BOOLEAN)(!diskState->Selected[r]); ToggleRegion(diskState, r, NULL); } } } } } } else { DiskState->Selected[RegionIndex] = (BOOLEAN)(!DiskState->Selected[RegionIndex]); ToggleRegion(DiskState, RegionIndex, NULL); } LBCursorListBoxItem = DiskState->Disk; LBCursorRegion = RegionIndex; ToggleLBCursor(NULL); AdjustMenuAndStatus(); } VOID MouseSelection( IN BOOL MultipleSel, IN OUT PPOINT Point ) /*++ Routine Description: This routine is called when the user clicks in the list box. It determines which disk region the user has clicked on before calling the common selection subroutine. Arguments: MultipleSel - whether the user has made a multiple selection (ie, control-clicked). point - screen coords of the click Return Value: None. --*/ { PDISKSTATE diskState; DWORD selectedItem; DWORD x, y; DWORD i; RECT rc; BOOL valid; if ((selectedItem = SendMessage(hwndList, LB_GETCURSEL, 0, 0)) == LB_ERR) { return; } // user has clicked on a list box item. diskState = Disks[selectedItem]; // Ignore clicks on off-line disks. if (diskState->OffLine) { return; } ScreenToClient(hwndList, Point); x = Point->x; y = Point->y; GetClientRect(hwndList,&rc); // first make sure that the click was within a bar and not in space // between two bars for (valid=FALSE, i=rc.top; i<=(DWORD)rc.bottom; i+=GraphHeight) { if ((y >= i+BarTopYOffset) && (y <= i+BarBottomYOffset)) { valid = TRUE; break; } } if (!valid) { return; } // determine which region he has clicked on for (i=0; iRegionCount; i++) { if ((x >= (unsigned)diskState->LeftRight[i].Left) && (x <= (unsigned)diskState->LeftRight[i].Right)) { break; } } if (i == diskState->RegionCount) { return; // region not found. Ignore the click. } Selection(MultipleSel, diskState, i); } LONG CalcBarTop( DWORD Bar ) /*++ Routine Description: This routine calculates the current top y coord of a given bar. The value is in listbox client coords. Arguments: Bar - # of bar whose position is desired Return Value: Y-coord, or -1 if bar is not visible. --*/ { LONG barDelta = (LONG)Bar - SendMessage(hwndList, LB_GETTOPINDEX, 0, 0); LONG pos = -1; RECT rc; if (barDelta >= 0) { // BUGBUG check bottom too GetClientRect(hwndList,&rc); pos = rc.top + (barDelta * GraphHeight); } return pos; } VOID ResetLBCursorRegion( VOID ) /*++ Routine Description: This routine resets the list box focus cursor to the 0th (leftmost) region on the current disk. If the 0th region is the extended partition, focus is set to the first logical volume or free space with the extended partition instead. Arguments: None. Return Value: None. --*/ { PDISKSTATE diskState = Disks[LBCursorListBoxItem]; unsigned i; LBCursorRegion = 0; if (IsExtended(diskState->RegionArray[LBCursorRegion].SysID)) { for (i=0; iRegionCount; i++) { if (diskState->RegionArray[i].RegionType == REGION_LOGICAL) { LBCursorRegion = i; return; } } FDASSERT(0); } } VOID ToggleLBCursor( IN HDC hdc ) /*++ Routine Description: This routine visually toggles the focus state of the disk region described by the LBCursorListBoxItem and LBCursorRegion globals. Arguments: hdc - If non-NULL, device context to use for drawing. If NULL, we'll first get a DC via GetDC(). Return Value: None. --*/ { PDISKSTATE lBCursorDisk = Disks[LBCursorListBoxItem]; LONG barTop = CalcBarTop(LBCursorListBoxItem); RECT rc; HDC hdcActual; if (barTop != -1) { hdcActual = hdc ? hdc : GetDC(hwndList); LBCursorOn = !LBCursorOn; rc.left = lBCursorDisk->LeftRight[LBCursorRegion].Left; rc.right = lBCursorDisk->LeftRight[LBCursorRegion].Right + 1; rc.top = barTop + BarTopYOffset; rc.bottom = barTop + BarBottomYOffset; FrameRect(hdcActual, &rc, GetStockObject(LBCursorOn ? WHITE_BRUSH : BLACK_BRUSH)); if (LBCursorOn) { // BUGBUG really want a dotted line. DrawFocusRect(hdcActual, &rc); } if (!hdc) { ReleaseDC(hwndList, hdcActual); } } } VOID ForceLBRedraw( VOID ) /*++ Routine Description: This routine forces redraw of the listbox by invalidating its entire client area. Arguments: None. Return Value: None. --*/ { InvalidateRect(hwndList,NULL,FALSE); UpdateWindow(hwndList); } VOID ToggleRegion( IN PDISKSTATE DiskState, IN DWORD RegionIndex, IN HDC hdc ) /*++ Routine Description: This routine visually toggles the selection state of a given disk region. Arguments: DiskState - master structure for disk containing region to select RegionIndex - which region on the disk to toggle hdc - if non-NULL, device context to use for drawing. If NULL, we'll first get a device context via GetDC(). Return Value: None. --*/ { PLEFTRIGHT leftRight = &DiskState->LeftRight[RegionIndex]; LONG barTop = CalcBarTop(DiskState->Disk); // BUGBUG disk# as lb index# BOOL selected = (BOOL)DiskState->Selected[RegionIndex]; HBRUSH hbr = GetStockObject(BLACK_BRUSH); HDC hdcActual; RECT rc; int i; if (barTop != -1) { hdcActual = hdc ? hdc : GetDC(hwndList); rc.left = leftRight->Left + 1; rc.right = leftRight->Right; rc.top = barTop + BarTopYOffset + 1; rc.bottom = barTop + BarBottomYOffset - 1; if (selected) { for (i=0; iLeft, barTop + BarTopYOffset, leftRight->Right - leftRight->Left, barTop + BarBottomYOffset, DiskState->hDCMem, leftRight->Left, BarTopYOffset, SRCCOPY); } if (!hdc) { ReleaseDC(hwndList, hdcActual); } } } DWORD InitializeListBox( IN HWND hwndListBox ) /*++ Routine Description: This routine sets up the list box. This includes creating disk state structures, drawing the graphs for each disk off screen, and adding the disks to the list box. It also includes determining the initial volume labels and type names for all significant partitions. Arguments: hwndListBox - handle of the list box that will hold the disk graphs Return Value: Windows error code (esp. out of memory) --*/ { PPERSISTENT_REGION_DATA regionData; TCHAR windowsDir[MAX_PATH]; unsigned i; PDISKSTATE diskState; DWORD ec; ULONG r; BOOL diskSignaturesCreated, temp; // First, create the array that will hold the diskstates, // the IsDiskRemovable array and the RemovableDiskReservedDriveLetters // array. Disks = Malloc(DiskCount * sizeof(PDISKSTATE)); IsDiskRemovable = (PBOOLEAN)Malloc(DiskCount * sizeof(BOOLEAN)); RemovableDiskReservedDriveLetters = (PCHAR)Malloc(DiskCount * sizeof(CHAR)); // Determine which disks are removable and which are unpartitioned. for (i=0; iRegionCount; r++) { if (DmSignificantRegion(&diskState->RegionArray[r])) { regionData = Malloc(sizeof(PERSISTENT_REGION_DATA)); DmInitPersistentRegionData(regionData, NULL, NULL, NULL, NO_DRIVE_LETTER_YET); regionData->VolumeExists = TRUE; } else { regionData = NULL; } DmSetPersistentRegionData(&diskState->RegionArray[r], regionData); } // add the item to the listbox while (((ec = SendMessage(hwndListBox, LB_ADDSTRING, 0, 0)) == LB_ERR) || (ec == LB_ERRSPACE)) { ConfirmOutOfMemory(); } } // Read the configuration registry if ((ec = InitializeFt(diskSignaturesCreated)) != NO_ERROR) { ErrorDialog(ec); return ec; } // Determine drive letter mappings InitializeDriveLetterInfo(); // Determine volume labels and type names. InitVolumeLabelsAndTypeNames(); // Determine which disk is the boot disk. if (GetWindowsDirectory(windowsDir, sizeof(windowsDir)/sizeof(TCHAR)) < 2 || windowsDir[1] != TEXT(':')) { BootDiskNumber = (ULONG)-1; BootPartitionNumber = (ULONG)-1; } else { BootDiskNumber = GetDiskNumberFromDriveLetter((CHAR)windowsDir[0]); BootPartitionNumber = GetPartitionNumberFromDriveLetter((CHAR)windowsDir[0]); } // Locate and create data structures for any DoubleSpace volumes DblSpaceInitialize(); for (i=0; iitemID != (DWORD)(-1)) && (pDrawItem->itemAction == ODA_DRAWENTIRE)) { pDiskState = Disks[pDrawItem->itemID]; // blt the disk's bar from the off-screen bitmap to the screen BitBlt(pDrawItem->hDC, pDrawItem->rcItem.left, pDrawItem->rcItem.top, pDrawItem->rcItem.right - pDrawItem->rcItem.left + 1, pDrawItem->rcItem.bottom - pDrawItem->rcItem.top + 1, pDiskState->hDCMem, 0, 0, SRCCOPY); // if we just overwrote the focus cursor, redraw it if (pDrawItem->itemID == LBCursorListBoxItem) { LBCursorOn = FALSE; ToggleLBCursor(pDrawItem->hDC); } // select any items selected in this bar for (temp=0; tempRegionCount; temp++) { if (pDiskState->Selected[temp]) { ToggleRegion(pDiskState, temp, pDrawItem->hDC); } } } }