diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/modules/rkt/rkt_scener.c | 215 |
1 files changed, 160 insertions, 55 deletions
diff --git a/src/modules/rkt/rkt_scener.c b/src/modules/rkt/rkt_scener.c index cee46ad..3998e3b 100644 --- a/src/modules/rkt/rkt_scener.c +++ b/src/modules/rkt/rkt_scener.c @@ -67,6 +67,9 @@ typedef struct rkt_scener_t { til_setting_t *cur_setting; const til_setting_desc_t *cur_desc; til_setting_t *cur_invalid; + til_setting_t *cur_edited; + unsigned replacement:1; /* set when editing / replacing scener->scene */ + unsigned editing:1; /* set when editing */ } new_scene; } rkt_scener_t; @@ -340,6 +343,53 @@ static int rkt_scener_handle_input_scenes(rkt_context_t *ctxt) } +/* edit the scene @ scener->scene */ +static int rkt_scener_edit_scene(rkt_context_t *ctxt) +{ + rkt_scener_t *scener; + til_settings_t *scenes_settings; + til_setting_t *scene_setting; + til_settings_t *new_settings; + const char *buf, *label; + + assert(ctxt); + scener = ctxt->scener; + assert(scener); + assert(scener->input); + assert(!scener->new_scene.settings); + + /* XXX TODO: should we expect to be called with scene=RKT_EXIT_SCENE_IDX? */ + assert(scener->scene < ctxt->n_scenes); + + scenes_settings = ((rkt_setup_t *)ctxt->til_module_context.setup)->scenes_settings; + + if (!til_settings_get_value_by_idx(scenes_settings, scener->scene, &scene_setting)) + return rkt_scener_err_close(scener, ENOENT); + + buf = til_settings_as_arg(scene_setting->value_as_nested_settings); + if (!buf) + return rkt_scener_err_close(scener, ENOMEM); + + label = til_settings_get_label(scene_setting->value_as_nested_settings); + + new_settings = til_settings_new(NULL, scenes_settings, label, buf); + if (!new_settings) + return rkt_scener_err_close(scener, ENOMEM); + + scener->input = til_str_free(scener->input); + scener->new_scene.settings = new_settings; + scener->new_scene.cur_setting = NULL; + scener->new_scene.cur_desc = NULL; + scener->new_scene.cur_invalid = NULL; + scener->new_scene.cur_edited = NULL; + scener->new_scene.replacement = 1; + scener->new_scene.editing = 1; + scener->state = RKT_SCENER_FSM_SEND_NEWSCENE_SETUP; + + return 0; +} + + static int rkt_scener_handle_input_newscene(rkt_context_t *ctxt) { rkt_scener_t *scener; @@ -375,6 +425,9 @@ static int rkt_scener_handle_input_newscene(rkt_context_t *ctxt) scener->new_scene.cur_setting = NULL; scener->new_scene.cur_desc = NULL; scener->new_scene.cur_invalid = NULL; + scener->new_scene.cur_edited = NULL; + scener->new_scene.replacement = 0; + scener->new_scene.editing = 0; scener->state = RKT_SCENER_FSM_SEND_NEWSCENE_SETUP; return 0; @@ -387,9 +440,11 @@ static int rkt_scener_handle_input_newscene_setup(rkt_context_t *ctxt) til_setting_t *setting; const til_setting_desc_t *desc; const til_setting_t *invalid; + int editing; size_t len; const char *buf; const char *value; + const char *preferred; assert(ctxt); scener = ctxt->scener; @@ -400,12 +455,19 @@ static int rkt_scener_handle_input_newscene_setup(rkt_context_t *ctxt) setting = scener->new_scene.cur_setting; desc = scener->new_scene.cur_desc; invalid = scener->new_scene.cur_invalid; + editing = scener->new_scene.editing; if (invalid && setting == invalid && !desc) desc = invalid->desc; assert(desc); - + + preferred = desc->spec.preferred; + if (scener->new_scene.editing && setting) + preferred = setting->value; + + assert(preferred); + /* we're adding a setting, the input may be a raw string, or * it might be a subscript of an array of values, it all depends on * scener->new_scene.cur_desc @@ -425,7 +487,7 @@ static int rkt_scener_handle_input_newscene_setup(rkt_context_t *ctxt) */ buf = til_str_buf(scener->input, &len); if (!len) { - value = desc->spec.preferred; + value = preferred; } else { til_str_t *output; @@ -467,11 +529,19 @@ static int rkt_scener_handle_input_newscene_setup(rkt_context_t *ctxt) if (invalid && setting == invalid) { /* TODO: add til_setting_set_value() ? */ free((void *)setting->value); - setting->value = strdup(value); + setting->value = strdup(value); /* TODO: ENOMEM */ scener->new_scene.cur_invalid = NULL; /* try again */ - } else if (!til_settings_add_value(desc->container, desc->spec.key, value)) + } else if (editing && setting) { + if (setting->value != value) { + free((void *)setting->value); + setting->value = strdup(value); /* TODO: ENOMEM */ + } + } else if (!(setting = til_settings_add_value(desc->container, desc->spec.key, value))) return -ENOMEM; + if (editing) + scener->new_scene.cur_edited = setting; + scener->input = til_str_free(scener->input); scener->state = RKT_SCENER_FSM_SEND_NEWSCENE_SETUP; @@ -600,32 +670,7 @@ static int rkt_scener_handle_input_scene(rkt_context_t *ctxt) case 'E': /* edit scene settings */ case 'e': - /* TODO run through a setup cycle seeded with current settings */ - /* so editing is very similar to creating a new scene... - * but the current scene's as_args are used to seed the create, - * instead of leaving it blank as in NEWSCENE. - * - * The main reason I haven't already done this is I want to have the - * input as_args settings show up as raw input at the initial New scene - * prompt, in a way that the user can then arrow-key/backspace over and - * change things in the same manner as if they has copy and pasted it into - * a text editor. But I'm not sure I can achieve that without going full ANSI - * BBS control codes up in here... still investigating. TODO - */ - return rkt_scener_send_message(scener, - "\n\nScene editing not _yet_ implemented, create a new one?\n" - "\n" - "Keep in mind, it's the Rocket 'scene' track that governs scene ordering/visibility,\n" - "think of these as more like Samples/Instruments in a tracker.\n" - "So you can relatively harmlessly have unused scenes in here.\n" - "\n" - "A temporary way to edit is copy and paste the settings shown above into a text editor,\n" - "make your tweaks (you can also just delete parts you want to revisit interactive setup for),\n" - "then paste it into the initial New scene raw input prompt (*discard* the quotes!).\n" - "\n" - "One advantage to this approach is you'll always have the old scene's settings for a retry.\n" - "\n", - RKT_SCENER_FSM_SEND_SCENE); + return rkt_scener_edit_scene(ctxt); case 'R': /* randomize only the settings (keep existing module) */ case 'r': { @@ -982,9 +1027,21 @@ int rkt_scener_update(rkt_context_t *ctxt) til_setting_t *setting = scener->new_scene.cur_setting; const til_setting_desc_t *desc = scener->new_scene.cur_desc; til_setting_t *invalid = scener->new_scene.cur_invalid; + til_setting_t *edited = scener->new_scene.cur_edited; + int editing = scener->new_scene.editing; assert(desc); + if (editing && setting && setting != invalid && + !setting->desc && setting != edited) { + /* we have an existing setting and haven't made it available for editing yet, + * so go back to the setup prompt for it, which will set cur_edited when edited. + */ + scener->state = RKT_SCENER_FSM_SEND_NEWSCENE_SETUP_PROMPT; + + return 0; + } + if (setting && setting != invalid && !setting->desc) { /* Apply override before, or after the spec_check()? unclear. * TODO This probably also needs to move into a til_settings helper @@ -1047,7 +1104,7 @@ int rkt_scener_update(rkt_context_t *ctxt) */ /* these *may* move into helpers, so scoping them as ad-hoc unnamed functions for now */ - { /* expand scenes settings */ + if (!scener->new_scene.replacement) { /* expand scenes settings */ til_settings_t *scenes_settings; til_setting_t *scene_setting; char *label, *as_arg; @@ -1081,9 +1138,24 @@ int rkt_scener_update(rkt_context_t *ctxt) return rkt_scener_err_close(scener, r); scene_setting->value_as_nested_settings = scener->new_scene.settings; + + } else { /* simply replace current nested settings */ + til_settings_t *scenes_settings; + til_setting_t *scene_setting; + + scenes_settings = ((rkt_setup_t *)ctxt->til_module_context.setup)->scenes_settings; + if (!til_settings_get_value_by_idx(scenes_settings, scener->scene, &scene_setting)) + return rkt_scener_err_close(scener, ENOENT); + + /* FIXME TODO: keep this around until finishing the context replacement, + * and put it back in all the failure cases before that point. + */ + til_settings_free(scene_setting->value_as_nested_settings); + + scene_setting->value_as_nested_settings = scener->new_scene.settings; } - { /* expand context scenes, finalize setup, create context */ + { /* finalize setup, create context, expand context scenes or replace existing */ const char *module_name; const til_module_t *module; til_module_context_t *module_ctxt; @@ -1120,27 +1192,35 @@ int rkt_scener_update(rkt_context_t *ctxt) if (r < 0) return rkt_scener_err_close(scener, r); - /* have context, almost there, enlarge scener->scenes, bump scener->n_scenes */ - new_scenes = realloc(ctxt->scenes, (ctxt->n_scenes + 1) * sizeof(*new_scenes)); - if (!new_scenes) { - til_module_context_free(module_ctxt); - - return rkt_scener_err_close(scener, r); - } - - new_scenes[ctxt->n_scenes].module_ctxt = module_ctxt; - ctxt->scenes = new_scenes; - ctxt->n_scenes++; + if (!scener->new_scene.replacement) { + /* have context, almost there, enlarge scener->scenes, bump scener->n_scenes */ + new_scenes = realloc(ctxt->scenes, (ctxt->n_scenes + 1) * sizeof(*new_scenes)); + if (!new_scenes) { + til_module_context_free(module_ctxt); - /* added! */ + return rkt_scener_err_close(scener, r); + } - scener->scene = ctxt->n_scenes - 1; + new_scenes[ctxt->n_scenes].module_ctxt = module_ctxt; + ctxt->scenes = new_scenes; + ctxt->n_scenes++; + + /* added! */ + scener->scene = ctxt->n_scenes - 1; + } else { + /* have context, replace what's there */ + assert(scener->scene < ctxt->n_scenes); + ctxt->scenes[scener->scene].module_ctxt = til_module_context_free(ctxt->scenes[scener->scene].module_ctxt); + ctxt->scenes[scener->scene].module_ctxt = module_ctxt; + } } scener->new_scene.settings = NULL; return rkt_scener_send_message(scener, - "\n\nNew scene added successfully...\n", + scener->new_scene.replacement ? + "\n\nScene replaced successfully...\n" : + "\n\nNew scene added successfully...\n", RKT_SCENER_FSM_SEND_SCENES); } @@ -1149,12 +1229,28 @@ int rkt_scener_update(rkt_context_t *ctxt) const til_setting_desc_t *desc = scener->new_scene.cur_desc; til_setting_t *invalid = scener->new_scene.cur_invalid; til_str_t *output; + const char *def; + char *label = NULL; - if (invalid && setting == invalid && !desc) + if (invalid && setting == invalid && !desc) /* XXX: should we really be checking !desc? */ desc = invalid->desc; assert(desc); + def = desc->spec.preferred; + if (scener->new_scene.editing && setting) + def = setting->value; + + if (!desc->spec.key && setting) { /* get the positional label */ + /* TODO: this is so when the desc has no key/name context, as editing bare-value settings, + * we can show _something_. But this feels very ad-hoc/hacky and seems like it should + * be handled by some magic in til, or some helper to get these desc paths in a more + * setting-aware way optionally when there is an associated setting onhand. + */ + if (til_settings_label_setting(desc->container, setting, &label) < 0) + return rkt_scener_err_close(scener, ENOMEM); + } + /* construct a prompt based on cur_desc * * this was derived from setup_interactively(), but til_str_t-centric and @@ -1165,8 +1261,8 @@ int rkt_scener_update(rkt_context_t *ctxt) return rkt_scener_err_close(scener, ENOMEM); if (desc->spec.values) { - unsigned i, preferred = 0; int width = 0; + unsigned i; for (i = 0; desc->spec.values[i]; i++) { int len; @@ -1180,7 +1276,12 @@ int rkt_scener_update(rkt_context_t *ctxt) if (til_setting_desc_strprint_path(desc, output) < 0) return rkt_scener_err_close(scener, ENOMEM); - if (til_str_appendf(output, ":\n %s:\n", desc->spec.name) < 0) + if (til_str_appendf(output, "%s%s:\n%s%s%s", + label ? "/" : "", + label ? : "", + label ? "" : " ", + label ? "" : desc->spec.name, + label ? "" : ":\n") < 0) return rkt_scener_err_close(scener, ENOMEM); for (i = 0; desc->spec.values[i]; i++) { @@ -1188,13 +1289,9 @@ int rkt_scener_update(rkt_context_t *ctxt) desc->spec.annotations ? ": " : "", desc->spec.annotations ? desc->spec.annotations[i] : "") < 0) return rkt_scener_err_close(scener, ENOMEM); - - if (!strcasecmp(desc->spec.preferred, desc->spec.values[i])) - preferred = i; } - if (til_str_appendf(output, " Enter a value 0-%u [%u (%s)]: ", - i - 1, preferred, desc->spec.preferred) < 0) + if (til_str_appendf(output, " Enter a value 0-%u [%s]: ", i - 1, def) < 0) return rkt_scener_err_close(scener, ENOMEM); } else { @@ -1202,10 +1299,18 @@ int rkt_scener_update(rkt_context_t *ctxt) if (til_setting_desc_strprint_path(desc, output) < 0) return rkt_scener_err_close(scener, ENOMEM); - if (til_str_appendf(output, ":\n %s [%s]: ", desc->spec.name, desc->spec.preferred) < 0) + if (til_str_appendf(output, "%s%s:\n %s%s[%s]: ", + label ? "/" : "", + label ? : "", + label ? "" : desc->spec.name, + label ? "" : " ", + def) < 0) return rkt_scener_err_close(scener, ENOMEM); } + /* FIXME TODO: label and output are leaked in all the ENOMEM returns above! */ + free(label); + return rkt_scener_send(scener, output, RKT_SCENER_FSM_RECV_NEWSCENE_SETUP); } |