1 #include <curses.h>
2 #include <stddef.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include <assert.h>
6 #include "mucurses.h"
7 #include "cursor.h"
8 
9 /** @file
10  *
11  * Soft label key functions
12  */
13 
14 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
15 
16 #define MIN_SPACE_SIZE 2
17 
18 #define SLK_MAX_LABEL_LEN 8
19 
20 #define SLK_MAX_NUM_LABELS 12
21 
22 #define SLK_MAX_NUM_SPACES 2
23 
24 struct _softlabel {
25 	// label string
26 	char label[SLK_MAX_LABEL_LEN];
27 	/* Format of soft label
28 	   0: left justify
29 	   1: centre justify
30 	   2: right justify
31 	 */
32 	unsigned int fmt;
33 };
34 
35 struct _softlabelkeys {
36 	struct _softlabel fkeys[SLK_MAX_NUM_LABELS];
37 	attr_t attrs;
38 	/* Soft label layout format
39 	   0: 3-2-3
40 	   1: 4-4
41 	   2: 4-4-4
42 	   3: 4-4-4 with index line
43 	*/
44 	unsigned int fmt;
45 	unsigned int max_label_len;
46 	unsigned int maj_space_len;
47 	unsigned int num_labels;
48 	unsigned int num_spaces;
49 	unsigned int spaces[SLK_MAX_NUM_SPACES];
50 	struct cursor_pos saved_cursor;
51 	attr_t saved_attrs;
52 	short saved_pair;
53 };
54 
55 static struct _softlabelkeys *slks;
56 
57 /*
58   I either need to break the primitives here, or write a collection of
59   functions specifically for SLKs that directly access the screen
60   functions - since this technically isn't part of stdscr, I think
61   this should be ok...
62  */
63 
_enter_slk(void)64 static void _enter_slk ( void ) {
65 	_store_curs_pos ( stdscr, &slks->saved_cursor );
66 	wattr_get ( stdscr, &slks->saved_attrs, &slks->saved_pair, NULL );
67 	LINES++;
68 	wmove ( stdscr, LINES, 0 );
69 	wattrset ( stdscr, slks->attrs );
70 }
71 
_leave_slk(void)72 static void _leave_slk ( void ) {
73 	LINES--;
74 	wattr_set ( stdscr, slks->saved_attrs, slks->saved_pair, NULL );
75 	_restore_curs_pos ( stdscr, &slks->saved_cursor );
76 }
77 
_print_label(struct _softlabel sl)78 static void _print_label ( struct _softlabel sl ) {
79 	int space_ch;
80 	char str[SLK_MAX_LABEL_LEN + 1];
81 
82 	assert ( slks->max_label_len <= SLK_MAX_LABEL_LEN );
83 	space_ch = ' ';
84 	memset ( str, 0, sizeof ( str ) );
85 
86 	// protect against gaps in the soft label keys array
87 	if ( ! sl.label[0] ) {
88 		memset( str, space_ch, (size_t)(slks->max_label_len) );
89 	} else {
90 		/* we need to pad the label with varying amounts of leading
91 		   pad depending on the format of the label */
92 		if ( sl.fmt == 1 ) {
93 			memset( str, space_ch,
94 				(size_t)(slks->max_label_len
95 					 - strlen(sl.label)) / 2 );
96 		}
97 		if ( sl.fmt == 2 ) {
98 			memset( str, space_ch,
99 				(size_t)(slks->max_label_len
100 					 - strlen(sl.label)) );
101 		}
102 		strcat(str,sl.label);
103 
104 		// post-padding
105 		memset(str+strlen(str), space_ch,
106 		       (size_t)(slks->max_label_len - strlen(str)) );
107 	}
108 
109 	// print the formatted label
110 	_wputstr ( stdscr, str, NOWRAP, slks->max_label_len );
111 }
112 
113 /**
114  * Return the attribute used for the soft function keys
115  *
116  * @ret attrs	the current attributes of the soft function keys
117  */
slk_attr(void)118 attr_t slk_attr ( void ) {
119 	return ( slks == NULL ? 0 : slks->attrs );
120 }
121 
122 /**
123  * Turn off soft function key attributes
124  *
125  * @v attrs	attribute bit mask
126  * @ret rc	return status code
127  */
slk_attroff(const chtype attrs)128 int slk_attroff ( const chtype attrs ) {
129 	if ( slks == NULL )
130 		return ERR;
131 	slks->attrs &= ~( attrs & A_ATTRIBUTES );
132 	return OK;
133 }
134 
135 /**
136  * Turn on soft function key attributes
137  *
138  * @v attrs	attribute bit mask
139  * @ret rc	return status code
140  */
slk_attron(const chtype attrs)141 int slk_attron ( const chtype attrs ) {
142 	if ( slks == NULL )
143 		return ERR;
144 	slks->attrs |= ( attrs & A_ATTRIBUTES );
145 	return OK;
146 }
147 
148 /**
149  * Set soft function key attributes
150  *
151  * @v attrs	attribute bit mask
152  * @ret rc	return status code
153  */
slk_attrset(const chtype attrs)154 int slk_attrset ( const chtype attrs ) {
155 	if ( slks == NULL )
156 		return ERR;
157 	slks->attrs = ( attrs & A_ATTRIBUTES );
158 	return OK;
159 }
160 
161 /**
162  * Turn off soft function key attributes
163  *
164  * @v attrs	attribute bit mask
165  * @v *opts	undefined (for future implementation)
166  * @ret rc	return status code
167  */
slk_attr_off(const attr_t attrs,void * opts __unused)168 int slk_attr_off ( const attr_t attrs, void *opts __unused ) {
169 	return slk_attroff( attrs );
170 }
171 
172 /**
173  * Turn on soft function key attributes
174  *
175  * @v attrs	attribute bit mask
176  * @v *opts	undefined (for future implementation)
177  * @ret rc	return status code
178  */
slk_attr_on(attr_t attrs,void * opts __unused)179 int slk_attr_on ( attr_t attrs, void *opts __unused ) {
180 	return slk_attron( attrs );
181 }
182 
183 /**
184  * Set soft function key attributes
185  *
186  * @v attrs			attribute bit mask
187  * @v colour_pair_number	colour pair integer
188  * @v *opts			undefined (for future implementation)
189  * @ret rc			return status code
190  */
slk_attr_set(const attr_t attrs,short colour_pair_number,void * opts __unused)191 int slk_attr_set ( const attr_t attrs, short colour_pair_number,
192 		   void *opts __unused ) {
193 	if ( slks == NULL )
194 		return ERR;
195 
196 	if ( ( unsigned short )colour_pair_number > COLORS )
197 		return ERR;
198 
199 	slks->attrs = ( (unsigned short)colour_pair_number << CPAIR_SHIFT ) |
200 		( attrs & A_ATTRIBUTES );
201 	return OK;
202 }
203 
204 /**
205  * Clear the soft function key labels from the screen
206  *
207  * @ret rc	return status code
208  */
slk_clear(void)209 int slk_clear ( void ) {
210 	if ( slks == NULL )
211 		return ERR;
212 
213 	_enter_slk();
214 	wclrtoeol ( stdscr );
215 	_leave_slk();
216 
217 	return OK;
218 }
219 
220 /**
221  * Set soft label colour pair
222  */
slk_colour(short colour_pair_number)223 int slk_colour ( short colour_pair_number ) {
224 	if ( slks == NULL )
225 		return ERR;
226 	if ( ( unsigned short )colour_pair_number > COLORS )
227 		return ERR;
228 
229 	slks->attrs = ( (unsigned short)colour_pair_number << CPAIR_SHIFT )
230 		| ( slks->attrs & A_ATTRIBUTES );
231 
232 	return OK;
233 }
234 
235 /**
236  * Initialise the soft function keys
237  *
238  * @v fmt	format of keys
239  * @ret rc	return status code
240  */
slk_init(int fmt)241 int slk_init ( int fmt ) {
242 	unsigned short nmaj, nmin, nblocks, available_width;
243 
244 	if ( (unsigned)fmt > 3 ) {
245 		return ERR;
246 	}
247 
248 	/* There seems to be no API call to free this data structure... */
249 	if ( ! slks )
250 		slks = calloc(1,sizeof(*slks));
251 	if ( ! slks )
252 		return ERR;
253 
254 	slks->attrs = A_DEFAULT;
255 	slks->fmt = fmt;
256 	switch(fmt) {
257 	case 0:
258 		nblocks = 8; nmaj = 2; nmin = 5;
259 		slks->spaces[0] = 2; slks->spaces[1] = 4;
260 		break;
261 	case 1:
262 		nblocks = 8; nmaj = 1; nmin = 6;
263 		slks->spaces[0] = 3;
264 		break;
265 	case 2:
266 		// same allocations as format 3
267 	case 3:
268 		nblocks = 12; nmaj = 2; nmin = 9;
269 		slks->spaces[0] = 3; slks->spaces[1] = 7;
270 		break;
271 	default:
272 		return ERR;
273 	}
274 
275 	// determine maximum label length and major space size
276 	available_width = COLS - ( ( MIN_SPACE_SIZE * nmaj ) + nmin );
277 	slks->max_label_len = available_width / nblocks;
278 	slks->maj_space_len = MIN_SPACE_SIZE +
279 		( available_width % nblocks ) / nmaj;
280 	slks->num_spaces = nmaj;
281 	slks->num_labels = nblocks;
282 
283 	// strip a line from the screen
284 	LINES -= 1;
285 
286 	return OK;
287 }
288 
289 /**
290  * Return the label for the specified soft key
291  *
292  * @v labnum	soft key identifier
293  * @ret label	return label
294  */
slk_label(int labnum)295 char* slk_label ( int labnum ) {
296 	if ( slks == NULL )
297 		return NULL;
298 
299 	return slks->fkeys[labnum].label;
300 }
301 
302 /**
303  * Restore soft function key labels to the screen
304  *
305  * @ret rc	return status code
306  */
slk_restore(void)307 int slk_restore ( void ) {
308 	unsigned int i, j, pos_x,
309 		*next_space, *last_space;
310 	chtype space_ch;
311 
312 	if ( slks == NULL )
313 		return ERR;
314 
315 	pos_x = 0;
316 
317 	_enter_slk();
318 
319 	space_ch = (chtype)' ' | slks->attrs;
320 	next_space = &(slks->spaces[0]);
321 	last_space = &(slks->spaces[slks->num_spaces-1]);
322 
323 	for ( i = 0; i < slks->num_labels ; i++ ) {
324 		_print_label( slks->fkeys[i] );
325 		pos_x += slks->max_label_len;
326 
327 		if ( i == *next_space ) {
328 			for ( j = 0; j < slks->maj_space_len; j++, pos_x++ )
329 				_wputch ( stdscr, space_ch, NOWRAP );
330 			if ( next_space < last_space )
331 				next_space++;
332 		} else {
333 			if ( pos_x < COLS )
334 				_wputch ( stdscr, space_ch, NOWRAP );
335 			pos_x++;
336 		}
337 	}
338 
339 	_leave_slk();
340 
341 	return OK;
342 }
343 
344 /**
345  * Configure specified soft key
346  *
347  * @v labnum	soft label position to configure
348  * @v *label	string to use as soft key label
349  * @v fmt	justification format of label
350  * @ret rc	return status code
351  */
slk_set(int labnum,const char * label,int fmt)352 int slk_set ( int labnum, const char *label, int fmt ) {
353 	if ( slks == NULL )
354 		return ERR;
355 	if ( (unsigned short)labnum >= slks->num_labels )
356 		return ERR;
357 	if ( (unsigned short)fmt >= 3 )
358 		return ERR;
359 
360 	strncpy(slks->fkeys[labnum].label, label,
361 		(sizeof(slks->fkeys[labnum].label) - 1));
362 	slks->fkeys[labnum].fmt = fmt;
363 
364 	return OK;
365 }
366