poe

Уређивач .po фајлова
git clone https://git.sr.ht/~strahinja/poe
Дневник | Датотеке | Референце | ПРОЧИТАЈМЕ | ЛИЦЕНЦА

чување a21adffccd6fc4e4a1a9569caf8b24c03c927e2d
родитељ 135074b08970e4a87deedc7a418f3c079854feb8
Аутор: Страхиња Радић <sr@strahinja.org>
Датум:   Fri, 13 Feb 2026 10:55:08 +0100

WIP continue cleaning up code based on sled

Diffstat:
MTODO | 8+++++---
MTODO.done | 3+++
Mpoe.c | 2061+++++++++++++++++++++++++++++++++++++++----------------------------------------
Mpoe.h | 1-
измењених датотека: 4, додавања: 1030(+), брисања: 1043(-)

diff --git a/TODO b/TODO @@ -5,9 +5,11 @@ TODO entries) [ ] Tidy up basic logic everywhere (based on sled) - -[x] More sophisticated "not PO file" detection - avoid opening "binary" - (relatively speaking) files + [ ] Check cleanness against sled, function-by-function + Last: erase_search_prev_word + [ ] str* -> memccpy + [ ] Tidy up return codes and error handling + [ ] Introduce position_to (sled) < > Wrap long lines? Not if it would complicate the code too much. diff --git a/TODO.done b/TODO.done @@ -1,6 +1,9 @@ Done todos ========== +[x] More sophisticated "not PO file" detection - avoid opening "binary" + (relatively speaking) files + [x] Add pledge(2) and unveil(2) [x] Switch to strlcpy, strlcat similar to sled; compare util.c and util.h to diff --git a/poe.c b/poe.c @@ -31,10 +31,10 @@ #define TB_IMPL #include "draw.h" -#include "poe.h" #include "config.h" #include "limit.h" #include "po.h" +#include "poe.h" #include "util.h" #include "version.h" @@ -47,6 +47,7 @@ extern const char* prompt_overwrite; static int cancel_callback(struct DrawState* state, struct tb_event* ev) { + assert((state != NULL) && (ev != NULL)); if (ev->ch == 'y') save_msgstr(state); else @@ -55,6 +56,162 @@ cancel_callback(struct DrawState* state, struct tb_event* ev) return 0; } +static int +cancel_close(struct DrawState* state) +{ + assert(state != NULL); + if (!state->error && state->in_prompt) + { + state->in_prompt = 0; + *state->prompt = 0; + } + else if (state->show_help) + state->show_help = 0; + else if (state->show_edit) + cancel_msgstr(state); + else if (state->show_search) + cancel_search(state); + else + state->error = ERR_EXIT_KEY; + return 0; +} + +static int +cancel_msgstr(struct DrawState* state) +{ + ssize_t i; + + assert(state != NULL); + if (!state->in_prompt && state->dirty) + { + state->in_prompt = 1; + STRLCPY(prompt_cancel_dirty, state->prompt, MAXBUFLINE); + state->prompt_callback = cancel_callback; + return 0; + } + + state->show_edit = 0; + state->edit_info_focused = 0; + + for (i = 0; i < state->input_rows_count; i++) + free_bufferline(state->input_buffer + i); + free(state->input_buffer); + state->input_buffer = NULL; + state->input_rows_count = 0; + if (state->info_buffer) + for (i = 0; i < state->info_rows_count; i++) + free_bufferline(state->info_buffer + i); + free(state->info_buffer); + state->info_buffer = NULL; + state->info_rows_count = 0; + state->dirty = state->dirty_before_edit; + state->fuzzy_count = state->fuzzy_before_edit; + switch (state->input_changed_fuzzy) + { + case SET: + state->entries[state->msgid_number - 1].flags &= ~FL_FUZZY; + break; + case CLEARED: + state->entries[state->msgid_number - 1].flags |= FL_FUZZY; + break; + default:; + } + state->input_changed_fuzzy = UNCHANGED; + tb_hide_cursor(); + return 0; +} + +static int +cancel_search(struct DrawState* state) +{ + assert(state != NULL); + state->show_search = 0; + *state->search = 0; + tb_hide_cursor(); + return 0; +} + +static int +copy_msgid_to_input(struct DrawState* state) +{ + struct PoEntry* current; + struct BufferLine* newbuf; + struct BufferLine* pbuffer; + const uint32_t* pmsgid; + const uint32_t* old_pmsgid; + uint32_t* newtext; + ssize_t msgid_num_lines, lines_to_init, lines_to_free, i; + size_t len, newlen; + + assert(state != NULL); + + if (state->edit_info_focused) + return 0; + + if (!state->in_prompt && *state->input_buffer->text) + { + state->in_prompt = 1; + STRLCPY(prompt_overwrite, state->prompt, MAXBUFLINE); + state->prompt_callback = overwrite_copy_msgid_callback; + return 0; + } + + if (state->msgid_number == 1) + { + state->error = ERR_ILLEGAL_ON_FIRST; + return 0; + } + + assert(state->entries != NULL); + current = &state->entries[state->msgid_number - 1]; + msgid_num_lines = u32_lines_in_string(current->msgid, 0, 0); + lines_to_init = state->input_rows_count < msgid_num_lines + ? msgid_num_lines - state->input_rows_count + : 0; + lines_to_free = state->input_rows_count > msgid_num_lines + ? state->input_rows_count - msgid_num_lines + : 0; + for (i = 0; i < lines_to_free; i++) + free_bufferline( + state->input_buffer + state->input_rows_count - i - 1); + newbuf = realloc(state->input_buffer, + msgid_num_lines * sizeof(struct BufferLine)); + pmsgid = current->msgid; + + if (!newbuf) + return 0; + + pbuffer = NULL; + for (i = 0; i < lines_to_init; i++) + { + pbuffer = newbuf + msgid_num_lines - 1 - i; + init_bufferline(pbuffer); + newtext = calloc(MAXBUFLINE, sizeof(uint32_t)); + if (!newtext) + return 0; + pbuffer->text = newtext; + } + + pbuffer = newbuf; + while (pmsgid && *pmsgid) + { + len = 0; + newlen = 0; + old_pmsgid = pmsgid; + len = u32_next_line(current->msgid, &pmsgid, 0, 0); + u32_strncpy(pbuffer->text, old_pmsgid, MIN(len + 1, MAXBUFLINE)); + newlen = u32_decode_tabs(pbuffer->text, MAXBUFLINE); + pbuffer->length = newlen; + pbuffer++; + } + + state->input_rows_count = msgid_num_lines; + state->input_buffer = newbuf; + state->dirty = 1; + + return 0; +} + #define MAXCOPYBUF 8192 int @@ -122,41 +279,6 @@ cp_cleanup: return result; } -/*static int -create_file(struct DrawState* state) -{ - struct BufferLine* newchunk = NULL; - uint32_t* newtext = NULL; - - assert(state != NULL); - newchunk = calloc(ALLOC_DELTA, sizeof(struct BufferLine)); - if (!newchunk) - { - state->running = 0; - return 1; - } - for (ssize_t row = 0; row < ALLOC_DELTA; row++) - { - init_bufferline(newchunk + row); - newtext = calloc(LINE_DEFAULT, sizeof(uint32_t)); - if (!newtext) - { - state->running = 0; - return 1; - } - newchunk[row].size = LINE_DEFAULT; - newchunk[row].text = newtext; - } - state->buffer = newchunk; - state->buffer_size = ALLOC_DELTA; - state->rows_count = 1; - recalculate_line_numbers(state); - state->coord.row = 0; - state->dirty = 1; - - return 0; -}*/ - static char* dirname(char* dir, char* path, const ssize_t dir_size) { @@ -189,1195 +311,966 @@ dirname(char* dir, char* path, const ssize_t dir_size) return dir; } -static void -goto_msgid(struct DrawState* state, const ssize_t msgid_number) +static int +edit_page_down(struct DrawState* state) { - ssize_t last_non_obsolete - = state->real_msgid_count - state->obsolete_at_end_count; - - if (msgid_number > state->real_msgid_count) - return; - - state->msgid_number = msgid_number; + assert(state != NULL); + if (state->edit_info_focused) + { + if (state->info_first_shown.row + (EDIT_HEIGHT - 3) / 2 + < state->info_rows_count - 1) + state->info_first_shown.row += (EDIT_HEIGHT - 3) / 2; + return 0; + } + if (state->dirty) + save_msgstr(state); + else /* No need to call cancel_msgstr */ + state->dirty = state->dirty_before_edit; + move_list_down(state); + state->msgstr_index = 0; + return show_edit(state); +} - if (state->msgid_number < state->first_shown_msgid) - state->first_shown_msgid = state->msgid_number; - else if (state->msgid_number - > state->first_shown_msgid + state->maxy - 2) - state->first_shown_msgid - = state->msgid_number - (state->maxy - 2); - else if (last_non_obsolete < state->msgid_number + state->maxy - 2) - state->first_shown_msgid = (last_non_obsolete > state->maxy - 2 - ? last_non_obsolete - (state->maxy - 2) - : 1); +static int +edit_page_up(struct DrawState* state) +{ + assert(state != NULL); + if (state->edit_info_focused) + { + if (state->info_first_shown.row > (EDIT_HEIGHT - 3) / 2) + state->info_first_shown.row -= (EDIT_HEIGHT - 3) / 2; + else + move_start(state); + return 0; + } + if (state->dirty) + save_msgstr(state); + else /* No need to call cancel_msgstr */ + state->dirty = state->dirty_before_edit; + move_list_up(state); + state->msgstr_index = 0; + return show_edit(state); } -static void -handle_key_event(struct tb_event* ev, struct DrawState* state) +static int +erase_backwards(struct DrawState* state) { - const struct Key* current = NULL; + const struct BufferLine* current_line; + struct BufferLine* buffer_line; - assert((state != NULL) && (ev != NULL)); - state->error = ERR_NONE; - errno = 0; + assert((state != NULL) && (state->input_buffer != NULL)); + buffer_line = &state->input_buffer[state->coord.row]; + if (state->edit_info_focused) + return 1; - if (state->in_prompt) + if (state->coord.col > 0) { - if (ev->ch && strchr("YyNn", ev->ch)) + ssize_t i = state->coord.col; + assert(buffer_line->text != NULL); + while (i <= buffer_line->length) { - if (state->prompt_callback) - state->prompt_callback(state, ev); - state->in_prompt = 0; - *state->prompt = 0; + buffer_line->text[i - 1] = buffer_line->text[i]; + i++; } - else if (ev->key == TB_KEY_ESC) - cancel_close(state); - return; - } - - /* Remember to also edit the following condition if you - * reassign motion keys */ - /* TODO: Condition in sled also contains: - && ev->key != TB_KEY_PGDN && ev->key != TB_KEY_PGUP - && ev->key != TB_KEY_CTRL_B && ev->key != TB_KEY_CTRL_F - && !(IN(state->mode, M_COMMAND) && strchr("jkgGHLM", ev->ch))) - Check? - */ - if (ev->key != TB_KEY_ARROW_DOWN && ev->key != TB_KEY_ARROW_UP - && state->show_edit) - state->saved_column = -1; - - for (size_t key = 0; key < LEN(top_level_keys); key++) - { - current = top_level_keys + key; - if (((current->ev.mod == ev->mod) - && (current->ev.key == ev->key) && !ev->ch) - || (current->ev.ch && (current->ev.ch == ev->ch))) - if (current->callback) - { - current->callback(state); - return; - } - } - - if (state->show_edit) - { - for (size_t key = 0; key < LEN(edit_keys); key++) - { - current = edit_keys + key; - if (((current->ev.mod == ev->mod) - && (current->ev.key == ev->key) && !ev->ch) - || (current->ev.ch - && (current->ev.ch == ev->ch))) - if (current->callback) - { - current->callback(state); - return; - } - } - if (!state->edit_info_focused && ev->ch) - { - insert_char(state, ev->ch); - return; - } - } - - if (state->show_search) - { - for (size_t key = 0; key < LEN(search_keys); key++) - { - current = search_keys + key; - if (((current->ev.mod == ev->mod) - && (current->ev.key == ev->key) && !ev->ch) - || (current->ev.ch - && (current->ev.ch == ev->ch))) - if (current->callback) - { - current->callback(state); - return; - } - } - if (ev->ch) + buffer_line->text[i - 1] = 0; + buffer_line->length--; + /* TODO: position_to */ + state->coord.col--; + if (state->input_first_shown.col > 0) { - insert_search_char(state, ev->ch); - return; + current_line = &state->input_buffer[state->coord.row]; + if (display_length(current_line->text, + current_line->length) + >= state->edit_width - 4) + state->input_first_shown.col--; } + state->input_display_column + = display_length(state->input_buffer[state->coord.row] + .text, + state->coord.col); + if (state->input_display_column < state->input_first_shown.col) + state->input_first_shown.col + = state->input_display_column; + state->dirty = 1; } - - if (state->show_help) - { - state->error = ERR_DLG_OPEN; - return; - } - - for (size_t key = 0; key < LEN(main_keys); key++) + else if (state->coord.row > 0) { - current = main_keys + key; - if (((current->ev.mod == ev->mod) - && (current->ev.key == ev->key) && !ev->ch) - || (current->ev.ch && (current->ev.ch == ev->ch))) - if (current->callback) - { - current->callback(state); - return; - } + move_up(state); + move_end(state); + erase_forward(state); } - - if (state->edit_info_focused) - state->error = ERR_DLG_FOCUS_EDIT; - else if (state->show_edit || state->show_search) - state->error = ERR_UNKNOWN_KEY; - else - state->error = ERR_UNKNOWN_KEY_HELP; + return 0; } static int -insert_char(struct DrawState* state, uint32_t ch) +erase_forward(struct DrawState* state) { struct BufferLine* buffer_line; - ssize_t len; - assert(state != NULL); + size_t i; + + assert((state != NULL) && (state->input_buffer != NULL)); + buffer_line = &state->input_buffer[state->coord.row]; if (state->edit_info_focused) return 1; - buffer_line = &state->input_buffer[state->coord.row]; - len = buffer_line->length; - if (state->coord.col < MAXBUFLINE - 1) + + assert(buffer_line->text != NULL); + if (buffer_line->text[state->coord.col]) { - ssize_t i = len + 1; - int add_null = buffer_line->text[state->coord.col] == 0; - while (i < MAXBUFLINE - 2 && i > state->coord.col) + i = state->coord.col; + while (i < MAXBUFLINE - 2 && buffer_line->text[i + 1]) { - buffer_line->text[i] = buffer_line->text[i - 1]; - i--; + buffer_line->text[i] = buffer_line->text[i + 1]; + i++; } - buffer_line->text[state->coord.col++] = ch; - buffer_line->length++; - if (state->coord.col == MAXBUFLINE) - state->coord.col = MAXBUFLINE - 1; - state->input_display_column - = display_length(state->input_buffer[state->coord.row] - .text, - state->coord.col); - if (state->input_display_column - state->input_first_shown.col - > state->edit_width - 4) + buffer_line->text[i] = 0; + buffer_line->length--; + state->dirty = 1; + } + else if (state->coord.row < state->input_rows_count - 1) + { + if (!join_lines(state, state->coord.row, 1)) { - state->input_first_shown.col - = state->input_display_column - - state->edit_width + 4; - state->input_display_column = display_length( - state->input_buffer[state->coord.row].text, - state->coord.col); + state->error = ERR_CANT_ALLOC; + state->running = 0; } - if (add_null) - buffer_line->text[state->coord.col] = 0; } - state->dirty = 1; return 0; } static int -insert_line(struct DrawState* state) +erase_prev_word(struct DrawState* state) { - struct BufferLine* newbuf = NULL; + uint32_t* pline; + ssize_t len; + int i, j, d; + assert(state != NULL); if (state->edit_info_focused) return 1; + i = state->coord.col; + len = state->input_buffer[state->coord.row].length; + pline = state->input_buffer[state->coord.row].text; - state->input_rows_count++; - newbuf = realloc(state->input_buffer, - state->input_rows_count * sizeof(struct BufferLine)); - if (!newbuf) - { - state->error = ERR_CANT_ALLOC; - state->running = 0; - return 0; - } + if (i > 0 && u32_is_word_boundary(pline[i - 1], 0)) + i--; + while (i > 0 && u32_is_word_boundary(pline[i], 0)) + i--; + while (i > 0 && !u32_is_word_boundary(pline[i], 0) + && !u32_is_word_boundary(pline[i - 1], 0)) + i--; - state->input_buffer = newbuf; - init_bufferline(&state->input_buffer[state->input_rows_count - 1]); - uint32_t* newtext = calloc(MAXBUFLINE, sizeof(uint32_t)); - if (!newtext) - { - state->error = ERR_CANT_ALLOC; - state->running = 0; - return 0; - } - state->input_buffer[state->input_rows_count - 1].text = newtext; - for (ssize_t i = state->input_rows_count - 1; i > state->coord.row + 1; - i--) - { - u32_strncpy(state->input_buffer[i].text, - state->input_buffer[i - 1].text, MAXBUFLINE); - state->input_buffer[i].length - = state->input_buffer[i - 1].length; - } - u32_strncpy(state->input_buffer[state->coord.row + 1].text, - state->input_buffer[state->coord.row].text + state->coord.col, - MAXBUFLINE); - state->input_buffer[state->coord.row + 1].length - = u32_strlen(state->input_buffer[state->coord.row + 1].text); - state->input_buffer[state->coord.row].text[state->coord.col] = 0; - state->input_buffer[state->coord.row].length - = u32_strlen(state->input_buffer[state->coord.row].text); + d = state->coord.col - i; - state->coord.row++; - if (state->coord.row - > state->input_first_shown.row + (EDIT_HEIGHT - 3) / 2) - state->input_first_shown.row - = state->coord.row - (EDIT_HEIGHT - 3) / 2; - move_start(state); - state->dirty = 1; + for (j = i; j < len - d; j++) + pline[j] = pline[j + d]; + len -= d; + state->input_buffer[state->coord.row].length = len; + pline[len] = 0; + state->coord.col = i; + state->input_display_column + = display_length(state->input_buffer[state->coord.row].text, + state->coord.col); + if (state->input_display_column < state->input_first_shown.col) + state->input_first_shown.col = state->input_display_column; + state->dirty = 1; return 0; } -static void -insert_search_char(struct DrawState* state, uint32_t ch) +static int +erase_search_backwards(struct DrawState* state) { - ssize_t len = u32_strlen(state->search); - if (state->search_column < MAXSEARCH - 1) + size_t i; + assert(state != NULL); + if (state->search_column > 0) { - ssize_t i = len + 1; - int add_null = state->search[state->search_column] == 0; - while (i < MAXSEARCH - 2 && i > state->search_column) + i = state->search_column; + do { - state->search[i] = state->search[i - 1]; - i--; + state->search[i - 1] = state->search[i]; + i++; + } while (state->search[i]); + state->search[i - 1] = 0; + state->search_column--; + if (state->search_column < state->search_first_shown_column) + { + if (state->search_column > state->search_width - 4) + state->search_first_shown_column + = state->search_column + - state->search_width + 4; + else + state->search_first_shown_column = 0; } - state->search[state->search_column++] = ch; - if (state->search_column == MAXSEARCH) - state->search_column = MAXSEARCH - 1; state->search_display_column = display_length(state->search, state->search_column); - if (state->search_display_column - - state->search_first_shown_column - > state->search_width - 4) - { - state->search_first_shown_column - = state->search_display_column - - state->search_width + 4; - state->search_display_column - = display_length(state->search, - state->search_column); - } - if (add_null) - state->search[state->search_column] = 0; - next_match(state); + return next_match(state); } + return 0; } -static struct DrawState* -join_lines(struct DrawState* state, size_t first_line, int forward) +static int +erase_search_forward(struct DrawState* state) { - state->coord.col = state->input_buffer[first_line].length; - state->input_display_column - = display_length(state->input_buffer[state->coord.row].text, - state->coord.col); - u32_strncat(state->input_buffer[first_line].text, - state->input_buffer[first_line + 1].text, MAXBUFLINE); - state->input_buffer[first_line].length - = u32_strlen(state->input_buffer[first_line].text); - if (!forward) - { - state->coord.row--; - if (state->coord.row < 0) - state->coord.row = 0; - } - for (ssize_t i = first_line + 1; i < state->input_rows_count - 1; i++) + size_t i; + assert(state != NULL); + if (state->search[state->search_column]) { - u32_strncpy(state->input_buffer[i].text, - state->input_buffer[i + 1].text, MAXBUFLINE); - state->input_buffer[i].length - = state->input_buffer[i + 1].length; - } - free_bufferline(state->input_buffer + state->input_rows_count - 1); - state->input_rows_count--; - struct BufferLine* newbuf = realloc(state->input_buffer, - state->input_rows_count * sizeof(struct BufferLine)); - if (!newbuf) - return NULL; - state->input_buffer = newbuf; - state->dirty = 1; + i = state->search_column; + while (i < MAXSEARCH - 2 && state->search[i + 1]) + { + state->search[i] = state->search[i + 1]; + i++; + } + state->search[i] = 0; - return state; + return next_match(state); + } + return 0; } -static struct BufferLine* -load_info(struct DrawState* state) +static int +erase_search_prev_word(struct DrawState* state) { - struct PoEntry* first = &state->entries[0]; - struct PoEntry* current = &state->entries[state->msgid_number - 1]; - struct BufferLine* newbuf = NULL; - struct BufferLine* pnewbuf = NULL; - const uint32_t* pumsgid = NULL; - ssize_t msgid_num_lines = u32_lines_in_string(current->msgid, 0, 0); - ssize_t msgid_plural_num_lines - = u32_lines_in_string(current->msgid_plural, 0, 0); - ssize_t num_lines = 0; - ssize_t newlen = 0; + uint32_t* pline; + ssize_t len; + int i, j, d; - for (ssize_t i = 0; i < state->info_rows_count; i++) - free_bufferline(state->info_buffer + i); - free(state->info_buffer); + assert(state != NULL); + pline = state->search; + len = u32_strlen(state->search); + i = state->search_column; + if (i > 0 && u32_is_word_boundary(pline[i - 1], 0)) + i--; + while (i > 0 && u32_is_word_boundary(pline[i], 0)) + i--; + while (i > 0 && !u32_is_word_boundary(pline[i], 0) + && !u32_is_word_boundary(pline[i - 1], 0)) + i--; - state->info_rows_maxlen = 0; + d = state->search_column - i; - if (current->msgid) - num_lines += msgid_num_lines + 1; - if (current->msgid_plural) + for (j = i; j < len - d; j++) + pline[j] = pline[j + d]; + + len -= state->search_column - i; + pline[len] = 0; + state->search_column = i; + state->search_display_column + = display_length(state->search, state->search_column); + + if (state->search_column < state->search_first_shown_column) { - num_lines += msgid_plural_num_lines + 1; - if (first->plural_forms) - num_lines += 3; + if (state->search_column > state->search_width - 4) + state->search_first_shown_column = state->search_column + - state->search_width + 4; + else + state->search_first_shown_column = 0; } - if (current->comments) - num_lines += current->comment_lines + 1; - if (current->extr_comments) - num_lines += current->extr_comment_lines + 1; - if (current->ref_comments) - num_lines += current->ref_comment_lines + 1; - newbuf = calloc(num_lines, sizeof(struct BufferLine)); - if (!newbuf) - return NULL; + state->search_display_column + = display_length(state->search, state->search_column); - pnewbuf = newbuf; - for (ssize_t i = 0; i < num_lines; i++) + return next_match(state); +} + +static int +erase_search_to_end(struct DrawState* state) +{ + state->search[state->search_column] = 0; + move_search_end(state); + return next_match(state); +} + +static int +erase_search_to_start(struct DrawState* state) +{ + size_t i = state->search_column; + size_t len = u32_strlen(state->search); + do { - init_bufferline(pnewbuf); - uint32_t* newtext = calloc(MAXBUFLINE, sizeof(uint32_t)); - if (!newtext) - return NULL; - pnewbuf->text = newtext; - pnewbuf++; - } + state->search[i - state->search_column] = state->search[i]; + i++; + } while (state->search[i]); + state->search[len - state->search_column] = 0; + move_search_start(state); + return next_match(state); +} - state->info_rows_count = num_lines; +static int +erase_to_end(struct DrawState* state) +{ + struct BufferLine* buffer_line = NULL; - pnewbuf = newbuf; - newlen = 0; - pumsgid = NULL; - if (current->msgid) + if (state->edit_info_focused) + return 1; + + buffer_line = &state->input_buffer[state->coord.row]; + buffer_line->text[state->coord.col] = 0; + buffer_line->length = state->coord.col; + move_end(state); + state->dirty = 1; + return 0; +} + +static int +erase_to_start(struct DrawState* state) +{ + struct BufferLine* buffer_line; + ssize_t i; + ssize_t len; + + i = state->coord.col; + buffer_line = &state->input_buffer[state->coord.row]; + len = buffer_line->length; + + if (state->edit_info_focused) + return 1; + + do { - pumsgid = current->msgid; - newlen = u32_u8_strncpy(pnewbuf->text, "msgid:", MAXBUFLINE); - pnewbuf->length = newlen; - if (newlen > state->info_rows_maxlen) - state->info_rows_maxlen = newlen; - pnewbuf->fg = colors[C_EDIT_MSGID_HEADING].fg; - pnewbuf->bg = colors[C_EDIT_MSGID_HEADING].bg; - pnewbuf++; - while (*pumsgid) - { - size_t len = 0; - const uint32_t* old_pumsgid = pumsgid; + buffer_line->text[i - state->coord.col] = buffer_line->text[i]; + i++; + } while (buffer_line->text[i]); + buffer_line->text[len - state->coord.col] = 0; + buffer_line->length = len - state->coord.col; + move_start(state); + state->dirty = 1; + return 0; +} - len = u32_next_line(current->msgid, &pumsgid, 0, 0); - u32_strncpy(pnewbuf->text, old_pumsgid, - MIN(len + 1, MAXBUFLINE)); - newlen = u32_decode_tabs(pnewbuf->text, MAXBUFLINE); - pnewbuf->length = newlen; - if (newlen > state->info_rows_maxlen) - state->info_rows_maxlen = newlen; - pnewbuf++; - } +static int +exit_program(struct DrawState* state) +{ + if (state->show_edit) + { + state->error = ERR_DLG_OPEN; + return 0; } - if (current->msgid_plural) + if (state->dirty) { - newlen = u32_u8_strncpy(pnewbuf->text, - "msgid_plural:", MAXBUFLINE); - pnewbuf->length = newlen; - if (newlen > state->info_rows_maxlen) - state->info_rows_maxlen = newlen; - pnewbuf->fg = colors[C_EDIT_MSGID_HEADING].fg; - pnewbuf->bg = colors[C_EDIT_MSGID_HEADING].bg; - pnewbuf++; - pumsgid = current->msgid_plural; - while (*pumsgid) - { - size_t len = 0; - const uint32_t* old_pumsgid = pumsgid; + state->in_prompt = 1; + STRLCPY(prompt_dirty, state->prompt, MAXBUFLINE); + state->prompt_callback = quit_callback; + } + else + state->running = 0; + //*state->error = 0; + return 0; +} - len = u32_next_line(current->msgid_plural, &pumsgid, 0, - 0); - u32_strncpy(pnewbuf->text, old_pumsgid, - MIN(len + 1, MAXBUFLINE)); - newlen = u32_decode_tabs(pnewbuf->text, MAXBUFLINE); - pnewbuf->length = newlen; - if (newlen > state->info_rows_maxlen) - state->info_rows_maxlen = newlen; - pnewbuf++; - } - if (first->plural_forms) - { - newlen = u32_u8_strncpy(pnewbuf->text, - "Plural forms:", MAXBUFLINE); - pnewbuf->length = newlen; - if (newlen > state->info_rows_maxlen) - state->info_rows_maxlen = newlen; - pnewbuf->fg = colors[C_EDIT_MSGID_HEADING].fg; - pnewbuf->bg = colors[C_EDIT_MSGID_HEADING].bg; - pnewbuf++; - newlen = u32_strncpy(pnewbuf->text, first->plural_forms, - MAXBUFLINE); - pnewbuf->length = newlen; - if (newlen > state->info_rows_maxlen) - state->info_rows_maxlen = newlen; - pnewbuf++; - } - } - if (current->comments) +static void +goto_msgid(struct DrawState* state, const ssize_t msgid_number) +{ + ssize_t last_non_obsolete + = state->real_msgid_count - state->obsolete_at_end_count; + + if (msgid_number > state->real_msgid_count) + return; + + state->msgid_number = msgid_number; + + if (state->msgid_number < state->first_shown_msgid) + state->first_shown_msgid = state->msgid_number; + else if (state->msgid_number + > state->first_shown_msgid + state->maxy - 2) + state->first_shown_msgid + = state->msgid_number - (state->maxy - 2); + else if (last_non_obsolete < state->msgid_number + state->maxy - 2) + state->first_shown_msgid = (last_non_obsolete > state->maxy - 2 + ? last_non_obsolete - (state->maxy - 2) + : 1); +} + +static void +handle_key_event(struct tb_event* ev, struct DrawState* state) +{ + const struct Key* current = NULL; + + assert((state != NULL) && (ev != NULL)); + state->error = ERR_NONE; + errno = 0; + + if (state->in_prompt) { - newlen = u32_u8_strncpy(pnewbuf->text, "Comments:", MAXBUFLINE); - pnewbuf->length = newlen; - if (newlen > state->info_rows_maxlen) - state->info_rows_maxlen = newlen; - pnewbuf->fg = colors[C_EDIT_MSGID_HEADING].fg; - pnewbuf->bg = colors[C_EDIT_MSGID_HEADING].bg; - pnewbuf++; - for (size_t i = 0; i < current->comment_lines; i++) + if (ev->ch && strchr("YyNn", ev->ch)) { - uint32_t* comment = current->comments[i]; - size_t len = u32_strlen(comment); - newlen = u32_strncpy(pnewbuf->text, comment, - MIN(len + 1, MAXBUFLINE)); - pnewbuf->length = newlen; - if (newlen > state->info_rows_maxlen) - state->info_rows_maxlen = newlen; - pnewbuf++; + if (state->prompt_callback) + state->prompt_callback(state, ev); + state->in_prompt = 0; + *state->prompt = 0; } + else if (ev->key == TB_KEY_ESC) + cancel_close(state); + return; } - if (current->extr_comments) + + /* Remember to also edit the following condition if you + * reassign motion keys */ + /* TODO: Condition in sled also contains: + && ev->key != TB_KEY_PGDN && ev->key != TB_KEY_PGUP + && ev->key != TB_KEY_CTRL_B && ev->key != TB_KEY_CTRL_F + && !(IN(state->mode, M_COMMAND) && strchr("jkgGHLM", ev->ch))) + Check? + */ + if (ev->key != TB_KEY_ARROW_DOWN && ev->key != TB_KEY_ARROW_UP + && state->show_edit) + state->saved_column = -1; + + for (size_t key = 0; key < LEN(top_level_keys); key++) { - newlen = u32_u8_strncpy(pnewbuf->text, - "Translation comments:", MAXBUFLINE); - pnewbuf->length = newlen; - if (newlen > state->info_rows_maxlen) - state->info_rows_maxlen = newlen; - pnewbuf->fg = colors[C_EDIT_MSGID_HEADING].fg; - pnewbuf->bg = colors[C_EDIT_MSGID_HEADING].bg; - pnewbuf++; - for (size_t i = 0; i < current->extr_comment_lines; i++) + current = top_level_keys + key; + if (((current->ev.mod == ev->mod) + && (current->ev.key == ev->key) && !ev->ch) + || (current->ev.ch && (current->ev.ch == ev->ch))) + if (current->callback) + { + current->callback(state); + return; + } + } + + if (state->show_edit) + { + for (size_t key = 0; key < LEN(edit_keys); key++) { - uint32_t* extr_comment = current->extr_comments[i]; - size_t len = u32_strlen(extr_comment); - newlen = u32_strncpy(pnewbuf->text, extr_comment, - MIN(len + 1, MAXBUFLINE)); - pnewbuf->length = newlen; - if (newlen > state->info_rows_maxlen) - state->info_rows_maxlen = newlen; - pnewbuf++; + current = edit_keys + key; + if (((current->ev.mod == ev->mod) + && (current->ev.key == ev->key) && !ev->ch) + || (current->ev.ch + && (current->ev.ch == ev->ch))) + if (current->callback) + { + current->callback(state); + return; + } + } + if (!state->edit_info_focused && ev->ch) + { + insert_char(state, ev->ch); + return; } } - if (current->ref_comments) + + if (state->show_search) { - newlen = u32_u8_strncpy(pnewbuf->text, - "References:", MAXBUFLINE); - pnewbuf->length = newlen; - if (newlen > state->info_rows_maxlen) - state->info_rows_maxlen = newlen; - pnewbuf->fg = colors[C_EDIT_MSGID_HEADING].fg; - pnewbuf->bg = colors[C_EDIT_MSGID_HEADING].bg; - pnewbuf++; - for (size_t i = 0; i < current->ref_comment_lines; i++) + for (size_t key = 0; key < LEN(search_keys); key++) { - char* ref_comment = current->ref_comments[i]; - size_t len = strlen(ref_comment); - newlen = u32_u8_strncpy(pnewbuf->text, ref_comment, - MIN(len + 1, MAXBUFLINE)); - pnewbuf->length = newlen; - if (newlen > state->info_rows_maxlen) - state->info_rows_maxlen = newlen; - pnewbuf++; + current = search_keys + key; + if (((current->ev.mod == ev->mod) + && (current->ev.key == ev->key) && !ev->ch) + || (current->ev.ch + && (current->ev.ch == ev->ch))) + if (current->callback) + { + current->callback(state); + return; + } + } + if (ev->ch) + { + insert_search_char(state, ev->ch); + return; } } - state->info_buffer = newbuf; - - return state->info_buffer; -} - -static struct BufferLine* -load_msgstr(struct DrawState* state) -{ - struct PoEntry* current; - struct BufferLine* newbuf; - struct BufferLine* pnewbuf; - size_t num_lines; - int msgstr_index; - - assert((state != NULL) && (state->entries != NULL)); - current = &state->entries[state->msgid_number - 1]; - newbuf = NULL; - pnewbuf = NULL; - msgstr_index = state->msgstr_index; - num_lines = current->msgstr_count == 0 - ? 1 - : u32_lines_in_string(*(current->msgstr + msgstr_index), 0, 0); - - for (ssize_t i = 0; i < state->input_rows_count; i++) - free_bufferline(state->input_buffer + i); - free(state->input_buffer); - - newbuf = calloc(num_lines, sizeof(struct BufferLine)); - if (!newbuf) - return NULL; - - pnewbuf = newbuf; - for (size_t i = 0; i < num_lines; i++) + if (state->show_help) { - init_bufferline(pnewbuf); - uint32_t* newtext = calloc(MAXBUFLINE, sizeof(uint32_t)); - if (!newtext) - return NULL; - pnewbuf->text = newtext; - pnewbuf++; + state->error = ERR_DLG_OPEN; + return; } - state->input_rows_count = num_lines; - - if (current->msgstr_count > 0) + for (size_t key = 0; key < LEN(main_keys); key++) { - const uint32_t* pumsgstr = *(current->msgstr + msgstr_index); - if (pumsgstr) - { - pnewbuf = newbuf; - while (*pumsgstr) + current = main_keys + key; + if (((current->ev.mod == ev->mod) + && (current->ev.key == ev->key) && !ev->ch) + || (current->ev.ch && (current->ev.ch == ev->ch))) + if (current->callback) { - size_t len = 0; - size_t newlen = 0; - const uint32_t* old_pumsgstr = pumsgstr; - len = u32_next_line(*(current->msgstr - + msgstr_index), - &pumsgstr, 0, 0); - u32_strncpy(pnewbuf->text, old_pumsgstr, - MIN(len + 1, MAXBUFLINE)); - newlen = u32_decode_tabs(pnewbuf->text, - MAXBUFLINE); - pnewbuf->length = newlen; - pnewbuf++; + current->callback(state); + return; } - } - else - newbuf[0].length = 0; } - else - newbuf[0].length = 0; - state->input_buffer = newbuf; - return state->input_buffer; + if (state->edit_info_focused) + state->error = ERR_DLG_FOCUS_EDIT; + else if (state->show_edit || state->show_search) + state->error = ERR_UNKNOWN_KEY; + else + state->error = ERR_UNKNOWN_KEY_HELP; } -static size_t -next_msgid(struct DrawState* state, const int delta) +static int +hide_search(struct DrawState* state) { - size_t ret = state->msgid_number; - size_t start = ret; - size_t last_non_obsolete - = state->real_msgid_count - state->obsolete_at_end_count; - - while (ret < last_non_obsolete + 1) - { - ret++; - if (!state->entries[ret - 1].obsolete && ret >= start + delta) - break; - } - - return ret == last_non_obsolete + 1 ? last_non_obsolete : ret; -} - -static int -overwrite_copy_msgid_callback(struct DrawState* state, struct tb_event* ev) -{ - if (ev->ch == 'y') - copy_msgid_to_input(state); - state->prompt_callback = NULL; + state->show_search = 0; + tb_hide_cursor(); return 0; } static int -overwrite_paste_callback(struct DrawState* state, struct tb_event* ev) +insert_char(struct DrawState* state, uint32_t ch) { - if (ev->ch == 'y') - paste_paste_buffer_to_input(state); - state->prompt_callback = NULL; + struct BufferLine* buffer_line; + ssize_t len; + assert(state != NULL); + if (state->edit_info_focused) + return 1; + buffer_line = &state->input_buffer[state->coord.row]; + len = buffer_line->length; + if (state->coord.col < MAXBUFLINE - 1) + { + ssize_t i = len + 1; + int add_null = buffer_line->text[state->coord.col] == 0; + while (i < MAXBUFLINE - 2 && i > state->coord.col) + { + buffer_line->text[i] = buffer_line->text[i - 1]; + i--; + } + buffer_line->text[state->coord.col++] = ch; + buffer_line->length++; + if (state->coord.col == MAXBUFLINE) + state->coord.col = MAXBUFLINE - 1; + state->input_display_column + = display_length(state->input_buffer[state->coord.row] + .text, + state->coord.col); + if (state->input_display_column - state->input_first_shown.col + > state->edit_width - 4) + { + state->input_first_shown.col + = state->input_display_column + - state->edit_width + 4; + state->input_display_column = display_length( + state->input_buffer[state->coord.row].text, + state->coord.col); + } + if (add_null) + buffer_line->text[state->coord.col] = 0; + } + state->dirty = 1; return 0; } -static size_t -prev_msgid(struct DrawState* state, const int delta) +static int +insert_line(struct DrawState* state) { - ssize_t ret = state->msgid_number; - ssize_t start = ret; + struct BufferLine* newbuf = NULL; - if (start < delta) - return state->real_msgid_count ? 1 : 0; + if (state->edit_info_focused) + return 1; - while (ret > 1) + state->input_rows_count++; + newbuf = realloc(state->input_buffer, + state->input_rows_count * sizeof(struct BufferLine)); + if (!newbuf) { - ret--; - if (!state->entries[ret - 1].obsolete && ret <= start - delta) - break; + state->error = ERR_CANT_ALLOC; + state->running = 0; + return 0; } - return ret; -} + state->input_buffer = newbuf; + init_bufferline(&state->input_buffer[state->input_rows_count - 1]); + uint32_t* newtext = calloc(MAXBUFLINE, sizeof(uint32_t)); + if (!newtext) + { + state->error = ERR_CANT_ALLOC; + state->running = 0; + return 0; + } + state->input_buffer[state->input_rows_count - 1].text = newtext; + for (ssize_t i = state->input_rows_count - 1; i > state->coord.row + 1; + i--) + { + u32_strncpy(state->input_buffer[i].text, + state->input_buffer[i - 1].text, MAXBUFLINE); + state->input_buffer[i].length + = state->input_buffer[i - 1].length; + } + u32_strncpy(state->input_buffer[state->coord.row + 1].text, + state->input_buffer[state->coord.row].text + state->coord.col, + MAXBUFLINE); + state->input_buffer[state->coord.row + 1].length + = u32_strlen(state->input_buffer[state->coord.row + 1].text); + state->input_buffer[state->coord.row].text[state->coord.col] = 0; + state->input_buffer[state->coord.row].length + = u32_strlen(state->input_buffer[state->coord.row].text); -int -print_error(const int code, const char* msg, ...) -{ - char buf[MAXBUFLINE]; - va_list args; - va_start(args, msg); - vsnprintf(buf, sizeof(buf), msg, args); - va_end(args); - fprintf(stderr, "%s: %s\n", PROGRAM_NAME, buf); - return code; -} + state->coord.row++; + if (state->coord.row + > state->input_first_shown.row + (EDIT_HEIGHT - 3) / 2) + state->input_first_shown.row + = state->coord.row - (EDIT_HEIGHT - 3) / 2; + move_start(state); + state->dirty = 1; -static int -quit_callback(struct DrawState* state, struct tb_event* ev) -{ - if (ev->ch == 'y') - write_file(state); - state->prompt_callback = NULL; - state->running = 0; return 0; } -void -redraw_status(struct DrawState* state) +static void +insert_search_char(struct DrawState* state, uint32_t ch) { - assert(state != NULL); - if (state->recalculate_size) + ssize_t len = u32_strlen(state->search); + if (state->search_column < MAXSEARCH - 1) { - recalculate_size(state); - state->recalculate_size = 0; + ssize_t i = len + 1; + int add_null = state->search[state->search_column] == 0; + while (i < MAXSEARCH - 2 && i > state->search_column) + { + state->search[i] = state->search[i - 1]; + i--; + } + state->search[state->search_column++] = ch; + if (state->search_column == MAXSEARCH) + state->search_column = MAXSEARCH - 1; + state->search_display_column + = display_length(state->search, state->search_column); + if (state->search_display_column + - state->search_first_shown_column + > state->search_width - 4) + { + state->search_first_shown_column + = state->search_display_column + - state->search_width + 4; + state->search_display_column + = display_length(state->search, + state->search_column); + } + if (add_null) + state->search[state->search_column] = 0; + + next_match(state); } - (void)draw_status(state); - tb_present(); } static int -cancel_close(struct DrawState* state) +insert_search_space(struct DrawState* state) { - assert(state != NULL); - if (!state->error && state->in_prompt) - { - state->in_prompt = 0; - *state->prompt = 0; - } - else if (state->show_help) - state->show_help = 0; - else if (state->show_edit) - cancel_msgstr(state); - else if (state->show_search) - cancel_search(state); - else - state->error = ERR_EXIT_KEY; - + insert_search_char(state, (uint32_t)L' '); return 0; } static int -cancel_msgstr(struct DrawState* state) +insert_search_tab(struct DrawState* state) { - if (!state->in_prompt && state->dirty) - { - state->in_prompt = 1; - STRLCPY(prompt_cancel_dirty, state->prompt, MAXBUFLINE); - state->prompt_callback = cancel_callback; - return 0; - } - - state->show_edit = 0; - state->edit_info_focused = 0; - - for (ssize_t i = 0; i < state->input_rows_count; i++) - free_bufferline(state->input_buffer + i); - free(state->input_buffer); - state->input_buffer = NULL; - state->input_rows_count = 0; - if (state->info_buffer) - for (ssize_t i = 0; i < state->info_rows_count; i++) - free_bufferline(state->info_buffer + i); - free(state->info_buffer); - state->info_buffer = NULL; - state->info_rows_count = 0; - state->dirty = state->dirty_before_edit; - state->fuzzy_count = state->fuzzy_before_edit; - switch (state->input_changed_fuzzy) - { - case SET: - state->entries[state->msgid_number - 1].flags &= ~FL_FUZZY; - break; - case CLEARED: - state->entries[state->msgid_number - 1].flags |= FL_FUZZY; - break; - default:; - } - state->input_changed_fuzzy = UNCHANGED; - tb_hide_cursor(); + insert_search_char(state, (uint32_t)L'\t'); return 0; } static int -cancel_search(struct DrawState* state) +insert_space(struct DrawState* state) { - state->show_search = 0; - *state->search = 0; - tb_hide_cursor(); + insert_char(state, (uint32_t)L' '); return 0; } static int -copy_msgid_to_input(struct DrawState* state) +insert_tab(struct DrawState* state) { - struct PoEntry* current; - struct BufferLine* newbuf; - struct BufferLine* pbuffer; - const uint32_t* pmsgid; - ssize_t msgid_num_lines; - ssize_t lines_to_init; - ssize_t lines_to_free; - - if (state->edit_info_focused) - return 0; + insert_char(state, (uint32_t)L'\t'); + return 0; +} - if (!state->in_prompt && *state->input_buffer->text) - { - state->in_prompt = 1; - STRLCPY(prompt_overwrite, state->prompt, MAXBUFLINE); - state->prompt_callback = overwrite_copy_msgid_callback; - return 0; - } - - if (state->msgid_number == 1) - { - state->error = ERR_ILLEGAL_ON_FIRST; - return 0; - } - - current = &state->entries[state->msgid_number - 1]; - msgid_num_lines = u32_lines_in_string(current->msgid, 0, 0); - lines_to_init = state->input_rows_count < msgid_num_lines - ? msgid_num_lines - state->input_rows_count - : 0; - lines_to_free = state->input_rows_count > msgid_num_lines - ? state->input_rows_count - msgid_num_lines - : 0; - for (ssize_t i = 0; i < lines_to_free; i++) - free_bufferline( - state->input_buffer + state->input_rows_count - i - 1); - newbuf = realloc(state->input_buffer, - msgid_num_lines * sizeof(struct BufferLine)); - pmsgid = current->msgid; - - if (!newbuf) - return 0; - - pbuffer = NULL; - for (ssize_t i = 0; i < lines_to_init; i++) - { - uint32_t* newtext; - pbuffer = newbuf + msgid_num_lines - 1 - i; - init_bufferline(pbuffer); - newtext = calloc(MAXBUFLINE, sizeof(uint32_t)); - if (!newtext) - return 0; - pbuffer->text = newtext; - } - - pbuffer = newbuf; - while (pmsgid && *pmsgid) +static struct DrawState* +join_lines(struct DrawState* state, size_t first_line, int forward) +{ + state->coord.col = state->input_buffer[first_line].length; + state->input_display_column + = display_length(state->input_buffer[state->coord.row].text, + state->coord.col); + u32_strncat(state->input_buffer[first_line].text, + state->input_buffer[first_line + 1].text, MAXBUFLINE); + state->input_buffer[first_line].length + = u32_strlen(state->input_buffer[first_line].text); + if (!forward) { - size_t len = 0; - size_t newlen = 0; - const uint32_t* old_pmsgid = pmsgid; - len = u32_next_line(current->msgid, &pmsgid, 0, 0); - u32_strncpy(pbuffer->text, old_pmsgid, MIN(len + 1, MAXBUFLINE)); - newlen = u32_decode_tabs(pbuffer->text, MAXBUFLINE); - pbuffer->length = newlen; - pbuffer++; + state->coord.row--; + if (state->coord.row < 0) + state->coord.row = 0; } - - state->input_rows_count = msgid_num_lines; - state->input_buffer = newbuf; - state->dirty = 1; - - return 0; -} - -static int -edit_page_down(struct DrawState* state) -{ - if (state->edit_info_focused) + for (ssize_t i = first_line + 1; i < state->input_rows_count - 1; i++) { - if (state->info_first_shown.row + (EDIT_HEIGHT - 3) / 2 - < state->info_rows_count - 1) - state->info_first_shown.row += (EDIT_HEIGHT - 3) / 2; - return 0; + u32_strncpy(state->input_buffer[i].text, + state->input_buffer[i + 1].text, MAXBUFLINE); + state->input_buffer[i].length + = state->input_buffer[i + 1].length; } + free_bufferline(state->input_buffer + state->input_rows_count - 1); + state->input_rows_count--; + struct BufferLine* newbuf = realloc(state->input_buffer, + state->input_rows_count * sizeof(struct BufferLine)); + if (!newbuf) + return NULL; + state->input_buffer = newbuf; + state->dirty = 1; - if (state->dirty) - save_msgstr(state); - else /* No need to call cancel_msgstr */ - state->dirty = state->dirty_before_edit; - move_list_down(state); - state->msgstr_index = 0; - return show_edit(state); + return state; } -static int -edit_page_up(struct DrawState* state) +static struct BufferLine* +load_info(struct DrawState* state) { - if (state->edit_info_focused) - { - if (state->info_first_shown.row > (EDIT_HEIGHT - 3) / 2) - state->info_first_shown.row -= (EDIT_HEIGHT - 3) / 2; - else - move_start(state); - return 0; - } + struct PoEntry* first = &state->entries[0]; + struct PoEntry* current = &state->entries[state->msgid_number - 1]; + struct BufferLine* newbuf = NULL; + struct BufferLine* pnewbuf = NULL; + const uint32_t* pumsgid = NULL; + ssize_t msgid_num_lines = u32_lines_in_string(current->msgid, 0, 0); + ssize_t msgid_plural_num_lines + = u32_lines_in_string(current->msgid_plural, 0, 0); + ssize_t num_lines = 0; + ssize_t newlen = 0; - if (state->dirty) - save_msgstr(state); - else /* No need to call cancel_msgstr */ - state->dirty = state->dirty_before_edit; - move_list_up(state); - state->msgstr_index = 0; - return show_edit(state); -} + for (ssize_t i = 0; i < state->info_rows_count; i++) + free_bufferline(state->info_buffer + i); + free(state->info_buffer); -static int -erase_backwards(struct DrawState* state) -{ - struct BufferLine* buffer_line; - assert(state != NULL); - buffer_line = &state->input_buffer[state->coord.row]; - if (state->edit_info_focused) - return 1; + state->info_rows_maxlen = 0; - if (state->coord.col > 0) + if (current->msgid) + num_lines += msgid_num_lines + 1; + if (current->msgid_plural) { - ssize_t i = state->coord.col; - do - { - buffer_line->text[i - 1] = buffer_line->text[i]; - i++; - /* TODO: Use size? */ - } while (buffer_line->text[i]); - buffer_line->text[i - 1] = 0; - buffer_line->length--; - state->coord.col--; - if (state->input_first_shown.col > 0) - { - const struct BufferLine* current_line - = &state->input_buffer[state->coord.row]; - if (display_length(current_line->text, - current_line->length) - >= state->edit_width - 4) - state->input_first_shown.col--; - } - state->input_display_column - = display_length(state->input_buffer[state->coord.row] - .text, - state->coord.col); - if (state->input_display_column < state->input_first_shown.col) - state->input_first_shown.col - = state->input_display_column; - state->dirty = 1; + num_lines += msgid_plural_num_lines + 1; + if (first->plural_forms) + num_lines += 3; } - else if (state->coord.row > 0) + if (current->comments) + num_lines += current->comment_lines + 1; + if (current->extr_comments) + num_lines += current->extr_comment_lines + 1; + if (current->ref_comments) + num_lines += current->ref_comment_lines + 1; + newbuf = calloc(num_lines, sizeof(struct BufferLine)); + if (!newbuf) + return NULL; + + pnewbuf = newbuf; + for (ssize_t i = 0; i < num_lines; i++) { - move_up(state); - move_end(state); - erase_forward(state); + init_bufferline(pnewbuf); + uint32_t* newtext = calloc(MAXBUFLINE, sizeof(uint32_t)); + if (!newtext) + return NULL; + pnewbuf->text = newtext; + pnewbuf++; } - return 0; -} -static int -erase_forward(struct DrawState* state) -{ - struct BufferLine* buffer_line = &state->input_buffer[state->coord.row]; - if (state->edit_info_focused) - return 1; + state->info_rows_count = num_lines; - if (buffer_line->text[state->coord.col]) + pnewbuf = newbuf; + newlen = 0; + pumsgid = NULL; + if (current->msgid) { - size_t i = state->coord.col; - while (i < MAXBUFLINE - 2 && buffer_line->text[i + 1]) + pumsgid = current->msgid; + newlen = u32_u8_strncpy(pnewbuf->text, "msgid:", MAXBUFLINE); + pnewbuf->length = newlen; + if (newlen > state->info_rows_maxlen) + state->info_rows_maxlen = newlen; + pnewbuf->fg = colors[C_EDIT_MSGID_HEADING].fg; + pnewbuf->bg = colors[C_EDIT_MSGID_HEADING].bg; + pnewbuf++; + while (*pumsgid) { - buffer_line->text[i] = buffer_line->text[i + 1]; - i++; + size_t len = 0; + const uint32_t* old_pumsgid = pumsgid; + + len = u32_next_line(current->msgid, &pumsgid, 0, 0); + u32_strncpy(pnewbuf->text, old_pumsgid, + MIN(len + 1, MAXBUFLINE)); + newlen = u32_decode_tabs(pnewbuf->text, MAXBUFLINE); + pnewbuf->length = newlen; + if (newlen > state->info_rows_maxlen) + state->info_rows_maxlen = newlen; + pnewbuf++; } - buffer_line->text[i] = 0; - buffer_line->length--; - state->dirty = 1; } - else if (state->coord.row < state->input_rows_count - 1) + if (current->msgid_plural) { - if (!join_lines(state, state->coord.row, 1)) + newlen = u32_u8_strncpy(pnewbuf->text, + "msgid_plural:", MAXBUFLINE); + pnewbuf->length = newlen; + if (newlen > state->info_rows_maxlen) + state->info_rows_maxlen = newlen; + pnewbuf->fg = colors[C_EDIT_MSGID_HEADING].fg; + pnewbuf->bg = colors[C_EDIT_MSGID_HEADING].bg; + pnewbuf++; + pumsgid = current->msgid_plural; + while (*pumsgid) { - state->error = ERR_CANT_ALLOC; - state->running = 0; - } - } - return 0; -} - -static int -erase_prev_word(struct DrawState* state) -{ - uint32_t* pline; - ssize_t len; - int i; - int d; - - if (state->edit_info_focused) - return 1; - - i = state->coord.col; - len = state->input_buffer[state->coord.row].length; - pline = state->input_buffer[state->coord.row].text; - - if (i > 0 && u32_is_word_boundary(pline[i - 1], 0)) - i--; - while (i > 0 && u32_is_word_boundary(pline[i], 0)) - i--; - while (i > 0 && !u32_is_word_boundary(pline[i], 0) - && !u32_is_word_boundary(pline[i - 1], 0)) - i--; - - d = state->coord.col - i; - - for (ssize_t j = i; j < len - d; j++) - pline[j] = pline[j + d]; - - len -= d; - state->input_buffer[state->coord.row].length = len; - pline[len] = 0; - state->coord.col = i; - state->input_display_column - = display_length(state->input_buffer[state->coord.row].text, - state->coord.col); - if (state->input_display_column < state->input_first_shown.col) - state->input_first_shown.col = state->input_display_column; - state->dirty = 1; - return 0; -} + size_t len = 0; + const uint32_t* old_pumsgid = pumsgid; -static int -erase_search_backwards(struct DrawState* state) -{ - if (state->search_column > 0) - { - size_t i = state->search_column; - do - { - state->search[i - 1] = state->search[i]; - i++; - } while (state->search[i]); - state->search[i - 1] = 0; - state->search_column--; - if (state->search_column < state->search_first_shown_column) + len = u32_next_line(current->msgid_plural, &pumsgid, 0, + 0); + u32_strncpy(pnewbuf->text, old_pumsgid, + MIN(len + 1, MAXBUFLINE)); + newlen = u32_decode_tabs(pnewbuf->text, MAXBUFLINE); + pnewbuf->length = newlen; + if (newlen > state->info_rows_maxlen) + state->info_rows_maxlen = newlen; + pnewbuf++; + } + if (first->plural_forms) { - if (state->search_column > state->search_width - 4) - state->search_first_shown_column - = state->search_column - - state->search_width + 4; - else - state->search_first_shown_column = 0; + newlen = u32_u8_strncpy(pnewbuf->text, + "Plural forms:", MAXBUFLINE); + pnewbuf->length = newlen; + if (newlen > state->info_rows_maxlen) + state->info_rows_maxlen = newlen; + pnewbuf->fg = colors[C_EDIT_MSGID_HEADING].fg; + pnewbuf->bg = colors[C_EDIT_MSGID_HEADING].bg; + pnewbuf++; + newlen = u32_strncpy(pnewbuf->text, first->plural_forms, + MAXBUFLINE); + pnewbuf->length = newlen; + if (newlen > state->info_rows_maxlen) + state->info_rows_maxlen = newlen; + pnewbuf++; } - state->search_display_column - = display_length(state->search, state->search_column); - - return next_match(state); } - return 0; -} - -static int -erase_search_forward(struct DrawState* state) -{ - if (state->search[state->search_column]) + if (current->comments) { - size_t i = state->search_column; - while (i < MAXSEARCH - 2 && state->search[i + 1]) + newlen = u32_u8_strncpy(pnewbuf->text, "Comments:", MAXBUFLINE); + pnewbuf->length = newlen; + if (newlen > state->info_rows_maxlen) + state->info_rows_maxlen = newlen; + pnewbuf->fg = colors[C_EDIT_MSGID_HEADING].fg; + pnewbuf->bg = colors[C_EDIT_MSGID_HEADING].bg; + pnewbuf++; + for (size_t i = 0; i < current->comment_lines; i++) { - state->search[i] = state->search[i + 1]; - i++; + uint32_t* comment = current->comments[i]; + size_t len = u32_strlen(comment); + newlen = u32_strncpy(pnewbuf->text, comment, + MIN(len + 1, MAXBUFLINE)); + pnewbuf->length = newlen; + if (newlen > state->info_rows_maxlen) + state->info_rows_maxlen = newlen; + pnewbuf++; } - state->search[i] = 0; - - return next_match(state); } - return 0; -} - -static int -erase_search_prev_word(struct DrawState* state) -{ - uint32_t* pline; - ssize_t len; - int i; - - pline = state->search; - len = u32_strlen(state->search); - i = state->search_column; - if (i > 0 && u32_is_word_boundary(pline[i - 1], 0)) - i--; - while (i > 0 && u32_is_word_boundary(pline[i], 0)) - i--; - while (i > 0 && !u32_is_word_boundary(pline[i], 0) - && !u32_is_word_boundary(pline[i - 1], 0)) - i--; - - int d = state->search_column - i; - - for (int j = i; j < len - d; j++) - pline[j] = pline[j + d]; - - len -= state->search_column - i; - pline[len] = 0; - state->search_column = i; - state->search_display_column - = display_length(state->search, state->search_column); - - if (state->search_column < state->search_first_shown_column) - { - if (state->search_column > state->search_width - 4) - state->search_first_shown_column = state->search_column - - state->search_width + 4; - else - state->search_first_shown_column = 0; - } - state->search_display_column - = display_length(state->search, state->search_column); - - return next_match(state); -} - -static int -erase_search_to_end(struct DrawState* state) -{ - state->search[state->search_column] = 0; - move_search_end(state); - return next_match(state); -} - -static int -erase_search_to_start(struct DrawState* state) -{ - size_t i = state->search_column; - size_t len = u32_strlen(state->search); - do + if (current->extr_comments) { - state->search[i - state->search_column] = state->search[i]; - i++; - } while (state->search[i]); - state->search[len - state->search_column] = 0; - move_search_start(state); - return next_match(state); -} - -static int -erase_to_end(struct DrawState* state) -{ - struct BufferLine* buffer_line = NULL; + newlen = u32_u8_strncpy(pnewbuf->text, + "Translation comments:", MAXBUFLINE); + pnewbuf->length = newlen; + if (newlen > state->info_rows_maxlen) + state->info_rows_maxlen = newlen; + pnewbuf->fg = colors[C_EDIT_MSGID_HEADING].fg; + pnewbuf->bg = colors[C_EDIT_MSGID_HEADING].bg; + pnewbuf++; + for (size_t i = 0; i < current->extr_comment_lines; i++) + { + uint32_t* extr_comment = current->extr_comments[i]; + size_t len = u32_strlen(extr_comment); + newlen = u32_strncpy(pnewbuf->text, extr_comment, + MIN(len + 1, MAXBUFLINE)); + pnewbuf->length = newlen; + if (newlen > state->info_rows_maxlen) + state->info_rows_maxlen = newlen; + pnewbuf++; + } + } + if (current->ref_comments) + { + newlen = u32_u8_strncpy(pnewbuf->text, + "References:", MAXBUFLINE); + pnewbuf->length = newlen; + if (newlen > state->info_rows_maxlen) + state->info_rows_maxlen = newlen; + pnewbuf->fg = colors[C_EDIT_MSGID_HEADING].fg; + pnewbuf->bg = colors[C_EDIT_MSGID_HEADING].bg; + pnewbuf++; + for (size_t i = 0; i < current->ref_comment_lines; i++) + { + char* ref_comment = current->ref_comments[i]; + size_t len = strlen(ref_comment); + newlen = u32_u8_strncpy(pnewbuf->text, ref_comment, + MIN(len + 1, MAXBUFLINE)); + pnewbuf->length = newlen; + if (newlen > state->info_rows_maxlen) + state->info_rows_maxlen = newlen; + pnewbuf++; + } + } - if (state->edit_info_focused) - return 1; + state->info_buffer = newbuf; - buffer_line = &state->input_buffer[state->coord.row]; - buffer_line->text[state->coord.col] = 0; - buffer_line->length = state->coord.col; - move_end(state); - state->dirty = 1; - return 0; + return state->info_buffer; } -static int -erase_to_start(struct DrawState* state) +static struct BufferLine* +load_msgstr(struct DrawState* state) { - struct BufferLine* buffer_line; - ssize_t i; - ssize_t len; + struct PoEntry* current; + struct BufferLine* newbuf; + struct BufferLine* pnewbuf; + size_t num_lines; + int msgstr_index; - i = state->coord.col; - buffer_line = &state->input_buffer[state->coord.row]; - len = buffer_line->length; + assert((state != NULL) && (state->entries != NULL)); + current = &state->entries[state->msgid_number - 1]; + newbuf = NULL; + pnewbuf = NULL; + msgstr_index = state->msgstr_index; + num_lines = current->msgstr_count == 0 + ? 1 + : u32_lines_in_string(*(current->msgstr + msgstr_index), 0, 0); - if (state->edit_info_focused) - return 1; + for (ssize_t i = 0; i < state->input_rows_count; i++) + free_bufferline(state->input_buffer + i); + free(state->input_buffer); - do - { - buffer_line->text[i - state->coord.col] = buffer_line->text[i]; - i++; - } while (buffer_line->text[i]); - buffer_line->text[len - state->coord.col] = 0; - buffer_line->length = len - state->coord.col; - move_start(state); - state->dirty = 1; - return 0; -} + newbuf = calloc(num_lines, sizeof(struct BufferLine)); + if (!newbuf) + return NULL; -static int -exit_program(struct DrawState* state) -{ - if (state->show_edit) + pnewbuf = newbuf; + for (size_t i = 0; i < num_lines; i++) { - state->error = ERR_DLG_OPEN; - return 0; + init_bufferline(pnewbuf); + uint32_t* newtext = calloc(MAXBUFLINE, sizeof(uint32_t)); + if (!newtext) + return NULL; + pnewbuf->text = newtext; + pnewbuf++; } - if (state->dirty) + + state->input_rows_count = num_lines; + + if (current->msgstr_count > 0) { - state->in_prompt = 1; - STRLCPY(prompt_dirty, state->prompt, MAXBUFLINE); - state->prompt_callback = quit_callback; + const uint32_t* pumsgstr = *(current->msgstr + msgstr_index); + if (pumsgstr) + { + pnewbuf = newbuf; + while (*pumsgstr) + { + size_t len = 0; + size_t newlen = 0; + const uint32_t* old_pumsgstr = pumsgstr; + len = u32_next_line(*(current->msgstr + + msgstr_index), + &pumsgstr, 0, 0); + u32_strncpy(pnewbuf->text, old_pumsgstr, + MIN(len + 1, MAXBUFLINE)); + newlen = u32_decode_tabs(pnewbuf->text, + MAXBUFLINE); + pnewbuf->length = newlen; + pnewbuf++; + } + } + else + newbuf[0].length = 0; } else - state->running = 0; - //*state->error = 0; - return 0; -} - -static int -hide_search(struct DrawState* state) -{ - state->show_search = 0; - tb_hide_cursor(); - return 0; -} - -static int -insert_search_space(struct DrawState* state) -{ - insert_search_char(state, (uint32_t)L' '); - return 0; -} - -static int -insert_search_tab(struct DrawState* state) -{ - insert_search_char(state, (uint32_t)L'\t'); - return 0; -} - -static int -insert_space(struct DrawState* state) -{ - insert_char(state, (uint32_t)L' '); - return 0; -} + newbuf[0].length = 0; -static int -insert_tab(struct DrawState* state) -{ - insert_char(state, (uint32_t)L'\t'); - return 0; + state->input_buffer = newbuf; + return state->input_buffer; } static int @@ -1906,6 +1799,42 @@ next_untranslated(struct DrawState* state) return 0; } +static size_t +next_msgid(struct DrawState* state, const int delta) +{ + size_t ret = state->msgid_number; + size_t start = ret; + size_t last_non_obsolete + = state->real_msgid_count - state->obsolete_at_end_count; + + while (ret < last_non_obsolete + 1) + { + ret++; + if (!state->entries[ret - 1].obsolete && ret >= start + delta) + break; + } + + return ret == last_non_obsolete + 1 ? last_non_obsolete : ret; +} + +static int +overwrite_copy_msgid_callback(struct DrawState* state, struct tb_event* ev) +{ + if (ev->ch == 'y') + copy_msgid_to_input(state); + state->prompt_callback = NULL; + return 0; +} + +static int +overwrite_paste_callback(struct DrawState* state, struct tb_event* ev) +{ + if (ev->ch == 'y') + paste_paste_buffer_to_input(state); + state->prompt_callback = NULL; + return 0; +} + static int paste_paste_buffer_to_input(struct DrawState* state) { @@ -2018,6 +1947,60 @@ prev_match(struct DrawState* state) return 0; } +static size_t +prev_msgid(struct DrawState* state, const int delta) +{ + ssize_t ret = state->msgid_number; + ssize_t start = ret; + + if (start < delta) + return state->real_msgid_count ? 1 : 0; + + while (ret > 1) + { + ret--; + if (!state->entries[ret - 1].obsolete && ret <= start - delta) + break; + } + + return ret; +} + +int +print_error(const int code, const char* msg, ...) +{ + char buf[MAXBUFLINE]; + va_list args; + va_start(args, msg); + vsnprintf(buf, sizeof(buf), msg, args); + va_end(args); + fprintf(stderr, "%s: %s\n", PROGRAM_NAME, buf); + return code; +} + +static int +quit_callback(struct DrawState* state, struct tb_event* ev) +{ + if (ev->ch == 'y') + write_file(state); + state->prompt_callback = NULL; + state->running = 0; + return 0; +} + +void +redraw_status(struct DrawState* state) +{ + assert(state != NULL); + if (state->recalculate_size) + { + recalculate_size(state); + state->recalculate_size = 0; + } + (void)draw_status(state); + tb_present(); +} + static int prev_plural(struct DrawState* state) { diff --git a/poe.h b/poe.h @@ -8,7 +8,6 @@ static int cancel_msgstr(struct DrawState* state); static int cancel_search(struct DrawState* state); static int copy_msgid_to_input(struct DrawState* state); int cp(const char* from, const char* to); -//static int create_file(struct DrawState* state); static char* dirname(char* dir, char* path, const ssize_t dir_size); static int edit_page_down(struct DrawState* state); static int edit_page_up(struct DrawState* state);