Mercurial > hgrepos > Python2 > PyMuPDF
diff mupdf-source/platform/gl/gl-input.c @ 2:b50eed0cc0ef upstream
ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4.
The directory name has changed: no version number in the expanded directory now.
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Mon, 15 Sep 2025 11:43:07 +0200 |
| parents | |
| children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mupdf-source/platform/gl/gl-input.c Mon Sep 15 11:43:07 2025 +0200 @@ -0,0 +1,622 @@ +// Copyright (C) 2004-2022 Artifex Software, Inc. +// +// This file is part of MuPDF. +// +// MuPDF is free software: you can redistribute it and/or modify it under the +// terms of the GNU Affero General Public License as published by the Free +// Software Foundation, either version 3 of the License, or (at your option) +// any later version. +// +// MuPDF 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 Affero General Public License for more +// details. +// +// You should have received a copy of the GNU Affero General Public License +// along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html> +// +// Alternative licensing terms are available from the licensor. +// For commercial licensing, see <https://www.artifex.com/> or contact +// Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco, +// CA 94129, USA, for further information. + +#include "gl-app.h" + +#include <string.h> +#include <stdio.h> + +static char *find_string_location(char *s, char *e, float w, float x) +{ + int c; + while (s < e) + { + int n = fz_chartorune(&c, s); + float cw = ui_measure_character(c); + if (w + (cw / 2) >= x) + return s; + w += cw; + s += n; + } + return e; +} + +static char *find_input_location(struct line *lines, int n, float left, float top, float x, float y) +{ + int i = 0; + if (y > top) i = (y - top) / ui.lineheight; + if (i >= n) i = n - 1; + return find_string_location(lines[i].a, lines[i].b, left, x); +} + +static inline int myisalnum(char *s) +{ + int cat, c; + fz_chartorune(&c, s); + cat = ucdn_get_general_category(c); + if (cat >= UCDN_GENERAL_CATEGORY_LL && cat <= UCDN_GENERAL_CATEGORY_LU) + return 1; + if (cat >= UCDN_GENERAL_CATEGORY_ND && cat <= UCDN_GENERAL_CATEGORY_NO) + return 1; + return 0; +} + +static char *home_line(char *p, char *start) +{ + while (p > start) + { + if (p[-1] == '\n' || p[-1] == '\r') + return p; + --p; + } + return p; +} + +static char *end_line(char *p, char *end) +{ + while (p < end) + { + if (p[0] == '\n' || p[0] == '\r') + return p; + ++p; + } + return p; +} + +static char *up_line(char *p, char *start) +{ + while (p > start) + { + --p; + if (*p == '\n' || *p == '\r') + return p; + } + return p; +} + +static char *down_line(char *p, char *end) +{ + while (p < end) + { + if (*p == '\n' || *p == '\r') + return p+1; + ++p; + } + return p; +} + +static char *prev_char(char *p, char *start) +{ + --p; + while ((*p & 0xC0) == 0x80 && p > start) /* skip middle and final multibytes */ + --p; + return p; +} + +static char *next_char(char *p) +{ + ++p; + while ((*p & 0xC0) == 0x80) /* skip middle and final multibytes */ + ++p; + return p; +} + +static char *prev_word(char *p, char *start) +{ + while (p > start && !myisalnum(prev_char(p, start))) p = prev_char(p, start); + while (p > start && myisalnum(prev_char(p, start))) p = prev_char(p, start); + return p; +} + +static char *next_word(char *p, char *end) +{ + while (p < end && !myisalnum(p)) p = next_char(p); + while (p < end && myisalnum(p)) p = next_char(p); + return p; +} + +static void ui_input_delete_selection(struct input *input) +{ + char *p = input->p < input->q ? input->p : input->q; + char *q = input->p > input->q ? input->p : input->q; + memmove(p, q, input->end - q); + input->end -= q - p; + *input->end = 0; + input->p = input->q = p; +} + +static void ui_input_paste(struct input *input, const char *buf) +{ + int n = (int)strlen(buf); + if (input->widget) + { + char *newtext; + int selStart = input->p - input->text; + int selEnd = input->q - input->text; + if (pdf_edit_text_field_value(ctx, input->widget, input->text, buf, &selStart, &selEnd, &newtext)) + { + size_t len = strlen(newtext); + if (len > sizeof(input->text)-1) + len = sizeof(input->text)-1; + memcpy(input->text, newtext, len); + input->text[len] = 0; + fz_free(ctx, newtext); + input->p = input->text + selStart; + input->q = input->p; + input->end = input->text + len; + } + return; + } + if (input->p != input->q) + ui_input_delete_selection(input); + if (input->end + n + 1 < input->text + sizeof(input->text)) + { + memmove(input->p + n, input->p, input->end - input->p); + memmove(input->p, buf, n); + input->p += n; + input->end += n; + *input->end = 0; + } + input->q = input->p; +} + +static void ui_do_copy(struct input *input) +{ + if (input->p != input->q) + { + char buf[sizeof input->text]; + char *p = input->p < input->q ? input->p : input->q; + char *q = input->p > input->q ? input->p : input->q; + memmove(buf, p, q - p); + buf[q-p] = 0; + ui_set_clipboard(buf); + } +} + +static void ui_do_cut(struct input *input) +{ + if (input->p != input->q) + { + ui_do_copy(input); + ui_input_delete_selection(input); + } +} + +static void ui_do_paste(struct input *input, int multiline) +{ + const char *buf = ui_get_clipboard(); + char *p, *oneline = NULL; + + if (!buf) + return; + + if (multiline) + { + ui_input_paste(input, buf); + return; + } + + p = oneline = strdup(buf); + while (*p) + { + if (*p == '\n' || *p == '\r') + *p = ' '; + p++; + } + ui_input_paste(input, oneline); + free(oneline); +} + +static int ui_input_key(struct input *input, int multiline) +{ + switch (ui.key) + { + case 0: + return UI_INPUT_NONE; + case KEY_LEFT: + if (ui.mod == GLUT_ACTIVE_CTRL + GLUT_ACTIVE_SHIFT) + { + input->q = prev_word(input->q, input->text); + } + else if (ui.mod == GLUT_ACTIVE_CTRL) + { + if (input->p != input->q) + input->p = input->q = input->p < input->q ? input->p : input->q; + else + input->p = input->q = prev_word(input->q, input->text); + } + else if (ui.mod == GLUT_ACTIVE_SHIFT) + { + if (input->q > input->text) + input->q = prev_char(input->q, input->text); + } + else if (ui.mod == 0) + { + if (input->p != input->q) + input->p = input->q = input->p < input->q ? input->p : input->q; + else if (input->q > input->text) + input->p = input->q = prev_char(input->q, input->text); + } + break; + case KEY_RIGHT: + if (ui.mod == GLUT_ACTIVE_CTRL + GLUT_ACTIVE_SHIFT) + { + input->q = next_word(input->q, input->end); + } + else if (ui.mod == GLUT_ACTIVE_CTRL) + { + if (input->p != input->q) + input->p = input->q = input->p > input->q ? input->p : input->q; + else + input->p = input->q = next_word(input->q, input->end); + } + else if (ui.mod == GLUT_ACTIVE_SHIFT) + { + if (input->q < input->end) + input->q = next_char(input->q); + } + else if (ui.mod == 0) + { + if (input->p != input->q) + input->p = input->q = input->p > input->q ? input->p : input->q; + else if (input->q < input->end) + input->p = input->q = next_char(input->q); + } + break; + case KEY_UP: + if (ui.mod & GLUT_ACTIVE_SHIFT) + input->q = up_line(input->q, input->text); + else + input->p = input->q = up_line(input->p, input->text); + break; + case KEY_DOWN: + if (ui.mod & GLUT_ACTIVE_SHIFT) + input->q = down_line(input->q, input->end); + else + input->p = input->q = down_line(input->q, input->end); + break; + case KEY_HOME: + if (ui.mod == GLUT_ACTIVE_CTRL + GLUT_ACTIVE_SHIFT) + input->q = input->text; + else if (ui.mod == GLUT_ACTIVE_SHIFT) + input->q = home_line(input->q, input->text); + else if (ui.mod == GLUT_ACTIVE_CTRL) + input->p = input->q = input->text; + else if (ui.mod == 0) + input->p = input->q = home_line(input->p, input->text); + break; + case KEY_END: + if (ui.mod == GLUT_ACTIVE_CTRL + GLUT_ACTIVE_SHIFT) + input->q = input->end; + else if (ui.mod == GLUT_ACTIVE_SHIFT) + input->q = end_line(input->q, input->end); + else if (ui.mod == GLUT_ACTIVE_CTRL) + input->p = input->q = input->end; + else if (ui.mod == 0) + input->p = input->q = end_line(input->p, input->end); + break; + case KEY_DELETE: + if (ui.mod == GLUT_ACTIVE_SHIFT) + { + ui_do_cut(input); + } + else if (input->p != input->q) + { + ui_input_delete_selection(input); + } + else if (input->p < input->end) + { + char *np = next_char(input->p); + memmove(input->p, np, input->end - np); + input->end -= np - input->p; + *input->end = 0; + input->q = input->p; + } + break; + case KEY_ESCAPE: + ui.focus = NULL; + return UI_INPUT_NONE; + case KEY_ENTER: + if (!multiline) + { + ui.focus = NULL; + return UI_INPUT_ACCEPT; + } + ui_input_paste(input, "\n"); + break; + case KEY_BACKSPACE: + if (input->p != input->q) + ui_input_delete_selection(input); + else if (input->p > input->text) + { + char *pp; + if (ui.mod == GLUT_ACTIVE_CTRL) + pp = prev_word(input->p, input->text); + else + pp = prev_char(input->p, input->text); + memmove(pp, input->p, input->end - input->p); + input->end -= input->p - pp; + *input->end = 0; + input->q = input->p = pp; + } + break; + case KEY_CTL_A: + input->p = input->q = input->text; + break; + case KEY_CTL_E: + input->p = input->q = input->end; + break; + case KEY_CTL_W: + if (input->p != input->q) + ui_input_delete_selection(input); + else + { + input->p = prev_word(input->p, input->text); + ui_input_delete_selection(input); + } + break; + case KEY_CTL_U: + input->p = input->q = input->end = input->text; + *input->end = 0; + break; + case KEY_CTL_C: + ui_do_copy(input); + break; + case KEY_CTL_X: + ui_do_cut(input); + break; + case KEY_CTL_V: + ui_do_paste(input, multiline); + break; + case KEY_INSERT: + if (ui.mod == GLUT_ACTIVE_CTRL) + ui_do_copy(input); + if (ui.mod == GLUT_ACTIVE_SHIFT) + ui_do_paste(input, multiline); + break; + default: + if (ui.key >= 32 && ui.plain) + { + int cat = ucdn_get_general_category(ui.key); + if (ui.key == ' ' || (cat >= UCDN_GENERAL_CATEGORY_LL && cat < UCDN_GENERAL_CATEGORY_ZL)) + { + char buf[9]; + int n = fz_runetochar(buf, ui.key); + buf[n] = 0; + ui_input_paste(input, buf); + } + } + break; + } + return UI_INPUT_EDIT; +} + +void ui_input_init(struct input *input, const char *text) +{ + fz_strlcpy(input->text, text, sizeof input->text); + input->end = input->text + strlen(input->text); + input->p = input->text; + input->q = input->end; + input->scroll = 0; +} + +int ui_input(struct input *input, int width, int height) +{ + struct line lines[500]; + fz_irect area; + float ax, bx; + int ay, sy; + char *p, *q; + int state; + int i, n; + + if (ui.focus == input) + state = ui_input_key(input, height > 1); + else + state = UI_INPUT_NONE; + + area = ui_pack(width, ui.lineheight * height + 6); + ui_draw_bevel_rect(area, UI_COLOR_TEXT_BG, 1); + area = fz_expand_irect(area, -2); + + if (height > 1) + area.x1 -= ui.lineheight; + + n = ui_break_lines(input->text, lines, height > 1 ? nelem(lines) : 1, area.x1-area.x0-2, NULL); + + if (height > 1) + ui_scrollbar(area.x1, area.y0, area.x1+ui.lineheight, area.y1, &input->scroll, 1, fz_maxi(0, n-height)+1, NULL); + else + input->scroll = 0; + + ax = area.x0 + 2; + bx = area.x1 - 2; + ay = area.y0 + 1; + sy = input->scroll * ui.lineheight; + + if (ui_mouse_inside(area)) + { + ui.hot = input; + if (!ui.active || ui.active == input) + ui.cursor = GLUT_CURSOR_TEXT; + if (!ui.active && ui.down) + { + input->p = find_input_location(lines, n, ax, ay-sy, ui.x, ui.y); + ui.active = input; + } + } + + if (ui.active == input) + { + input->q = find_input_location(lines, n, ax, ay-sy, ui.x, ui.y); + ui.focus = input; + } + + p = input->p < input->q ? input->p : input->q; + q = input->p > input->q ? input->p : input->q; + + for (i = input->scroll; i < n && i < input->scroll+height; ++i) + { + char *a = lines[i].a, *b = lines[i].b; + if (ui.focus == input) + { + if (p >= a && p <= b && q >= a && q <= b) + { + float px = ax + ui_measure_string_part(a, p); + float qx = px + ui_measure_string_part(p, q); + glColorHex(UI_COLOR_TEXT_SEL_BG); + glRectf(px, ay, qx+1, ay + ui.lineheight); + glColorHex(UI_COLOR_TEXT_FG); + ui_draw_string_part(ax, ay, a, p); + glColorHex(UI_COLOR_TEXT_SEL_FG); + ui_draw_string_part(px, ay, p, q); + glColorHex(UI_COLOR_TEXT_FG); + ui_draw_string_part(qx, ay, q, b); + } + else if (p < a && q >= a && q <= b) + { + float qx = ax + ui_measure_string_part(a, q); + glColorHex(UI_COLOR_TEXT_SEL_BG); + glRectf(ax, ay, qx+1, ay + ui.lineheight); + glColorHex(UI_COLOR_TEXT_SEL_FG); + ui_draw_string_part(ax, ay, a, q); + glColorHex(UI_COLOR_TEXT_FG); + ui_draw_string_part(qx, ay, q, b); + } + else if (p >= a && p <= b && q > b) + { + float px = ax + ui_measure_string_part(a, p); + glColorHex(UI_COLOR_TEXT_SEL_BG); + glRectf(px, ay, bx, ay + ui.lineheight); + glColorHex(UI_COLOR_TEXT_FG); + ui_draw_string_part(ax, ay, a, p); + glColorHex(UI_COLOR_TEXT_SEL_FG); + ui_draw_string_part(px, ay, p, b); + } + else if (p < a && q > b) + { + glColorHex(UI_COLOR_TEXT_SEL_BG); + glRectf(ax, ay, bx, ay + ui.lineheight); + glColorHex(UI_COLOR_TEXT_SEL_FG); + ui_draw_string_part(ax, ay, a, b); + } + else + { + glColorHex(UI_COLOR_TEXT_FG); + ui_draw_string_part(ax, ay, a, b); + } + } + else + { + glColorHex(UI_COLOR_TEXT_FG); + ui_draw_string_part(ax, ay, a, b); + } + ay += ui.lineheight; + } + + return state; +} + +void ui_readline_init(struct readline *readline, const char *text) +{ + int i; + + memset(readline->buffer, 0, sizeof readline->buffer); + for (i = 0; i < UI_READLINE_SIZE; i++) + readline->history[i] = &(readline->buffer[i][0]); + + readline->used = 0; + readline->current = -1; + + ui_input_init(&readline->input, text ? text : ""); +} + +const char *ui_readline(struct readline *readline, int width) +{ + int state; + + /* Override key up/down to navigate history. */ + switch (ui.key) + { + case KEY_UP: + ui.key = 0; + + if (readline->current == -1) /* no history entries */ + break; + if (readline->current == 0) /* no older entries */ + break; + + readline->current--; + ui_input_init(&readline->input, readline->history[readline->current]); + readline->input.p = readline->input.q = readline->input.end; + break; + case KEY_DOWN: + ui.key = 0; + + if (readline->current == -1) /* no history entries */ + break; + if (readline->current == readline->used) /* no newer entries */ + break; + if (readline->current == readline->used - 1) /* at insertion point */ + { + readline->current++; + ui_input_init(&readline->input, ""); + readline->input.p = readline->input.q = readline->input.end; + break; + } + + readline->current++; + ui_input_init(&readline->input, readline->history[readline->current]); + readline->input.p = readline->input.q = readline->input.end; + break; + } + + state = ui_input(&readline->input, width, 1); + + /* Remember line in history. */ + if (state == UI_INPUT_ACCEPT) + { + char *accepted; + + if (readline->used == UI_READLINE_SIZE) + { + char *tmp = readline->history[0]; + memmove(readline->history, readline->history + 1, (UI_READLINE_SIZE - 1) * sizeof(char *)); + readline->history[UI_READLINE_SIZE - 1] = tmp; + + fz_strlcpy(readline->history[UI_READLINE_SIZE - 1], readline->input.text, UI_INPUT_SIZE); + accepted = readline->history[UI_READLINE_SIZE - 1]; + readline->current = UI_READLINE_SIZE; + } + else + { + fz_strlcpy(readline->history[readline->used], readline->input.text, UI_INPUT_SIZE); + accepted = readline->history[readline->used]; + readline->used++; + readline->current = readline->used; + } + + return accepted; + } + + return NULL; +}
