pebblisp/src/calc.c

399 lines
11 KiB
C
Raw Normal View History

2016-04-18 04:25:36 -04:00
#include <stdio.h>
#include <limits.h>
#include "calc.h"
/** Text Editing **/
// Get the number of tokens in the current list
static inline int8_t tokenCount()
{
return using_func_tokens?
sizeof(func_tokens) / sizeof(func_tokens[0]) :
sizeof(tokens) / sizeof(tokens[0]);
2016-04-18 04:25:36 -04:00
}
// Get current token from tokens[] or func_tokens[], as appropriate
static inline const char* getToken(int8_t n)
{
int8_t t = n % tokenCount();
return using_func_tokens? func_tokens[t] : tokens[t];
2020-05-04 18:14:41 -04:00
}
// Update the current code text with the contents of `mytext`,
// and add the current selected_token to the end
2016-04-18 04:25:36 -04:00
static void updateText()
{
const char *token = getToken(selected_token);
2016-04-18 04:25:36 -04:00
strcpy(temptext, mytext);
2020-05-03 17:00:45 -04:00
strcat(temptext, token[0] == ' ' ? "_": // Display space as underscore
token[0] == '\n'? "\\n": // Display newline as \n
token); // Display others literally
2016-04-18 04:25:36 -04:00
text_layer_set_text(s_input_text_layer, temptext);
}
// Cycle through the current list of tokens
static void cycle_tokens(ClickRecognizerRef recognizer, void *context)
{
2020-05-04 18:14:41 -04:00
// Change current token
if(click_recognizer_get_button_id(recognizer) == BUTTON_ID_DOWN)
selected_token++;
else
selected_token--;
2016-04-18 04:25:36 -04:00
2020-05-04 18:14:41 -04:00
// If selected token is outside of range, wrap around
if(selected_token < 0)
selected_token = tokenCount() - 1;
else if(selected_token >= tokenCount())
selected_token = 0;
updateText();
2016-04-18 04:25:36 -04:00
}
static const char *fonts[] = {
FONT_KEY_GOTHIC_28_BOLD,
FONT_KEY_GOTHIC_24_BOLD,
FONT_KEY_GOTHIC_18_BOLD,
FONT_KEY_GOTHIC_14_BOLD
};
static const int smallestFont = 3;
static void adjustFont()
{
int i = 0;
int size = 0;
while(temptext[i++]) {
if(temptext[i] == '\n')
size += 10;
size++;
}
int f = size < 50 ? smallestFont - 3 :
size < 80 ? smallestFont - 2 :
size < 100 ? smallestFont - 1 :
smallestFont;
text_layer_set_font(s_input_text_layer,
fonts_get_system_font(fonts[f]));
}
static void custom_unload(Window *window)
{
text_layer_destroy(s_heading_text_layer);
window_destroy(window);
s_custom_window = NULL;
}
/**
* In normal token list, backspace if possible, otherwise return to script list
* In function token list, return to normal token list
*/
static void click_backspace(ClickRecognizerRef recognizer, void *context)
{
if(s_custom_window) {
window_stack_remove(s_custom_window, true);
return;
}
if(!using_func_tokens) {
if(mytext[0] == '\0') {
window_stack_remove(window_stack_get_top_window(), true);
} else {
mytext[strlen(mytext) - 1] = '\0';
updateText();
}
2020-05-03 21:24:36 -04:00
} else {
using_func_tokens = false;
2020-05-03 21:24:36 -04:00
updateText();
}
adjustFont();
2020-05-03 21:24:36 -04:00
}
2016-04-18 04:25:36 -04:00
// Adds the current string to the main string
static void add_token()
{
2016-04-18 04:25:36 -04:00
strcat(mytext, getToken(selected_token));
selected_token = 0;
if(using_func_tokens) {
using_func_tokens = false;
}
2016-04-18 04:25:36 -04:00
updateText();
adjustFont();
2016-04-18 04:25:36 -04:00
}
/** Code running and saving **/
2020-05-04 18:14:41 -04:00
// Calculate result, display it and reset
static void calculate()
{
2020-05-03 17:00:45 -04:00
Object obj = parseEval(mytext, &env);
2020-05-07 20:32:01 -04:00
char temp[RESULT_LENGTH-2] = "";
2020-05-04 18:14:41 -04:00
2020-05-04 10:03:35 -04:00
stringObj(temp, &obj);
2020-05-07 20:32:01 -04:00
snprintf(resulttext, RESULT_LENGTH, "R:%s", temp);
2020-05-03 21:24:36 -04:00
text_layer_set_text(s_result_text_layer, resulttext);
2016-04-18 04:25:36 -04:00
}
// Button press handler
static void click_select(ClickRecognizerRef recognizer, void *context)
{
if(!using_func_tokens && selected_token == tokenCount() - 1) {
2016-04-18 04:25:36 -04:00
calculate();
} else if(!using_func_tokens && selected_token == tokenCount() - 2) {
using_func_tokens = true;
selected_token = 0;
updateText();
} else {
add_token();
}
2016-04-18 04:25:36 -04:00
}
// Saves text in editor to persistent storage
static void click_save(ClickRecognizerRef recognizer, void *context)
{
int8_t i = strlen(temptext);
for(unsigned j = 0; j < strlen(getToken(selected_token)); j++) {
temptext[i-(1 + j)] = '\0';
}
2020-05-05 19:21:54 -04:00
persist_write_string(current_code, temptext);
window_stack_pop(true);
}
// Sets the code_window click functions
static void code_click_subscribe(void *context)
{
window_single_repeating_click_subscribe(BUTTON_ID_UP, 100, cycle_tokens);
window_single_repeating_click_subscribe(BUTTON_ID_DOWN, 100, cycle_tokens);
window_single_click_subscribe(BUTTON_ID_SELECT, click_select);
window_long_click_subscribe(BUTTON_ID_SELECT, 500, click_save, NULL);
window_single_click_subscribe(BUTTON_ID_BACK, click_backspace);
2016-04-18 04:25:36 -04:00
}
static void code_window_load(Window *window)
{
Layer *window_layer = window_get_root_layer(window);
GRect bounds = layer_get_bounds(window_layer);
2020-05-05 19:21:54 -04:00
// Register click config provider
window_set_click_config_provider(s_code_window, code_click_subscribe);
2020-05-05 19:21:54 -04:00
// Input text layer setup
s_input_text_layer = text_layer_create(bounds);
text_layer_set_text(s_input_text_layer, getToken(1));
text_layer_set_font(s_input_text_layer, fonts_get_system_font(FONT_KEY_GOTHIC_28_BOLD));
layer_add_child(window_get_root_layer(s_code_window), text_layer_get_layer(s_input_text_layer));
// Result text layer setup
GRect result_bounds = GRect(6, 128, 132, 132);
s_result_text_layer = text_layer_create(result_bounds);
text_layer_set_text(s_result_text_layer, "R: ");
text_layer_set_font(s_result_text_layer, fonts_get_system_font(FONT_KEY_GOTHIC_18_BOLD));
text_layer_set_text_alignment(s_result_text_layer, GTextAlignmentRight);
layer_add_child(window_get_root_layer(s_code_window), text_layer_get_layer(s_result_text_layer));
// Push the window, setting the window animation to 'true'
window_stack_push(s_code_window, true);
// If possible, load the previous code text
if(persist_exists(current_code)) {
persist_read_string(current_code, mytext, SMAX_LENGTH);
updateText();
adjustFont();
}
2020-05-05 19:21:54 -04:00
}
static Object run_script(Object script_num, Object obj, struct Environment *e) {
int n = script_num.number - 1;
if(persist_exists(n)) {
char *code = malloc(sizeof(char) * SMAX_LENGTH);
persist_read_string(n, code, SMAX_LENGTH);
Object o = parseEval(code, e);
free(code);
return o;
}
return errorObject(SCRIPT_NOT_FOUND);
}
static void code_window_unload(Window *window)
{
// Save the current code text
persist_write_string(current_code, temptext);
text_layer_destroy(s_result_text_layer);
text_layer_destroy(s_input_text_layer);
window_destroy(window);
s_code_window = NULL;
2020-05-05 19:21:54 -04:00
}
void code_window_push()
{
if(!s_code_window) {
s_code_window = window_create();
WindowHandlers wh = {
.load = code_window_load,
.unload = code_window_unload };
window_set_window_handlers(s_code_window, wh);
}
window_stack_push(s_code_window, true);
2020-05-05 19:21:54 -04:00
}
/** Menu Window **/
2020-05-05 19:21:54 -04:00
static void select_callback(struct MenuLayer *menu_layer,
MenuIndex *cell_index, void *context)
{
current_code = cell_index->row;
code_window_push();
}
static uint16_t get_num_rows_callback(MenuLayer *menu_layer,
uint16_t section_index, void *context)
{
return SCRIPT_COUNT;
}
static void draw_row_callback(GContext *ctx, const Layer *cell_layer,
MenuIndex *cell_index, void *context)
{
static char s_buff[16];
snprintf(s_buff, sizeof(s_buff), "Script %d", 1 + (int)cell_index->row);
// Draw this row's index
menu_cell_basic_draw(ctx, cell_layer, s_buff, NULL, NULL);
}
static int16_t get_cell_height_callback(struct MenuLayer *menu_layer,
MenuIndex *cell_index, void *context)
{
return CELL_HEIGHT;
}
2020-05-05 19:21:54 -04:00
static void menu_load(Window *window)
{
Layer *window_layer = window_get_root_layer(window);
GRect bounds = layer_get_bounds(window_layer);
2020-05-05 19:21:54 -04:00
s_menu_layer = menu_layer_create(bounds);
menu_layer_set_click_config_onto_window(s_menu_layer, window);
#if defined(PBL_COLOR)
menu_layer_set_normal_colors(s_menu_layer, GColorBlack, GColorWhite);
menu_layer_set_highlight_colors(s_menu_layer, GColorDukeBlue, GColorWhite);
#endif
menu_layer_set_callbacks(s_menu_layer, NULL, (MenuLayerCallbacks) {
.get_num_rows = get_num_rows_callback,
.draw_row = draw_row_callback,
.get_cell_height = get_cell_height_callback,
.select_click = select_callback,
});
layer_add_child(window_layer, menu_layer_get_layer(s_menu_layer));
2020-05-05 19:21:54 -04:00
}
static void menu_unload(Window *window)
{
menu_layer_destroy(s_menu_layer);
}
/** Custom Window **/
2020-05-05 19:21:54 -04:00
static void custom_load(Window *window)
{
Layer *window_layer = window_get_root_layer(window);
GRect bounds = layer_get_bounds(window_layer);
// Register click config provider
window_set_click_config_provider(s_custom_window, code_click_subscribe);
// Header text layer setup
s_heading_text_layer = text_layer_create(bounds);
text_layer_set_text(s_heading_text_layer, header);
text_layer_set_font(s_heading_text_layer,
fonts_get_system_font(FONT_KEY_BITHAM_30_BLACK));
layer_add_child(window_get_root_layer(s_custom_window),
text_layer_get_layer(s_heading_text_layer));
// Push the window, setting the window animation to 'true'
window_stack_push(s_custom_window, true);
}
Object add_window(Object obj1, Object obj2, struct Environment *env)
{
printf("ADD_WINDOW\n");
if(obj1.type == TYPE_STRING) {
strcpy(header, obj1.string);
}
if(!s_custom_window) {
s_custom_window = window_create();
WindowHandlers wh = {
.load = custom_load,
.unload = custom_unload };
window_set_window_handlers(s_custom_window, wh);
}
window_stack_push(s_custom_window, true);
return numberObject(1);
}
static void inbox_received_callback(DictionaryIterator *iter, void *context) {
Tuple *script_tuple = dict_find(iter, MESSAGE_KEY_ScriptText);
if(script_tuple) {
if(!s_code_window) {
current_code = 0;
code_window_push();
}
char *script_text = script_tuple->value->cstring;
snprintf(temptext, sizeof(temptext), "%s", script_text);
snprintf(mytext, sizeof(mytext), "%s", script_text);
updateText();
}
}
/** General **/
static struct Environment pebbleEnv()
{
struct Environment e = defaultEnv();
// Needs two args
addFunc("window", &add_window, &e);
addFunc("sc", &run_script, &e);
parseEval("(def win (fn (a) (window a 1)))", &e);
parseEval("(def s (fn (a) (sc a 0)))", &e);
return e;
}
static void init(void)
{
env = pebbleEnv();
2020-05-05 19:21:54 -04:00
s_menu_window = window_create();
window_set_window_handlers(s_menu_window, (WindowHandlers) {
.load = menu_load,
.unload = menu_unload
});
window_stack_push(s_menu_window, true);
app_message_open(inbox_size, outbox_size);
app_message_register_inbox_received(inbox_received_callback);
2020-05-05 19:21:54 -04:00
}
static void deinit(void)
{
2020-05-04 18:14:41 -04:00
deleteEnv(&env);
text_layer_destroy(s_input_text_layer);
2020-05-05 19:21:54 -04:00
window_destroy(s_menu_window);
2016-04-18 04:25:36 -04:00
}
int main(void)
{
2016-04-18 04:25:36 -04:00
init();
app_event_loop();
deinit();
}