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