Topic: tech dcc pc src prev next
tech dcc pc src > settings.c
#include "settings.h"
#include <string.h>
#include "cfg.h"
#include "dcc.h"
#include "lcd.h"
#include "menu.h"
#include "packet.h"
#include "pins.h"
#define SIZEOF_ARRAY(a) (sizeof(a)/sizeof(a[0]))
typedef unsigned char index_type;
void Settings_Menu()
{
static const char *items[] = {
"Locomotives",
"Accessories",
0
};
int option = 0;
while(
(option = Menu_GetOption("Settings", items, (unsigned char)option)) > -1
)
{
switch(option)
{
case 0:
Settings_Locomotives();
break;
case 1:
Settings_Accessories();
break;
}
}
}
void Settings_Accessories()
{
typedef struct
{
unsigned char index;
cfg_address_type addresses[CFG_ACCESSORY_LIST_LENGTH+1];
} accessories_state;
accessories_state context = {
.index = 0
};
void load_addresses(accessories_state *context_)
{
Cfg_Accessories(context_->addresses);
}
load_addresses(&context);
int count(const accessories_state *context_)
{
return (Cfg_AddressCount(context_->addresses) + 1);
}
void button(void *v, const switch_type sw)
{
accessories_state *context_ = (accessories_state*)v;
if(HERMES_SWITCH_NO(sw) == HERMES_SWITCH_BACK)
Menu_Finish();
if(HERMES_SWITCH_NO(sw) == HERMES_RE_ACW)
{
context_->index =
(unsigned char)((count(context_) + context_->index - 1) % count(context_));
Menu_Redraw();
}
if(HERMES_SWITCH_NO(sw) == HERMES_RE_CW)
{
context_->index =
(unsigned char)((context_->index + 1) % count(context_));
Menu_Redraw();
}
if(HERMES_SWITCH_NO(sw) == HERMES_RE_SEL)
{
if(context_->index == 0)
{
// Add a new accessory.
if(Cfg_AddressCount(context_->addresses) == CFG_ACCESSORY_LIST_LENGTH)
{
Menu_Notice("List full");
}
else
{
int address =
Menu_GetInt("Number", -1, HERMES_MAX_ACC_ADDRESS);
if(address == -1)
{
// The user cancelled the address input.
}
else if(Cfg_AccessoryAddressValid((cfg_address_type)address))
{
Cfg_SaveAccessory((cfg_address_type)address);
load_addresses(context_);
Menu_Redraw();
}
else
Menu_Notice("Invalid address");
}
}
else
{
// Confirm, then delete the accessory.
if(Menu_Question("Delete?"))
{
Cfg_DeleteAccessory(context_->addresses[context_->index - 1]);
load_addresses(context_);
--context_->index;
Menu_Redraw();
}
}
}
}
void draw(void *v)
{
accessories_state *context_ = (accessories_state*)v;
LCD_DefineHermesChars(HERMES_LCD_CUSTOM_MODE_MENU);
LCD_Clear();
LCD_PutS("Acc.");
LCD_PrintListIndex(context_->index + 1, count(context_));
LCD_Move(0, 1);
if(context_->index == 0)
LCD_PutS("New");
else
LCD_PutI((int)context_->addresses[context_->index - 1]);
}
Menu_EventLoop(button, draw, 0, &context);
}
void Settings_Locomotives()
{
typedef struct
{
unsigned char index;
cfg_address_type addresses[CFG_LOCO_LIST_LENGTH+1];
} settings_state;
settings_state context = {
.index = 0
};
void load_addresses(settings_state *context_)
{
Cfg_Addresses(context_->addresses);
}
load_addresses(&context);
int count(const settings_state *context_)
{
return (Cfg_AddressCount(context_->addresses) + 1);
}
void button(void *v, const switch_type sw)
{
settings_state *context_ = (settings_state*)v;
if(HERMES_SWITCH_NO(sw) == HERMES_SWITCH_BACK)
Menu_Finish();
if(HERMES_SWITCH_NO(sw) == HERMES_RE_ACW)
{
context_->index =
(unsigned char)((count(context_) + context_->index - 1) % count(context_));
Menu_Redraw();
}
if(HERMES_SWITCH_NO(sw) == HERMES_RE_CW)
{
context_->index =
(unsigned char)((context_->index + 1) % count(context_));
Menu_Redraw();
}
if(HERMES_SWITCH_NO(sw) == HERMES_RE_SEL)
{
if(context_->index == 0)
{
if(Cfg_NextFreeLoco() == CFG_NULL_INDEX)
{
// Creating a new locomotive: all slots are taken.
Menu_Notice("List full");
}
else
{
// Create a new locomotive.
int address =
Menu_GetInt("Address", -1, HERMES_MAX_LOCO_ADDRESS);
if(!Cfg_LocoAddressValid((cfg_address_type)address))
Menu_Notice("Invalid address");
else if(
Cfg_AddrToIndex((cfg_address_type)address) != CFG_NULL_INDEX
)
// New address is taken.
Menu_Notice("Address taken");
else
Settings_LocomotiveList((cfg_address_type)address);
}
}
else
{
// Display settings for a locomotive in EEPROM.
Settings_LocomotiveList(context_->addresses[context_->index - 1]);
}
unsigned char old_address_count = Cfg_AddressCount(context_->addresses);
load_addresses(context_);
// In case this locomotive was deleted.
if(
context_->index > 0 &&
old_address_count > Cfg_AddressCount(context_->addresses)
)
context_->index = (unsigned char)(context_->index - 1);
}
}
void draw(void *v)
{
settings_state *context_ = (settings_state*)v;
LCD_DefineHermesChars(HERMES_LCD_CUSTOM_MODE_MENU);
LCD_Clear();
LCD_PutS("Locos");
LCD_PrintListIndex(context_->index + 1, count(context_));
LCD_Move(0, 1);
if(context_->index == 0)
LCD_PutS("New");
else
LCD_PutI((int)context_->addresses[context_->index - 1]);
}
Menu_EventLoop(button, draw, 0, &context);
}
void Settings_LocomotiveList(cfg_address_type address)
{
typedef struct
{
cfg_loco loco;
index_type index;
cfg_loco_index_type loco_index;
} state;
state context;
cfg_loco_index_type loco_index = Cfg_AddrToIndex(address);
if(loco_index == CFG_NULL_INDEX)
{
// New locomotive.
loco_index = Cfg_NextFreeLoco();
context.loco = Cfg_DefaultLoco();
context.loco.address = address;
context.index = 0;
context.loco_index = loco_index;
}
else
{
// Existing locomotive.
context.loco = Cfg_ReadLocoFromIndex(loco_index);
context.index = 0;
context.loco_index = loco_index;
}
enum items {
ADDRESS,
FUNCTIONS,
SPEED_STEPS,
DELETE,
END_ITEMS
};
void write_loco(state *context_)
{
Cfg_WriteLocoToIndex(context_->loco_index, context_->loco);
}
write_loco(&context);
void button(void *v, const switch_type sw)
{
state *context_ = (state*)v;
if(HERMES_SWITCH_NO(sw) == HERMES_SWITCH_BACK)
Menu_Finish();
if(HERMES_SWITCH_NO(sw) == HERMES_RE_ACW)
{
context_->index = (unsigned char)((END_ITEMS + context_->index - 1) % END_ITEMS);
Menu_Redraw();
}
if(HERMES_SWITCH_NO(sw) == HERMES_RE_CW)
{
context_->index = (unsigned char)((context_->index + 1) % END_ITEMS);
Menu_Redraw();
}
if(HERMES_SWITCH_NO(sw) == HERMES_RE_SEL)
{
switch(context_->index)
{
/*case ADDRESS:*/
/*{*/
/*int addr_in =*/
/*Menu_GetInt("Address", -1, HERMES_MAX_LOCO_ADDRESS);*/
/*if(addr_in == -1)*/
/*// Cancelled.*/
/*break;*/
/*cfg_address_type addr = (cfg_address_type)addr_in;*/
/*if(!Cfg_LocoAddressValid(addr))*/
/*Menu_Notice("Invalid address");*/
/*else if(*/
/*Cfg_AddrToIndex(addr) !=*/
/*context_->loco_index &&*/
/*Cfg_AddrToIndex(addr) != CFG_NULL_INDEX*/
/*)*/
/*{*/
/*Menu_Notice("Address taken");*/
/*}*/
/*else*/
/*{*/
/*context_->loco.address = addr;*/
/*write_loco(context_);*/
/*}*/
/*}*/
/*break;*/
case SPEED_STEPS:
{
static const char *s[] =
{ "128 (default)", "28 (older locos)", 0 };
int n = Menu_GetOption("Speed Steps", s, 0);
if(n > -1)
{
static const unsigned char modes[] = {
SPEED_STEPS_128,
SPEED_STEPS_28
};
context_->loco.speed_steps = modes[n];
write_loco(context_);
}
}
break;
case DELETE:
if(Menu_Question("Delete loco?"))
{
context_->loco = Cfg_DefaultLoco();
write_loco(context_);
Menu_Finish();
}
break;
case FUNCTIONS:
Settings_FunctionList(&(context_->loco));
write_loco(context_);
break;
}
}
}
const char *speed_step_str(unsigned char steps)
{
switch(steps)
{
case SPEED_STEPS_128:
return "128 (default)";
case SPEED_STEPS_28:
return "28 (older locos)";
default:
return "Unknown";
}
}
void draw(void *v)
{
state *context_ = (state*)v;
LCD_DefineHermesChars(HERMES_LCD_CUSTOM_MODE_MENU);
LCD_Clear();
LCD_PutS("Loco ");
LCD_PutI((int)context_->loco.address);
LCD_PrintListIndex(context_->index + 1, END_ITEMS);
LCD_Move(0, 1);
switch(context_->index)
{
case ADDRESS:
LCD_PutS("Address (");
LCD_PutI((int)context_->loco.address);
LCD_PutC(')');
break;
case SPEED_STEPS:
LCD_PutS(speed_step_str(context_->loco.speed_steps));
break;
case FUNCTIONS:
LCD_PutS("Functions");
break;
case DELETE:
LCD_PutS("Delete");
break;
}
}
Menu_EventLoop(button, draw, 0, &context);
}
void Settings_FunctionList(cfg_loco *loco)
{
typedef struct
{
unsigned char index;
cfg_loco *loco;
} state;
state context = {
.index = 0,
.loco = loco
};
int count(const state *context_)
{
// + 1 for the 'New' entry.
return (Cfg_LocoFunctions(context_->loco) + 1);
}
// Get the currently selected function number.
unsigned char selected_function(const state *context_)
{
unsigned char index = context_->index;
functions_type f = context_->loco->f_enable;
for(unsigned char i = 0; i < 32; ++i)
{
if(f & 1)
--index;
if(index == 0)
return (unsigned char)i;
f >>= 1;
}
return 0;
}
void button(void *v, const switch_type sw)
{
state *context_ = (state*)v;
if(HERMES_SWITCH_NO(sw) == HERMES_SWITCH_BACK)
Menu_Finish();
if(HERMES_SWITCH_NO(sw) == HERMES_RE_ACW)
{
context_->index =
(unsigned char)((count(context_) + context_->index - 1) % count(context_));
Menu_Redraw();
}
if(HERMES_SWITCH_NO(sw) == HERMES_RE_CW)
{
context_->index =
(unsigned char)((context_->index + 1) % count(context_));
Menu_Redraw();
}
if(HERMES_SWITCH_NO(sw) == HERMES_RE_SEL)
{
if(context_->index == 0)
{
// Enable a function.
int function =
Menu_GetInt("Function No.", -1, HERMES_MAX_FUNCTION);
if(function == -1)
{
// The user cancelled the function number input.
}
else if(function > 28)
{
Menu_Notice("Invalid function");
}
else
{
bool momentary = Menu_Question("Momentary?");
context_->loco->f_enable = (context_->loco->f_enable | (1UL << function));
if(momentary)
{
// Momentary functions cannot be on by default.
context_->loco->f_default = (context_->loco->f_default & ~(1UL << function));
context_->loco->f_momentary = (context_->loco->f_momentary | (1UL << function));
}
else
{
// For toggle functions, ask whether the function is on by
// default.
bool enable_default = Menu_Question("Default on?");
if(enable_default)
context_->loco->f_default = (context_->loco->f_default | (1UL << function));
else
context_->loco->f_default = (context_->loco->f_default & ~(1UL << function));
context_->loco->f_momentary = (context_->loco->f_momentary & ~(1UL << function));
}
Menu_Redraw();
}
}
else
{
// Confirm, then delete the function.
if(Menu_Question("Delete?"))
{
unsigned char f_no = selected_function(context_);
context_->loco->f_default = (context_->loco->f_default & ~(1UL << f_no));
context_->loco->f_enable = (context_->loco->f_enable & ~(1UL << f_no));
context_->loco->f_momentary = (context_->loco->f_momentary & ~(1UL << f_no));
--context_->index;
Menu_Redraw();
}
}
}
}
void draw(void *v)
{
state *context_ = (state*)v;
LCD_DefineHermesChars(HERMES_LCD_CUSTOM_MODE_MENU);
LCD_Clear();
LCD_PutS("Function");
LCD_PrintListIndex(context_->index + 1, count(context_));
LCD_Move(0, 1);
if(context_->index == 0)
LCD_PutS("New");
else
{
unsigned char f_no = selected_function(context_);
bool momentary = context_->loco->f_momentary & (functions_type)(1 << f_no);
bool enable_default = context_->loco->f_default & (functions_type)(1 << f_no);
LCD_PutI(selected_function(context_));
LCD_PutS(
momentary ?
" (momentary)" :
(enable_default ? " (toggle, on)" : " (toggle, off)")
);
}
}
Menu_EventLoop(button, draw, 0, &context);
}