blob: b8a0371917453fc5436d9da7c3d6c706bca6fc4a [file] [log] [blame]
srs56943860cbe2011-09-10 20:29:53 -04001/*
2 * Implementation of GPTData class derivative with curses-based text-mode
3 * interaction
4 * Copyright (C) 2011 Roderick W. Smith
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 *
20 */
21
22#include <iostream>
23#include <string>
24#include <sstream>
25#include <ncurses.h>
26#include "gptcurses.h"
27#include "support.h"
28
29using namespace std;
30
31// # of lines to reserve for general information and headers (RESERVED_TOP)
32// and for options and messages (RESERVED_BOTTOM)
33#define RESERVED_TOP 7
34#define RESERVED_BOTTOM 5
35
36int GPTDataCurses::numInstances = 0;
37
38GPTDataCurses::GPTDataCurses(void) {
39 if (numInstances > 0) {
40 refresh();
41 } else {
42 initscr();
43 cbreak();
44 noecho();
45 intrflush(stdscr, false);
46 keypad(stdscr, true);
47 nonl();
48 numInstances++;
49 } // if/else
50 firstSpace = NULL;
51 lastSpace = NULL;
52 currentSpace = NULL;
53 currentSpaceNum = -1;
54 whichOptions = ""; // current set of options
55 currentKey = 'b'; // currently selected option
56} // GPTDataCurses constructor
57
58GPTDataCurses::~GPTDataCurses(void) {
59 numInstances--;
60 if ((numInstances == 0) && !isendwin())
61 endwin();
62} // GPTDataCurses destructor
63
64/************************************************
65 * *
66 * Functions relating to Spaces data structures *
67 * *
68 ************************************************/
69
70void GPTDataCurses::EmptySpaces(void) {
71 Space *trash;
72
73 while (firstSpace != NULL) {
74 trash = firstSpace;
75 firstSpace = firstSpace->nextSpace;
76 delete trash;
77 } // if
78 numSpaces = 0;
79 lastSpace = NULL;
80} // GPTDataCurses::EmptySpaces()
81
82// Create Spaces from partitions. Does NOT creates Spaces to represent
83// unpartitioned space on the disk.
84// Returns the number of Spaces created.
85int GPTDataCurses::MakeSpacesFromParts(void) {
86 uint i;
87 Space *tempSpace;
88
89 EmptySpaces();
90 for (i = 0; i < numParts; i++) {
91 if (partitions[i].IsUsed()) {
92 tempSpace = new Space;
93 tempSpace->firstLBA = partitions[i].GetFirstLBA();
94 tempSpace->lastLBA = partitions[i].GetLastLBA();
95 tempSpace->origPart = &partitions[i];
96 tempSpace->partNum = (int) i;
97 LinkToEnd(tempSpace);
98 } // if
99 } // for
100 return numSpaces;
101} // GPTDataCurses::MakeSpacesFromParts()
102
103// Add a single empty Space to the current Spaces linked list and sort the result....
104void GPTDataCurses::AddEmptySpace(uint64_t firstLBA, uint64_t lastLBA) {
105 Space *tempSpace;
106
107 tempSpace = new Space;
108 tempSpace->firstLBA = firstLBA;
109 tempSpace->lastLBA = lastLBA;
110 tempSpace->origPart = &emptySpace;
111 tempSpace->partNum = -1;
112 LinkToEnd(tempSpace);
113 SortSpaces();
114} // GPTDataCurses::AddEmptySpace();
115
116// Add Spaces to represent the unallocated parts of the partition table.
117// Returns the number of Spaces added.
118int GPTDataCurses::AddEmptySpaces(void) {
119 int numAdded = 0;
120 Space *current;
121
122 SortSpaces();
123 if (firstSpace == NULL) {
124 AddEmptySpace(GetFirstUsableLBA(), GetLastUsableLBA());
125 numAdded++;
126 } else {
127 current = firstSpace;
128 while ((current != NULL) /* && (current->partNum != -1) */ ) {
129 if ((current == firstSpace) && (current->firstLBA > GetFirstUsableLBA())) {
130 AddEmptySpace(GetFirstUsableLBA(), current->firstLBA - 1);
131 numAdded++;
132 } // if
133 if ((current == lastSpace) && (current->lastLBA < GetLastUsableLBA())) {
134 AddEmptySpace(current->lastLBA + 1, GetLastUsableLBA());
135 numAdded++;
136 } // if
137 if ((current->prevSpace != NULL) && (current->prevSpace->lastLBA < (current->firstLBA - 1))) {
138 AddEmptySpace(current->prevSpace->lastLBA + 1, current->firstLBA - 1);
139 numAdded++;
140 } // if
141 current = current->nextSpace;
142 } // while
143 } // if/else
144 return numAdded;
145} // GPTDataCurses::AddEmptySpaces()
146
147// Remove the specified Space from the linked list and set its previous and
148// next pointers to NULL.
149void GPTDataCurses::UnlinkSpace(Space *theSpace) {
150 if (theSpace != NULL) {
151 if (theSpace->prevSpace != NULL)
152 theSpace->prevSpace->nextSpace = theSpace->nextSpace;
153 if (theSpace->nextSpace != NULL)
154 theSpace->nextSpace->prevSpace = theSpace->prevSpace;
155 if (theSpace == firstSpace)
156 firstSpace = theSpace->nextSpace;
157 if (theSpace == lastSpace)
158 lastSpace = theSpace->prevSpace;
159 theSpace->nextSpace = NULL;
160 theSpace->prevSpace = NULL;
161 numSpaces--;
162 } // if
163} // GPTDataCurses::UnlinkSpace
164
165// Link theSpace to the end of the current linked list.
166void GPTDataCurses::LinkToEnd(Space *theSpace) {
167 if (lastSpace == NULL) {
168 firstSpace = lastSpace = theSpace;
169 theSpace->nextSpace = NULL;
170 theSpace->prevSpace = NULL;
171 } else {
172 theSpace->prevSpace = lastSpace;
173 theSpace->nextSpace = NULL;
174 lastSpace->nextSpace = theSpace;
175 lastSpace = theSpace;
176 } // if/else
177 numSpaces++;
178} // GPTDataCurses::LinkToEnd()
179
180// Sort spaces into ascending order by on-disk position.
181void GPTDataCurses::SortSpaces(void) {
182 Space *oldFirst, *oldLast, *earliest = NULL, *current = NULL;
183
184 oldFirst = firstSpace;
185 oldLast = lastSpace;
186 firstSpace = lastSpace = NULL;
187 while (oldFirst != NULL) {
188 current = earliest = oldFirst;
189 while (current != NULL) {
190 if (current->firstLBA < earliest->firstLBA)
191 earliest = current;
192 current = current->nextSpace;
193 } // while
194 if (oldFirst == earliest)
195 oldFirst = earliest->nextSpace;
196 if (oldLast == earliest)
197 oldLast = earliest->prevSpace;
198 UnlinkSpace(earliest);
199 LinkToEnd(earliest);
200 } // while
201} // GPTDataCurses::SortSpaces()
202
203// Identify the spaces on the disk, a "space" being defined as a partition
204// or an empty gap between, before, or after partitions. The spaces are
205// presented to users in the main menu display.
206void GPTDataCurses::IdentifySpaces(void) {
207 MakeSpacesFromParts();
208 AddEmptySpaces();
209} // GPTDataCurses::IdentifySpaces()
210
211/**************************
212 * *
213 * Data display functions *
214 * *
215 **************************/
216
217// Display a single Space on line # lineNum.
218// Returns a pointer to the space being displayed
219Space* GPTDataCurses::ShowSpace(int spaceNum, int lineNum) {
220 Space *space;
221 int i = 0;
222 char temp[40];
223
224 space = firstSpace;
225 while ((space != NULL) && (i < spaceNum)) {
226 space = space->nextSpace;
227 i++;
228 } // while
229 if ((space != NULL) && (lineNum < (LINES - 5))) {
230 ClearLine(lineNum);
231 if (space->partNum == -1) { // space is empty
232 move(lineNum, 12);
233 printw(BytesToIeee((space->lastLBA - space->firstLBA + 1), blockSize).c_str());
234 move(lineNum, 24);
235 printw("free space");
236 } else { // space holds a partition
237 move(lineNum, 3);
238 printw("%d", space->partNum + 1);
239 move(lineNum, 12);
240 printw(BytesToIeee((space->lastLBA - space->firstLBA + 1), blockSize).c_str());
241 move(lineNum, 24);
242 printw(space->origPart->GetTypeName().c_str());
243 move(lineNum, 50);
244 #ifdef USE_UTF16
245 space->origPart->GetDescription().extract(0, 39, temp, 39);
246 printw(temp);
247 #else
248 printw(space->origPart->GetDescription().c_str());
249 #endif
250 } // if/else
251 } // if
252 return space;
253} // GPTDataCurses::ShowSpace
254
255// Display the partitions, being sure that the space #selected is displayed
256// and highlighting that space.
257// Returns the number of the space being shown (should be selected, but will
258// be -1 if something weird happens)
259int GPTDataCurses::DisplayParts(int selected) {
260 int lineNum = 5, i = 0, retval = -1, numToShow, pageNum;
261 string theLine;
262
263 move(lineNum++, 0);
264 theLine = "Part. # Size Partition Type Partition Name";
265 printw(theLine.c_str());
266 move(lineNum++, 0);
267 theLine = "----------------------------------------------------------------";
268 printw(theLine.c_str());
269 numToShow = LINES - RESERVED_TOP - RESERVED_BOTTOM;
270 pageNum = selected / numToShow;
271 for (i = pageNum * numToShow; i <= (pageNum + 1) * numToShow - 1; i++) {
272 if (i < numSpaces) { // real space; show it
273 if (i == selected) {
274 attron(A_REVERSE);
275 currentSpaceNum = i;
276 currentSpace = ShowSpace(i, lineNum++);
277 attroff(A_REVERSE);
278 DisplayOptions(i);
279 retval = selected;
280 } else {
281 ShowSpace(i, lineNum++);
282 }
283 } else { // blank in display
284 ClearLine(lineNum++);
285 } // if/else
286 } // for
287 refresh();
288 return retval;
289} // GPTDataCurses::DisplayParts()
290
291/**********************************************
292 * *
293 * Functions corresponding to main menu items *
294 * *
295 **********************************************/
296
297// Delete the specified partition and re-detect partitions and spaces....
298void GPTDataCurses::DeletePartition(int partNum) {
299 if (!GPTData::DeletePartition(partNum))
300 Report("Could not delete partition!");
301 IdentifySpaces();
302 if (currentSpaceNum >= numSpaces) {
303 currentSpaceNum = numSpaces - 1;
304 currentSpace = lastSpace;
305 } // if
306} // GPTDataCurses::DeletePartition()
307
308// Displays information on the specified partition
309void GPTDataCurses::ShowInfo(int partNum) {
310 uint64_t size;
311 char temp[NAME_SIZE / 2 + 1];
312
313 clear();
314 move(2, (COLS - 29) / 2);
315 printw("Information for partition #%d\n\n", partNum + 1);
316 printw("Partition GUID code: %s (%s)\n", partitions[partNum].GetType().AsString().c_str(),
317 partitions[partNum].GetTypeName().c_str());
318 printw("Partition unique GUID: %s\n", partitions[partNum].GetUniqueGUID().AsString().c_str());
319 printw("First sector: %lld (at %s)\n", partitions[partNum].GetFirstLBA(),
320 BytesToIeee(partitions[partNum].GetFirstLBA(), blockSize).c_str());
321 printw("Last sector: %lld (at %s)\n", partitions[partNum].GetLastLBA(),
322 BytesToIeee(partitions[partNum].GetLastLBA(), blockSize).c_str());
323 size = partitions[partNum].GetLastLBA() - partitions[partNum].GetFirstLBA();
324 printw("Partition size: %lld sectors (%s)\n", size, BytesToIeee(size, blockSize).c_str());
325 printw("Attribute flags: %016x\n", partitions[partNum].GetAttributes().GetAttributes());
326 #ifdef USE_UTF16
327 partitions[partNum].GetDescription().extract(0, NAME_SIZE / 2, temp, NAME_SIZE / 2);
328 printw("Partition name: '%s'\n", temp);
329 #else
330 printw("Partition name: '%s'\n", partitions[partNum].GetDescription().c_str());
331 #endif
332 PromptToContinue();
333} // GPTDataCurses::ShowInfo()
334
335// Prompt for and change a partition's name....
336void GPTDataCurses::ChangeName(int partNum) {
337 char temp[NAME_SIZE / 2 + 1];
338
339 if (ValidPartNum(partNum)) {
340 move(LINES - 4, 0);
341 clrtobot();
342 move(LINES - 4, 0);
343 #ifdef USE_UTF16
344 partitions[partNum].GetDescription().extract(0, NAME_SIZE / 2, temp, NAME_SIZE / 2);
345 printw("Current partition name is '%s'\n", temp);
346 #else
347 printw("Current partition name is '%s'\n", partitions[partNum].GetDescription().c_str());
348 #endif
349 printw("Enter new partition name, or <Enter> to use the current name:\n");
350 echo();
351 getnstr(temp, NAME_SIZE / 2);
352 partitions[partNum].SetName((string) temp);
353 noecho();
354 } // if
355} // GPTDataCurses::ChangeName()
356
357// Change the partition's type code....
358void GPTDataCurses::ChangeType(int partNum) {
359 char temp[80] = "L\0";
360 PartType tempType;
361
362 echo();
363 do {
364 move(LINES - 4, 0);
365 clrtobot();
366 move(LINES - 4, 0);
367 printw("Current type is %04x (%s)\n", partitions[partNum].GetType().GetHexType(), partitions[partNum].GetTypeName().c_str());
368 printw("Hex code or GUID (L to show codes, Enter = %04x): ", partitions[partNum].GetType().GetHexType());
369 getnstr(temp, 79);
370 if ((temp[0] == 'L') || (temp[0] == 'l')) {
371 ShowTypes();
372 } else {
373 if (temp[0] == '\0')
374 tempType = partitions[partNum].GetType().GetHexType();
375 tempType = temp;
376 partitions[partNum].SetType(tempType);
377 } // if
378 } while ((temp[0] == 'L') || (temp[0] == 'l') || (partitions[partNum].GetType() == (GUIDData) "0x0000"));
379 noecho();
380} // GPTDataCurses::ChangeType
381
382// Sets the partition alignment value
383void GPTDataCurses::SetAlignment(void) {
384 int alignment;
385
386 move(LINES - 4, 0);
387 clrtobot();
388 printw("Current partition alignment, in sectors, is %d.", GetAlignment());
389 do {
390 move(LINES - 3, 0);
391 printw("Type new alignment value, in sectors: ");
392 echo();
393 scanw("%d", &alignment);
394 noecho();
395 } while ((alignment == 0) || (alignment > MAX_ALIGNMENT));
396 GPTData::SetAlignment(alignment);
397} // GPTDataCurses::SetAlignment()
398
399// Verify the data structures. Note that this function leaves curses mode and
400// relies on the underlying GPTData::Verify() function to report on problems
401void GPTDataCurses::Verify(void) {
402 char junk;
403
404 def_prog_mode();
405 endwin();
406 GPTData::Verify();
407 cout << "\nPress the <Enter> key to continue: ";
408 cin.get(junk);
409 reset_prog_mode();
410 refresh();
411} // GPTDataCurses::Verify()
412
413// Create a new partition in the space pointed to by currentSpace.
414void GPTDataCurses::MakeNewPart(void) {
415 uint64_t size, newFirstLBA = 0, newLastLBA = 0;
416 int partNum;
417 char inLine[80];
418
419 move(LINES - 4, 0);
420 clrtobot();
421 while ((newFirstLBA < currentSpace->firstLBA) || (newFirstLBA > currentSpace->lastLBA)) {
422 newFirstLBA = currentSpace->firstLBA;
423 move(LINES - 4, 0);
424 clrtoeol();
425 newFirstLBA = currentSpace->firstLBA;
426 Align(&newFirstLBA);
427 printw("First sector (%lld-%lld, default = %lld): ", newFirstLBA, currentSpace->lastLBA, newFirstLBA);
428 echo();
429 getnstr(inLine, 79);
430 noecho();
431 newFirstLBA = IeeeToInt(inLine, blockSize, currentSpace->firstLBA, currentSpace->lastLBA, newFirstLBA);
432 Align(&newFirstLBA);
433 } // while
434 size = currentSpace->lastLBA - newFirstLBA + 1;
435 while ((newLastLBA > currentSpace->lastLBA) || (newLastLBA < newFirstLBA)) {
436 move(LINES - 3, 0);
437 clrtoeol();
438 printw("Size in sectors or {KMGTP} (default = %lld): ", size);
439 echo();
440 getnstr(inLine, 79);
441 noecho();
442 newLastLBA = newFirstLBA + IeeeToInt(inLine, blockSize, 1, size, size) - 1;
443 } // while
444 partNum = FindFirstFreePart();
445 if (CreatePartition(partNum, newFirstLBA, newLastLBA)) { // created OK; set type code & name....
446 ChangeType(partNum);
447 ChangeName(partNum);
448 } else {
449 Report("Error creating partition!");
450 } // if/else
451} // GPTDataCurses::MakeNewPart()
452
453// Prompt user for permission to save data and, if it's given, do so!
454void GPTDataCurses::SaveData(void) {
455 string answer = "";
456 char inLine[80];
457
458 move(LINES - 4, 0);
459 clrtobot();
460 move (LINES - 2, 14);
461 printw("Warning!! This may destroy data on your disk!");
462 echo();
463 while ((answer != "yes") && (answer != "no")) {
464 move (LINES - 4, 2);
465 printw("Are you sure you want to write the partition table to disk? (yes or no): ");
466 getnstr(inLine, 79);
467 answer = inLine;
468 if ((answer != "yes") && (answer != "no")) {
469 move(LINES - 2, 0);
470 clrtoeol();
471 move(LINES - 2, 14);
472 printw("Please enter 'yes' or 'no'");
473 } // if
474 } // while()
475 noecho();
476 if (answer == "yes") {
477 if (SaveGPTData(1)) {
478 if (!myDisk.DiskSync())
479 Report("The kernel may be using the old partition table. Reboot to use the new\npartition table!");
480 } else {
481 Report("Problem saving data! Your partition table may be damaged!");
482 }
483 }
484} // GPTDataCurses::SaveData()
485
486// Back up the partition table, prompting user for a filename....
487void GPTDataCurses::Backup(void) {
488 char inLine[80];
489
490 ClearBottom();
491 move(LINES - 3, 0);
492 printw("Enter backup filename to save: ");
493 echo();
494 getnstr(inLine, 79);
495 noecho();
496 SaveGPTBackup(inLine);
497} // GPTDataCurses::Backup()
498
499// Load a GPT backup from a file
500void GPTDataCurses::LoadBackup(void) {
501 char inLine[80];
502
503 ClearBottom();
504 move(LINES - 3, 0);
505 printw("Enter backup filename to load: ");
506 echo();
507 getnstr(inLine, 79);
508 noecho();
509 if (!LoadGPTBackup(inLine))
510 Report("Restoration failed!");
511 IdentifySpaces();
512} // GPTDataCurses::LoadBackup()
513
514// Display some basic help information
515void GPTDataCurses::ShowHelp(void) {
516 int i = 0;
517
518 clear();
519 move(0, (COLS - 22) / 2);
520 printw("Help screen for cgdisk");
521 move(2, 0);
522 printw("This is cgdisk, a curses-based disk partitioning program. You can use it\n");
523 printw("to create, delete, and modify partitions on your hard disk.\n\n");
524 attron(A_BOLD);
525 printw("Use cgdisk only on GUID Partition Table (GPT) disks!\n");
526 attroff(A_BOLD);
527 printw("Use cfdisk on Master Boot Record (MBR) disks.\n\n");
528 printw("Command Meaning\n");
529 printw("------- -------\n");
530 while (menuMain[i].key != 0) {
531 printw(" %c %s\n", menuMain[i].key, menuMain[i].desc.c_str());
532 i++;
533 } // while()
534 PromptToContinue();
535} // GPTDataCurses::ShowHelp()
536
537/************************************
538 * *
539 * User input and menuing functions *
540 * *
541 ************************************/
542
543// Change the currently-selected space....
544void GPTDataCurses::ChangeSpaceSelection(int delta) {
545 if (currentSpace != NULL) {
546 while ((delta > 0) && (currentSpace->nextSpace != NULL)) {
547 currentSpace = currentSpace->nextSpace;
548 delta--;
549 currentSpaceNum++;
550 } // while
551 while ((delta < 0) && (currentSpace->prevSpace != NULL)) {
552 currentSpace = currentSpace->prevSpace;
553 delta++;
554 currentSpaceNum--;
555 } // while
556 } // if
557 // Below will hopefully never be true; bad counting error (bug), so reset to
558 // the first Space as a failsafe....
559 if (DisplayParts(currentSpaceNum) != currentSpaceNum) {
560 currentSpaceNum = 0;
561 currentSpace = firstSpace;
562 DisplayParts(currentSpaceNum);
563 } // if
564} // GPTDataCurses
565
566// Move option selection left or right....
567void GPTDataCurses::MoveSelection(int delta) {
568 int newKeyNum;
569
570 // Begin with a sanity check to ensure a valid key is selected....
571 if (whichOptions.find(currentKey) == string::npos)
572 currentKey = 'n';
573 newKeyNum = whichOptions.find(currentKey);
574 newKeyNum += delta;
575 if (newKeyNum < 0)
576 newKeyNum = whichOptions.length() - 1;
577 newKeyNum %= whichOptions.length();
578 currentKey = whichOptions[newKeyNum];
579 DisplayOptions(currentKey);
580} // GPTDataCurses::MoveSelection()
581
582// Show user's options. Refers to currentSpace to determine which options to show.
583// Highlights the option with the key selectedKey; or a default if that's invalid.
584void GPTDataCurses::DisplayOptions(char selectedKey) {
585 uint i, j = 0, firstLine, numPerLine;
586 string optionName, optionDesc = "";
587
588 if (currentSpace != NULL) {
589 if (currentSpace->partNum == -1) { // empty space is selected
590 whichOptions = EMPTY_SPACE_OPTIONS;
591 if (whichOptions.find(selectedKey) == string::npos)
592 selectedKey = 'n';
593 } else { // a partition is selected
594 whichOptions = PARTITION_OPTIONS;
595 if (whichOptions.find(selectedKey) == string::npos)
596 selectedKey = 't';
597 } // if/else
598
599 firstLine = LINES - 4;
600 numPerLine = (COLS - 8) / 12;
601 ClearBottom();
602 move(firstLine, 0);
603 for (i = 0; i < whichOptions.length(); i++) {
604 optionName = "";
605 for (j = 0; menuMain[j].key; j++) {
606 if (menuMain[j].key == whichOptions[i]) {
607 optionName = menuMain[j].name;
608 if (whichOptions[i] == selectedKey)
609 optionDesc = menuMain[j].desc;
610 } // if
611 } // for
612 move(firstLine + i / numPerLine, (i % numPerLine) * 12 + 4);
613 if (whichOptions[i] == selectedKey) {
614 attron(A_REVERSE);
615 printw("[ %s ]", optionName.c_str());
616 attroff(A_REVERSE);
617 } else {
618 printw("[ %s ]", optionName.c_str());
619 } // if/else
620 } // for
621 move(LINES - 1, (COLS - optionDesc.length()) / 2);
622 printw(optionDesc.c_str());
623 currentKey = selectedKey;
624 } // if
625} // GPTDataCurses::DisplayOptions()
626
627// Accept user input and process it. Returns when the program should terminate.
628void GPTDataCurses::AcceptInput() {
629 int inputKey, exitNow = 0;
630
631 do {
632 refresh();
633 inputKey = getch();
634 switch (inputKey) {
635 case KEY_UP:
636 ChangeSpaceSelection(-1);
637 break;
638 case KEY_DOWN:
639 ChangeSpaceSelection(+1);
640 break;
641 case 339: // page up key
642 ChangeSpaceSelection(RESERVED_TOP + RESERVED_BOTTOM - LINES);
643 break;
644 case 338: // page down key
645 ChangeSpaceSelection(LINES - RESERVED_TOP - RESERVED_BOTTOM);
646 break;
647 case KEY_LEFT:
648 MoveSelection(-1);
649 break;
650 case KEY_RIGHT:
651 MoveSelection(+1);
652 break;
653 case KEY_ENTER: case 13:
654 exitNow = Dispatch(currentKey);
655 break;
656 case 27: // escape key
657 exitNow = 1;
658 break;
659 default:
660 exitNow = Dispatch(inputKey);
661 break;
662 } // switch()
663 } while (!exitNow);
664} // GPTDataCurses::AcceptInput()
665
666// Operation has been selected, so do it. Returns 1 if the program should
667// terminate on return from this program, 0 otherwise.
668int GPTDataCurses::Dispatch(char operation) {
669 int exitNow = 0;
670
671 switch (operation) {
672 case 'a': case 'A':
673 SetAlignment();
674 break;
675 case 'b': case 'B':
676 Backup();
677 break;
678 case 'd': case 'D':
679 if (ValidPartNum(currentSpace->partNum))
680 DeletePartition(currentSpace->partNum);
681 break;
682 case 'h': case 'H':
683 ShowHelp();
684 break;
685 case 'i': case 'I':
686 if (ValidPartNum(currentSpace->partNum))
687 ShowInfo(currentSpace->partNum);
688 break;
689 case 'l': case 'L':
690 LoadBackup();
691 break;
692 case 'm': case 'M':
693 if (ValidPartNum(currentSpace->partNum))
694 ChangeName(currentSpace->partNum);
695 break;
696 case 'n': case 'N':
697 if (currentSpace->partNum < 0) {
698 MakeNewPart();
699 IdentifySpaces();
700 } // if
701 break;
702 case 'q': case 'Q':
703 exitNow = 1;
704 break;
705 case 't': case 'T':
706 if (ValidPartNum(currentSpace->partNum))
707 ChangeType(currentSpace->partNum);
708 break;
709 case 'v': case 'V':
710 Verify();
711 break;
712 case 'w': case 'W':
713 SaveData();
714 break;
715 default:
716 break;
717 } // switch()
718 DrawMenu();
719 return exitNow;
720} // GPTDataCurses::Dispatch()
721
722// Draws the main menu
723void GPTDataCurses::DrawMenu(void) {
724 string title="cgdisk ";
725 title += GPTFDISK_VERSION;
726 string drive="Disk Drive: ";
727 drive += device;
728 ostringstream size;
729 size << "Size: " << diskSize << ", " << BytesToIeee(diskSize, blockSize);
730
731 clear();
732 move(0, (COLS - title.length()) / 2);
733 printw(title.c_str());
734 move(2, (COLS - drive.length()) / 2);
735 printw(drive.c_str());
736 move(3, (COLS - size.str().length()) / 2);
737 printw(size.str().c_str());
738 DisplayParts(currentSpaceNum);
739} // DrawMenu
740
741int GPTDataCurses::MainMenu(void) {
742 if (((LINES - RESERVED_TOP - RESERVED_BOTTOM) < 2) || (COLS < 80)) {
743 Report("Display is too small; it must be at least 80 x 14 characters!");
744 } else {
745 if (GPTData::Verify() > 0)
746 Report("Warning! Problems found on disk! Use the Verify function to learn more.\n"
747 "Using gdisk or some other program may be necessary to repair the problems.");
748 IdentifySpaces();
749 currentSpaceNum = 0;
750 DrawMenu();
751 AcceptInput();
752 } // if/else
753 endwin();
754 return 0;
755} // GPTDataCurses::MainMenu
756
757/***********************************************************
758 * *
759 * Non-class support functions (mostly related to ncurses) *
760 * *
761 ***********************************************************/
762
763// Clears the specified line of all data....
764void ClearLine(int lineNum) {
765 move(lineNum, 0);
766 clrtoeol();
767} // ClearLine()
768
769// Clear the last few lines of the display
770void ClearBottom(void) {
771 move(LINES - RESERVED_BOTTOM, 0);
772 clrtobot();
773} // ClearBottom()
774
775void PromptToContinue(void) {
776 ClearBottom();
777 move(LINES - 2, (COLS - 29) / 2);
778 printw("Press any key to continue....");
779 cbreak();
780 getch();
781} // PromptToContinue()
782
783// Display one line of text on the screen and prompt to press any key to continue.
784void Report(string theText) {
785 clear();
786 move(0, 0);
787 printw(theText.c_str());
788 move(LINES - 2, (COLS - 29) / 2);
789 printw("Press any key to continue....");
790 cbreak();
791 getch();
792} // Report()
793
794// Displays all the partition type codes and then prompts to continue....
795// NOTE: This function temporarily exits curses mode as a matter of
796// convenience.
797void ShowTypes(void) {
798 PartType tempType;
799 char junk;
800
801 def_prog_mode();
802 endwin();
803 tempType.ShowAllTypes();
804 cout << "\nPress the <Enter> key to continue: ";
805 cin.get(junk);
806 reset_prog_mode();
807 refresh();
808} // ShowTypes()