summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTor Andersson <tor.andersson@artifex.com>2015-08-12 16:01:35 +0200
committerTor Andersson <tor.andersson@artifex.com>2015-10-06 11:21:23 +0200
commite6501a90a16255df2784101bf512b2e828c00dea (patch)
tree298d3b1711e3c95fd0b04205735d1b22d4f44211
parent9b06cb54767af4e9fe2e45463466d6195adea8b7 (diff)
downloadmupdf-e6501a90a16255df2784101bf512b2e828c00dea.tar.xz
glut: Add text field for search.
-rw-r--r--platform/glut/glut-main.c387
1 files changed, 384 insertions, 3 deletions
diff --git a/platform/glut/glut-main.c b/platform/glut/glut-main.c
index c445cb77..0e54d7ae 100644
--- a/platform/glut/glut-main.c
+++ b/platform/glut/glut-main.c
@@ -1,5 +1,7 @@
#include "mupdf/fitz.h"
+// TODO: event queue and handle key/mouse based on 'active' inside display()
+
#ifdef __APPLE__
#include <OpenGL/OpenGL.h>
#include <GLUT/glut.h>
@@ -8,6 +10,12 @@
#include <GL/freeglut.h>
#endif
+struct input
+{
+ int text[512];
+ int *end, *p, *q;
+};
+
struct ui
{
int x, y, down, middle, right;
@@ -162,6 +170,7 @@ static float oldrotate = 0, currentrotate = 0;
static int isfullscreen = 0;
static int showoutline = 0;
static int showlinks = 0;
+static int showsearch = 0;
static int history_count = 0;
static int history[256];
@@ -169,6 +178,13 @@ static int future_count = 0;
static int future[256];
static int marks[10];
+static struct input search_input = { { 0 }, 0 };
+static char *search_needle = 0;
+static int search_dir = 1;
+static int search_page = -1;
+static int search_hit_count = 0;
+static fz_rect search_hit_bbox[500];
+
static void update_title(void)
{
static char buf[256];
@@ -281,6 +297,23 @@ static void pop_future(void)
push_history();
}
+void do_search_page(fz_document *doc, int number, char *needle, fz_cookie *cookie)
+{
+ fz_page *page = fz_load_page(ctx, doc, number);
+
+ fz_text_sheet *sheet = fz_new_text_sheet(ctx);
+ fz_text_page *text = fz_new_text_page(ctx);
+ fz_device *dev = fz_new_text_device(ctx, sheet, text);
+ fz_run_page(ctx, page, dev, &fz_identity, cookie);
+ fz_drop_device(ctx, dev);
+
+ search_hit_count = fz_search_text_page(ctx, text, needle, search_hit_bbox, nelem(search_hit_bbox));
+
+ fz_drop_text_page(ctx, text);
+ fz_drop_text_sheet(ctx, sheet);
+ fz_drop_page(ctx, page);
+}
+
static void draw_string(float x, float y, const char *s)
{
int c;
@@ -292,9 +325,10 @@ static void draw_string(float x, float y, const char *s)
}
}
+#if 0
static float measure_string(const char *s)
{
- int w, c;
+ int w = 0, c;
while (*s)
{
s += fz_chartorune(&c, s);
@@ -302,6 +336,217 @@ static float measure_string(const char *s)
}
return w;
}
+#endif
+
+static void draw_string_part(float x, float y, const int *s, const int *e)
+{
+ glRasterPos2f(x + 0.375f, y + 0.375f + 11);
+ while (s < e)
+ glutBitmapCharacter(GLUT_BITMAP_HELVETICA_12, *s++);
+}
+
+static float measure_string_part(const int *s, const int *e)
+{
+ int w = 0;
+ while (s < e)
+ w += glutBitmapWidth(GLUT_BITMAP_HELVETICA_12, *s++);
+ return w;
+}
+
+static inline int isalnum(int c)
+{
+ int 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 int *skip_word_left(int *p, int *start)
+{
+ while (p > start && !isalnum(p[-1])) --p;
+ while (p > start && isalnum(p[-1])) --p;
+ return p;
+}
+
+static int *skip_word_right(int *p, int *end)
+{
+ while (p < end && !isalnum(p[0])) ++p;
+ while (p < end && isalnum(p[0])) ++p;
+ return p;
+}
+
+static void ui_input_draw(int x0, int y0, int x1, int y1, struct input *input)
+{
+ float px, qx, ex;
+ int *p, *q;
+
+ glColor4f(1, 1, 1, 1);
+ glRectf(x0, y0, x1, y1);
+
+ p = input->p < input->q ? input->p : input->q;
+ q = input->p > input->q ? input->p : input->q;
+
+ px = x0 + 2 + measure_string_part(input->text, p);
+ qx = px + measure_string_part(p, q);
+ ex = qx + measure_string_part(q, input->end);
+
+ glColor4f(0.6, 0.6, 1.0, 1);
+ glRectf(px, y0 + 2, qx+1, y1 - 2);
+
+ glColor4f(0, 0, 0, 1);
+ draw_string_part(x0 + 2, y0, input->text, input->end);
+}
+
+static void ui_input_delete_selection(struct input *input)
+{
+ int *p = input->p < input->q ? input->p : input->q;
+ int *q = input->p > input->q ? input->p : input->q;
+ memmove(p, q, (input->end - q) * sizeof (*p));
+ input->end -= q - p;
+ input->p = input->q = p;
+}
+
+static int ui_input_keyboard(int key, struct input *input)
+{
+ int cat;
+
+ switch (key)
+ {
+ default:
+ cat = ucdn_get_general_category(key);
+ if (key == ' ' || (cat >= UCDN_GENERAL_CATEGORY_LL && cat < UCDN_GENERAL_CATEGORY_ZL))
+ {
+ if (input->p != input->q)
+ ui_input_delete_selection(input);
+ memmove(input->p + 1, input->p, (input->end - input->p) * sizeof (*input->p));
+ ++(input->end);
+ *(input->p++) = key;
+ input->q = input->p;
+ }
+ break;
+ case 27:
+ return -1;
+ case '\r':
+ return 1;
+ case '\b':
+ if (input->p != input->q)
+ ui_input_delete_selection(input);
+ else if (input->p > input->text && input->end > input->text)
+ {
+ memmove(input->p - 1, input->p, (input->end - input->p) * sizeof (*input->p));
+ input->q = --(input->p);
+ --(input->end);
+ }
+ break;
+ case 127:
+ if (input->p != input->q)
+ ui_input_delete_selection(input);
+ else if (input->p < input->end)
+ {
+ memmove(input->p, input->p + 1, (input->end - input->p - 1) * sizeof (*input->p));
+ input->q = input->p;
+ --(input->end);
+ }
+ break;
+ case 'A' - 64:
+ input->p = input->q = input->text;
+ break;
+ case 'E' - 64:
+ input->p = input->q = input->end;
+ break;
+ case 'W' - 64:
+ if (input->p != input->q)
+ ui_input_delete_selection(input);
+ else
+ {
+ input->p = skip_word_left(input->p, input->text);
+ ui_input_delete_selection(input);
+ }
+ break;
+ case 'U' - 64:
+ input->p = input->q = input->end = input->text;
+ break;
+ }
+
+ return 0;
+}
+
+static int ui_input_special(int key, int mod, struct input *input)
+{
+ if (mod == GLUT_ACTIVE_CTRL + GLUT_ACTIVE_SHIFT)
+ {
+ switch (key)
+ {
+ case GLUT_KEY_LEFT: input->q = skip_word_left(input->q, input->text); break;
+ case GLUT_KEY_RIGHT: input->q = skip_word_right(input->q, input->end); break;
+ case GLUT_KEY_UP: case GLUT_KEY_HOME: input->q = input->text; break;
+ case GLUT_KEY_DOWN: case GLUT_KEY_END: input->q = input->end; break;
+ }
+ }
+ else if (mod == GLUT_ACTIVE_CTRL)
+ {
+ switch (key)
+ {
+ case GLUT_KEY_LEFT:
+ if (input->p != input->q)
+ input->p = input->q = input->p < input->q ? input->p : input->q;
+ else
+ input->p = input->q = skip_word_left(input->q, input->text);
+ break;
+ case GLUT_KEY_RIGHT:
+ if (input->p != input->q)
+ input->p = input->q = input->p > input->q ? input->p : input->q;
+ else
+ input->p = input->q = skip_word_right(input->q, input->end);
+ break;
+ case GLUT_KEY_HOME:
+ case GLUT_KEY_UP:
+ input->p = input->q = input->text;
+ break;
+ case GLUT_KEY_END:
+ case GLUT_KEY_DOWN:
+ input->p = input->q = input->end;
+ break;
+ }
+ }
+ else if (mod == GLUT_ACTIVE_SHIFT)
+ {
+ switch (key)
+ {
+ case GLUT_KEY_LEFT: if (input->q > input->text) input->q = --(input->q); break;
+ case GLUT_KEY_RIGHT: if (input->q < input->end) input->q = ++(input->q); break;
+ case GLUT_KEY_HOME: input->q = input->text; break;
+ case GLUT_KEY_END: input->q = input->end; break;
+ }
+ }
+ else if (mod == 0)
+ {
+ switch (key)
+ {
+ case GLUT_KEY_LEFT:
+ 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 = --(input->q);
+ break;
+ case GLUT_KEY_RIGHT:
+ 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 = ++(input->q);
+ break;
+ case GLUT_KEY_HOME:
+ input->p = input->q = input->text;
+ break;
+ case GLUT_KEY_END:
+ input->p = input->q = input->end;
+ break;
+ }
+ }
+ return 0;
+}
static void ui_scrollbar(int x0, int y0, int x1, int y1, int *value, int page, int max)
{
@@ -524,6 +769,34 @@ static void draw_links(fz_link *link, int xofs, int yofs, float zoom, float rota
glDisable(GL_BLEND);
}
+static void draw_search_hits(int xofs, int yofs, float zoom, float rotate)
+{
+ fz_matrix ctm;
+ fz_rect r;
+ int i;
+
+ xofs -= page_x;
+ yofs -= page_y;
+
+ fz_scale(&ctm, zoom / 72, zoom / 72);
+ fz_pre_rotate(&ctm, -rotate);
+
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+
+ for (i = 0; i < search_hit_count; ++i)
+ {
+ r = search_hit_bbox[i];
+
+ fz_transform_rect(&r, &ctm);
+
+ glColor4f(1, 0, 0, 0.4);
+ glRectf(xofs + r.x0, yofs + r.y0, xofs + r.x1, yofs + r.y1);
+ }
+
+ glDisable(GL_BLEND);
+}
+
static void toggle_fullscreen(void)
{
static int oldw = 100, oldh = 100, oldx = 0, oldy = 0;
@@ -740,11 +1013,19 @@ static void display(void)
draw_image(page_tex, &r);
draw_links(links, x, y, currentzoom, currentrotate);
+ if (search_page == currentpage && search_hit_count > 0)
+ draw_search_hits(x, y, currentzoom, currentrotate);
+
if (showoutline)
{
draw_outline(outline, canvas_x);
}
+ if (showsearch)
+ {
+ ui_input_draw(canvas_x, 0, canvas_x + canvas_w, 15, &search_input);
+ }
+
ui_end();
glutSwapBuffers();
@@ -752,13 +1033,69 @@ static void display(void)
ogl_assert(ctx, "swap buffers");
}
+char *
+fz_utf8_from_rune_string(fz_context *ctx, const int *s)
+{
+ const int *src = s;
+ char *d;
+ char *dst;
+ int len = 1;
+
+ while (*src)
+ {
+ len += fz_runelen(*src++);
+ }
+
+ d = fz_malloc(ctx, len);
+ if (d != NULL)
+ {
+ dst = d;
+ src = s;
+ while (*src)
+ {
+ dst += fz_runetochar(dst, *src++);
+ }
+ *dst = 0;
+ }
+ return d;
+}
+
static void keyboard(unsigned char key, int x, int y)
{
- if (key == 27 || key == 'q')
- exit(0);
+ int i, start;
+
+ if (showsearch)
+ {
+ int state = ui_input_keyboard(key, &search_input);
+ if (state == -1)
+ {
+ showsearch = 0;
+ }
+ else if (state == 1)
+ {
+ showsearch = 0;
+ search_page = -1;
+ if (search_needle)
+ {
+ fz_free(ctx, search_needle);
+ search_needle = NULL;
+ }
+ if (search_input.end > search_input.text)
+ {
+ *(search_input.end) = 0;
+ search_needle = fz_utf8_from_rune_string(ctx, search_input.text);
+ goto dosearch;
+ }
+ }
+ glutPostRedisplay();
+ return;
+ }
switch (key)
{
+ case 'q':
+ exit(0);
+ break;
case 'm':
if (number == 0)
push_history();
@@ -783,6 +1120,26 @@ static void keyboard(unsigned char key, int x, int y)
pop_future();
}
break;
+ case 'N': case 'n': dosearch:
+ if (!search_needle)
+ break;
+ if (search_page == currentpage)
+ start = currentpage + (key == 'N' ? -search_dir : search_dir);
+ else
+ start = currentpage;
+ for (i = start; i >= 0 && i < fz_count_pages(ctx, doc); i += (key == 'N' ? -search_dir : search_dir))
+ {
+ printf("searching page %d\n", i);
+ do_search_page(doc, i, search_needle, NULL);
+ if (search_hit_count)
+ {
+ printf("found match '%s' at page %d\n", search_needle, i);
+ jump_to_page(i);
+ search_page = currentpage;
+ break;
+ }
+ }
+ break;
case 'f': toggle_fullscreen(); break;
case 'w': shrinkwrap(); break;
case 'W': auto_zoom_w(); break;
@@ -803,6 +1160,8 @@ static void keyboard(unsigned char key, int x, int y)
case ']': currentrotate -= 90; break;
case 'o': showoutline = !showoutline; break;
case 'l': showlinks = !showlinks; break;
+ case '/': search_dir = 1; showsearch = 1; break;
+ case '?': search_dir = -1; showsearch = 1; break;
}
if (key >= '0' && key <= '9')
@@ -815,6 +1174,9 @@ static void keyboard(unsigned char key, int x, int y)
while (currentrotate < 0) currentrotate += 360;
while (currentrotate >= 360) currentrotate -= 360;
+ if (currentpage != search_page)
+ search_page = -1;
+
glutPostRedisplay();
}
@@ -825,6 +1187,21 @@ static void special(int key, int x, int y)
if (key == GLUT_KEY_F4 && mod == GLUT_ACTIVE_ALT)
exit(0);
+ if (showsearch)
+ {
+ int state = ui_input_special(key, mod, &search_input);
+ if (state == -1)
+ {
+ showsearch = 0;
+ }
+ else if (state == 1)
+ {
+ showsearch = 1;
+ }
+ glutPostRedisplay();
+ return;
+ }
+
switch (key)
{
case GLUT_KEY_UP: scroll_y -= 10; break;
@@ -884,6 +1261,10 @@ int main(int argc, char **argv)
memset(&ui, 0, sizeof ui);
+ search_input.p = search_input.text;
+ search_input.q = search_input.p;
+ search_input.end = search_input.p;
+
glutCreateWindow(filename);
ctx = fz_new_context(NULL, NULL, 0);