8ba5d7684566dd2ac6b9035492dda3582adef44a
[gpxe.git] / src / hci / tui / settings_ui.c
1 /*
2  * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of the
7  * License, or any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18
19 #include <string.h>
20 #include <curses.h>
21 #include <console.h>
22 #include <gpxe/settings.h>
23 #include <gpxe/editbox.h>
24
25 /** @file
26  *
27  * Configuration settings UI
28  *
29  */
30
31 #include <gpxe/nvo.h>
32 extern struct nvo_block *ugly_nvo_hack;
33
34 /* Colour pairs */
35 #define CPAIR_NORMAL    1
36 #define CPAIR_SELECT    2
37 #define CPAIR_EDIT      3
38
39 /* Screen layout */
40 #define SETTINGS_LIST_ROW       3
41 #define SETTINGS_LIST_COL       1
42
43 /** Layout of text within a setting widget */
44 struct setting_row {
45         char start[0];
46         char pad1[1];
47         char name[15];
48         char pad2[1];
49         char value[60];
50         char pad3[1];
51         char nul;
52 } __attribute__ (( packed ));
53
54 /** A setting widget */
55 struct setting_widget {
56         /** Configuration context */
57         struct config_context *context;
58         /** Configuration setting */
59         struct config_setting *setting;
60         /** Screen row */
61         unsigned int row;
62         /** Screen column */
63         unsigned int col;
64         /** Edit box widget used for editing setting */
65         struct edit_box editbox;
66         /** Editing active flag */
67         int editing;
68         /** Buffer for setting's value */
69         char value[256]; /* enough size for a DHCP string */
70 };
71
72 /** Registered configuration settings */
73 static struct config_setting
74 config_settings[0] __table_start ( config_settings );
75 static struct config_setting
76 config_settings_end[0] __table_end ( config_settings );
77 #define NUM_SETTINGS ( ( unsigned ) ( config_settings_end - config_settings ) )
78
79 /**
80  * Load setting widget value from configuration context
81  *
82  * @v widget            Setting widget
83  *
84  */
85 static void load_setting ( struct setting_widget *widget ) {
86
87         /* Mark as not editing */
88         widget->editing = 0;
89
90         /* Read current setting value */
91         if ( widget->setting->type->show ( widget->context, widget->setting,
92                                            widget->value,
93                                            sizeof ( widget->value ) ) != 0 ) {
94                 widget->value[0] = '\0';
95         }       
96
97         /* Initialise edit box */
98         init_editbox ( &widget->editbox, widget->value,
99                        sizeof ( widget->value ), NULL, widget->row,
100                        ( widget->col + offsetof ( struct setting_row, value )),
101                        sizeof ( ( ( struct setting_row * ) NULL )->value ) );
102 }
103
104 /**
105  * Save setting widget value back to configuration context
106  *
107  * @v widget            Setting widget
108  */
109 static int save_setting ( struct setting_widget *widget ) {
110         widget->editing = 0;
111         return widget->setting->type->set ( widget->context, widget->setting,
112                                             widget->value );
113 }
114
115 /**
116  * Initialise setting widget
117  *
118  * @v widget            Setting widget
119  * @v context           Configuration context
120  * @v setting           Configuration setting
121  * @v row               Screen row
122  * @v col               Screen column
123  */
124 static void init_setting ( struct setting_widget *widget,
125                            struct config_context *context,
126                            struct config_setting *setting,
127                            unsigned int row, unsigned int col ) {
128
129         /* Initialise widget structure */
130         memset ( widget, 0, sizeof ( *widget ) );
131         widget->context = context;
132         widget->setting = setting;
133         widget->row = row;
134         widget->col = col;
135
136         /* Read current setting value */
137         load_setting ( widget );
138 }
139
140 /**
141  * Draw setting widget
142  *
143  * @v widget            Setting widget
144  */
145 static void draw_setting ( struct setting_widget *widget ) {
146         struct setting_row row;
147         unsigned int len;
148         unsigned int curs_col;
149         char *value;
150
151         /* Fill row with spaces */
152         memset ( &row, ' ', sizeof ( row ) );
153         row.nul = '\0';
154
155         /* Construct dot-padded name */
156         memset ( row.name, '.', sizeof ( row.name ) );
157         len = strlen ( widget->setting->name );
158         if ( len > sizeof ( row.name ) )
159                 len = sizeof ( row.name );
160         memcpy ( row.name, widget->setting->name, len );
161
162         /* Construct space-padded value */
163         value = widget->value;
164         if ( ! *value )
165                 value = "<not specified>";
166         len = strlen ( value );
167         if ( len > sizeof ( row.value ) )
168                 len = sizeof ( row.value );
169         memcpy ( row.value, value, len );
170         curs_col = ( widget->col + offsetof ( typeof ( row ), value )
171                      + len );
172
173         /* Print row */
174         mvprintw ( widget->row, widget->col, "%s", row.start );
175         move ( widget->row, curs_col );
176         if ( widget->editing )
177                 draw_editbox ( &widget->editbox );
178 }
179
180 /**
181  * Edit setting widget
182  *
183  * @v widget            Setting widget
184  * @v key               Key pressed by user
185  * @ret key             Key returned to application, or zero
186  */
187 static int edit_setting ( struct setting_widget *widget, int key ) {
188         widget->editing = 1;
189         return edit_editbox ( &widget->editbox, key );
190 }
191
192 /**
193  * Initialise setting widget by index
194  *
195  * @v widget            Setting widget
196  * @v context           Configuration context
197  * @v index             Index of setting with settings list
198  */
199 static void init_setting_index ( struct setting_widget *widget,
200                                  struct config_context *context,
201                                  unsigned int index ) {
202         init_setting ( widget, context, &config_settings[index],
203                        ( SETTINGS_LIST_ROW + index ), SETTINGS_LIST_COL );
204 }
205
206 static void main_loop ( struct config_context *context ) {
207         struct setting_widget widget;
208         unsigned int current = 0;
209         unsigned int next;
210         int i;
211         int key;
212         int rc;
213
214         /* Print initial screen content */
215         color_set ( CPAIR_NORMAL, NULL );
216         for ( i = ( NUM_SETTINGS - 1 ) ; i >= 0 ; i-- ) {
217                 init_setting_index ( &widget, context, i );
218                 draw_setting ( &widget );
219         }
220
221         while ( 1 ) {
222                 /* Redraw current setting */
223                 color_set ( ( widget.editing ? CPAIR_EDIT : CPAIR_SELECT ),
224                             NULL );
225                 draw_setting ( &widget );
226                 color_set ( CPAIR_NORMAL, NULL );
227
228                 key = getchar();
229                 if ( widget.editing ) {
230                         key = edit_setting ( &widget, key );
231                         switch ( key ) {
232                         case 0x0a: /* Enter */
233                                 if ( ( rc = save_setting ( &widget ) ) != 0 ) {
234                                         
235                                 }
236                                 break;
237                         case 0x03: /* Ctrl-C */
238                                 load_setting ( &widget );
239                                 break;
240                         default:
241                                 /* Do nothing */
242                                 break;
243                         }
244                 } else {
245                         next = current;
246                         switch ( key ) {
247                         case '+':
248                                 if ( next < ( NUM_SETTINGS - 1 ) )
249                                         next++;
250                                 break;
251                         case '-':
252                                 if ( next > 0 )
253                                         next--;
254                                 break;
255                         default:
256                                 edit_setting ( &widget, key );
257                                 break;
258                         }       
259                         if ( next != current ) {
260                                 draw_setting ( &widget );
261                                 init_setting_index ( &widget, context, next );
262                                 current = next;
263                         }
264                 }
265         }
266         
267 }
268
269 void uitest ( void ) {
270         struct config_context dummy_context;
271
272         dummy_context.options = ugly_nvo_hack->options;
273
274         initscr();
275         start_color();
276         init_pair ( CPAIR_NORMAL, COLOR_WHITE, COLOR_BLUE );
277         init_pair ( CPAIR_SELECT, COLOR_WHITE, COLOR_RED );
278         init_pair ( CPAIR_EDIT, COLOR_BLACK, COLOR_GREEN );
279         color_set ( CPAIR_NORMAL, NULL );
280         erase();
281         
282         main_loop ( &dummy_context );
283
284         endwin();
285 }