source: poppler/vendor/current/glib/demo/find.c@ 660

Last change on this file since 660 was 660, checked in by Silvan Scherrer, 12 years ago

poppler: update vendor to 0.24.0

File size: 16.2 KB
Line 
1/*
2 * Copyright (C) 2008 Carlos Garcia Campos <carlosgc@gnome.org>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2, or (at your option)
7 * any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
17 */
18
19#include "find.h"
20
21enum {
22 TITLE_COLUMN,
23 X1_COLUMN,
24 Y1_COLUMN,
25 X2_COLUMN,
26 Y2_COLUMN,
27
28 VISIBLE_COLUMN,
29 PAGE_COLUMN,
30 PAGE_RECT,
31 N_COLUMNS
32};
33
34typedef struct {
35 PopplerDocument *doc;
36
37 GtkWidget *treeview;
38 GtkWidget *darea;
39 GtkWidget *entry;
40 GtkWidget *progress;
41
42 PopplerFindFlags options;
43 gint n_pages;
44 gint page_index;
45
46 guint idle_id;
47
48 cairo_surface_t *surface;
49 gint selected_page;
50 GdkRectangle selected_match;
51} PgdFindDemo;
52
53static void
54pgd_find_free (PgdFindDemo *demo)
55{
56 if (!demo)
57 return;
58
59 if (demo->idle_id > 0) {
60 g_source_remove (demo->idle_id);
61 demo->idle_id = 0;
62 }
63
64 if (demo->doc) {
65 g_object_unref (demo->doc);
66 demo->doc = NULL;
67 }
68
69 if (demo->surface) {
70 cairo_surface_destroy (demo->surface);
71 demo->surface = NULL;
72 }
73
74 g_free (demo);
75}
76
77static void
78pgd_find_update_progress (PgdFindDemo *demo,
79 gint scanned)
80{
81 gchar *str;
82
83 str = g_strdup_printf ("Searching ... (%d%%)",
84 MIN (scanned * 100 / demo->n_pages, 100));
85 gtk_progress_bar_set_text (GTK_PROGRESS_BAR (demo->progress), str);
86 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (demo->progress),
87 MIN ((gdouble)scanned / demo->n_pages, 1.0));
88 g_free (str);
89}
90
91static gboolean
92pgd_find_find_text (PgdFindDemo *demo)
93{
94 PopplerPage *page;
95 GList *matches;
96 GTimer *timer;
97 GtkTreeModel *model;
98
99 page = poppler_document_get_page (demo->doc, demo->page_index);
100 if (!page) {
101 demo->page_index++;
102 return demo->page_index < demo->n_pages;
103 }
104
105 model = gtk_tree_view_get_model (GTK_TREE_VIEW (demo->treeview));
106 timer = g_timer_new ();
107 matches = poppler_page_find_text_with_options (page, gtk_entry_get_text (GTK_ENTRY (demo->entry)), demo->options);
108 g_timer_stop (timer);
109 if (matches) {
110 GtkTreeIter iter;
111 gchar *str;
112 GList *l;
113 gdouble height;
114 gint n_match = 0;
115
116 str = g_strdup_printf ("%d matches found on page %d in %.4f seconds",
117 g_list_length (matches), demo->page_index + 1,
118 g_timer_elapsed (timer, NULL));
119
120 gtk_tree_store_append (GTK_TREE_STORE (model), &iter, NULL);
121 gtk_tree_store_set (GTK_TREE_STORE (model), &iter,
122 TITLE_COLUMN, str,
123 VISIBLE_COLUMN, FALSE,
124 PAGE_COLUMN, demo->page_index,
125 -1);
126 g_free (str);
127
128 poppler_page_get_size (page, NULL, &height);
129
130 for (l = matches; l && l->data; l = g_list_next (l)) {
131 PopplerRectangle *rect = (PopplerRectangle *)l->data;
132 GtkTreeIter iter_child;
133 gchar *x1, *y1, *x2, *y2;
134 gdouble tmp;
135
136 str = g_strdup_printf ("Match %d", ++n_match);
137 x1 = g_strdup_printf ("%.2f", rect->x1);
138 y1 = g_strdup_printf ("%.2f", rect->y1);
139 x2 = g_strdup_printf ("%.2f", rect->x2);
140 y2 = g_strdup_printf ("%.2f", rect->y2);
141
142 tmp = rect->y1;
143 rect->y1 = height - rect->y2;
144 rect->y2 = height - tmp;
145
146 gtk_tree_store_append (GTK_TREE_STORE (model), &iter_child, &iter);
147 gtk_tree_store_set (GTK_TREE_STORE (model), &iter_child,
148 TITLE_COLUMN, str,
149 X1_COLUMN, x1,
150 Y1_COLUMN, y1,
151 X2_COLUMN, x2,
152 Y2_COLUMN, y2,
153 VISIBLE_COLUMN, TRUE,
154 PAGE_COLUMN, demo->page_index,
155 PAGE_RECT, rect,
156 -1);
157 g_free (str);
158 g_free (x1);
159 g_free (y1);
160 g_free (x2);
161 g_free (y2);
162 g_object_weak_ref (G_OBJECT (model),
163 (GWeakNotify)poppler_rectangle_free,
164 rect);
165 }
166 g_list_free (matches);
167 }
168
169 g_timer_destroy (timer);
170 g_object_unref (page);
171
172 demo->page_index++;
173 pgd_find_update_progress (demo, demo->page_index);
174
175 return demo->page_index < demo->n_pages;
176}
177
178static cairo_surface_t *
179pgd_find_render_page (PgdFindDemo *demo)
180{
181 cairo_t *cr;
182 PopplerPage *page;
183 gdouble width, height;
184 cairo_surface_t *surface = NULL;
185
186 page = poppler_document_get_page (demo->doc, demo->selected_page);
187 if (!page)
188 return NULL;
189
190 poppler_page_get_size (page, &width, &height);
191 gtk_widget_set_size_request (demo->darea, width, height);
192
193 surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24,
194 width, height);
195 cr = cairo_create (surface);
196
197 cairo_save (cr);
198 cairo_set_source_rgb (cr, 1, 1, 1);
199 cairo_rectangle (cr, 0, 0, width, height);
200 cairo_fill (cr);
201 cairo_restore (cr);
202
203 cairo_save (cr);
204 poppler_page_render (page, cr);
205 cairo_restore (cr);
206
207 cairo_destroy (cr);
208 g_object_unref (page);
209
210 return surface;
211}
212
213static gboolean
214pgd_find_viewer_drawing_area_draw (GtkWidget *area,
215 cairo_t *cr,
216 PgdFindDemo *demo)
217{
218 if (demo->selected_page == -1)
219 return FALSE;
220
221 if (!demo->surface) {
222 demo->surface = pgd_find_render_page (demo);
223 if (!demo->surface)
224 return FALSE;
225 }
226
227 cairo_set_source_surface (cr, demo->surface, 0, 0);
228 cairo_paint (cr);
229
230 if (demo->selected_match.width > 0 && demo->selected_match.height > 0) {
231 cairo_set_source_rgb (cr, 1., 1., 0.);
232 cairo_set_operator (cr, CAIRO_OPERATOR_MULTIPLY);
233 gdk_cairo_rectangle (cr, &demo->selected_match);
234 cairo_fill (cr);
235 }
236
237 return TRUE;
238}
239
240static gboolean
241pgd_find_viewer_redraw (PgdFindDemo *demo)
242{
243 cairo_surface_destroy (demo->surface);
244 demo->surface = NULL;
245
246 gtk_widget_queue_draw (demo->darea);
247
248 return FALSE;
249}
250
251static void
252pgd_find_viewer_queue_redraw (PgdFindDemo *demo)
253{
254 g_idle_add ((GSourceFunc)pgd_find_viewer_redraw, demo);
255}
256
257static GtkTreeModel *
258pgd_find_create_model ()
259{
260 return GTK_TREE_MODEL (gtk_tree_store_new (N_COLUMNS,
261 G_TYPE_STRING,
262 G_TYPE_STRING, G_TYPE_STRING,
263 G_TYPE_STRING, G_TYPE_STRING,
264 G_TYPE_BOOLEAN, G_TYPE_UINT,
265 G_TYPE_POINTER));
266}
267
268static void
269pgd_find_button_clicked (GtkButton *button,
270 PgdFindDemo *demo)
271{
272 GtkTreeModel *model;
273
274 /* Delete the model and create a new one instead of
275 * just clearing it to make sure rectangle are free.
276 * This is a workaround because GtkTreeModel doesn't
277 * support boxed types and we have to store rectangles
278 * as pointers that are freed when the model is deleted.
279 */
280 model = pgd_find_create_model ();
281 gtk_tree_view_set_model (GTK_TREE_VIEW (demo->treeview), model);
282 g_object_unref (model);
283
284 demo->selected_page = -1;
285 pgd_find_viewer_queue_redraw (demo);
286
287 demo->page_index = 0;
288 pgd_find_update_progress (demo, demo->page_index);
289 if (demo->idle_id > 0)
290 g_source_remove (demo->idle_id);
291 demo->idle_id = g_idle_add ((GSourceFunc)pgd_find_find_text, demo);
292}
293
294static void
295pgd_find_button_sensitivity_cb (GtkWidget *button,
296 GtkEntry *entry)
297{
298 const gchar *text;
299
300 text = gtk_entry_get_text (entry);
301 gtk_widget_set_sensitive (button, text != NULL && text[0] != '\0');
302}
303
304static void
305pgd_find_selection_changed (GtkTreeSelection *treeselection,
306 PgdFindDemo *demo)
307{
308 GtkTreeModel *model;
309 GtkTreeIter iter;
310
311 if (gtk_tree_selection_get_selected (treeselection, &model, &iter)) {
312 guint page_index;
313 PopplerRectangle *rect;
314
315 gtk_tree_model_get (model, &iter,
316 PAGE_COLUMN, &page_index,
317 PAGE_RECT, &rect,
318 -1);
319
320 if (rect) {
321 demo->selected_match.x = rect->x1;
322 demo->selected_match.y = rect->y1;
323 demo->selected_match.width = rect->x2 - rect->x1;
324 demo->selected_match.height = rect->y2 - rect->y1;
325 } else {
326 demo->selected_match.width = 0;
327 demo->selected_match.height = 0;
328 }
329
330 if (page_index != demo->selected_page) {
331 demo->selected_page = page_index;
332 pgd_find_viewer_queue_redraw (demo);
333 } else {
334 gtk_widget_queue_draw (demo->darea);
335 }
336 }
337}
338
339static void
340pgd_find_case_sensitive_toggled (GtkToggleButton *togglebutton,
341 PgdFindDemo *demo)
342{
343 if (gtk_toggle_button_get_active (togglebutton))
344 demo->options |= POPPLER_FIND_CASE_SENSITIVE;
345 else
346 demo->options &= ~POPPLER_FIND_CASE_SENSITIVE;
347}
348
349static void
350pgd_find_backwards_toggled (GtkToggleButton *togglebutton,
351 PgdFindDemo *demo)
352{
353 if (gtk_toggle_button_get_active (togglebutton))
354 demo->options |= POPPLER_FIND_BACKWARDS;
355 else
356 demo->options &= ~POPPLER_FIND_BACKWARDS;
357}
358
359static void
360pgd_find_whole_words_toggled (GtkToggleButton *togglebutton,
361 PgdFindDemo *demo)
362{
363 if (gtk_toggle_button_get_active (togglebutton))
364 demo->options |= POPPLER_FIND_WHOLE_WORDS_ONLY;
365 else
366 demo->options &= ~POPPLER_FIND_WHOLE_WORDS_ONLY;
367}
368
369GtkWidget *
370pgd_find_create_widget (PopplerDocument *document)
371{
372 PgdFindDemo *demo;
373 GtkWidget *vbox, *hbox;
374 GtkWidget *button;
375 GtkWidget *swindow;
376 GtkWidget *checkbutton;
377 GtkTreeModel *model;
378 GtkWidget *treeview;
379 GtkCellRenderer *renderer;
380 GtkWidget *hpaned;
381 GtkTreeSelection *selection;
382
383 demo = g_new0 (PgdFindDemo, 1);
384
385 demo->doc = g_object_ref (document);
386
387 demo->n_pages = poppler_document_get_n_pages (document);
388 demo->selected_page = -1;
389 demo->options = POPPLER_FIND_DEFAULT;
390
391 hpaned = gtk_paned_new (GTK_ORIENTATION_HORIZONTAL);
392 gtk_paned_set_position (GTK_PANED (hpaned), 300);
393
394 vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
395
396 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
397
398 demo->entry = gtk_entry_new ();
399 gtk_box_pack_start (GTK_BOX (hbox), demo->entry, FALSE, TRUE, 0);
400 gtk_widget_show (demo->entry);
401
402 demo->progress = gtk_progress_bar_new ();
403 gtk_progress_bar_set_ellipsize (GTK_PROGRESS_BAR (demo->progress),
404 PANGO_ELLIPSIZE_END);
405 gtk_box_pack_start (GTK_BOX (hbox), demo->progress, TRUE, TRUE, 0);
406 gtk_widget_show (demo->progress);
407
408 button = gtk_button_new_with_label ("Find");
409 gtk_widget_set_sensitive (button, FALSE);
410 g_signal_connect (G_OBJECT (button), "clicked",
411 G_CALLBACK (pgd_find_button_clicked),
412 (gpointer)demo);
413 g_signal_connect_swapped (G_OBJECT (demo->entry), "changed",
414 G_CALLBACK (pgd_find_button_sensitivity_cb),
415 (gpointer)button);
416 gtk_box_pack_end (GTK_BOX (hbox), button, FALSE, FALSE, 0);
417 gtk_widget_show (button);
418
419 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 6);
420 gtk_widget_show (hbox);
421
422 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
423
424 checkbutton = gtk_check_button_new_with_label ("Case sensitive");
425 g_signal_connect (checkbutton, "toggled",
426 G_CALLBACK (pgd_find_case_sensitive_toggled),
427 demo);
428 gtk_box_pack_start (GTK_BOX (hbox), checkbutton, FALSE, FALSE, 0);
429 gtk_widget_show (checkbutton);
430
431 checkbutton = gtk_check_button_new_with_label ("Backwards");
432 g_signal_connect (checkbutton, "toggled",
433 G_CALLBACK (pgd_find_backwards_toggled),
434 demo);
435 gtk_box_pack_start (GTK_BOX (hbox), checkbutton, FALSE, FALSE, 0);
436 gtk_widget_show (checkbutton);
437
438 checkbutton = gtk_check_button_new_with_label ("Whole words only");
439 g_signal_connect (checkbutton, "toggled",
440 G_CALLBACK (pgd_find_whole_words_toggled),
441 demo);
442 gtk_box_pack_start (GTK_BOX (hbox), checkbutton, FALSE, FALSE, 0);
443 gtk_widget_show (checkbutton);
444
445 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
446 gtk_widget_show (hbox);
447
448 swindow = gtk_scrolled_window_new (NULL, NULL);
449 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swindow),
450 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
451
452 model = pgd_find_create_model ();
453 treeview = gtk_tree_view_new_with_model (model);
454 g_object_unref (model);
455 demo->treeview = treeview;
456 gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (treeview), TRUE);
457 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
458 gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
459 g_signal_connect (selection, "changed",
460 G_CALLBACK (pgd_find_selection_changed),
461 demo);
462
463 renderer = gtk_cell_renderer_text_new ();
464 gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview),
465 TITLE_COLUMN, "Matches",
466 renderer,
467 "text", TITLE_COLUMN,
468 NULL);
469
470 renderer = gtk_cell_renderer_text_new ();
471 gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview),
472 X1_COLUMN, "X1",
473 renderer,
474 "text", X1_COLUMN,
475 "visible", VISIBLE_COLUMN,
476 NULL);
477 renderer = gtk_cell_renderer_text_new ();
478 gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview),
479 Y1_COLUMN, "Y1",
480 renderer,
481 "text", Y1_COLUMN,
482 "visible", VISIBLE_COLUMN,
483 NULL);
484 renderer = gtk_cell_renderer_text_new ();
485 gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview),
486 X2_COLUMN, "X2",
487 renderer,
488 "text", X2_COLUMN,
489 "visible", VISIBLE_COLUMN,
490 NULL);
491 renderer = gtk_cell_renderer_text_new ();
492 gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview),
493 Y2_COLUMN, "Y2",
494 renderer,
495 "text", Y2_COLUMN,
496 "visible", VISIBLE_COLUMN,
497 NULL);
498 gtk_container_add (GTK_CONTAINER (swindow), treeview);
499 gtk_widget_show (treeview);
500
501 gtk_paned_add1 (GTK_PANED (hpaned), swindow);
502 gtk_widget_show (swindow);
503
504 demo->darea = gtk_drawing_area_new ();
505 g_signal_connect (demo->darea, "draw",
506 G_CALLBACK (pgd_find_viewer_drawing_area_draw),
507 demo);
508
509 swindow = gtk_scrolled_window_new (NULL, NULL);
510#if GTK_CHECK_VERSION(3, 7, 8)
511 gtk_container_add(GTK_CONTAINER(swindow), demo->darea);
512#else
513 gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (swindow), demo->darea);
514#endif
515 gtk_widget_show (demo->darea);
516
517 gtk_paned_add2 (GTK_PANED (hpaned), swindow);
518 gtk_widget_show (swindow);
519
520 gtk_box_pack_start (GTK_BOX (vbox), hpaned, TRUE, TRUE, 0);
521 gtk_widget_show (hpaned);
522
523 g_object_weak_ref (G_OBJECT (vbox),
524 (GWeakNotify)pgd_find_free,
525 (gpointer)demo);
526
527 return vbox;
528}
Note: See TracBrowser for help on using the repository browser.