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