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, 8, &attr);
48 	wmove(partitions, y, 15);
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, min_partlist_width;
78 	int cur_scroll = 0;
79 	int key, fkey;
80 	int cur_button = 0, 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 	min_partlist_width = 0;
106 	dlg_tab_correct_str(prompt);
107 	dlg_button_layout(buttons, &min_width);
108 	dlg_auto_size(title, prompt, &height, &width, 2, min_width);
109 	height += partlist_height;
110 	partlist_width = width - 2*MARGIN;
111 	dlg_print_size(height, width);
112 	dlg_ctl_size(height, width);
113 
114 	x = dlg_box_x_ordinate(width);
115 	y = dlg_box_y_ordinate(height);
116 
117 	dialog = dlg_new_window(height, width, y, x);
118 	dlg_register_window(dialog, "diskeditorbox", binding);
119 	dlg_register_buttons(dialog, "diskeditorbox", buttons);
120 
121 	dlg_draw_box(dialog, 0, 0, height, width, dialog_attr, border_attr);
122 	dlg_draw_bottom_box(dialog);
123 	dlg_draw_title(dialog, title);
124 	wattrset(dialog, dialog_attr);
125 
126 	/* Partition list sub-window */
127 	partitions = dlg_sub_window(dialog, partlist_height, partlist_width,
128 	    y + 3, x + 1);
129 	dlg_register_window(partitions, "partlist", binding);
130 	dlg_register_buttons(partitions, "partlist", buttons);
131 	wattrset(partitions, menubox_attr);
132 
133 	dlg_item_help(help_text[cur_button]);
134 	dlg_draw_buttons(dialog, height - 2*MARGIN, 0, buttons,
135 	    cur_button, FALSE, width);
136 	dlg_print_autowrap(dialog, prompt, height, width);
137 
138 	if (selected != NULL)
139 		cur_part = *selected;
140 	if (nscroll != NULL)
141 		cur_scroll = *nscroll;
142 	if (cur_part - cur_scroll >= partlist_height - 2 ||
143 	    cur_part - cur_scroll < 0)
144 		cur_scroll = cur_part;
145 
146 repaint:
147 	dlg_draw_box(dialog, 3, 1,  partlist_height, partlist_width,
148 	    menubox_border_attr, menubox_attr);
149 	for (i = cur_scroll; i < MIN(cur_scroll + partlist_height - 2, nitems);
150 	    i++)
151 		print_partedit_item(partitions, items, i, cur_scroll,
152 		    i == cur_part);
153 	if (nitems > partlist_height - 2)
154 		dlg_draw_arrows(partitions, cur_scroll > 0,
155 		    nitems > cur_scroll + partlist_height - 2,
156 		    partlist_width - 5, 0, partlist_height - 1);
157 	wrefresh(partitions);
158 
159 	while (result == DLG_EXIT_UNKNOWN) {
160 		key = dlg_mouse_wgetch(dialog, &fkey);
161 		if ((i = dlg_char_to_button(key, buttons)) >= 0) {
162 			cur_button = i;
163 			dlg_item_help(help_text[cur_button]);
164 			dlg_draw_buttons(dialog, height - 2*MARGIN, 0, buttons,
165 			    cur_button, FALSE, width);
166 			break;
167 		}
168 
169 		if (!fkey)
170 			continue;
171 
172 		switch (key) {
173 		case DLGK_FIELD_NEXT:
174 			cur_button = dlg_next_button(buttons, cur_button);
175 			if (cur_button < 0)
176 				cur_button = 0;
177 			dlg_item_help(help_text[cur_button]);
178 			dlg_draw_buttons(dialog, height - 2*MARGIN, 0, buttons,
179 			    cur_button, FALSE, width);
180 			break;
181 		case DLGK_FIELD_PREV:
182 			cur_button = dlg_prev_button(buttons, cur_button);
183 			if (cur_button < 0)
184 				cur_button = 0;
185 			dlg_item_help(help_text[cur_button]);
186 			dlg_draw_buttons(dialog, height - 2*MARGIN, 0, buttons,
187 			    cur_button, FALSE, width);
188 			break;
189 		case DLGK_ITEM_NEXT:
190 			if (cur_part == nitems - 1)
191 				break; /* End of list */
192 
193 			/* Deselect old item */
194 			print_partedit_item(partitions, items, cur_part,
195 			    cur_scroll, 0);
196 			/* Select new item */
197 			cur_part++;
198 			if (cur_part - cur_scroll >= partlist_height - 2) {
199 				cur_scroll = cur_part;
200 				goto repaint;
201 			}
202 			print_partedit_item(partitions, items, cur_part,
203 			    cur_scroll, 1);
204 			wrefresh(partitions);
205 			break;
206 		case DLGK_ITEM_PREV:
207 			if (cur_part == 0)
208 				break; /* Start 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 < 0) {
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_PAGE_NEXT:
224 			cur_scroll += (partlist_height - 2);
225 			if (cur_scroll + partlist_height - 2 >= nitems)
226 				cur_scroll = nitems - (partlist_height - 2);
227 			if (cur_scroll < 0)
228 				cur_scroll = 0;
229 			if (cur_part < cur_scroll)
230 				cur_part = cur_scroll;
231 			goto repaint;
232 		case DLGK_PAGE_PREV:
233 			cur_scroll -= (partlist_height - 2);
234 			if (cur_scroll < 0)
235 				cur_scroll = 0;
236 			if (cur_part >= cur_scroll + partlist_height - 2)
237 				cur_part = cur_scroll;
238 			goto repaint;
239 		case DLGK_PAGE_FIRST:
240 			cur_scroll = 0;
241 			cur_part = cur_scroll;
242 			goto repaint;
243 		case DLGK_PAGE_LAST:
244 			cur_scroll = nitems - (partlist_height - 2);
245 			if (cur_scroll < 0)
246 				cur_scroll = 0;
247 			cur_part = cur_scroll;
248 			goto repaint;
249 		case DLGK_ENTER:
250 			goto done;
251 		default:
252 			if (is_DLGK_MOUSE(key)) {
253 				cur_button = key - M_EVENT;
254 				dlg_item_help(help_text[cur_button]);
255 				dlg_draw_buttons(dialog, height - 2*MARGIN, 0,
256 				    buttons, cur_button, FALSE, width);
257 				goto done;
258 			}
259 			break;
260 		}
261 	}
262 
263 done:
264 	if (selected != NULL)
265 		*selected = cur_part;
266 	if (nscroll != NULL)
267 		*nscroll = cur_scroll;
268 
269 	dlg_del_window(partitions);
270 	dlg_del_window(dialog);
271 	dlg_mouse_free_regions();
272 
273 	return (cur_button);
274 }
275 
276