1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2011 Nathan Whitehorn
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 *
28 * $FreeBSD$
29 */
30
31#include <stdio.h>
32#include <unistd.h>
33#include <libutil.h>
34#include <dialog.h>
35#include <dlg_keys.h>
36
37#include "diskeditor.h"
38
39static void
40print_partedit_item(WINDOW *partitions, struct partedit_item *items,
41    int item, int nscroll, int selected)
42{
43	chtype attr = A_NORMAL;
44	char sizetext[16];
45	int y = item - nscroll + 1;
46
47	wattrset(partitions, selected ? item_selected_attr : item_attr);
48	wmove(partitions, y, MARGIN + items[item].indentation*2);
49	dlg_print_text(partitions, items[item].name, 10, &attr);
50	wmove(partitions, y, 17);
51	wattrset(partitions, item_attr);
52
53	humanize_number(sizetext, 7, items[item].size, "B", HN_AUTOSCALE,
54	    HN_DECIMAL);
55	dlg_print_text(partitions, sizetext, 8, &attr);
56	wmove(partitions, y, 25);
57	dlg_print_text(partitions, items[item].type, 15, &attr);
58	wmove(partitions, y, 40);
59	if (items[item].mountpoint != NULL)
60		dlg_print_text(partitions, items[item].mountpoint, 8, &attr);
61}
62
63int
64diskeditor_show(const char *title, const char *cprompt,
65    struct partedit_item *items, int nitems, int *selected, int *nscroll)
66{
67	WINDOW *dialog, *partitions;
68	char *prompt;
69	const char *buttons[] =
70	    { "Create", "Delete", "Modify", "Revert", "Auto", "Finish", NULL };
71	const char *help_text[] = {
72	    "Add a new partition", "Delete selected partition or partitions",
73	    "Change partition type or mountpoint",
74	    "Revert changes to disk setup", "Use guided partitioning tool",
75	    "Exit partitioner (will ask whether to save changes)", NULL };
76	int x, y;
77	int i;
78	int height, width, min_width;
79	int partlist_height, partlist_width;
80	int cur_scroll = 0;
81	int key, fkey;
82	int cur_button = 5, cur_part = 0;
83	int result = DLG_EXIT_UNKNOWN;
84
85	static DLG_KEYS_BINDING binding[] = {
86		ENTERKEY_BINDINGS,
87		DLG_KEYS_DATA( DLGK_ENTER,      ' ' ),
88		DLG_KEYS_DATA( DLGK_ITEM_NEXT, KEY_DOWN ),
89		DLG_KEYS_DATA( DLGK_ITEM_PREV, KEY_UP ),
90		DLG_KEYS_DATA( DLGK_FIELD_NEXT, KEY_RIGHT ),
91		DLG_KEYS_DATA( DLGK_FIELD_NEXT, TAB ),
92		DLG_KEYS_DATA( DLGK_FIELD_PREV, KEY_BTAB ),
93		DLG_KEYS_DATA( DLGK_FIELD_PREV, KEY_LEFT ),
94
95		SCROLLKEY_BINDINGS,
96		END_KEYS_BINDING
97	};
98
99	static DLG_KEYS_BINDING binding2[] = {
100		INPUTSTR_BINDINGS,
101		ENTERKEY_BINDINGS,
102		DLG_KEYS_DATA( DLGK_FIELD_NEXT, TAB ),
103		DLG_KEYS_DATA( DLGK_FIELD_PREV, KEY_BTAB ),
104		DLG_KEYS_DATA( DLGK_ITEM_NEXT,  CHR_NEXT ),
105		DLG_KEYS_DATA( DLGK_ITEM_NEXT,  KEY_DOWN ),
106		DLG_KEYS_DATA( DLGK_ITEM_NEXT,  KEY_NEXT ),
107		DLG_KEYS_DATA( DLGK_ITEM_PREV,  CHR_PREVIOUS ),
108		DLG_KEYS_DATA( DLGK_ITEM_PREV,  KEY_PREVIOUS ),
109		DLG_KEYS_DATA( DLGK_ITEM_PREV,  KEY_UP ),
110		DLG_KEYS_DATA( DLGK_PAGE_NEXT,  KEY_NPAGE ),
111		DLG_KEYS_DATA( DLGK_PAGE_PREV,  KEY_PPAGE ),
112		END_KEYS_BINDING
113	};
114
115	/*
116	 * Set up editor window.
117	 */
118	prompt = dlg_strclone(cprompt);
119
120	min_width = 50;
121	height = width = 0;
122	partlist_height = 10;
123	dlg_tab_correct_str(prompt);
124	dlg_button_layout(buttons, &min_width);
125	dlg_auto_size(title, prompt, &height, &width, 2, min_width);
126	height += partlist_height;
127	partlist_width = width - 2*MARGIN;
128	dlg_print_size(height, width);
129	dlg_ctl_size(height, width);
130
131	x = dlg_box_x_ordinate(width);
132	y = dlg_box_y_ordinate(height);
133
134	dialog = dlg_new_window(height, width, y, x);
135	dlg_register_window(dialog, "diskeditorbox", binding);
136	dlg_register_buttons(dialog, "diskeditorbox", buttons);
137
138	dlg_draw_box(dialog, 0, 0, height, width, dialog_attr, border_attr);
139	dlg_draw_bottom_box(dialog);
140	dlg_draw_title(dialog, title);
141	wattrset(dialog, dialog_attr);
142
143	/* Partition list sub-window */
144	partitions = dlg_sub_window(dialog, partlist_height, partlist_width,
145	    y + 3, x + 1);
146	dlg_register_window(partitions, "partlist", binding2);
147	dlg_register_buttons(partitions, "partlist", buttons);
148	wattrset(partitions, menubox_attr);
149
150	dlg_item_help(help_text[cur_button]);
151	dlg_draw_buttons(dialog, height - 2*MARGIN, 0, buttons,
152	    cur_button, FALSE, width);
153	dlg_print_autowrap(dialog, prompt, height, width);
154
155	if (selected != NULL)
156		cur_part = *selected;
157	if (nscroll != NULL)
158		cur_scroll = *nscroll;
159	if (cur_part - cur_scroll >= partlist_height - 2 ||
160	    cur_part - cur_scroll < 0)
161		cur_scroll = cur_part;
162
163repaint:
164	dlg_draw_box(dialog, 3, 1,  partlist_height, partlist_width,
165	    menubox_border_attr, menubox_attr);
166	for (i = cur_scroll; i < MIN(cur_scroll + partlist_height - 2, nitems);
167	    i++)
168		print_partedit_item(partitions, items, i, cur_scroll,
169		    i == cur_part);
170	if (nitems > partlist_height - 2)
171		dlg_draw_arrows(partitions, cur_scroll > 0,
172		    nitems > cur_scroll + partlist_height - 2,
173		    partlist_width - 5, 0, partlist_height - 1);
174	wrefresh(partitions);
175
176	while (result == DLG_EXIT_UNKNOWN) {
177		key = dlg_mouse_wgetch(dialog, &fkey);
178		if ((i = dlg_char_to_button(key, buttons)) >= 0) {
179			cur_button = i;
180			dlg_item_help(help_text[cur_button]);
181			dlg_draw_buttons(dialog, height - 2*MARGIN, 0, buttons,
182			    cur_button, FALSE, width);
183			break;
184		}
185
186		if (!fkey)
187			continue;
188
189		switch (key) {
190		case DLGK_FIELD_NEXT:
191			cur_button = dlg_next_button(buttons, cur_button);
192			if (cur_button < 0)
193				cur_button = 0;
194			dlg_item_help(help_text[cur_button]);
195			dlg_draw_buttons(dialog, height - 2*MARGIN, 0, buttons,
196			    cur_button, FALSE, width);
197			break;
198		case DLGK_FIELD_PREV:
199			cur_button = dlg_prev_button(buttons, cur_button);
200			if (cur_button < 0)
201				cur_button = 0;
202			dlg_item_help(help_text[cur_button]);
203			dlg_draw_buttons(dialog, height - 2*MARGIN, 0, buttons,
204			    cur_button, FALSE, width);
205			break;
206		case DLGK_ITEM_NEXT:
207			if (cur_part == nitems - 1)
208				break; /* End of list */
209
210			/* Deselect old item */
211			print_partedit_item(partitions, items, cur_part,
212			    cur_scroll, 0);
213			/* Select new item */
214			cur_part++;
215			if (cur_part - cur_scroll >= partlist_height - 2) {
216				cur_scroll = cur_part;
217				goto repaint;
218			}
219			print_partedit_item(partitions, items, cur_part,
220			    cur_scroll, 1);
221			wrefresh(partitions);
222			break;
223		case DLGK_ITEM_PREV:
224			if (cur_part == 0)
225				break; /* Start of list */
226
227			/* Deselect old item */
228			print_partedit_item(partitions, items, cur_part,
229			    cur_scroll, 0);
230			/* Select new item */
231			cur_part--;
232			if (cur_part - cur_scroll < 0) {
233				cur_scroll = cur_part;
234				goto repaint;
235			}
236			print_partedit_item(partitions, items, cur_part,
237			    cur_scroll, 1);
238			wrefresh(partitions);
239			break;
240		case DLGK_PAGE_NEXT:
241			cur_scroll += (partlist_height - 2);
242			if (cur_scroll + partlist_height - 2 >= nitems)
243				cur_scroll = nitems - (partlist_height - 2);
244			if (cur_scroll < 0)
245				cur_scroll = 0;
246			if (cur_part < cur_scroll)
247				cur_part = cur_scroll;
248			goto repaint;
249		case DLGK_PAGE_PREV:
250			cur_scroll -= (partlist_height - 2);
251			if (cur_scroll < 0)
252				cur_scroll = 0;
253			if (cur_part >= cur_scroll + partlist_height - 2)
254				cur_part = cur_scroll;
255			goto repaint;
256		case DLGK_PAGE_FIRST:
257			cur_scroll = 0;
258			cur_part = cur_scroll;
259			goto repaint;
260		case DLGK_PAGE_LAST:
261			cur_scroll = nitems - (partlist_height - 2);
262			if (cur_scroll < 0)
263				cur_scroll = 0;
264			cur_part = cur_scroll;
265			goto repaint;
266		case DLGK_ENTER:
267			goto done;
268		default:
269			if (is_DLGK_MOUSE(key)) {
270				cur_button = key - M_EVENT;
271				dlg_item_help(help_text[cur_button]);
272				dlg_draw_buttons(dialog, height - 2*MARGIN, 0,
273				    buttons, cur_button, FALSE, width);
274				goto done;
275			}
276			break;
277		}
278	}
279
280done:
281	if (selected != NULL)
282		*selected = cur_part;
283	if (nscroll != NULL)
284		*nscroll = cur_scroll;
285
286	dlg_del_window(partitions);
287	dlg_del_window(dialog);
288	dlg_mouse_free_regions();
289
290	return (cur_button);
291}
292
293