1 /* Public Domain Curses */
2 
3 #include <curspriv.h>
4 
5 /*man-start**************************************************************
6 
7 slk
8 ---
9 
10 ### Synopsis
11 
12     int slk_init(int fmt);
13     int slk_set(int labnum, const char *label, int justify);
14     int slk_refresh(void);
15     int slk_noutrefresh(void);
16     char *slk_label(int labnum);
17     int slk_clear(void);
18     int slk_restore(void);
19     int slk_touch(void);
20     int slk_attron(const chtype attrs);
21     int slk_attr_on(const attr_t attrs, void *opts);
22     int slk_attrset(const chtype attrs);
23     int slk_attr_set(const attr_t attrs, short color_pair, void *opts);
24     int slk_attroff(const chtype attrs);
25     int slk_attr_off(const attr_t attrs, void *opts);
26     int slk_color(short color_pair);
27 
28     int slk_wset(int labnum, const wchar_t *label, int justify);
29 
30     int PDC_mouse_in_slk(int y, int x);
31     void PDC_slk_free(void);
32     void PDC_slk_initialize(void);
33 
34     wchar_t *slk_wlabel(int labnum)
35 
36 ### Description
37 
38    These functions manipulate a window that contain Soft Label Keys
39    (SLK). To use the SLK functions, a call to slk_init() must be
40    made BEFORE initscr() or newterm(). slk_init() removes 1 or 2
41    lines from the useable screen, depending on the format selected.
42 
43    The line(s) removed from the screen are used as a separate
44    window, in which SLKs are displayed.
45 
46    slk_init() requires a single parameter which describes the
47    format of the SLKs as follows:
48 
49    0       3-2-3 format
50    1       4-4 format
51    2       4-4-4 format (ncurses extension)
52    3       4-4-4 format with index line (ncurses extension)
53    2 lines used
54    55      5-5 format (pdcurses format)
55 
56    In PDCurses,  one can alternatively set fmt as a series of hex
57    digits specifying the format.  For example,  0x414 would result
58    in 4-1-4 format; 0x21b3 would result in 2-1-11-3 format;  and
59    so on.  Also,  negating fmt results in the index line being added.
60 
61    Also,  in PDCurses,  one can call slk_init() at any time
62    _after_ initscr(),  to reset the label format.  If you do this,
63    you'll need to reset the label text and call slk_refresh().  And
64    you can't toggle the index line.  (Doing so would add/remove a line
65    from the useable screen,  which would be hard to handle correctly.)
66 
67    slk_refresh(), slk_noutrefresh() and slk_touch() are analogous
68    to refresh(), noutrefresh() and touch().
69 
70 ### Return Value
71 
72    All functions return OK on success and ERR on error.
73 
74 ### Portability
75                              X/Open    BSD    SYS V
76     slk_init                    Y       -       Y
77     slk_set                     Y       -       Y
78     slk_refresh                 Y       -       Y
79     slk_noutrefresh             Y       -       Y
80     slk_label                   Y       -       Y
81     slk_clear                   Y       -       Y
82     slk_restore                 Y       -       Y
83     slk_touch                   Y       -       Y
84     slk_attron                  Y       -       Y
85     slk_attrset                 Y       -       Y
86     slk_attroff                 Y       -       Y
87     slk_attr_on                 Y
88     slk_attr_set                Y
89     slk_attr_off                Y
90     slk_wset                    Y
91     PDC_mouse_in_slk            -       -       -
92     PDC_slk_free                -       -       -
93     PDC_slk_initialize          -       -       -
94     slk_wlabel                  -       -       -
95 
96 **man-end****************************************************************/
97 
98 #include <stdlib.h>
99 
100 static int label_length = 0;
101 static int n_labels = 0;
102 static int label_fmt = 0;
103 static int label_line = 0;
104 static bool hidden = FALSE;
105 
106 #define MAX_LABEL_LENGTH 32
107 
108 static struct SLK {
109     chtype label[MAX_LABEL_LENGTH];
110     int len;
111     int format;
112     int start_col;
113 } *slk = (struct SLK *)NULL;
114 
115 /* See comments above on this function.   */
116 
slk_init(int fmt)117 int slk_init(int fmt)
118 {
119     int i;
120 
121     PDC_LOG(("slk_init() - called\n"));
122 
123     switch (fmt)
124     {
125     case 0:  /* 3 - 2 - 3 */
126         label_fmt = 0x323;
127         break;
128 
129     case 1:   /* 4 - 4 */
130         label_fmt = 0x44;
131         break;
132 
133     case 2:   /* 4 4 4 */
134         label_fmt = 0x444;
135         break;
136 
137     case 3:   /* 4 4 4  with index */
138         label_fmt = -0x444;
139         break;
140 
141     case 55:  /* 5 - 5 */
142         label_fmt = 0x55;
143         break;
144 
145     default:
146         label_fmt = fmt;
147         break;
148     }
149 
150     traceon( );
151     n_labels = 0;
152     for( i = abs( label_fmt); i; i /= 16)
153        n_labels += i % 16;
154 
155     PDC_LOG(("slk_init: fmt %d, %d labels, %p\n",
156                fmt, n_labels, slk));
157     if( slk)
158         free( slk);
159     slk = calloc(n_labels, sizeof(struct SLK));
160     PDC_LOG(( "New slk: %p; SP = %p\n", slk, SP));
161     traceoff( );
162 
163     if (!slk)
164         n_labels = 0;
165     if( SP)
166         {
167         if( SP->slk_winptr)
168             wclear( SP->slk_winptr);
169         PDC_slk_initialize( );
170         }
171 
172     return slk ? OK : ERR;
173 }
174 
175 /* draw a single button */
176 
_drawone(int num)177 static void _drawone(int num)
178 {
179     int i, col, slen;
180 
181     if (hidden)
182         return;
183 
184     slen = slk[num].len;
185 
186     switch (slk[num].format)
187     {
188     case 0:  /* LEFT */
189         col = 0;
190         break;
191 
192     case 1:  /* CENTER */
193         col = (label_length - slen) / 2;
194 
195         if (col + slen > label_length)
196             --col;
197         break;
198 
199     default:  /* RIGHT */
200         col = label_length - slen;
201     }
202 
203     if( col < 0)  /* Ensure start of label is visible */
204         col = 0;
205     wmove(SP->slk_winptr, label_line, slk[num].start_col);
206 
207     for (i = 0; i < label_length; ++i)
208         waddch(SP->slk_winptr, (i >= col && i < (col + slen)) ?
209                slk[num].label[i - col] : ' ');
210 }
211 
212 /* redraw each button */
213 
_redraw(void)214 static void _redraw(void)
215 {
216     int i;
217 
218     if( !hidden)
219     {
220         for (i = 0; i < n_labels; ++i)
221             _drawone(i);
222         if (label_fmt < 0)
223         {
224             const chtype save_attr = SP->slk_winptr->_attrs;
225 
226             wattrset(SP->slk_winptr, A_NORMAL);
227             wmove(SP->slk_winptr, 0, 0);
228             whline(SP->slk_winptr, 0, COLS);
229 
230             for (i = 0; i < n_labels; i++)
231                 mvwprintw(SP->slk_winptr, 0, slk[i].start_col, "F%d", i + 1);
232 
233             SP->slk_winptr->_attrs = save_attr;
234         }
235     }
236 }
237 
238 /* slk_set() Used to set a slk label to a string.
239 
240    labnum  = 1 - 8 (or 10) (number of the label)
241    label   = string (8 or 7 bytes total), or NULL
242    justify = 0 : left, 1 : center, 2 : right  */
243 
slk_set(int labnum,const char * label,int justify)244 int slk_set(int labnum, const char *label, int justify)
245 {
246 #ifdef PDC_WIDE
247     wchar_t wlabel[MAX_LABEL_LENGTH];
248 
249     PDC_mbstowcs(wlabel, label, MAX_LABEL_LENGTH - 1);
250     return slk_wset(labnum, wlabel, justify);
251 #else
252     PDC_LOG(("slk_set() - called\n"));
253 
254     if (labnum < 1 || labnum > n_labels || justify < 0 || justify > 2)
255         return ERR;
256 
257     labnum--;
258 
259     if (!label || !(*label))
260     {
261         /* Clear the label */
262 
263         *slk[labnum].label = 0;
264         slk[labnum].format = 0;
265         slk[labnum].len = 0;
266     }
267     else
268     {
269         int i;
270 
271         /* Skip leading spaces */
272 
273         while( *label == ' ')
274             label++;
275 
276         /* Copy it */
277 
278         for (i = 0; label[i] && i < MAX_LABEL_LENGTH - 1; i++)
279             slk[labnum].label[i] = label[i];
280 
281         /* Drop trailing spaces */
282 
283         while( i && label[i - 1] == ' ')
284             i--;
285 
286         slk[labnum].label[i] = 0;
287         slk[labnum].format = justify;
288         slk[labnum].len = i;
289     }
290 
291     _drawone(labnum);
292 
293     return OK;
294 #endif
295 }
296 
slk_refresh(void)297 int slk_refresh(void)
298 {
299     PDC_LOG(("slk_refresh() - called\n"));
300 
301     return (slk_noutrefresh() == ERR) ? ERR : doupdate();
302 }
303 
slk_noutrefresh(void)304 int slk_noutrefresh(void)
305 {
306     PDC_LOG(("slk_noutrefresh() - called\n"));
307 
308     return wnoutrefresh(SP->slk_winptr);
309 }
310 
slk_label(int labnum)311 char *slk_label(int labnum)
312 {
313     static char temp[MAX_LABEL_LENGTH + 1];
314 #ifdef PDC_WIDE
315     wchar_t *wtemp = slk_wlabel(labnum);
316 
317     PDC_wcstombs(temp, wtemp, MAX_LABEL_LENGTH);
318 #else
319     chtype *p;
320     int i;
321 
322     PDC_LOG(("slk_label() - called\n"));
323 
324     if (labnum < 1 || labnum > n_labels)
325         return (char *)0;
326 
327     for (i = 0, p = slk[labnum - 1].label; *p; i++)
328         temp[i] = (char)*p++;    /* BJG */
329 
330     temp[i] = '\0';
331 #endif
332     return temp;
333 }
334 
slk_clear(void)335 int slk_clear(void)
336 {
337     PDC_LOG(("slk_clear() - called\n"));
338 
339     hidden = TRUE;
340     werase(SP->slk_winptr);
341     return wrefresh(SP->slk_winptr);
342 }
343 
slk_restore(void)344 int slk_restore(void)
345 {
346     PDC_LOG(("slk_restore() - called\n"));
347 
348     hidden = FALSE;
349     _redraw();
350     return wrefresh(SP->slk_winptr);
351 }
352 
slk_touch(void)353 int slk_touch(void)
354 {
355     PDC_LOG(("slk_touch() - called\n"));
356 
357     return touchwin(SP->slk_winptr);
358 }
359 
slk_attron(const chtype attrs)360 int slk_attron(const chtype attrs)
361 {
362     int rc;
363 
364     PDC_LOG(("slk_attron() - called\n"));
365 
366     rc = wattron(SP->slk_winptr, attrs);
367     _redraw();
368 
369     return rc;
370 }
371 
slk_attr_on(const attr_t attrs,void * opts)372 int slk_attr_on(const attr_t attrs, void *opts)
373 {
374     PDC_LOG(("slk_attr_on() - called\n"));
375 
376     return slk_attron(attrs);
377 }
378 
slk_attroff(const chtype attrs)379 int slk_attroff(const chtype attrs)
380 {
381     int rc;
382 
383     PDC_LOG(("slk_attroff() - called\n"));
384 
385     rc = wattroff(SP->slk_winptr, attrs);
386     _redraw();
387 
388     return rc;
389 }
390 
slk_attr_off(const attr_t attrs,void * opts)391 int slk_attr_off(const attr_t attrs, void *opts)
392 {
393     PDC_LOG(("slk_attr_off() - called\n"));
394 
395     return slk_attroff(attrs);
396 }
397 
slk_attrset(const chtype attrs)398 int slk_attrset(const chtype attrs)
399 {
400     int rc;
401 
402     PDC_LOG(("slk_attrset() - called\n"));
403 
404     rc = wattrset(SP->slk_winptr, attrs);
405     _redraw();
406 
407     return rc;
408 }
409 
slk_color(short color_pair)410 int slk_color(short color_pair)
411 {
412     int rc;
413 
414     PDC_LOG(("slk_color() - called\n"));
415 
416     rc = wcolor_set(SP->slk_winptr, color_pair, NULL);
417     _redraw();
418 
419     return rc;
420 }
421 
slk_attr_set(const attr_t attrs,short color_pair,void * opts)422 int slk_attr_set(const attr_t attrs, short color_pair, void *opts)
423 {
424     PDC_LOG(("slk_attr_set() - called\n"));
425 
426     return slk_attrset(attrs | COLOR_PAIR(color_pair));
427 }
428 
_slk_calc(void)429 static void _slk_calc(void)
430 {
431     int i, j, idx, remaining_space;
432     int n_groups = 0, group_size[10];
433 
434     label_length = COLS / n_labels;
435     if (label_length > MAX_LABEL_LENGTH)
436         label_length = MAX_LABEL_LENGTH;
437     remaining_space = COLS - label_length * n_labels + 1;
438     for( i = abs( label_fmt); i; i /= 16)
439         group_size[n_groups++] = i % 16;
440                /* We really want at least two spaces between groups: */
441     while( label_length > 1 && remaining_space < n_groups - 1)
442     {
443         label_length--;
444         remaining_space += n_labels;
445     }
446 
447     for( i = idx = 0; i < n_groups; i++)
448         for( j = 0; j < group_size[i]; j++, idx++)
449             slk[idx].start_col = label_length * idx
450                      + (i ? (i * remaining_space) / (n_groups - 1) : 0);
451 
452     if( label_length)
453        --label_length;
454 
455     /* make sure labels are all in window */
456 
457     _redraw();
458 }
459 
PDC_slk_initialize(void)460 void PDC_slk_initialize(void)
461 {
462     if (slk)
463     {
464         if( label_fmt < 0)
465         {
466             SP->slklines = 2;
467             label_line = 1;
468         }
469         else
470             SP->slklines = 1;
471 
472         if (!SP->slk_winptr)
473         {
474             if ( !(SP->slk_winptr = newwin(SP->slklines, COLS,
475                                            LINES - SP->slklines, 0)) )
476                 return;
477 
478             wattrset(SP->slk_winptr, A_REVERSE);
479         }
480 
481         _slk_calc();
482 
483         touchwin(SP->slk_winptr);
484     }
485 }
486 
PDC_slk_free(void)487 void PDC_slk_free(void)
488 {
489     if (slk)
490     {
491         if (SP->slk_winptr)
492         {
493             delwin(SP->slk_winptr);
494             SP->slk_winptr = (WINDOW *)NULL;
495         }
496 
497         free(slk);
498         slk = (struct SLK *)NULL;
499 
500         label_length = 0;
501         n_labels = 0;
502         label_fmt = 0;
503         label_line = 0;
504         hidden = FALSE;
505     }
506 }
507 
PDC_mouse_in_slk(int y,int x)508 int PDC_mouse_in_slk(int y, int x)
509 {
510     int i;
511 
512     PDC_LOG(("PDC_mouse_in_slk() - called: y->%d x->%d\n", y, x));
513 
514     /* If the line on which the mouse was clicked is NOT the last line
515        of the screen, or the SLKs are hidden,  we are not interested in it. */
516 
517     if (!slk || hidden || !SP->slk_winptr
518                         || (y != SP->slk_winptr->_begy + label_line))
519         return 0;
520 
521     for (i = 0; i < n_labels; i++)
522         if (x >= slk[i].start_col && x < (slk[i].start_col + label_length))
523             return i + 1;
524 
525     return 0;
526 }
527 
528 #ifdef PDC_WIDE
slk_wset(int labnum,const wchar_t * label,int justify)529 int slk_wset(int labnum, const wchar_t *label, int justify)
530 {
531     PDC_LOG(("slk_wset() - called\n"));
532 
533     if (labnum < 1 || labnum > n_labels || justify < 0 || justify > 2)
534         return ERR;
535 
536     labnum--;
537 
538     if (!label || !(*label))
539     {
540         /* Clear the label */
541 
542         *slk[labnum].label = 0;
543         slk[labnum].format = 0;
544         slk[labnum].len = 0;
545     }
546     else
547     {
548         int i;
549 
550         /* Skip leading spaces */
551 
552         while( *label == L' ')
553             label++;
554 
555         /* Copy it */
556 
557         for (i = 0; label[i] && i < MAX_LABEL_LENGTH - 1; i++)
558             slk[labnum].label[i] = label[i];
559 
560         /* Drop trailing spaces */
561 
562         while( i && label[i - 1] == L' ')
563             i--;
564 
565         slk[labnum].label[i] = 0;
566         slk[labnum].format = justify;
567         slk[labnum].len = i;
568     }
569 
570     _drawone(labnum);
571 
572     return OK;
573 }
574 
slk_wlabel(int labnum)575 wchar_t *slk_wlabel(int labnum)
576 {
577     static wchar_t temp[MAX_LABEL_LENGTH + 1];
578     chtype *p;
579     int i;
580 
581     PDC_LOG(("slk_wlabel() - called\n"));
582 
583     if (labnum < 1 || labnum > n_labels)
584         return (wchar_t *)0;
585 
586     for (i = 0, p = slk[labnum - 1].label; *p; i++)
587         temp[i] = (wchar_t)*p++;
588 
589     temp[i] = '\0';
590 
591     return temp;
592 }
593 #endif
594