274 lines
6.4 KiB
C
274 lines
6.4 KiB
C
/* Reflow Oven Controller
|
|
*
|
|
* Copyright (C) 2020 Mario Hüttel <mario.huettel@gmx.net>
|
|
*
|
|
* This file is part of the Reflow Oven Controller Project.
|
|
*
|
|
* The reflow oven controller is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* The Reflow Oven Control Firmware is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with the reflow oven controller project.
|
|
* If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <reflow-controller/ui/menu.h>
|
|
#include <stddef.h>
|
|
#include <stdio.h>
|
|
#include <stdarg.h>
|
|
|
|
void menu_handle(struct lcd_menu *menu, int16_t rotary_encoder_delta, enum button_state push_button)
|
|
{
|
|
menu_func_t tmp;
|
|
|
|
|
|
if (!menu)
|
|
return;
|
|
|
|
menu->inputs.push_button = push_button;
|
|
menu->inputs.rotary_encoder_delta += rotary_encoder_delta;
|
|
|
|
if (menu->active_entry == NULL)
|
|
menu->active_entry = menu->root_entry;
|
|
|
|
tmp = menu->active_entry;
|
|
|
|
if (menu->active_entry_type == MENU_ENTRY_FIRST_ENTER && push_button != BUTTON_IDLE) {
|
|
menu->inputs.button_ready = false;
|
|
}
|
|
|
|
if (menu->active_entry_type == MENU_ENTRY_FIRST_ENTER) {
|
|
menu->active_entry(menu, menu->active_entry_type, menu->init_parent);
|
|
} else {
|
|
menu->active_entry(menu, menu->active_entry_type, NULL);
|
|
}
|
|
|
|
if (menu->active_entry_type != MENU_ENTRY_CONTINUE && tmp == menu->active_entry) {
|
|
menu->active_entry_type = MENU_ENTRY_CONTINUE;
|
|
}
|
|
|
|
if (push_button == BUTTON_IDLE)
|
|
menu->inputs.button_ready = true;
|
|
}
|
|
|
|
void menu_init(struct lcd_menu *menu, menu_func_t root_node, void (*display_update)(uint8_t row, const char *data))
|
|
{
|
|
if (!menu)
|
|
return;
|
|
|
|
menu->root_entry = root_node;
|
|
menu->active_entry = root_node;
|
|
menu->init_parent = NULL;
|
|
menu->inputs.push_button = BUTTON_IDLE;
|
|
menu->inputs.button_ready = false;
|
|
menu->inputs.rotary_encoder_delta = 0;
|
|
menu->active_entry_type = MENU_ENTRY_FIRST_ENTER;
|
|
menu->update_display = display_update;
|
|
}
|
|
|
|
void menu_entry_dropback(struct lcd_menu *menu, menu_func_t parent_func)
|
|
{
|
|
if (!menu)
|
|
return;
|
|
if (parent_func)
|
|
menu->active_entry = parent_func;
|
|
else
|
|
menu->active_entry = menu->root_entry;
|
|
|
|
menu->active_entry_type = MENU_ENTRY_DROPBACK;
|
|
}
|
|
|
|
void menu_entry_enter(struct lcd_menu *menu, menu_func_t entry, bool handle_immediately)
|
|
{
|
|
if (!menu)
|
|
return;
|
|
|
|
menu->init_parent = menu->active_entry;
|
|
menu->active_entry_type = MENU_ENTRY_FIRST_ENTER;
|
|
menu->active_entry = entry;
|
|
|
|
if (handle_immediately)
|
|
menu_handle(menu, 0, menu->inputs.push_button);
|
|
}
|
|
|
|
void menu_lcd_output(struct lcd_menu *menu, uint8_t row_num, const char *text)
|
|
{
|
|
if (!menu || !menu->update_display)
|
|
return;
|
|
|
|
menu->update_display(row_num, text);
|
|
}
|
|
|
|
void menu_lcd_outputf(struct lcd_menu *menu, uint8_t row_num, const char *format, ...)
|
|
{
|
|
char buff[64];
|
|
va_list valist;
|
|
|
|
va_start(valist, format);
|
|
vsnprintf(buff, sizeof(buff), format, valist);
|
|
buff[sizeof(buff) - 1] = '\0';
|
|
menu_lcd_output(menu, row_num, buff);
|
|
va_end(valist);
|
|
}
|
|
|
|
void menu_list_display(struct menu_list *list, uint32_t top_row, uint32_t bottom_row)
|
|
{
|
|
uint8_t row_count;
|
|
uint32_t mid_row;
|
|
uint32_t count_above_mid;
|
|
uint32_t count_below_mid;
|
|
uint32_t start_index;
|
|
uint32_t current_row;
|
|
uint32_t current_idx;
|
|
char workbuff[64];
|
|
|
|
if (!list || !list->update_display)
|
|
return;
|
|
|
|
if (bottom_row < top_row)
|
|
return;
|
|
|
|
if (list->entry_count == 0) {
|
|
for (current_row = top_row; current_row <= bottom_row; current_row++) {
|
|
list->update_display((uint8_t)current_row, "");
|
|
}
|
|
return;
|
|
}
|
|
|
|
/* Calculate list parameters */
|
|
row_count = bottom_row - top_row + 1;
|
|
mid_row = (top_row + bottom_row) / 2;
|
|
count_above_mid = mid_row - top_row;
|
|
count_below_mid = bottom_row - mid_row;
|
|
|
|
/* Check if there are more elements above the and below the currently selected one that can be displayed. in this case position
|
|
* active entry in center
|
|
*/
|
|
if (list->currently_selected > count_above_mid && (list->entry_count - list->currently_selected - 1) > count_below_mid) {
|
|
start_index = list->currently_selected - count_above_mid;
|
|
} else if (list->currently_selected < count_above_mid) {
|
|
start_index = 0;
|
|
} else if ((list->entry_count - list->currently_selected - 1) <= count_below_mid) {
|
|
if (list->entry_count < row_count)
|
|
start_index = 0;
|
|
else
|
|
start_index = list->entry_count - row_count;
|
|
} else {
|
|
start_index = 0;
|
|
}
|
|
|
|
for (current_row = top_row, current_idx = start_index; current_row <= bottom_row; current_row++, current_idx++) {
|
|
if (current_idx >= list->entry_count)
|
|
break;
|
|
|
|
snprintf(workbuff, sizeof(workbuff), "%c%s", (current_idx == list->currently_selected ? '>' : ' '),
|
|
list->entry_names[current_idx]);
|
|
workbuff[sizeof(workbuff)-1] = 0;
|
|
list->update_display((uint8_t)current_row, workbuff);
|
|
}
|
|
|
|
}
|
|
|
|
void menu_list_compute_count(struct menu_list *list)
|
|
{
|
|
uint32_t count = 0;
|
|
|
|
if (!list)
|
|
return;
|
|
|
|
for (count = 0; list->entry_names[count] != NULL; count++);
|
|
list->entry_count = count;
|
|
}
|
|
|
|
void menu_list_scroll_down(struct menu_list *list)
|
|
{
|
|
if (!list)
|
|
return;
|
|
|
|
if (list->currently_selected < list->entry_count - 1) {
|
|
list->currently_selected++;
|
|
}
|
|
}
|
|
|
|
void menu_list_enter_selected_entry(struct menu_list *list, struct lcd_menu *menu)
|
|
{
|
|
menu_func_t entry;
|
|
|
|
if (!list)
|
|
return;
|
|
|
|
if (!list->submenu_list)
|
|
return;
|
|
|
|
entry = list->submenu_list[list->currently_selected];
|
|
|
|
if (!entry)
|
|
return;
|
|
|
|
menu_entry_enter(menu, entry, false);
|
|
}
|
|
|
|
void menu_list_scroll_up(struct menu_list *list)
|
|
{
|
|
if (!list)
|
|
return;
|
|
|
|
if (list->currently_selected > 0)
|
|
list->currently_selected--;
|
|
}
|
|
|
|
void menu_ack_rotary_delta(struct lcd_menu *menu)
|
|
{
|
|
if (!menu)
|
|
return;
|
|
|
|
menu->inputs.rotary_encoder_delta = 0;
|
|
}
|
|
|
|
int16_t menu_get_rotary_delta(const struct lcd_menu *menu)
|
|
{
|
|
int16_t ret = 0;
|
|
|
|
if (menu)
|
|
ret = menu->inputs.rotary_encoder_delta;
|
|
|
|
return ret;
|
|
}
|
|
|
|
enum button_state menu_get_button_state(const struct lcd_menu *menu)
|
|
{
|
|
enum button_state ret = BUTTON_IDLE;
|
|
|
|
if (menu)
|
|
ret = menu->inputs.push_button;
|
|
|
|
return ret;
|
|
}
|
|
|
|
bool menu_get_button_ready_state(const struct lcd_menu *menu)
|
|
{
|
|
bool ret = false;
|
|
|
|
if (menu)
|
|
ret = menu->inputs.button_ready;
|
|
|
|
return ret;
|
|
}
|
|
|
|
void menu_display_clear(struct lcd_menu *menu)
|
|
{
|
|
uint8_t i;
|
|
|
|
if (!menu || !menu->update_display)
|
|
return;
|
|
|
|
for (i = 0; i < 4; i++)
|
|
menu->update_display(i, "");
|
|
}
|