1 /* 2 * ReactOS kernel 3 * Copyright (C) 2002, 2003, 2004, 2005 ReactOS Team 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License along 16 * with this program; if not, write to the Free Software Foundation, Inc., 17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 */ 19 /* 20 * COPYRIGHT: See COPYING in the top level directory 21 * PROJECT: ReactOS text-mode setup 22 * FILE: base/setup/usetup/partlist.c 23 * PURPOSE: Partition list functions 24 * PROGRAMMER: Casper S. Hornstrup (chorns@users.sourceforge.net) 25 */ 26 27 #include "usetup.h" 28 29 #define NDEBUG 30 #include <debug.h> 31 32 /* HELPERS FOR DISK AND PARTITION DESCRIPTIONS ******************************/ 33 34 VOID 35 GetPartitionTypeString( 36 IN PPARTENTRY PartEntry, 37 OUT PSTR strBuffer, 38 IN ULONG cchBuffer) 39 { 40 if (PartEntry->PartitionType == PARTITION_ENTRY_UNUSED) 41 { 42 RtlStringCchCopyA(strBuffer, cchBuffer, 43 MUIGetString(STRING_FORMATUNUSED)); 44 } 45 else if (IsContainerPartition(PartEntry->PartitionType)) 46 { 47 RtlStringCchCopyA(strBuffer, cchBuffer, 48 MUIGetString(STRING_EXTENDED_PARTITION)); 49 } 50 else 51 { 52 UINT i; 53 54 /* Do the table lookup */ 55 if (PartEntry->DiskEntry->DiskStyle == PARTITION_STYLE_MBR) 56 { 57 for (i = 0; i < ARRAYSIZE(MbrPartitionTypes); ++i) 58 { 59 if (PartEntry->PartitionType == MbrPartitionTypes[i].Type) 60 { 61 RtlStringCchCopyA(strBuffer, cchBuffer, 62 MbrPartitionTypes[i].Description); 63 return; 64 } 65 } 66 } 67 #if 0 // TODO: GPT support! 68 else if (PartEntry->DiskEntry->DiskStyle == PARTITION_STYLE_GPT) 69 { 70 for (i = 0; i < ARRAYSIZE(GptPartitionTypes); ++i) 71 { 72 if (IsEqualPartitionType(PartEntry->PartitionType, 73 GptPartitionTypes[i].Guid)) 74 { 75 RtlStringCchCopyA(strBuffer, cchBuffer, 76 GptPartitionTypes[i].Description); 77 return; 78 } 79 } 80 } 81 #endif 82 83 /* We are here because the partition type is unknown */ 84 if (cchBuffer > 0) *strBuffer = '\0'; 85 } 86 87 if ((cchBuffer > 0) && (*strBuffer == '\0')) 88 { 89 RtlStringCchPrintfA(strBuffer, cchBuffer, 90 MUIGetString(STRING_PARTTYPE), 91 PartEntry->PartitionType); 92 } 93 } 94 95 VOID 96 PrettifySize1( 97 IN OUT PULONGLONG Size, 98 OUT PCSTR* Unit) 99 { 100 ULONGLONG DiskSize = *Size; 101 102 if (DiskSize >= 10 * GB) /* 10 GB */ 103 { 104 DiskSize = RoundingDivide(DiskSize, GB); 105 *Unit = MUIGetString(STRING_GB); 106 } 107 else 108 { 109 DiskSize = RoundingDivide(DiskSize, MB); 110 if (DiskSize == 0) 111 DiskSize = 1; 112 *Unit = MUIGetString(STRING_MB); 113 } 114 115 *Size = DiskSize; 116 } 117 118 VOID 119 PrettifySize2( 120 IN OUT PULONGLONG Size, 121 OUT PCSTR* Unit) 122 { 123 ULONGLONG PartSize = *Size; 124 125 #if 0 126 if (PartSize >= 10 * GB) /* 10 GB */ 127 { 128 PartSize = RoundingDivide(PartSize, GB); 129 *Unit = MUIGetString(STRING_GB); 130 } 131 else 132 #endif 133 if (PartSize >= 10 * MB) /* 10 MB */ 134 { 135 PartSize = RoundingDivide(PartSize, MB); 136 *Unit = MUIGetString(STRING_MB); 137 } 138 else 139 { 140 PartSize = RoundingDivide(PartSize, KB); 141 *Unit = MUIGetString(STRING_KB); 142 } 143 144 *Size = PartSize; 145 } 146 147 VOID 148 PartitionDescription( 149 IN PPARTENTRY PartEntry, 150 OUT PSTR strBuffer, 151 IN SIZE_T cchBuffer) 152 { 153 PSTR pBuffer = strBuffer; 154 size_t cchBufferSize = cchBuffer; 155 ULONGLONG PartSize; 156 PCSTR Unit; 157 158 /* Get the partition size */ 159 PartSize = PartEntry->SectorCount.QuadPart * PartEntry->DiskEntry->BytesPerSector; 160 PrettifySize2(&PartSize, &Unit); 161 162 if (PartEntry->IsPartitioned == FALSE) 163 { 164 /* Unpartitioned space: Just display the description and size */ 165 RtlStringCchPrintfExA(pBuffer, cchBufferSize, 166 &pBuffer, &cchBufferSize, 0, 167 " %s%-.30s", 168 PartEntry->LogicalPartition ? " " : "", // Optional indentation 169 MUIGetString(STRING_UNPSPACE)); 170 171 RtlStringCchPrintfA(pBuffer, cchBufferSize, 172 "%*s%6I64u %s", 173 38 - min(strlen(strBuffer), 38), "", // Indentation 174 PartSize, 175 Unit); 176 return; 177 } 178 179 // 180 // NOTE: This could be done with the next case. 181 // 182 if ((PartEntry->DiskEntry->DiskStyle == PARTITION_STYLE_MBR) && 183 IsContainerPartition(PartEntry->PartitionType)) 184 { 185 /* Extended partition container: Just display the partition's type and size */ 186 RtlStringCchPrintfExA(pBuffer, cchBufferSize, 187 &pBuffer, &cchBufferSize, 0, 188 " %-.30s", 189 MUIGetString(STRING_EXTENDED_PARTITION)); 190 191 RtlStringCchPrintfA(pBuffer, cchBufferSize, 192 "%*s%6I64u %s", 193 38 - min(strlen(strBuffer), 38), "", // Indentation 194 PartSize, 195 Unit); 196 return; 197 } 198 199 /* 200 * Not an extended partition container. 201 */ 202 203 /* Drive letter and partition number */ 204 RtlStringCchPrintfExA(pBuffer, cchBufferSize, 205 &pBuffer, &cchBufferSize, 0, 206 "%c%c %c %s(%lu) ", 207 (PartEntry->DriveLetter == 0) ? '-' : (CHAR)PartEntry->DriveLetter, 208 (PartEntry->DriveLetter == 0) ? '-' : ':', 209 PartEntry->BootIndicator ? '*' : ' ', 210 PartEntry->LogicalPartition ? " " : "", // Optional indentation 211 PartEntry->PartitionNumber); 212 213 /* 214 * If the volume's file system is recognized, display the volume label 215 * (if any) and the file system name. Otherwise, display the partition 216 * type if it's not a new partition. 217 */ 218 if (!PartEntry->New && *PartEntry->FileSystem && 219 _wcsicmp(PartEntry->FileSystem, L"RAW") != 0) 220 { 221 size_t cchLabelSize = 0; 222 if (*PartEntry->VolumeLabel) 223 { 224 RtlStringCchPrintfExA(pBuffer, cchBufferSize, 225 &pBuffer, &cchLabelSize, 0, 226 "\"%-.11S\" ", 227 PartEntry->VolumeLabel); 228 cchLabelSize = cchBufferSize - cchLabelSize; // Actual length of the label part. 229 cchBufferSize -= cchLabelSize; // And reset cchBufferSize to what it should be. 230 } 231 232 // TODO: Group this part together with the similar one 233 // from below once the strings are in the same encoding... 234 RtlStringCchPrintfExA(pBuffer, cchBufferSize, 235 &pBuffer, &cchBufferSize, 0, 236 "[%-.*S]", 237 /* The minimum length can be at most 11 since 238 * cchLabelSize can be at most == 11 + 3 == 14 */ 239 25 - min(cchLabelSize, 25), 240 PartEntry->FileSystem); 241 } 242 else 243 { 244 CHAR PartTypeString[32]; 245 PCSTR PartType = PartTypeString; 246 247 if (PartEntry->New) 248 { 249 /* Use this description if the partition is new (and thus, not formatted) */ 250 PartType = MUIGetString(STRING_UNFORMATTED); 251 } 252 else 253 { 254 /* If the partition is not new but its file system is not recognized 255 * (or is not formatted), use the partition type description. */ 256 GetPartitionTypeString(PartEntry, 257 PartTypeString, 258 ARRAYSIZE(PartTypeString)); 259 PartType = PartTypeString; 260 } 261 if (!PartType || !*PartType) 262 { 263 PartType = MUIGetString(STRING_FORMATUNKNOWN); 264 } 265 266 // TODO: Group this part together with the similar one 267 // from above once the strings are in the same encoding... 268 RtlStringCchPrintfExA(pBuffer, cchBufferSize, 269 &pBuffer, &cchBufferSize, 0, 270 "[%-.*s]", 271 25, 272 PartType); 273 } 274 275 /* Show the remaining free space only if a FS is mounted */ 276 // FIXME: We don't support that yet! 277 #if 0 278 if (*PartEntry->FileSystem) 279 { 280 RtlStringCchPrintfA(pBuffer, cchBufferSize, 281 "%*s%6I64u %s (%6I64u %s %s)", 282 38 - min(strlen(strBuffer), 38), "", // Indentation 283 PartSize, 284 Unit, 285 PartFreeSize, 286 Unit, 287 "free"); 288 } 289 else 290 #endif 291 { 292 RtlStringCchPrintfA(pBuffer, cchBufferSize, 293 "%*s%6I64u %s", 294 38 - min(strlen(strBuffer), 38), "", // Indentation 295 PartSize, 296 Unit); 297 } 298 } 299 300 VOID 301 DiskDescription( 302 IN PDISKENTRY DiskEntry, 303 OUT PSTR strBuffer, 304 IN SIZE_T cchBuffer) 305 { 306 ULONGLONG DiskSize; 307 PCSTR Unit; 308 309 /* Get the disk size */ 310 DiskSize = DiskEntry->SectorCount.QuadPart * DiskEntry->BytesPerSector; 311 PrettifySize1(&DiskSize, &Unit); 312 313 // 314 // FIXME: We *MUST* use TXTSETUP.SIF strings from section "DiskDriverMap" !! 315 // 316 if (DiskEntry->DriverName.Length > 0) 317 { 318 RtlStringCchPrintfA(strBuffer, cchBuffer, 319 MUIGetString(STRING_HDDINFO_1), 320 DiskSize, 321 Unit, 322 DiskEntry->DiskNumber, 323 DiskEntry->Port, 324 DiskEntry->Bus, 325 DiskEntry->Id, 326 &DiskEntry->DriverName, 327 DiskEntry->DiskStyle == PARTITION_STYLE_MBR ? "MBR" : 328 DiskEntry->DiskStyle == PARTITION_STYLE_GPT ? "GPT" : 329 "RAW"); 330 } 331 else 332 { 333 RtlStringCchPrintfA(strBuffer, cchBuffer, 334 MUIGetString(STRING_HDDINFO_2), 335 DiskSize, 336 Unit, 337 DiskEntry->DiskNumber, 338 DiskEntry->Port, 339 DiskEntry->Bus, 340 DiskEntry->Id, 341 DiskEntry->DiskStyle == PARTITION_STYLE_MBR ? "MBR" : 342 DiskEntry->DiskStyle == PARTITION_STYLE_GPT ? "GPT" : 343 "RAW"); 344 } 345 } 346 347 348 /* FUNCTIONS ****************************************************************/ 349 350 VOID 351 InitPartitionListUi( 352 IN OUT PPARTLIST_UI ListUi, 353 IN PPARTLIST List, 354 IN PPARTENTRY CurrentEntry OPTIONAL, 355 IN SHORT Left, 356 IN SHORT Top, 357 IN SHORT Right, 358 IN SHORT Bottom) 359 { 360 ListUi->List = List; 361 // ListUi->FirstShown = NULL; 362 // ListUi->LastShown = NULL; 363 364 ListUi->Left = Left; 365 ListUi->Top = Top; 366 ListUi->Right = Right; 367 ListUi->Bottom = Bottom; 368 369 ListUi->Line = 0; 370 ListUi->Offset = 0; 371 372 // ListUi->Redraw = TRUE; 373 374 /* Search for first usable disk and partition */ 375 if (!CurrentEntry) 376 { 377 ListUi->CurrentDisk = NULL; 378 ListUi->CurrentPartition = NULL; 379 380 if (!IsListEmpty(&List->DiskListHead)) 381 { 382 ListUi->CurrentDisk = CONTAINING_RECORD(List->DiskListHead.Flink, 383 DISKENTRY, ListEntry); 384 385 if (!IsListEmpty(&ListUi->CurrentDisk->PrimaryPartListHead)) 386 { 387 ListUi->CurrentPartition = CONTAINING_RECORD(ListUi->CurrentDisk->PrimaryPartListHead.Flink, 388 PARTENTRY, ListEntry); 389 } 390 } 391 } 392 else 393 { 394 /* 395 * The CurrentEntry must belong to the associated partition list, 396 * and the latter must therefore not be empty. 397 */ 398 ASSERT(!IsListEmpty(&List->DiskListHead)); 399 ASSERT(CurrentEntry->DiskEntry->PartList == List); 400 401 ListUi->CurrentPartition = CurrentEntry; 402 ListUi->CurrentDisk = CurrentEntry->DiskEntry; 403 } 404 } 405 406 static 407 VOID 408 PrintEmptyLine( 409 IN PPARTLIST_UI ListUi) 410 { 411 COORD coPos; 412 ULONG Written; 413 USHORT Width; 414 USHORT Height; 415 416 Width = ListUi->Right - ListUi->Left - 1; 417 Height = ListUi->Bottom - ListUi->Top - 2; 418 419 coPos.X = ListUi->Left + 1; 420 coPos.Y = ListUi->Top + 1 + ListUi->Line; 421 422 if (ListUi->Line >= 0 && ListUi->Line <= Height) 423 { 424 FillConsoleOutputAttribute(StdOutput, 425 FOREGROUND_WHITE | BACKGROUND_BLUE, 426 Width, 427 coPos, 428 &Written); 429 430 FillConsoleOutputCharacterA(StdOutput, 431 ' ', 432 Width, 433 coPos, 434 &Written); 435 } 436 437 ListUi->Line++; 438 } 439 440 static 441 VOID 442 PrintPartitionData( 443 IN PPARTLIST_UI ListUi, 444 IN PDISKENTRY DiskEntry, 445 IN PPARTENTRY PartEntry) 446 { 447 COORD coPos; 448 ULONG Written; 449 USHORT Width; 450 USHORT Height; 451 UCHAR Attribute; 452 CHAR LineBuffer[100]; 453 454 PartitionDescription(PartEntry, LineBuffer, ARRAYSIZE(LineBuffer)); 455 456 Width = ListUi->Right - ListUi->Left - 1; 457 Height = ListUi->Bottom - ListUi->Top - 2; 458 459 coPos.X = ListUi->Left + 1; 460 coPos.Y = ListUi->Top + 1 + ListUi->Line; 461 462 Attribute = (ListUi->CurrentDisk == DiskEntry && 463 ListUi->CurrentPartition == PartEntry) ? 464 FOREGROUND_BLUE | BACKGROUND_WHITE : 465 FOREGROUND_WHITE | BACKGROUND_BLUE; 466 467 if (ListUi->Line >= 0 && ListUi->Line <= Height) 468 { 469 FillConsoleOutputCharacterA(StdOutput, 470 ' ', 471 Width, 472 coPos, 473 &Written); 474 } 475 coPos.X += 4; 476 Width -= 8; 477 if (ListUi->Line >= 0 && ListUi->Line <= Height) 478 { 479 FillConsoleOutputAttribute(StdOutput, 480 Attribute, 481 Width, 482 coPos, 483 &Written); 484 } 485 coPos.X++; 486 Width -= 2; 487 if (ListUi->Line >= 0 && ListUi->Line <= Height) 488 { 489 WriteConsoleOutputCharacterA(StdOutput, 490 LineBuffer, 491 min(strlen(LineBuffer), Width), 492 coPos, 493 &Written); 494 } 495 496 ListUi->Line++; 497 } 498 499 static 500 VOID 501 PrintDiskData( 502 IN PPARTLIST_UI ListUi, 503 IN PDISKENTRY DiskEntry) 504 { 505 PPARTENTRY PrimaryPartEntry, LogicalPartEntry; 506 PLIST_ENTRY PrimaryEntry, LogicalEntry; 507 COORD coPos; 508 ULONG Written; 509 USHORT Width; 510 USHORT Height; 511 CHAR LineBuffer[100]; 512 513 DiskDescription(DiskEntry, LineBuffer, ARRAYSIZE(LineBuffer)); 514 515 Width = ListUi->Right - ListUi->Left - 1; 516 Height = ListUi->Bottom - ListUi->Top - 2; 517 518 coPos.X = ListUi->Left + 1; 519 coPos.Y = ListUi->Top + 1 + ListUi->Line; 520 521 if (ListUi->Line >= 0 && ListUi->Line <= Height) 522 { 523 FillConsoleOutputAttribute(StdOutput, 524 FOREGROUND_WHITE | BACKGROUND_BLUE, 525 Width, 526 coPos, 527 &Written); 528 529 FillConsoleOutputCharacterA(StdOutput, 530 ' ', 531 Width, 532 coPos, 533 &Written); 534 } 535 536 coPos.X++; 537 if (ListUi->Line >= 0 && ListUi->Line <= Height) 538 { 539 WriteConsoleOutputCharacterA(StdOutput, 540 LineBuffer, 541 min((USHORT)strlen(LineBuffer), Width - 2), 542 coPos, 543 &Written); 544 } 545 546 ListUi->Line++; 547 548 /* Print separator line */ 549 PrintEmptyLine(ListUi); 550 551 /* Print partition lines */ 552 for (PrimaryEntry = DiskEntry->PrimaryPartListHead.Flink; 553 PrimaryEntry != &DiskEntry->PrimaryPartListHead; 554 PrimaryEntry = PrimaryEntry->Flink) 555 { 556 PrimaryPartEntry = CONTAINING_RECORD(PrimaryEntry, PARTENTRY, ListEntry); 557 558 PrintPartitionData(ListUi, 559 DiskEntry, 560 PrimaryPartEntry); 561 562 if (IsContainerPartition(PrimaryPartEntry->PartitionType)) 563 { 564 for (LogicalEntry = DiskEntry->LogicalPartListHead.Flink; 565 LogicalEntry != &DiskEntry->LogicalPartListHead; 566 LogicalEntry = LogicalEntry->Flink) 567 { 568 LogicalPartEntry = CONTAINING_RECORD(LogicalEntry, PARTENTRY, ListEntry); 569 570 PrintPartitionData(ListUi, 571 DiskEntry, 572 LogicalPartEntry); 573 } 574 } 575 } 576 577 /* Print separator line */ 578 PrintEmptyLine(ListUi); 579 } 580 581 VOID 582 DrawPartitionList( 583 IN PPARTLIST_UI ListUi) 584 { 585 PPARTLIST List = ListUi->List; 586 PLIST_ENTRY Entry, Entry2; 587 PDISKENTRY DiskEntry; 588 PPARTENTRY PartEntry = NULL; 589 COORD coPos; 590 ULONG Written; 591 USHORT Width; 592 USHORT Height; 593 SHORT i; 594 SHORT CurrentDiskLine; 595 SHORT CurrentPartLine; 596 SHORT LastLine; 597 BOOLEAN CurrentPartLineFound = FALSE; 598 BOOLEAN CurrentDiskLineFound = FALSE; 599 600 Width = ListUi->Right - ListUi->Left - 1; 601 Height = ListUi->Bottom - ListUi->Top - 2; 602 603 /* Calculate the line of the current disk and partition */ 604 CurrentDiskLine = 0; 605 CurrentPartLine = 0; 606 LastLine = 0; 607 608 for (Entry = List->DiskListHead.Flink; 609 Entry != &List->DiskListHead; 610 Entry = Entry->Flink) 611 { 612 DiskEntry = CONTAINING_RECORD(Entry, DISKENTRY, ListEntry); 613 614 LastLine += 2; 615 if (CurrentPartLineFound == FALSE) 616 { 617 CurrentPartLine += 2; 618 } 619 620 for (Entry2 = DiskEntry->PrimaryPartListHead.Flink; 621 Entry2 != &DiskEntry->PrimaryPartListHead; 622 Entry2 = Entry2->Flink) 623 { 624 PartEntry = CONTAINING_RECORD(Entry2, PARTENTRY, ListEntry); 625 if (PartEntry == ListUi->CurrentPartition) 626 { 627 CurrentPartLineFound = TRUE; 628 } 629 630 if (CurrentPartLineFound == FALSE) 631 { 632 CurrentPartLine++; 633 } 634 635 LastLine++; 636 } 637 638 if (CurrentPartLineFound == FALSE) 639 { 640 for (Entry2 = DiskEntry->LogicalPartListHead.Flink; 641 Entry2 != &DiskEntry->LogicalPartListHead; 642 Entry2 = Entry2->Flink) 643 { 644 PartEntry = CONTAINING_RECORD(Entry2, PARTENTRY, ListEntry); 645 if (PartEntry == ListUi->CurrentPartition) 646 { 647 CurrentPartLineFound = TRUE; 648 } 649 650 if (CurrentPartLineFound == FALSE) 651 { 652 CurrentPartLine++; 653 } 654 655 LastLine++; 656 } 657 } 658 659 if (DiskEntry == ListUi->CurrentDisk) 660 { 661 CurrentDiskLineFound = TRUE; 662 } 663 664 if (Entry->Flink != &List->DiskListHead) 665 { 666 if (CurrentDiskLineFound == FALSE) 667 { 668 CurrentPartLine++; 669 CurrentDiskLine = CurrentPartLine; 670 } 671 672 LastLine++; 673 } 674 else 675 { 676 LastLine--; 677 } 678 } 679 680 /* If it possible, make the disk name visible */ 681 if (CurrentPartLine < ListUi->Offset) 682 { 683 ListUi->Offset = CurrentPartLine; 684 } 685 else if (CurrentPartLine - ListUi->Offset > Height) 686 { 687 ListUi->Offset = CurrentPartLine - Height; 688 } 689 690 if (CurrentDiskLine < ListUi->Offset && CurrentPartLine - CurrentDiskLine < Height) 691 { 692 ListUi->Offset = CurrentDiskLine; 693 } 694 695 /* Draw upper left corner */ 696 coPos.X = ListUi->Left; 697 coPos.Y = ListUi->Top; 698 FillConsoleOutputCharacterA(StdOutput, 699 0xDA, // '+', 700 1, 701 coPos, 702 &Written); 703 704 /* Draw upper edge */ 705 coPos.X = ListUi->Left + 1; 706 coPos.Y = ListUi->Top; 707 if (ListUi->Offset == 0) 708 { 709 FillConsoleOutputCharacterA(StdOutput, 710 0xC4, // '-', 711 Width, 712 coPos, 713 &Written); 714 } 715 else 716 { 717 FillConsoleOutputCharacterA(StdOutput, 718 0xC4, // '-', 719 Width - 4, 720 coPos, 721 &Written); 722 coPos.X = ListUi->Right - 5; 723 WriteConsoleOutputCharacterA(StdOutput, 724 "(\x18)", // "(up)" 725 3, 726 coPos, 727 &Written); 728 coPos.X = ListUi->Right - 2; 729 FillConsoleOutputCharacterA(StdOutput, 730 0xC4, // '-', 731 2, 732 coPos, 733 &Written); 734 } 735 736 /* Draw upper right corner */ 737 coPos.X = ListUi->Right; 738 coPos.Y = ListUi->Top; 739 FillConsoleOutputCharacterA(StdOutput, 740 0xBF, // '+', 741 1, 742 coPos, 743 &Written); 744 745 /* Draw left and right edge */ 746 for (i = ListUi->Top + 1; i < ListUi->Bottom; i++) 747 { 748 coPos.X = ListUi->Left; 749 coPos.Y = i; 750 FillConsoleOutputCharacterA(StdOutput, 751 0xB3, // '|', 752 1, 753 coPos, 754 &Written); 755 756 coPos.X = ListUi->Right; 757 FillConsoleOutputCharacterA(StdOutput, 758 0xB3, //'|', 759 1, 760 coPos, 761 &Written); 762 } 763 764 /* Draw lower left corner */ 765 coPos.X = ListUi->Left; 766 coPos.Y = ListUi->Bottom; 767 FillConsoleOutputCharacterA(StdOutput, 768 0xC0, // '+', 769 1, 770 coPos, 771 &Written); 772 773 /* Draw lower edge */ 774 coPos.X = ListUi->Left + 1; 775 coPos.Y = ListUi->Bottom; 776 if (LastLine - ListUi->Offset <= Height) 777 { 778 FillConsoleOutputCharacterA(StdOutput, 779 0xC4, // '-', 780 Width, 781 coPos, 782 &Written); 783 } 784 else 785 { 786 FillConsoleOutputCharacterA(StdOutput, 787 0xC4, // '-', 788 Width - 4, 789 coPos, 790 &Written); 791 coPos.X = ListUi->Right - 5; 792 WriteConsoleOutputCharacterA(StdOutput, 793 "(\x19)", // "(down)" 794 3, 795 coPos, 796 &Written); 797 coPos.X = ListUi->Right - 2; 798 FillConsoleOutputCharacterA(StdOutput, 799 0xC4, // '-', 800 2, 801 coPos, 802 &Written); 803 } 804 805 /* Draw lower right corner */ 806 coPos.X = ListUi->Right; 807 coPos.Y = ListUi->Bottom; 808 FillConsoleOutputCharacterA(StdOutput, 809 0xD9, // '+', 810 1, 811 coPos, 812 &Written); 813 814 /* Print list entries */ 815 ListUi->Line = -ListUi->Offset; 816 817 for (Entry = List->DiskListHead.Flink; 818 Entry != &List->DiskListHead; 819 Entry = Entry->Flink) 820 { 821 DiskEntry = CONTAINING_RECORD(Entry, DISKENTRY, ListEntry); 822 823 /* Print disk entry */ 824 PrintDiskData(ListUi, DiskEntry); 825 } 826 } 827 828 VOID 829 ScrollDownPartitionList( 830 IN PPARTLIST_UI ListUi) 831 { 832 PPARTENTRY NextPart = GetNextPartition(ListUi->List, ListUi->CurrentPartition); 833 if (NextPart) 834 { 835 ListUi->CurrentPartition = NextPart; 836 ListUi->CurrentDisk = NextPart->DiskEntry; 837 DrawPartitionList(ListUi); 838 } 839 } 840 841 VOID 842 ScrollUpPartitionList( 843 IN PPARTLIST_UI ListUi) 844 { 845 PPARTENTRY PrevPart = GetPrevPartition(ListUi->List, ListUi->CurrentPartition); 846 if (PrevPart) 847 { 848 ListUi->CurrentPartition = PrevPart; 849 ListUi->CurrentDisk = PrevPart->DiskEntry; 850 DrawPartitionList(ListUi); 851 } 852 } 853 854 /* EOF */ 855