Subversion Repositories Projects

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
280 dhylands 1
/****************************************************************************
2
*
3
*   Copyright (c) 2006 Dave Hylands     <dhylands@gmail.com>
4
*
5
*   This program is free software; you can redistribute it and/or modify
6
*   it under the terms of the GNU General Public License version 2 as
7
*   published by the Free Software Foundation.
8
*
9
*   Alternatively, this software may be distributed under the terms of BSD
10
*   license.
11
*
12
*   See README and COPYING for more details.
13
*
14
****************************************************************************/
15
/**
16
*
17
*   @file   Menu.c
18
*
19
*   @brief  Menu definitions suitable for use on a simple LCD
20
*
21
*****************************************************************************/
22
 
23
/* ---- Include Files ----------------------------------------------------- */
24
 
25
#include <string.h>
26
#include "Config.h"
27
#include "Delay.h"
28
#include "Lcd.h"
29
#include "Log.h"
30
#include "Menu.h"
31
 
32
/* ---- Public Variables -------------------------------------------------- */
33
 
34
/* ---- Private Constants and Types --------------------------------------- */
35
 
36
#define MENU_LOG_ENABLED    0
37
 
38
#if MENU_LOG_ENABLED
39
#   define MENU_LOG( fmt, args... )  Log( fmt, ## args )
40
#else
41
#   define MENU_LOG( fmt, args... )
42
#endif
43
 
44
#if !defined( CFG_MENU_MAX_DEPTH )
45
#   define CFG_MENU_MAX_DEPTH   8
46
#endif
47
 
48
#define MENU_STACK_INACTIVE 0xFF
49
 
50
/* ---- Private Variables ------------------------------------------------- */
51
 
52
#if MENU_LOG_ENABLED
53
 
54
static char *gMenuEventStr[] =
55
{
56
    "Init", "Select", "Left", "Right", "Up", "Down", "Timer"
57
};
58
 
59
#endif
60
 
61
static  MENU_ProgItem_t    *gMenuStack[ CFG_MENU_MAX_DEPTH ];
62
static  uint8_t             gMenuStackTop;
63
static  uint8_t             gMenuIsEditing;
64
static  uint8_t             gMenuModified;
65
 
66
static  union
67
{
68
    uint8_t     byteVal;
69
    uint16_t    uintVal;
70
    int16_t     intVal;
71
 
72
} gMenuEditVal;
73
 
74
typedef void (*MENU_EventEditFunc_t)( MENU_Event_t event, MENU_MemItem_t *item );
75
 
76
static  void MENU_EventEditMenu( MENU_Event_t event, MENU_MemItem_t *item );
77
static  void MENU_EventEditProc( MENU_Event_t event, MENU_MemItem_t *item );
78
static  void MENU_EventEditByte( MENU_Event_t event, MENU_MemItem_t *item );
79
static  void MENU_EventEditUInt( MENU_Event_t event, MENU_MemItem_t *item );
80
 
81
static  const MENU_EventEditFunc_t    gMenuEditFunc[] =
82
{
83
    MENU_EventEditMenu,
84
    MENU_EventEditProc,
85
    MENU_EventEditByte,
86
    MENU_EventEditUInt,
87
};
88
 
89
 
90
/* ---- Private Function Prototypes --------------------------------------- */
91
 
92
/* ---- Functions --------------------------------------------------------- */
93
 
94
//***************************************************************************
95
/**
96
*   Reads a menu item from flash
97
*/
98
 
99
static inline MENU_MemItem_t *MENU_GetItem( const MENU_ProgItem_t *progItem, MENU_MemItem_t *memItem )
100
{
101
    memcpy_P( memItem, progItem, sizeof( *memItem ));
102
 
103
    return memItem;
104
 
105
} // MENU_GetItem
106
 
107
//***************************************************************************
108
/**
109
*   Determines if the menu is currently active or not
110
*/
111
 
112
uint8_t MENU_IsActive( void )
113
{
114
    return gMenuStackTop > 0;
115
}
116
 
117
//***************************************************************************
118
/**
119
*   Determines if the menu is currently active or not
120
*/
121
 
122
uint8_t MENU_IsModified( void )
123
{
124
    return gMenuModified;
125
}
126
 
127
//***************************************************************************
128
/**
129
*   Determines if the menu is currently active or not
130
*/
131
 
132
void MENU_ClearModified( void )
133
{
134
    gMenuModified = 0;
135
}
136
 
137
//***************************************************************************
138
/**
139
*   Determines if the menu is currently being edited or not
140
*/
141
 
142
static inline uint8_t MENU_IsEditing( void )
143
{
144
    return gMenuIsEditing;
145
}
146
 
147
//***************************************************************************
148
/**
149
*   Puts the indicated item on the top of the menu stack
150
*/
151
 
152
static inline void MENU_Activate( MENU_ProgItem_t *menuItem )
153
{
154
    gMenuStackTop++;
155
    gMenuStack[ gMenuStackTop ] = menuItem;
156
 
157
} // MENU_Activate
158
 
159
//***************************************************************************
160
/**
161
*   Delays for the specified number of milliseconds
162
*/
163
 
164
static inline void MENU_Delay( unsigned msec )
165
{
166
    ms_spin( msec );
167
 
168
} // MENU_Delay
169
 
170
//***************************************************************************
171
/**
172
*   Draws the current menu item
173
*/
174
 
175
void MENU_Draw( void )
176
{
177
    char           *s;
178
    char            fmtStr[ 10 ];
179
    uint16_t        val = 0;
180
    uint16_t        maxVal = 0;
181
    int16_t         ival = 0;
182
    int16_t         maxIVal = 0;
183
    uint8_t         maxLen;
184
    uint8_t         valLen;
185
    MENU_MemItem_t  item;
186
    uint8_t         signedVal = 0;
187
 
188
    if ( !MENU_IsActive() )
189
    {
190
        return;
191
    }
192
 
193
    if ( MENU_IsEditing() )
194
    {
195
        MENU_GetItem( gMenuStack[ gMenuStackTop ], &item );
196
    }
197
    else
198
    {
199
        // By only drawing when we're not editing, we reduce flicker
200
 
201
        LCD_Clear();
202
 
203
        // First of all, draw the title of the containing menu
204
 
205
        MENU_GetItem( gMenuStack[ gMenuStackTop - 1 ], &item );
206
        LCD_PutStr_P( item.name );
207
 
208
        // Now draw the currently selected item
209
 
210
        LCD_MoveTo( 0, 1 );
211
 
212
        MENU_GetItem( gMenuStack[ gMenuStackTop ], &item );
213
        LCD_PutStr_P( item.name );
214
    }
215
 
216
    switch ( item.type )
217
    {
218
        case MENU_TYPE_BYTE:
219
        {
220
            if ( MENU_IsEditing() )
221
            {
222
                val = gMenuEditVal.uintVal;
223
            }
224
            else
225
            {
226
                val = *item.val.byteVal.bytePtr;
227
            }
228
            maxVal =  item.val.byteVal.maxVal;
229
            break;
230
        }
231
 
232
        case MENU_TYPE_UINT:
233
        {
234
            if ( MENU_IsEditing() )
235
            {
236
                val = gMenuEditVal.uintVal;
237
            }
238
            else
239
            {
240
                val = *item.val.uintVal.uintPtr;
241
            }
242
            maxVal =  item.val.uintVal.maxVal;
243
            break;
244
        }
245
 
246
        case MENU_TYPE_INT:
247
        {
248
            signedVal = 1;
249
 
250
            if ( MENU_IsEditing() )
251
            {
252
                ival = gMenuEditVal.intVal;
253
            }
254
            else
255
            {
256
                ival = *item.val.intVal.intPtr;
257
            }
258
            maxIVal =  item.val.intVal.maxVal;
259
            break;
260
        }
261
 
262
        default:
263
        {
264
            return;
265
        }
266
    }
267
 
268
    if ( signedVal )
269
    {
270
        snprintf( fmtStr, sizeof( fmtStr ), "%d", maxIVal );
271
        maxLen = strlen( fmtStr );
272
        snprintf( fmtStr, sizeof( fmtStr ), "%d", ival );
273
    }
274
    else
275
    {
276
        snprintf( fmtStr, sizeof( fmtStr ), "%u", maxVal );
277
        maxLen = strlen( fmtStr );
278
        snprintf( fmtStr, sizeof( fmtStr ), "%u", val );
279
    }
280
 
281
    LCD_MoveTo( LCD_NumCols() - maxLen - 2, LCD_NumLines() - 1 );
282
 
283
    LCD_PutChar( MENU_IsEditing() ? '[' : ' ' );
284
 
285
    valLen = strlen( fmtStr );
286
    while ( maxLen > valLen )
287
    {
288
        LCD_PutChar( ' ' );
289
        maxLen--;
290
    }
291
 
292
    s = fmtStr;
293
    while ( valLen > 0 )
294
    {
295
        LCD_PutChar( *s++ );
296
        valLen--;
297
    }
298
 
299
    LCD_PutChar( MENU_IsEditing() ? ']' : ' ' );
300
 
301
} // MENU_Draw
302
 
303
//***************************************************************************
304
/**
305
*   Dumps the contents of the menu
306
*/
307
 
308
#if MENU_LOG_ENABLED
309
 
310
void MENU_Dump( uint8_t indent, MENU_ProgItem_t *menuItem )
311
{
312
    uint8_t         i;
313
    MENU_MemItem_t  item;
314
 
315
    while ( MENU_GetItem( menuItem, &item )->name != NULL )
316
    {
317
        for ( i = 0; i < indent; i++ )
318
        {
319
            Log( "  " );
320
        }
321
        Log( "Name: " );
322
        Log_P( item.name );
323
        Log( ", type: %d\n", item.type );
324
 
325
        if ( item.type == MENU_TYPE_MENU )
326
        {
327
            MENU_Dump( indent + 1, item.val.menu );
328
        }
329
        menuItem++;
330
    }
331
 
332
} // MENU_Dump
333
 
334
#endif
335
 
336
//***************************************************************************
337
/**
338
*   Called to process events while editing a "procedure"
339
*/
340
 
341
static void MENU_EventEditMenu( MENU_Event_t event, MENU_MemItem_t *item )
342
{
343
    MENU_LOG(  "MENU_EventEditMenu: %s\n", gMenuEventStr[ event ]);
344
 
345
    switch ( event )
346
    {
347
        case MENU_EVENT_SELECT:
348
        {
349
            if ( item->type != MENU_TYPE_MENU )
350
            {
351
                gMenuIsEditing = 1;
352
 
353
                MENU_Event( MENU_EVENT_INIT );
354
                break;
355
            }
356
 
357
            // Fall thru
358
        }
359
 
360
        case MENU_EVENT_RIGHT:
361
        {
362
            if ( item->type == MENU_TYPE_MENU )
363
            {
364
                // The item selected is another menu. Activate it.
365
 
366
                MENU_Activate( item->val.menu );
367
            }
368
            break;
369
        }
370
 
371
        case MENU_EVENT_LEFT:
372
        {
373
            gMenuStackTop--;
374
            break;
375
        }
376
 
377
        case MENU_EVENT_UP:
378
        {
379
            if ( gMenuStack[ gMenuStackTop ] <= MENU_GetItem( gMenuStack[ gMenuStackTop - 1 ], item )->val.menu )
380
            {
381
                while ( MENU_GetItem( gMenuStack[ gMenuStackTop ], item )->name != NULL )
382
                {
383
                    gMenuStack[ gMenuStackTop ]++;
384
                }
385
            }
386
            gMenuStack[ gMenuStackTop ]--;
387
            break;
388
        }
389
 
390
        case MENU_EVENT_DOWN:
391
        {
392
            gMenuStack[ gMenuStackTop ]++;
393
 
394
            if ( MENU_GetItem( gMenuStack[ gMenuStackTop ], item )->name == NULL )
395
            {
396
                gMenuStack[ gMenuStackTop ] = MENU_GetItem( gMenuStack[ gMenuStackTop - 1 ], item )->val.menu;
397
            }
398
            break;
399
        }
400
 
401
        default:
402
            break;
403
    }
404
 
405
} // MENU_EventEditMenu
406
 
407
//***************************************************************************
408
/**
409
*   Called to process events while editing a "procedure"
410
*/
411
 
412
static void MENU_EventEditProc( MENU_Event_t event, MENU_MemItem_t *item )
413
{
414
    const prog_char *str;
415
 
416
    MENU_LOG(  "MENU_EventEditProc: %s\n", gMenuEventStr[ event ]);
417
 
418
    if ( event == MENU_EVENT_LEFT )
419
    {
420
        // Acts like a cancel
421
 
422
        gMenuIsEditing = 0;
423
        return;
424
    }
425
 
426
    if (( str = item->val.proc( event )) != NULL )
427
    {
428
        LCD_Clear();
429
        LCD_PutStr_P( str );
430
        MENU_Delay( 1000 );
431
 
432
        gMenuIsEditing = 0;
433
    }
434
 
435
} // MENU_EventEditProc
436
 
437
//***************************************************************************
438
/**
439
*   Called to process events while editing an unsigned entity
440
*/
441
 
442
static void MENU_EventEditUnsigned( MENU_Event_t event, uint16_t *valPtr, uint16_t minVal, uint16_t maxVal )
443
{
444
    if ( *valPtr < minVal )
445
    {
446
        *valPtr = minVal;
447
    }
448
    if ( *valPtr > maxVal )
449
    {
450
        *valPtr = maxVal;
451
    }
452
 
453
    switch ( event )
454
    {
455
        case MENU_EVENT_DOWN:
456
        {
457
            if ( *valPtr > minVal  )
458
            {
459
                (*valPtr)--;
460
            }
461
            break;
462
        }
463
 
464
        case MENU_EVENT_UP:
465
        {
466
            if ( *valPtr < maxVal  )
467
            {
468
                (*valPtr)++;
469
            }
470
            break;
471
        }
472
 
473
        default:
474
            break;
475
    }
476
}
477
 
478
//***************************************************************************
479
/**
480
*   Called to process events while editing a BYTE
481
*/
482
 
483
static void MENU_EventEditByte( MENU_Event_t event, MENU_MemItem_t *item )
484
{
485
    MENU_LOG( "MENU_EventEditByte: %s\n", gMenuEventStr[ event ]);
486
 
487
    if ( event == MENU_EVENT_INIT )
488
    {
489
        gMenuEditVal.uintVal = *item->val.byteVal.bytePtr;
490
    }
491
 
492
    MENU_EventEditUnsigned( event, &gMenuEditVal.uintVal, item->val.byteVal.minVal, item->val.byteVal.maxVal );
493
 
494
    if ( event == MENU_EVENT_SELECT )
495
    {
496
        *item->val.byteVal.bytePtr = (uint8_t)gMenuEditVal.uintVal;
497
    }
498
 
499
} // MENU_EventEditByte
500
 
501
//***************************************************************************
502
/**
503
*   Called to process events while editing a UINT
504
*/
505
 
506
static void MENU_EventEditUInt( MENU_Event_t event, MENU_MemItem_t *item )
507
{
508
    MENU_LOG( "MENU_EventEditUInt: %s\n", gMenuEventStr[ event ]);
509
 
510
    if ( event == MENU_EVENT_INIT )
511
    {
512
        gMenuEditVal.uintVal = *item->val.uintVal.uintPtr;
513
    }
514
 
515
    MENU_EventEditUnsigned( event, &gMenuEditVal.uintVal, item->val.uintVal.minVal, item->val.uintVal.maxVal );
516
 
517
    if ( event == MENU_EVENT_SELECT )
518
    {
519
        *item->val.uintVal.uintPtr = gMenuEditVal.uintVal;
520
    }
521
 
522
} // MENU_EventEditUInt
523
 
524
//***************************************************************************
525
/**
526
*   Allows a menu procedure to exit the menu system
527
*/
528
 
529
void MENU_Exit( void )
530
{
531
    gMenuStackTop = 0;
532
    gMenuIsEditing = 0;
533
 
534
} // MENU_Exit
535
 
536
//***************************************************************************
537
/**
538
*   Initializes the menu subsystem and sets the top level menu
539
*/
540
 
541
void MENU_Init( MENU_ProgItem_t *topMenu )
542
{
543
    gMenuStack[ 0 ] = topMenu;
544
    gMenuStackTop = 0;
545
 
546
#if MENU_LOG_ENABLED
547
    MENU_Dump( 0,  topMenu );
548
#endif
549
 
550
} // MENU_Init
551
 
552
//***************************************************************************
553
/**
554
*   Handles a menu event (typically a key/button press)
555
*/
556
 
557
void MENU_Event( MENU_Event_t event )
558
{
559
    //MENU_LOG( "MENU_Event: %s\n", gMenuEventStr[ event ]);
560
 
561
    if ( !MENU_IsActive() )
562
    {
563
        MENU_MemItem_t  mainMenu;
564
 
565
        // Menu isn't currently being displayed. Activate at the top.
566
 
567
        gMenuStackTop = 0;
568
        gMenuModified = 0;
569
 
570
        MENU_GetItem( gMenuStack[ 0 ], &mainMenu );
571
 
572
        MENU_Activate( mainMenu.val.menu );
573
    }
574
    else
575
    {
576
        MENU_MemItem_t  topItem;
577
 
578
        MENU_GetItem( gMenuStack[ gMenuStackTop ], &topItem );
579
 
580
        if ( MENU_IsEditing() )
581
        {
582
            (gMenuEditFunc[ topItem.type ])( event, &topItem );
583
 
584
            if ( event == MENU_EVENT_LEFT )
585
            {
586
                // Acts like a cancel
587
 
588
                gMenuIsEditing = 0;
589
            }
590
            else
591
            if ( event == MENU_EVENT_SELECT )
592
            {
593
                // Value was selected
594
 
595
                gMenuModified = 1;
596
                gMenuIsEditing = 0;
597
            }
598
        }
599
        else
600
        {
601
            MENU_EventEditMenu( event, &topItem );
602
        }
603
    }
604
 
605
    if ( event != MENU_EVENT_TIMER )
606
    {
607
        MENU_Draw();
608
    }
609
 
610
} // MENU_Event
611