GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: lib/libmenu/m_driver.c Lines: 0 160 0.0 %
Date: 2017-11-13 Branches: 0 184 0.0 %

Line Branch Exec Source
1
/* $OpenBSD: m_driver.c,v 1.8 2010/01/12 23:22:07 nicm Exp $ */
2
3
/****************************************************************************
4
 * Copyright (c) 1998-2005,2008 Free Software Foundation, Inc.              *
5
 *                                                                          *
6
 * Permission is hereby granted, free of charge, to any person obtaining a  *
7
 * copy of this software and associated documentation files (the            *
8
 * "Software"), to deal in the Software without restriction, including      *
9
 * without limitation the rights to use, copy, modify, merge, publish,      *
10
 * distribute, distribute with modifications, sublicense, and/or sell       *
11
 * copies of the Software, and to permit persons to whom the Software is    *
12
 * furnished to do so, subject to the following conditions:                 *
13
 *                                                                          *
14
 * The above copyright notice and this permission notice shall be included  *
15
 * in all copies or substantial portions of the Software.                   *
16
 *                                                                          *
17
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
18
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
19
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
20
 * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
21
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
22
 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
23
 * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
24
 *                                                                          *
25
 * Except as contained in this notice, the name(s) of the above copyright   *
26
 * holders shall not be used in advertising or otherwise to promote the     *
27
 * sale, use or other dealings in this Software without prior written       *
28
 * authorization.                                                           *
29
 ****************************************************************************/
30
31
/****************************************************************************
32
 *   Author:  Juergen Pfeifer, 1995,1997                                    *
33
 ****************************************************************************/
34
35
/***************************************************************************
36
* Module m_driver                                                          *
37
* Central dispatching routine                                              *
38
***************************************************************************/
39
40
#include "menu.priv.h"
41
42
MODULE_ID("$Id: m_driver.c,v 1.8 2010/01/12 23:22:07 nicm Exp $")
43
44
/* Macros */
45
46
/* Remove the last character from the match pattern buffer */
47
#define Remove_Character_From_Pattern(menu) \
48
  (menu)->pattern[--((menu)->pindex)] = '\0'
49
50
/* Add a new character to the match pattern buffer */
51
#define Add_Character_To_Pattern(menu,ch) \
52
  { (menu)->pattern[((menu)->pindex)++] = (ch);\
53
    (menu)->pattern[(menu)->pindex] = '\0'; }
54
55
/*---------------------------------------------------------------------------
56
|   Facility      :  libnmenu
57
|   Function      :  static bool Is_Sub_String(
58
|                           bool IgnoreCaseFlag,
59
|                           const char *part,
60
|                           const char *string)
61
|
62
|   Description   :  Checks whether or not part is a substring of string.
63
|
64
|   Return Values :  TRUE   - if it is a substring
65
|                    FALSE  - if it is not a substring
66
+--------------------------------------------------------------------------*/
67
static bool
68
Is_Sub_String(
69
	       bool IgnoreCaseFlag,
70
	       const char *part,
71
	       const char *string
72
)
73
{
74
  assert(part && string);
75
  if (IgnoreCaseFlag)
76
    {
77
      while (*string && *part)
78
	{
79
	  if (toupper(UChar(*string++)) != toupper(UChar(*part)))
80
	    break;
81
	  part++;
82
	}
83
    }
84
  else
85
    {
86
      while (*string && *part)
87
	if (*part != *string++)
88
	  break;
89
      part++;
90
    }
91
  return ((*part) ? FALSE : TRUE);
92
}
93
94
/*---------------------------------------------------------------------------
95
|   Facility      :  libnmenu
96
|   Function      :  int _nc_Match_Next_Character_In_Item_Name(
97
|                           MENU *menu,
98
|                           int  ch,
99
|                           ITEM **item)
100
|
101
|   Description   :  This internal routine is called for a menu positioned
102
|                    at an item with three different classes of characters:
103
|                       - a printable character; the character is added to
104
|                         the current pattern and the next item matching
105
|                         this pattern is searched.
106
|                       - NUL; the pattern stays as it is and the next item
107
|                         matching the pattern is searched
108
|                       - BS; the pattern stays as it is and the previous
109
|                         item matching the pattern is searched
110
|
111
|                       The item parameter contains on call a pointer to
112
|                       the item where the search starts. On return - if
113
|                       a match was found - it contains a pointer to the
114
|                       matching item.
115
|
116
|   Return Values :  E_OK        - an item matching the pattern was found
117
|                    E_NO_MATCH  - nothing found
118
+--------------------------------------------------------------------------*/
119
NCURSES_EXPORT(int)
120
_nc_Match_Next_Character_In_Item_Name
121
(MENU * menu, int ch, ITEM ** item)
122
{
123
  bool found = FALSE, passed = FALSE;
124
  int idx, last;
125
126
  T((T_CALLED("_nc_Match_Next_Character(%p,%d,%p)"), menu, ch, item));
127
128
  assert(menu && item && *item);
129
  idx = (*item)->index;
130
131
  if (ch && ch != BS)
132
    {
133
      /* if we become to long, we need no further checking : there can't be
134
         a match ! */
135
      if ((menu->pindex + 1) > menu->namelen)
136
	RETURN(E_NO_MATCH);
137
138
      Add_Character_To_Pattern(menu, ch);
139
      /* we artificially position one item back, because in the do...while
140
         loop we start with the next item. This means, that with a new
141
         pattern search we always start the scan with the actual item. If
142
         we do a NEXT_PATTERN oder PREV_PATTERN search, we start with the
143
         one after or before the actual item. */
144
      if (--idx < 0)
145
	idx = menu->nitems - 1;
146
    }
147
148
  last = idx;			/* this closes the cycle */
149
150
  do
151
    {
152
      if (ch == BS)
153
	{			/* we have to go backward */
154
	  if (--idx < 0)
155
	    idx = menu->nitems - 1;
156
	}
157
      else
158
	{			/* otherwise we always go forward */
159
	  if (++idx >= menu->nitems)
160
	    idx = 0;
161
	}
162
      if (Is_Sub_String((bool)((menu->opt & O_IGNORECASE) != 0),
163
			menu->pattern,
164
			menu->items[idx]->name.str)
165
	)
166
	found = TRUE;
167
      else
168
	passed = TRUE;
169
    }
170
  while (!found && (idx != last));
171
172
  if (found)
173
    {
174
      if (!((idx == (*item)->index) && passed))
175
	{
176
	  *item = menu->items[idx];
177
	  RETURN(E_OK);
178
	}
179
      /* This point is reached, if we fully cycled through the item list
180
         and the only match we found is the starting item. With a NEXT_PATTERN
181
         or PREV_PATTERN scan this means, that there was no additional match.
182
         If we searched with an expanded new pattern, we should never reach
183
         this point, because if the expanded pattern matches also the actual
184
         item we will find it in the first attempt (passed==FALSE) and we
185
         will never cycle through the whole item array.
186
       */
187
      assert(ch == 0 || ch == BS);
188
    }
189
  else
190
    {
191
      if (ch && ch != BS && menu->pindex > 0)
192
	{
193
	  /* if we had no match with a new pattern, we have to restore it */
194
	  Remove_Character_From_Pattern(menu);
195
	}
196
    }
197
  RETURN(E_NO_MATCH);
198
}
199
200
/*---------------------------------------------------------------------------
201
|   Facility      :  libnmenu
202
|   Function      :  int menu_driver(MENU *menu, int c)
203
|
204
|   Description   :  Central dispatcher for the menu. Translates the logical
205
|                    request 'c' into a menu action.
206
|
207
|   Return Values :  E_OK            - success
208
|                    E_BAD_ARGUMENT  - invalid menu pointer
209
|                    E_BAD_STATE     - menu is in user hook routine
210
|                    E_NOT_POSTED    - menu is not posted
211
+--------------------------------------------------------------------------*/
212
NCURSES_EXPORT(int)
213
menu_driver(MENU * menu, int c)
214
{
215
#define NAVIGATE(dir) \
216
  if (!item->dir)\
217
     result = E_REQUEST_DENIED;\
218
  else\
219
     item = item->dir
220
221
  int result = E_OK;
222
  ITEM *item;
223
  int my_top_row, rdiff;
224
225
  T((T_CALLED("menu_driver(%p,%d)"), menu, c));
226
227
  if (!menu)
228
    RETURN(E_BAD_ARGUMENT);
229
230
  if (menu->status & _IN_DRIVER)
231
    RETURN(E_BAD_STATE);
232
  if (!(menu->status & _POSTED))
233
    RETURN(E_NOT_POSTED);
234
235
  item = menu->curitem;
236
237
  my_top_row = menu->toprow;
238
  assert(item);
239
240
  if ((c > KEY_MAX) && (c <= MAX_MENU_COMMAND))
241
    {
242
      if (!((c == REQ_BACK_PATTERN)
243
	    || (c == REQ_NEXT_MATCH) || (c == REQ_PREV_MATCH)))
244
	{
245
	  assert(menu->pattern);
246
	  Reset_Pattern(menu);
247
	}
248
249
      switch (c)
250
	{
251
	case REQ_LEFT_ITEM:
252
	    /*=================*/
253
	  NAVIGATE(left);
254
	  break;
255
256
	case REQ_RIGHT_ITEM:
257
	    /*==================*/
258
	  NAVIGATE(right);
259
	  break;
260
261
	case REQ_UP_ITEM:
262
	    /*===============*/
263
	  NAVIGATE(up);
264
	  break;
265
266
	case REQ_DOWN_ITEM:
267
	    /*=================*/
268
	  NAVIGATE(down);
269
	  break;
270
271
	case REQ_SCR_ULINE:
272
	    /*=================*/
273
	  if (my_top_row == 0 || !(item->up))
274
	    result = E_REQUEST_DENIED;
275
	  else
276
	    {
277
	      --my_top_row;
278
	      item = item->up;
279
	    }
280
	  break;
281
282
	case REQ_SCR_DLINE:
283
	    /*=================*/
284
	  if ((my_top_row + menu->arows >= menu->rows) || !(item->down))
285
	    {
286
	      /* only if the menu has less items than rows, we can deny the
287
	         request. Otherwise the epilogue of this routine adjusts the
288
	         top row if necessary */
289
	      result = E_REQUEST_DENIED;
290
	    }
291
	  else
292
	    {
293
	      my_top_row++;
294
	      item = item->down;
295
	    }
296
	  break;
297
298
	case REQ_SCR_DPAGE:
299
	    /*=================*/
300
	  rdiff = menu->rows - (menu->arows + my_top_row);
301
	  if (rdiff > menu->arows)
302
	    rdiff = menu->arows;
303
	  if (rdiff <= 0)
304
	    result = E_REQUEST_DENIED;
305
	  else
306
	    {
307
	      my_top_row += rdiff;
308
	      while (rdiff-- > 0 && item != 0 && item->down != 0)
309
		item = item->down;
310
	    }
311
	  break;
312
313
	case REQ_SCR_UPAGE:
314
	    /*=================*/
315
	  rdiff = (menu->arows < my_top_row) ? menu->arows : my_top_row;
316
	  if (rdiff <= 0)
317
	    result = E_REQUEST_DENIED;
318
	  else
319
	    {
320
	      my_top_row -= rdiff;
321
	      while (rdiff-- > 0 && item != 0 && item->up != 0)
322
		item = item->up;
323
	    }
324
	  break;
325
326
	case REQ_FIRST_ITEM:
327
	    /*==================*/
328
	  item = menu->items[0];
329
	  break;
330
331
	case REQ_LAST_ITEM:
332
	    /*=================*/
333
	  item = menu->items[menu->nitems - 1];
334
	  break;
335
336
	case REQ_NEXT_ITEM:
337
	    /*=================*/
338
	  if ((item->index + 1) >= menu->nitems)
339
	    {
340
	      if (menu->opt & O_NONCYCLIC)
341
		result = E_REQUEST_DENIED;
342
	      else
343
		item = menu->items[0];
344
	    }
345
	  else
346
	    item = menu->items[item->index + 1];
347
	  break;
348
349
	case REQ_PREV_ITEM:
350
	    /*=================*/
351
	  if (item->index <= 0)
352
	    {
353
	      if (menu->opt & O_NONCYCLIC)
354
		result = E_REQUEST_DENIED;
355
	      else
356
		item = menu->items[menu->nitems - 1];
357
	    }
358
	  else
359
	    item = menu->items[item->index - 1];
360
	  break;
361
362
	case REQ_TOGGLE_ITEM:
363
	    /*===================*/
364
	  if (menu->opt & O_ONEVALUE)
365
	    {
366
	      result = E_REQUEST_DENIED;
367
	    }
368
	  else
369
	    {
370
	      if (menu->curitem->opt & O_SELECTABLE)
371
		{
372
		  menu->curitem->value = !menu->curitem->value;
373
		  Move_And_Post_Item(menu, menu->curitem);
374
		  _nc_Show_Menu(menu);
375
		}
376
	      else
377
		result = E_NOT_SELECTABLE;
378
	    }
379
	  break;
380
381
	case REQ_CLEAR_PATTERN:
382
	    /*=====================*/
383
	  /* already cleared in prologue */
384
	  break;
385
386
	case REQ_BACK_PATTERN:
387
	    /*====================*/
388
	  if (menu->pindex > 0)
389
	    {
390
	      assert(menu->pattern);
391
	      Remove_Character_From_Pattern(menu);
392
	      pos_menu_cursor(menu);
393
	    }
394
	  else
395
	    result = E_REQUEST_DENIED;
396
	  break;
397
398
	case REQ_NEXT_MATCH:
399
	    /*==================*/
400
	  assert(menu->pattern);
401
	  if (menu->pattern[0])
402
	    result = _nc_Match_Next_Character_In_Item_Name(menu, 0, &item);
403
	  else
404
	    {
405
	      if ((item->index + 1) < menu->nitems)
406
		item = menu->items[item->index + 1];
407
	      else
408
		{
409
		  if (menu->opt & O_NONCYCLIC)
410
		    result = E_REQUEST_DENIED;
411
		  else
412
		    item = menu->items[0];
413
		}
414
	    }
415
	  break;
416
417
	case REQ_PREV_MATCH:
418
	    /*==================*/
419
	  assert(menu->pattern);
420
	  if (menu->pattern[0])
421
	    result = _nc_Match_Next_Character_In_Item_Name(menu, BS, &item);
422
	  else
423
	    {
424
	      if (item->index)
425
		item = menu->items[item->index - 1];
426
	      else
427
		{
428
		  if (menu->opt & O_NONCYCLIC)
429
		    result = E_REQUEST_DENIED;
430
		  else
431
		    item = menu->items[menu->nitems - 1];
432
		}
433
	    }
434
	  break;
435
436
	default:
437
	    /*======*/
438
	  result = E_UNKNOWN_COMMAND;
439
	  break;
440
	}
441
    }
442
  else
443
    {				/* not a command */
444
      if (!(c & ~((int)MAX_REGULAR_CHARACTER)) && isprint(UChar(c)))
445
	result = _nc_Match_Next_Character_In_Item_Name(menu, c, &item);
446
#ifdef NCURSES_MOUSE_VERSION
447
      else if (KEY_MOUSE == c)
448
	{
449
	  MEVENT event;
450
	  WINDOW *uwin = Get_Menu_UserWin(menu);
451
452
	  getmouse(&event);
453
	  if ((event.bstate & (BUTTON1_CLICKED |
454
			       BUTTON1_DOUBLE_CLICKED |
455
			       BUTTON1_TRIPLE_CLICKED))
456
	      && wenclose(uwin, event.y, event.x))
457
	    {			/* we react only if the click was in the userwin, that means
458
				 * inside the menu display area or at the decoration window.
459
				 */
460
	      WINDOW *sub = Get_Menu_Window(menu);
461
	      int ry = event.y, rx = event.x;	/* screen coordinates */
462
463
	      result = E_REQUEST_DENIED;
464
	      if (mouse_trafo(&ry, &rx, FALSE))
465
		{		/* rx, ry are now "curses" coordinates */
466
		  if (ry < sub->_begy)
467
		    {		/* we clicked above the display region; this is
468
				 * interpreted as "scroll up" request
469
				 */
470
		      if (event.bstate & BUTTON1_CLICKED)
471
			result = menu_driver(menu, REQ_SCR_ULINE);
472
		      else if (event.bstate & BUTTON1_DOUBLE_CLICKED)
473
			result = menu_driver(menu, REQ_SCR_UPAGE);
474
		      else if (event.bstate & BUTTON1_TRIPLE_CLICKED)
475
			result = menu_driver(menu, REQ_FIRST_ITEM);
476
		      RETURN(result);
477
		    }
478
		  else if (ry > sub->_begy + sub->_maxy)
479
		    {		/* we clicked below the display region; this is
480
				 * interpreted as "scroll down" request
481
				 */
482
		      if (event.bstate & BUTTON1_CLICKED)
483
			result = menu_driver(menu, REQ_SCR_DLINE);
484
		      else if (event.bstate & BUTTON1_DOUBLE_CLICKED)
485
			result = menu_driver(menu, REQ_SCR_DPAGE);
486
		      else if (event.bstate & BUTTON1_TRIPLE_CLICKED)
487
			result = menu_driver(menu, REQ_LAST_ITEM);
488
		      RETURN(result);
489
		    }
490
		  else if (wenclose(sub, event.y, event.x))
491
		    {		/* Inside the area we try to find the hit item */
492
		      int i, x, y, err;
493
494
		      ry = event.y;
495
		      rx = event.x;
496
		      if (wmouse_trafo(sub, &ry, &rx, FALSE))
497
			{
498
			  for (i = 0; i < menu->nitems; i++)
499
			    {
500
			      err = _nc_menu_cursor_pos(menu, menu->items[i],
501
							&y, &x);
502
			      if (E_OK == err)
503
				{
504
				  if ((ry == y) &&
505
				      (rx >= x) &&
506
				      (rx < x + menu->itemlen))
507
				    {
508
				      item = menu->items[i];
509
				      result = E_OK;
510
				      break;
511
				    }
512
				}
513
			    }
514
			  if (E_OK == result)
515
			    {	/* We found an item, now we can handle the click.
516
				 * A single click just positions the menu cursor
517
				 * to the clicked item. A double click toggles
518
				 * the item.
519
				 */
520
			      if (event.bstate & BUTTON1_DOUBLE_CLICKED)
521
				{
522
				  _nc_New_TopRow_and_CurrentItem(menu,
523
								 my_top_row,
524
								 item);
525
				  menu_driver(menu, REQ_TOGGLE_ITEM);
526
				  result = E_UNKNOWN_COMMAND;
527
				}
528
			    }
529
			}
530
		    }
531
		}
532
	    }
533
	  else
534
	    result = E_REQUEST_DENIED;
535
	}
536
#endif /* NCURSES_MOUSE_VERSION */
537
      else
538
	result = E_UNKNOWN_COMMAND;
539
    }
540
541
  if (E_OK == result)
542
    {
543
      /* Adjust the top row if it turns out that the current item unfortunately
544
         doesn't appear in the menu window */
545
      if (item->y < my_top_row)
546
	my_top_row = item->y;
547
      else if (item->y >= (my_top_row + menu->arows))
548
	my_top_row = item->y - menu->arows + 1;
549
550
      _nc_New_TopRow_and_CurrentItem(menu, my_top_row, item);
551
552
    }
553
554
  RETURN(result);
555
}
556
557
/* m_driver.c ends here */