/* gui-entry.c : irssi Copyright (C) 1999 Timo Sirainen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "module.h" #include #include #include #include #include #include #include #undef i_toupper #undef i_tolower #undef i_isalnum #define KILL_RING_MAX 10 static unichar i_toupper(unichar c) { if (term_type == TERM_TYPE_UTF8) return g_unichar_toupper(c); return c <= 255 ? toupper(c) : c; } static unichar i_tolower(unichar c) { if (term_type == TERM_TYPE_UTF8) return g_unichar_tolower(c); return c <= 255 ? tolower(c) : c; } static int i_isalnum(unichar c) { if (term_type == TERM_TYPE_UTF8) return (g_unichar_isalnum(c) || i_wcwidth(c) == 0); return c <= 255 ? isalnum(c) : 0; } GUI_ENTRY_REC *active_entry; static void entry_text_grow(GUI_ENTRY_REC *entry, int grow_size) { if (entry->text_len+grow_size < entry->text_alloc) return; entry->text_alloc = nearest_power(entry->text_alloc+grow_size); entry->text = g_realloc(entry->text, sizeof(unichar) * entry->text_alloc); if (entry->uses_extents) entry->extents = g_realloc(entry->extents, sizeof(char *) * entry->text_alloc); } GUI_ENTRY_REC *gui_entry_create(int xpos, int ypos, int width, int utf8) { GUI_ENTRY_REC *rec; rec = g_new0(GUI_ENTRY_REC, 1); rec->xpos = xpos; rec->ypos = ypos; rec->width = width; rec->text_alloc = 1024; rec->text = g_new(unichar, rec->text_alloc); rec->extents = NULL; rec->text[0] = '\0'; rec->utf8 = utf8; return rec; } static void destroy_extents(GUI_ENTRY_REC *entry) { if (entry->uses_extents) { int i; for (i = 0; i < entry->text_alloc; i++) { if (entry->extents[i] != NULL) { g_free(entry->extents[i]); } } } g_free(entry->extents); entry->extents = NULL; entry->uses_extents = FALSE; } void gui_entry_destroy(GUI_ENTRY_REC *entry) { GSList *tmp; g_return_if_fail(entry != NULL); if (active_entry == entry) gui_entry_set_active(NULL); for (tmp = entry->kill_ring; tmp != NULL; tmp = tmp->next) { GUI_ENTRY_CUTBUFFER_REC *rec = tmp->data; if (rec != NULL) { g_free(rec->cutbuffer); g_free(rec); } } g_slist_free(entry->kill_ring); destroy_extents(entry); g_free(entry->text); g_free(entry->prompt); g_free(entry); } /* big5 functions */ #define big5_width(ch) ((ch)>0xff ? 2:1) void unichars_to_big5(const unichar *str, char *out) { for (; *str != '\0'; str++) { if (*str > 0xff) *out++ = (*str >> 8) & 0xff; *out++ = *str & 0xff; } *out = '\0'; } int strlen_big5(const unsigned char *str) { int len=0; while (*str != '\0') { if (is_big5(str[0], str[1])) str++; len++; str++; } return len; } void unichars_to_big5_with_pos(const unichar *str, int spos, char *out, int *opos) { const unichar *sstart = str; char *ostart = out; *opos = 0; while(*str != '\0') { if(*str > 0xff) *out ++ = (*str >> 8) & 0xff; *out ++ = *str & 0xff; str ++; if(str - sstart == spos) *opos = out - ostart; } *out = '\0'; } void big5_to_unichars(const char *str, unichar *out) { const unsigned char *p = (const unsigned char *) str; while (*p != '\0') { if (is_big5(p[0], p[1])) { *out++ = p[0] << 8 | p[1]; p += 2; } else { *out++ = *p++; } } *out = '\0'; } /* Return screen length of plain string */ static int scrlen_str(const char *str, int utf8) { int len = 0; char *stripped; g_return_val_if_fail(str != NULL, 0); stripped = strip_codes(str); len = string_width(stripped, utf8 ? TREAT_STRING_AS_UTF8 : TREAT_STRING_AS_BYTES); g_free(stripped); return len; } /* ----------------------------- */ static int pos2scrpos(GUI_ENTRY_REC *entry, int pos, int cursor) { int i; int xpos = 0; if (!cursor && pos <= 0) return 0; if (entry->uses_extents && entry->extents[0] != NULL) { xpos += scrlen_str(entry->extents[0], entry->utf8); } for (i = 0; i < entry->text_len && i < pos; i++) { unichar c = entry->text[i]; const char *extent = entry->uses_extents ? entry->extents[i+1] : NULL; if (term_type == TERM_TYPE_BIG5) xpos += big5_width(c); else if (entry->utf8) xpos += unichar_isprint(c) ? i_wcwidth(c) : 1; else xpos++; if (extent != NULL) { xpos += scrlen_str(extent, entry->utf8); } } return xpos + pos - i; } static int scrpos2pos(GUI_ENTRY_REC *entry, int pos) { int i, width, xpos = 0; if (entry->uses_extents && entry->extents[0] != NULL) { xpos += scrlen_str(entry->extents[0], entry->utf8); } for (i = 0; i < entry->text_len && xpos < pos; i++) { unichar c = entry->text[i]; const char *extent = entry->uses_extents ? entry->extents[i+1] : NULL; if (term_type == TERM_TYPE_BIG5) width = big5_width(c); else if (entry->utf8) width = unichar_isprint(c) ? i_wcwidth(c) : 1; else width = 1; xpos += width; if (extent != NULL) { xpos += scrlen_str(extent, entry->utf8); } } return i; } /* Fixes the cursor position in screen */ static void gui_entry_fix_cursor(GUI_ENTRY_REC *entry) { int old_scrstart; /* assume prompt len == prompt scrlen */ int start = pos2scrpos(entry, entry->scrstart, FALSE); int now = pos2scrpos(entry, entry->pos, TRUE); old_scrstart = entry->scrstart; if (now-start < entry->width - 2 - entry->promptlen && now-start > 0) { entry->scrpos = now-start; } else if (now < entry->width - 1 - entry->promptlen) { entry->scrstart = 0; entry->scrpos = now; } else { entry->scrstart = scrpos2pos(entry, now-(entry->width - entry->promptlen)*2/3); start = pos2scrpos(entry, entry->scrstart, FALSE); entry->scrpos = now - start; } if (old_scrstart != entry->scrstart) entry->redraw_needed_from = 0; } static char *text_effects_only(const char *p) { GString *str; str = g_string_sized_new(strlen(p)); for (; *p != '\0'; p++) { if (*p == 4 && p[1] != '\0') { if (p[1] >= FORMAT_STYLE_SPECIAL) { g_string_append_len(str, p, 2); p++; continue; } /* irssi color */ if (p[2] != '\0') { #ifdef TERM_TRUECOLOR if (p[1] == FORMAT_COLOR_24) { if (p[3] == '\0') p += 2; else if (p[4] == '\0') p += 3; else if (p[5] == '\0') p += 4; else { g_string_append_len(str, p, 6); p += 5; } } else { #endif /* TERM_TRUECOLOR */ g_string_append_len(str, p, 3); p += 2; #ifdef TERM_TRUECOLOR } #endif /* TERM_TRUECOLOR */ continue; } } } return g_string_free(str, FALSE); } static void gui_entry_draw_from(GUI_ENTRY_REC *entry, int pos) { int i, start; int start_xpos, xpos, new_xpos, end_xpos; char *tmp; GString *str; start = entry->scrstart + pos; start_xpos = xpos = entry->xpos + entry->promptlen + pos2scrpos(entry, start, FALSE) - pos2scrpos(entry, entry->scrstart, FALSE); end_xpos = entry->xpos + entry->width; if (xpos > end_xpos) return; str = g_string_sized_new(entry->text_alloc); term_set_color(root_window, ATTR_RESET); /* term_move(root_window, xpos, entry->ypos); */ if (entry->uses_extents && entry->extents[0] != NULL) { g_string_append(str, entry->extents[0]); } for (i = 0; i < start && i < entry->text_len; i++) { const char *extent = entry->uses_extents ? entry->extents[i+1] : NULL; if (extent != NULL) { g_string_append(str, extent); } } if (i == 0) { xpos += scrlen_str(str->str, entry->utf8); } else { tmp = text_effects_only(str->str); g_string_assign(str, tmp); g_free(tmp); } for (; i < entry->text_len; i++) { unichar c = entry->text[i]; const char *extent = entry->uses_extents ? entry->extents[i+1] : NULL; new_xpos = xpos; if (entry->hidden) new_xpos++; else if (term_type == TERM_TYPE_BIG5) new_xpos += big5_width(c); else if (entry->utf8) new_xpos += unichar_isprint(c) ? i_wcwidth(c) : 1; else new_xpos++; if (new_xpos > end_xpos) break; if (entry->hidden) { g_string_append_c(str, ' '); } else if (unichar_isprint(c)) { if (entry->utf8) { g_string_append_unichar(str, c); } else if (term_type == TERM_TYPE_BIG5) { if(c > 0xff) g_string_append_c(str, (c >> 8) & 0xff); g_string_append_c(str, c & 0xff); } else { g_string_append_c(str, c); } } else { g_string_append_c(str, 4); g_string_append_c(str, FORMAT_STYLE_REVERSE); g_string_append_c(str, (c & 127)+'A'-1); g_string_append_c(str, 4); g_string_append_c(str, FORMAT_STYLE_REVERSE); } xpos = new_xpos; if (extent != NULL) { new_xpos += scrlen_str(extent, entry->utf8); if (new_xpos > end_xpos) break; g_string_append(str, extent); xpos = new_xpos; } } /* clear the rest of the input line */ if (xpos < end_xpos) { if (end_xpos == term_width) { g_string_append_c(str, 4); g_string_append_c(str, FORMAT_STYLE_CLRTOEOL); } else { while (xpos < end_xpos) { g_string_append_c(str, ' '); xpos++; } } } gui_printtext_internal(start_xpos, entry->ypos, str->str); g_string_free(str, TRUE); } static void gui_entry_draw(GUI_ENTRY_REC *entry) { if (entry->redraw_needed_from >= 0) { gui_entry_draw_from(entry, entry->redraw_needed_from); entry->redraw_needed_from = -1; } term_move_cursor(entry->xpos + entry->scrpos + entry->promptlen, entry->ypos); term_refresh(NULL); } static void gui_entry_redraw_from(GUI_ENTRY_REC *entry, int pos) { pos -= entry->scrstart; if (pos < 0) pos = 0; if (entry->redraw_needed_from == -1 || entry->redraw_needed_from > pos) entry->redraw_needed_from = pos; } void gui_entry_move(GUI_ENTRY_REC *entry, int xpos, int ypos, int width) { int old_width; g_return_if_fail(entry != NULL); if (entry->xpos != xpos || entry->ypos != ypos) { /* position in screen changed - needs a full redraw */ entry->xpos = xpos; entry->ypos = ypos; entry->width = width; gui_entry_redraw(entry); return; } if (entry->width == width) return; /* no changes */ if (width > entry->width) { /* input line grew - need to draw text at the end */ old_width = width; entry->width = width; gui_entry_redraw_from(entry, old_width); } else { /* input line shrinked - make sure the cursor is inside the input line */ entry->width = width; if (entry->pos - entry->scrstart > entry->width-2 - entry->promptlen) { gui_entry_fix_cursor(entry); } } gui_entry_draw(entry); } void gui_entry_set_active(GUI_ENTRY_REC *entry) { active_entry = entry; if (entry != NULL) { term_move_cursor(entry->xpos + entry->scrpos + entry->promptlen, entry->ypos); term_refresh(NULL); } } void gui_entry_set_prompt(GUI_ENTRY_REC *entry, const char *str) { int oldlen; g_return_if_fail(entry != NULL); oldlen = entry->promptlen; if (str != NULL) { g_free_not_null(entry->prompt); entry->prompt = g_strdup(str); entry->promptlen = scrlen_str(str, entry->utf8); } if (entry->prompt != NULL) gui_printtext_internal(entry->xpos, entry->ypos, entry->prompt); if (entry->promptlen != oldlen) { gui_entry_fix_cursor(entry); gui_entry_draw(entry); } } void gui_entry_set_hidden(GUI_ENTRY_REC *entry, int hidden) { g_return_if_fail(entry != NULL); entry->hidden = hidden; } void gui_entry_set_utf8(GUI_ENTRY_REC *entry, int utf8) { g_return_if_fail(entry != NULL); entry->utf8 = utf8; } void gui_entry_set_text(GUI_ENTRY_REC *entry, const char *str) { g_return_if_fail(entry != NULL); g_return_if_fail(str != NULL); entry->text_len = 0; entry->pos = 0; entry->text[0] = '\0'; destroy_extents(entry); gui_entry_insert_text(entry, str); } char *gui_entry_get_text(GUI_ENTRY_REC *entry) { char *buf; int i; g_return_val_if_fail(entry != NULL, NULL); if (entry->utf8) buf = g_ucs4_to_utf8(entry->text, -1, NULL, NULL, NULL); else { buf = g_malloc(entry->text_len*6 + 1); if (term_type == TERM_TYPE_BIG5) unichars_to_big5(entry->text, buf); else for (i = 0; i <= entry->text_len; i++) buf[i] = entry->text[i]; } return buf; } char *gui_entry_get_text_and_pos(GUI_ENTRY_REC *entry, int *pos) { char *buf; int i; g_return_val_if_fail(entry != NULL, NULL); if (entry->utf8) { buf = g_ucs4_to_utf8(entry->text, -1, NULL, NULL, NULL); *pos = g_utf8_offset_to_pointer(buf, entry->pos) - buf; } else { buf = g_malloc(entry->text_len*6 + 1); if(term_type==TERM_TYPE_BIG5) unichars_to_big5_with_pos(entry->text, entry->pos, buf, pos); else { for (i = 0; i <= entry->text_len; i++) buf[i] = entry->text[i]; *pos = entry->pos; } } return buf; } void gui_entry_insert_text(GUI_ENTRY_REC *entry, const char *str) { unichar chr; int i, len; const char *ptr; g_return_if_fail(entry != NULL); g_return_if_fail(str != NULL); gui_entry_redraw_from(entry, entry->pos); if (entry->utf8) { g_utf8_validate(str, -1, &ptr); len = g_utf8_pointer_to_offset(str, ptr); } else if (term_type == TERM_TYPE_BIG5) len = strlen_big5((const unsigned char *)str); else len = strlen(str); entry_text_grow(entry, len); /* make space for the string */ memmove(entry->text + entry->pos + len, entry->text + entry->pos, (entry->text_len-entry->pos + 1) * sizeof(unichar)); /* make space for the color */ if (entry->uses_extents) { memmove(entry->extents + entry->pos + len + 1, entry->extents + entry->pos + 1, (entry->text_len-entry->pos) * sizeof(char *)); for (i = 0; i < len; i++) { entry->extents[entry->pos + i + 1] = NULL; } } if (!entry->utf8) { if (term_type == TERM_TYPE_BIG5) { chr = entry->text[entry->pos + len]; big5_to_unichars(str, entry->text + entry->pos); entry->text[entry->pos + len] = chr; } else { for (i = 0; i < len; i++) entry->text[entry->pos + i] = str[i]; } } else { ptr = str; for (i = 0; i < len; i++) { entry->text[entry->pos + i] = g_utf8_get_char(ptr); ptr = g_utf8_next_char(ptr); } } entry->text_len += len; entry->pos += len; gui_entry_fix_cursor(entry); gui_entry_draw(entry); } void gui_entry_insert_char(GUI_ENTRY_REC *entry, unichar chr) { g_return_if_fail(entry != NULL); if (chr == 0 || chr == 13 || chr == 10) return; /* never insert NUL, CR or LF characters */ if (entry->utf8 && entry->pos == 0 && i_wcwidth(chr) == 0) return; gui_entry_redraw_from(entry, entry->pos); entry_text_grow(entry, 1); /* make space for the string */ memmove(entry->text + entry->pos + 1, entry->text + entry->pos, (entry->text_len-entry->pos + 1) * sizeof(unichar)); if (entry->uses_extents) { memmove(entry->extents + entry->pos + 1 + 1, entry->extents + entry->pos + 1, (entry->text_len-entry->pos) * sizeof(char *)); entry->extents[entry->pos + 1] = NULL; } entry->text[entry->pos] = chr; entry->text_len++; entry->pos++; gui_entry_fix_cursor(entry); gui_entry_draw(entry); } char *gui_entry_get_cutbuffer(GUI_ENTRY_REC *entry) { GUI_ENTRY_CUTBUFFER_REC *tmp; char *buf; int i; g_return_val_if_fail(entry != NULL, NULL); if (entry->kill_ring == NULL || entry->kill_ring->data == NULL) return NULL; tmp = entry->kill_ring->data; if (tmp->cutbuffer == NULL) return NULL; if (entry->utf8) buf = g_ucs4_to_utf8(tmp->cutbuffer, -1, NULL, NULL, NULL); else { buf = g_malloc(tmp->cutbuffer_len*6 + 1); if (term_type == TERM_TYPE_BIG5) unichars_to_big5(tmp->cutbuffer, buf); else for (i = 0; i <= tmp->cutbuffer_len; i++) buf[i] = tmp->cutbuffer[i]; } return buf; } char *gui_entry_get_next_cutbuffer(GUI_ENTRY_REC *entry) { GUI_ENTRY_CUTBUFFER_REC *tmp; g_return_val_if_fail(entry != NULL, NULL); if (entry->kill_ring == NULL) return NULL; tmp = entry->kill_ring->data; entry->kill_ring = g_slist_remove(entry->kill_ring, tmp); entry->kill_ring = g_slist_append(entry->kill_ring, tmp); return gui_entry_get_cutbuffer(entry); } void gui_entry_erase_to(GUI_ENTRY_REC *entry, int pos, CUTBUFFER_UPDATE_OP update_cutbuffer) { int newpos, size = 0; g_return_if_fail(entry != NULL); for (newpos = gui_entry_get_pos(entry); newpos > pos; size++) newpos = newpos - 1; gui_entry_erase(entry, size, update_cutbuffer); } static GUI_ENTRY_CUTBUFFER_REC *get_cutbuffer_rec(GUI_ENTRY_REC *entry, CUTBUFFER_UPDATE_OP update_cutbuffer) { GUI_ENTRY_CUTBUFFER_REC *tmp; g_return_val_if_fail(entry != NULL, NULL); if (entry->kill_ring == NULL) { /* no kill ring exists */ entry->kill_ring = g_slist_prepend(entry->kill_ring, (void *)NULL); } else { tmp = entry->kill_ring->data; if (tmp != NULL && tmp->cutbuffer_len > 0 && (!entry->previous_append_next_kill || update_cutbuffer == CUTBUFFER_UPDATE_REPLACE)) { /* a cutbuffer exists and should be replaced */ entry->kill_ring = g_slist_prepend(entry->kill_ring, (void *)NULL); } } if (g_slist_length(entry->kill_ring) > KILL_RING_MAX) { GUI_ENTRY_CUTBUFFER_REC *rec = g_slist_last(entry->kill_ring)->data; entry->kill_ring = g_slist_remove(entry->kill_ring, rec); if (rec != NULL) g_free(rec->cutbuffer); g_free(rec); } if (entry->kill_ring->data == NULL) { entry->kill_ring->data = g_new0(GUI_ENTRY_CUTBUFFER_REC, 1); } return entry->kill_ring->data; } void gui_entry_erase(GUI_ENTRY_REC *entry, int size, CUTBUFFER_UPDATE_OP update_cutbuffer) { size_t i, w = 0; g_return_if_fail(entry != NULL); if (size == 0 || entry->pos < size) return; if (update_cutbuffer != CUTBUFFER_UPDATE_NOOP) { int cutbuffer_new_size; unichar *tmpcutbuffer; GUI_ENTRY_CUTBUFFER_REC *tmp = get_cutbuffer_rec(entry, update_cutbuffer); if (tmp->cutbuffer_len == 0) { update_cutbuffer = CUTBUFFER_UPDATE_REPLACE; } cutbuffer_new_size = tmp->cutbuffer_len + size; tmpcutbuffer = tmp->cutbuffer; entry->append_next_kill = TRUE; switch (update_cutbuffer) { case CUTBUFFER_UPDATE_APPEND: tmp->cutbuffer = g_new(unichar, cutbuffer_new_size+1); memcpy(tmp->cutbuffer, tmpcutbuffer, tmp->cutbuffer_len * sizeof(unichar)); memcpy(tmp->cutbuffer + tmp->cutbuffer_len, entry->text + entry->pos - size, size * sizeof(unichar)); tmp->cutbuffer_len = cutbuffer_new_size; tmp->cutbuffer[cutbuffer_new_size] = '\0'; g_free(tmpcutbuffer); break; case CUTBUFFER_UPDATE_PREPEND: tmp->cutbuffer = g_new(unichar, cutbuffer_new_size+1); memcpy(tmp->cutbuffer, entry->text + entry->pos - size, size * sizeof(unichar)); memcpy(tmp->cutbuffer + size, tmpcutbuffer, tmp->cutbuffer_len * sizeof(unichar)); tmp->cutbuffer_len = cutbuffer_new_size; tmp->cutbuffer[cutbuffer_new_size] = '\0'; g_free(tmpcutbuffer); break; case CUTBUFFER_UPDATE_REPLACE: /* put erased text to cutbuffer */ if (tmp->cutbuffer_len < size) { g_free(tmp->cutbuffer); tmp->cutbuffer = g_new(unichar, size+1); } tmp->cutbuffer_len = size; tmp->cutbuffer[size] = '\0'; memcpy(tmp->cutbuffer, entry->text + entry->pos - size, size * sizeof(unichar)); break; case CUTBUFFER_UPDATE_NOOP: /* cannot happen, handled in "if" */ break; } } if (entry->utf8) while (entry->pos-size-w > 0 && i_wcwidth(entry->text[entry->pos-size-w]) == 0) w++; memmove(entry->text + entry->pos - size, entry->text + entry->pos, (entry->text_len-entry->pos+1) * sizeof(unichar)); if (entry->uses_extents) { for (i = entry->pos - size; i < entry->pos; i++) { if (entry->extents[i+1] != NULL) { g_free(entry->extents[i+1]); } } memmove(entry->extents + entry->pos - size + 1, entry->extents + entry->pos + 1, (entry->text_len - entry->pos) * sizeof(void *)); /* no null terminator here */ for (i = 0; i < size; i++) { entry->extents[entry->text_len - i] = NULL; } if (entry->text_len == size && entry->extents[0] != NULL) { g_free(entry->extents[0]); entry->extents[0] = NULL; } } entry->pos -= size; entry->text_len -= size; gui_entry_redraw_from(entry, entry->pos-w); gui_entry_fix_cursor(entry); gui_entry_draw(entry); } void gui_entry_erase_cell(GUI_ENTRY_REC *entry) { int size = 1; g_return_if_fail(entry != NULL); if (entry->utf8) while (entry->pos+size < entry->text_len && i_wcwidth(entry->text[entry->pos+size]) == 0) size++; memmove(entry->text + entry->pos, entry->text + entry->pos + size, (entry->text_len-entry->pos-size+1) * sizeof(unichar)); if (entry->uses_extents) { int i; for (i = 0; i < size; i++) { g_free(entry->extents[entry->pos + i + 1]); } memmove(entry->extents + entry->pos + 1, entry->extents + entry->pos + size + 1, (entry->text_len-entry->pos-size) * sizeof(char *)); for (i = 0; i < size; i++) { entry->extents[entry->text_len - i] = NULL; } if (entry->text_len == size && entry->extents[0] != NULL) { g_free(entry->extents[0]); entry->extents[0] = NULL; } } entry->text_len -= size; gui_entry_redraw_from(entry, entry->pos); gui_entry_fix_cursor(entry); gui_entry_draw(entry); } void gui_entry_erase_word(GUI_ENTRY_REC *entry, int to_space, CUTBUFFER_UPDATE_OP cutbuffer_op) { int to; g_return_if_fail(entry != NULL); if (entry->pos == 0) return; to = entry->pos - 1; if (to_space) { while (entry->text[to] == ' ' && to > 0) to--; while (entry->text[to] != ' ' && to > 0) to--; } else { while (!i_isalnum(entry->text[to]) && to > 0) to--; while (i_isalnum(entry->text[to]) && to > 0) to--; } if (to > 0) to++; gui_entry_erase(entry, entry->pos-to, cutbuffer_op); } void gui_entry_erase_next_word(GUI_ENTRY_REC *entry, int to_space, CUTBUFFER_UPDATE_OP cutbuffer_op) { int to, size; g_return_if_fail(entry != NULL); if (entry->pos == entry->text_len) return; to = entry->pos; if (to_space) { while (entry->text[to] == ' ' && to < entry->text_len) to++; while (entry->text[to] != ' ' && to < entry->text_len) to++; } else { while (!i_isalnum(entry->text[to]) && to < entry->text_len) to++; while (i_isalnum(entry->text[to]) && to < entry->text_len) to++; } size = to-entry->pos; entry->pos = to; gui_entry_erase(entry, size, cutbuffer_op); } void gui_entry_transpose_chars(GUI_ENTRY_REC *entry) { unichar chr; char *extent; if (entry->pos == 0 || entry->text_len < 2) return; if (entry->pos == entry->text_len) entry->pos--; /* swap chars */ chr = entry->text[entry->pos]; entry->text[entry->pos] = entry->text[entry->pos-1]; entry->text[entry->pos-1] = chr; if (entry->uses_extents) { extent = entry->extents[entry->pos+1]; entry->extents[entry->pos+1] = entry->extents[entry->pos]; entry->extents[entry->pos] = extent; } entry->pos++; gui_entry_redraw_from(entry, entry->pos-2); gui_entry_fix_cursor(entry); gui_entry_draw(entry); } void gui_entry_transpose_words(GUI_ENTRY_REC *entry) { int spos1, epos1, spos2, epos2; /* find last position */ epos2 = entry->pos; while (epos2 < entry->text_len && !i_isalnum(entry->text[epos2])) epos2++; while (epos2 < entry->text_len && i_isalnum(entry->text[epos2])) epos2++; /* find other position */ spos2 = epos2; while (spos2 > 0 && !i_isalnum(entry->text[spos2-1])) spos2--; while (spos2 > 0 && i_isalnum(entry->text[spos2-1])) spos2--; epos1 = spos2; while (epos1 > 0 && !i_isalnum(entry->text[epos1-1])) epos1--; spos1 = epos1; while (spos1 > 0 && i_isalnum(entry->text[spos1-1])) spos1--; /* do wordswap if any found */ if (spos1 < epos1 && epos1 < spos2 && spos2 < epos2) { unichar *first, *sep, *second; char **first_extent, **sep_extent, **second_extent; int i; first = (unichar *) g_malloc( (epos1 - spos1) * sizeof(unichar) ); sep = (unichar *) g_malloc( (spos2 - epos1) * sizeof(unichar) ); second = (unichar *) g_malloc( (epos2 - spos2) * sizeof(unichar) ); first_extent = (char **) g_malloc( (epos1 - spos1) * sizeof(char *) ); sep_extent = (char **) g_malloc( (spos2 - epos1) * sizeof(char *) ); second_extent = (char **) g_malloc( (epos2 - spos2) * sizeof(char *) ); for (i = spos1; i < epos1; i++) { first[i-spos1] = entry->text[i]; if (entry->uses_extents) first_extent[i-spos1] = entry->extents[i+1]; } for (i = epos1; i < spos2; i++) { sep[i-epos1] = entry->text[i]; if (entry->uses_extents) sep_extent[i-epos1] = entry->extents[i+1]; } for (i = spos2; i < epos2; i++) { second[i-spos2] = entry->text[i]; if (entry->uses_extents) second_extent[i-spos2] = entry->extents[i+1]; } entry->pos = spos1; for (i = 0; i < epos2-spos2; i++) { entry->text[entry->pos] = second[i]; if (entry->uses_extents) entry->extents[entry->pos+1] = second_extent[i]; entry->pos++; } for (i = 0; i < spos2-epos1; i++) { entry->text[entry->pos] = sep[i]; if (entry->uses_extents) entry->extents[entry->pos+1] = sep_extent[i]; entry->pos++; } for (i = 0; i < epos1-spos1; i++) { entry->text[entry->pos] = first[i]; if (entry->uses_extents) entry->extents[entry->pos+1] = first_extent[i]; entry->pos++; } g_free(first); g_free(sep); g_free(second); g_free(first_extent); g_free(sep_extent); g_free(second_extent); } gui_entry_redraw_from(entry, spos1); gui_entry_fix_cursor(entry); gui_entry_draw(entry); } void gui_entry_capitalize_word(GUI_ENTRY_REC *entry) { int pos = entry->pos; while (pos < entry->text_len && !i_isalnum(entry->text[pos])) pos++; if (pos < entry->text_len) { entry->text[pos] = i_toupper(entry->text[pos]); pos++; } while (pos < entry->text_len && i_isalnum(entry->text[pos])) { entry->text[pos] = i_tolower(entry->text[pos]); pos++; } gui_entry_redraw_from(entry, entry->pos); entry->pos = pos; gui_entry_fix_cursor(entry); gui_entry_draw(entry); } void gui_entry_downcase_word(GUI_ENTRY_REC *entry) { int pos = entry->pos; while (pos < entry->text_len && !i_isalnum(entry->text[pos])) pos++; while (pos < entry->text_len && i_isalnum(entry->text[pos])) { entry->text[pos] = i_tolower(entry->text[pos]); pos++; } gui_entry_redraw_from(entry, entry->pos); entry->pos = pos; gui_entry_fix_cursor(entry); gui_entry_draw(entry); } void gui_entry_upcase_word(GUI_ENTRY_REC *entry) { int pos = entry->pos; while (pos < entry->text_len && !i_isalnum(entry->text[pos])) pos++; while (pos < entry->text_len && i_isalnum(entry->text[pos])) { entry->text[pos] = i_toupper(entry->text[pos]); pos++; } gui_entry_redraw_from(entry, entry->pos); entry->pos = pos; gui_entry_fix_cursor(entry); gui_entry_draw(entry); } int gui_entry_get_pos(GUI_ENTRY_REC *entry) { g_return_val_if_fail(entry != NULL, 0); return entry->pos; } void gui_entry_set_pos(GUI_ENTRY_REC *entry, int pos) { g_return_if_fail(entry != NULL); if (pos >= 0 && pos <= entry->text_len) entry->pos = pos; gui_entry_fix_cursor(entry); gui_entry_draw(entry); } void gui_entry_set_text_and_pos_bytes(GUI_ENTRY_REC *entry, const char *str, int pos_bytes) { int pos, extents_alloc; char **extents; const char *ptr; g_return_if_fail(entry != NULL); extents = entry->extents; extents_alloc = entry->text_alloc; entry->extents = NULL; entry->uses_extents = FALSE; gui_entry_set_text(entry, str); if (entry->utf8) { g_utf8_validate(str, pos_bytes, &ptr); pos = g_utf8_pointer_to_offset(str, ptr); } else if (term_type == TERM_TYPE_BIG5) pos = strlen_big5((const unsigned char *)str) - strlen_big5((const unsigned char *)(str + pos_bytes)); else pos = pos_bytes; if (extents != NULL) { entry->uses_extents = TRUE; entry->extents = extents; if (extents_alloc < entry->text_alloc) { int i; entry->extents = g_realloc(entry->extents, sizeof(char *) * entry->text_alloc); for (i = extents_alloc; i < entry->text_alloc; i++) { entry->extents[i] = NULL; } } } gui_entry_redraw_from(entry, 0); gui_entry_set_pos(entry, pos); } void gui_entry_move_pos(GUI_ENTRY_REC *entry, int pos) { g_return_if_fail(entry != NULL); if (entry->pos + pos >= 0 && entry->pos + pos <= entry->text_len) entry->pos += pos; if (entry->utf8) { int step = pos < 0 ? -1 : 1; while(i_wcwidth(entry->text[entry->pos]) == 0 && entry->pos + step >= 0 && entry->pos + step <= entry->text_len) entry->pos += step; } gui_entry_fix_cursor(entry); gui_entry_draw(entry); } static void gui_entry_move_words_left(GUI_ENTRY_REC *entry, int count, int to_space) { int pos; pos = entry->pos; while (count > 0 && pos > 0) { if (to_space) { while (pos > 0 && entry->text[pos-1] == ' ') pos--; while (pos > 0 && entry->text[pos-1] != ' ') pos--; } else { while (pos > 0 && !i_isalnum(entry->text[pos-1])) pos--; while (pos > 0 && i_isalnum(entry->text[pos-1])) pos--; } count--; } entry->pos = pos; } static void gui_entry_move_words_right(GUI_ENTRY_REC *entry, int count, int to_space) { int pos; pos = entry->pos; while (count > 0 && pos < entry->text_len) { if (to_space) { while (pos < entry->text_len && entry->text[pos] == ' ') pos++; while (pos < entry->text_len && entry->text[pos] != ' ') pos++; } else { while (pos < entry->text_len && !i_isalnum(entry->text[pos])) pos++; while (pos < entry->text_len && i_isalnum(entry->text[pos])) pos++; } count--; } entry->pos = pos; } void gui_entry_move_words(GUI_ENTRY_REC *entry, int count, int to_space) { g_return_if_fail(entry != NULL); if (count < 0) gui_entry_move_words_left(entry, -count, to_space); else if (count > 0) gui_entry_move_words_right(entry, count, to_space); gui_entry_fix_cursor(entry); gui_entry_draw(entry); } void gui_entry_redraw(GUI_ENTRY_REC *entry) { g_return_if_fail(entry != NULL); gui_entry_set_prompt(entry, NULL); gui_entry_redraw_from(entry, 0); gui_entry_fix_cursor(entry); gui_entry_draw(entry); } static void gui_entry_alloc_extents(GUI_ENTRY_REC *entry) { entry->uses_extents = TRUE; entry->extents = g_new0(char *, entry->text_alloc); } void gui_entry_set_extent(GUI_ENTRY_REC *entry, int pos, const char *text) { int update = FALSE; g_return_if_fail(entry != NULL); if (pos < 0 || pos > entry->text_len) return; if (text == NULL) return; if (!entry->uses_extents) { gui_entry_alloc_extents(entry); } if (g_strcmp0(entry->extents[pos], text) != 0) { g_free(entry->extents[pos]); if (*text == '\0') { entry->extents[pos] = NULL; } else { entry->extents[pos] = g_strdup(text); } update = TRUE; } if (update) { gui_entry_redraw_from(entry, pos - 1); gui_entry_fix_cursor(entry); gui_entry_draw(entry); } } void gui_entry_set_extents(GUI_ENTRY_REC *entry, int pos, int len, const char *left, const char *right) { int end, update = FALSE; g_return_if_fail(entry != NULL); if (pos < 0 || len < 0 || pos > entry->text_len) return; end = pos + len; if (end > entry->text_len) end = entry->text_len; if (!entry->uses_extents) { gui_entry_alloc_extents(entry); } if (g_strcmp0(entry->extents[pos], left) != 0) { g_free(entry->extents[pos]); if (*left == '\0') { entry->extents[pos] = NULL; } else { entry->extents[pos] = g_strdup(left); } update = TRUE; } if (pos != end && g_strcmp0(entry->extents[end], right) != 0) { g_free(entry->extents[end]); if (*right == '\0') { entry->extents[end] = NULL; } else { entry->extents[end] = g_strdup(right); } update = TRUE; } if (update) { gui_entry_redraw_from(entry, pos - 1); gui_entry_fix_cursor(entry); gui_entry_draw(entry); } } void gui_entry_clear_extents(GUI_ENTRY_REC *entry, int pos, int len) { int i, end, update = FALSE; g_return_if_fail(entry != NULL); if (pos < 0 || len < 0 || pos > entry->text_len) return; end = pos + len; if (end > entry->text_len) end = entry->text_len; if (!entry->uses_extents) { return; } for (i = pos; i <= end; i++) { if (entry->extents[i] != NULL) { g_free(entry->extents[i]); entry->extents[i] = NULL; update = TRUE; } } if (update) { gui_entry_redraw_from(entry, pos); gui_entry_fix_cursor(entry); gui_entry_draw(entry); } } char *gui_entry_get_extent(GUI_ENTRY_REC *entry, int pos) { g_return_val_if_fail(entry != NULL, NULL); if (!entry->uses_extents) return NULL; if (pos < 0 || pos >= entry->text_len) return NULL; return entry->extents[pos]; } #define POS_FLAG "%|" GSList *gui_entry_get_text_and_extents(GUI_ENTRY_REC *entry) { GSList *list = NULL; GString *str; int i; g_return_val_if_fail(entry != NULL, NULL); if (entry->uses_extents && entry->extents[0] != NULL) { if (entry->pos == 0) { list = g_slist_prepend(list, g_strconcat(entry->extents[0], POS_FLAG, NULL)); } else { list = g_slist_prepend(list, g_strdup(entry->extents[0])); } } else { if (entry->pos == 0) { list = g_slist_prepend(list, g_strdup(POS_FLAG)); } else { list = g_slist_prepend(list, NULL); } } str = g_string_sized_new(entry->text_alloc); for (i = 0; i < entry->text_len; i++) { if (entry->utf8) { g_string_append_unichar(str, entry->text[i]); } else if (term_type == TERM_TYPE_BIG5) { if(entry->text[i] > 0xff) g_string_append_c(str, (entry->text[i] >> 8) & 0xff); g_string_append_c(str, entry->text[i] & 0xff); } else { g_string_append_c(str, entry->text[i]); } if (entry->pos == i+1 || (entry->uses_extents && entry->extents[i+1] != NULL)) { list = g_slist_prepend(list, g_strdup(str->str)); g_string_truncate(str, 0); if (entry->uses_extents && entry->extents[i+1] != NULL) { if (entry->pos == i+1) { list = g_slist_prepend(list, g_strconcat(entry->extents[i+1], POS_FLAG, NULL)); } else { list = g_slist_prepend(list, g_strdup(entry->extents[i+1])); } } else if (entry->pos == i+1) { list = g_slist_prepend(list, g_strdup(POS_FLAG)); } } } if (str->len > 0) { list = g_slist_prepend(list, g_strdup(str->str)); } list = g_slist_reverse(list); g_string_free(str, TRUE); return list; } void gui_entry_set_text_and_extents(GUI_ENTRY_REC *entry, GSList *list) { GSList *tmp; int pos = -1; int is_extent = 1; gui_entry_set_text(entry, ""); for (tmp = list, is_extent = TRUE; tmp != NULL; tmp = tmp->next, is_extent ^= 1) { if (is_extent) { char *extent; int len; if (tmp->data == NULL) continue; extent = g_strdup(tmp->data); len = strlen(extent); if (len >= strlen(POS_FLAG) && g_strcmp0(&extent[len-strlen(POS_FLAG)], POS_FLAG) == 0) { char *tmp; tmp = extent; extent = g_strndup(tmp, len - strlen(POS_FLAG)); g_free(tmp); pos = entry->pos; } if (strlen(extent) > 0) { gui_entry_set_extent(entry, entry->pos, extent); } g_free(extent); } else { gui_entry_insert_text(entry, tmp->data); } } gui_entry_set_pos(entry, pos); } void gui_entry_init(void) { } void gui_entry_deinit(void) { }