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 PVOLINFO VolInfo = (PartEntry->Volume ? &PartEntry->Volume->Info : NULL); 158 159 /* Get the partition size */ 160 PartSize = GetPartEntrySizeInBytes(PartEntry); 161 PrettifySize2(&PartSize, &Unit); 162 163 if (PartEntry->IsPartitioned == FALSE) 164 { 165 /* Unpartitioned space: Just display the description and size */ 166 RtlStringCchPrintfExA(pBuffer, cchBufferSize, 167 &pBuffer, &cchBufferSize, 0, 168 " %s%-.30s", 169 PartEntry->LogicalPartition ? " " : "", // Optional indentation 170 MUIGetString(STRING_UNPSPACE)); 171 172 RtlStringCchPrintfA(pBuffer, cchBufferSize, 173 "%*s%6I64u %s", 174 38 - min(strlen(strBuffer), 38), "", // Indentation 175 PartSize, 176 Unit); 177 return; 178 } 179 180 // 181 // NOTE: This could be done with the next case. 182 // 183 if ((PartEntry->DiskEntry->DiskStyle == PARTITION_STYLE_MBR) && 184 IsContainerPartition(PartEntry->PartitionType)) 185 { 186 /* Extended partition container: Just display the partition's type and size */ 187 RtlStringCchPrintfExA(pBuffer, cchBufferSize, 188 &pBuffer, &cchBufferSize, 0, 189 " %-.30s", 190 MUIGetString(STRING_EXTENDED_PARTITION)); 191 192 RtlStringCchPrintfA(pBuffer, cchBufferSize, 193 "%*s%6I64u %s", 194 38 - min(strlen(strBuffer), 38), "", // Indentation 195 PartSize, 196 Unit); 197 return; 198 } 199 200 /* 201 * Not an extended partition container. 202 */ 203 204 /* Drive letter and partition number */ 205 RtlStringCchPrintfExA(pBuffer, cchBufferSize, 206 &pBuffer, &cchBufferSize, 0, 207 "%c%c %c %s(%lu) ", 208 !(VolInfo && VolInfo->DriveLetter) ? '-' : (CHAR)VolInfo->DriveLetter, 209 !(VolInfo && VolInfo->DriveLetter) ? '-' : ':', 210 PartEntry->BootIndicator ? '*' : ' ', 211 PartEntry->LogicalPartition ? " " : "", // Optional indentation 212 PartEntry->PartitionNumber); 213 214 /* 215 * If the volume's file system is recognized, display the volume label 216 * (if any) and the file system name. Otherwise, display the partition 217 * type if it's not a new partition. 218 */ 219 if (VolInfo && IsFormatted(VolInfo)) 220 { 221 size_t cchLabelSize = 0; 222 if (*VolInfo->VolumeLabel) 223 { 224 RtlStringCchPrintfExA(pBuffer, cchBufferSize, 225 &pBuffer, &cchLabelSize, 0, 226 "\"%-.11S\" ", 227 VolInfo->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 VolInfo->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 (VolInfo && *VolInfo->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 = GetDiskSizeInBytes(DiskEntry); 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_HDDINFO1), 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_HDDINFO2), 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 CharUpperLeftCorner, // '+', 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 CharHorizontalLine, // '-', 711 Width, 712 coPos, 713 &Written); 714 } 715 else 716 { 717 FillConsoleOutputCharacterA(StdOutput, 718 CharHorizontalLine, // '-', 719 Width - 4, 720 coPos, 721 &Written); 722 { 723 CHAR szBuff[] = "(.)"; // "(up)" 724 szBuff[1] = CharUpArrow; 725 coPos.X = ListUi->Right - 5; 726 WriteConsoleOutputCharacterA(StdOutput, 727 szBuff, 728 3, 729 coPos, 730 &Written); 731 } 732 coPos.X = ListUi->Right - 2; 733 FillConsoleOutputCharacterA(StdOutput, 734 CharHorizontalLine, // '-', 735 2, 736 coPos, 737 &Written); 738 } 739 740 /* Draw upper right corner */ 741 coPos.X = ListUi->Right; 742 coPos.Y = ListUi->Top; 743 FillConsoleOutputCharacterA(StdOutput, 744 CharUpperRightCorner, // '+', 745 1, 746 coPos, 747 &Written); 748 749 /* Draw left and right edge */ 750 for (i = ListUi->Top + 1; i < ListUi->Bottom; i++) 751 { 752 coPos.X = ListUi->Left; 753 coPos.Y = i; 754 FillConsoleOutputCharacterA(StdOutput, 755 CharVerticalLine, // '|', 756 1, 757 coPos, 758 &Written); 759 760 coPos.X = ListUi->Right; 761 FillConsoleOutputCharacterA(StdOutput, 762 CharVerticalLine, //'|', 763 1, 764 coPos, 765 &Written); 766 } 767 768 /* Draw lower left corner */ 769 coPos.X = ListUi->Left; 770 coPos.Y = ListUi->Bottom; 771 FillConsoleOutputCharacterA(StdOutput, 772 CharLowerLeftCorner, // '+', 773 1, 774 coPos, 775 &Written); 776 777 /* Draw lower edge */ 778 coPos.X = ListUi->Left + 1; 779 coPos.Y = ListUi->Bottom; 780 if (LastLine - ListUi->Offset <= Height) 781 { 782 FillConsoleOutputCharacterA(StdOutput, 783 CharHorizontalLine, // '-', 784 Width, 785 coPos, 786 &Written); 787 } 788 else 789 { 790 FillConsoleOutputCharacterA(StdOutput, 791 CharHorizontalLine, // '-', 792 Width - 4, 793 coPos, 794 &Written); 795 { 796 CHAR szBuff[] = "(.)"; // "(down)" 797 szBuff[1] = CharDownArrow; 798 coPos.X = ListUi->Right - 5; 799 WriteConsoleOutputCharacterA(StdOutput, 800 szBuff, 801 3, 802 coPos, 803 &Written); 804 } 805 coPos.X = ListUi->Right - 2; 806 FillConsoleOutputCharacterA(StdOutput, 807 CharHorizontalLine, // '-', 808 2, 809 coPos, 810 &Written); 811 } 812 813 /* Draw lower right corner */ 814 coPos.X = ListUi->Right; 815 coPos.Y = ListUi->Bottom; 816 FillConsoleOutputCharacterA(StdOutput, 817 CharLowerRightCorner, // '+', 818 1, 819 coPos, 820 &Written); 821 822 /* Print list entries */ 823 ListUi->Line = -ListUi->Offset; 824 825 for (Entry = List->DiskListHead.Flink; 826 Entry != &List->DiskListHead; 827 Entry = Entry->Flink) 828 { 829 DiskEntry = CONTAINING_RECORD(Entry, DISKENTRY, ListEntry); 830 831 /* Print disk entry */ 832 PrintDiskData(ListUi, DiskEntry); 833 } 834 } 835 836 /** 837 * @param[in] Direction 838 * TRUE or FALSE to scroll to the next (down) or previous (up) entry, respectively. 839 **/ 840 VOID 841 ScrollUpDownPartitionList( 842 _In_ PPARTLIST_UI ListUi, 843 _In_ BOOLEAN Direction) 844 { 845 PPARTENTRY PartEntry = 846 (Direction ? GetNextPartition 847 : GetPrevPartition)(ListUi->List, ListUi->CurrentPartition); 848 if (PartEntry) 849 { 850 ListUi->CurrentPartition = PartEntry; 851 ListUi->CurrentDisk = PartEntry->DiskEntry; 852 DrawPartitionList(ListUi); 853 } 854 } 855 856 /* EOF */ 857