comparison mupdf-source/platform/gl/gl-form.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
comparison
equal deleted inserted replaced
1:1d09e1dec1d9 2:b50eed0cc0ef
1 // Copyright (C) 2004-2024 Artifex Software, Inc.
2 //
3 // This file is part of MuPDF.
4 //
5 // MuPDF is free software: you can redistribute it and/or modify it under the
6 // terms of the GNU Affero General Public License as published by the Free
7 // Software Foundation, either version 3 of the License, or (at your option)
8 // any later version.
9 //
10 // MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY
11 // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12 // FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
13 // details.
14 //
15 // You should have received a copy of the GNU Affero General Public License
16 // along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html>
17 //
18 // Alternative licensing terms are available from the licensor.
19 // For commercial licensing, see <https://www.artifex.com/> or contact
20 // Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco,
21 // CA 94129, USA, for further information.
22
23 #include "gl-app.h"
24
25 #include <string.h>
26 #include <stdio.h>
27
28 #include "mupdf/helpers/pkcs7-openssl.h"
29
30 static pdf_annot *sig_widget;
31 static char *sig_distinguished_name = NULL;
32 static pdf_signature_error sig_cert_error;
33 static pdf_signature_error sig_digest_error;
34 static int sig_valid_until;
35 static int sig_readonly;
36
37 static char cert_filename[PATH_MAX];
38 static struct input cert_password;
39
40 static int sign_flags = PDF_SIGNATURE_DEFAULT_APPEARANCE;
41 static struct input sign_reason_input;
42 static int sign_reason_input_initialised = 0;
43 static struct input sign_location_input;
44 static int sign_location_input_initialised = 0;
45 static char sign_image_filename[PATH_MAX];
46 static fz_image *sign_image = NULL;
47
48 static struct texture preview_tex = { 0 };
49 static int preview_needs_update = 0;
50
51 static void trace_field_value(pdf_annot *annot, const char *set_value)
52 {
53 const char *get_value = pdf_annot_field_value(ctx, annot);
54 trace_action("print('Set field %d:', repr(%q), repr(%q));\n", pdf_to_num(ctx, pdf_annot_obj(ctx, annot)), set_value, get_value);
55 }
56
57 int do_sign(void)
58 {
59 pdf_pkcs7_signer *signer = NULL;
60 int ok = 1;
61 int labels = sign_flags & PDF_SIGNATURE_SHOW_LABELS;
62 int graphic_name = sign_flags & PDF_SIGNATURE_SHOW_GRAPHIC_NAME;
63 int text_name = sign_flags & PDF_SIGNATURE_SHOW_TEXT_NAME;
64 int dn = sign_flags & PDF_SIGNATURE_SHOW_DN;
65 int date = sign_flags & PDF_SIGNATURE_SHOW_DATE;
66 int logo = sign_flags & PDF_SIGNATURE_SHOW_LOGO;
67
68 fz_var(signer);
69
70 fz_try(ctx)
71 {
72 trace_action("widget.sign(new PDFPKCS7Signer(%q, %q), {", cert_filename, cert_password.text);
73
74 trace_action("'showLabels':%s,", labels ? "true" : "false");
75 trace_action("'showDN':%s,", dn ? "true" : "false");
76 trace_action("'showDate':%s,", date ? "true" : "false");
77 trace_action("'showTextName':%s,", text_name ? "true" : "false");
78 trace_action("'showGraphicName':%s,", graphic_name ? "true" : "false");
79 trace_action("'showLogo':%s}, ", logo ? "true" : "false");
80
81 if (strlen(sign_image_filename) > 0)
82 trace_action("new Image(%q, null), ", sign_image_filename);
83 else
84 trace_action("null, ");
85
86 if (strlen(sign_reason_input.text) > 0)
87 trace_action("%q, ", sign_reason_input.text);
88 else
89 trace_action("null, ");
90
91 if (strlen(sign_location_input.text) > 0)
92 trace_action("%q);\n", sign_location_input.text);
93 else
94 trace_action("null);\n");
95
96 signer = pkcs7_openssl_read_pfx(ctx, cert_filename, cert_password.text);
97 pdf_sign_signature(ctx, sig_widget, signer, sign_flags, sign_image, sign_reason_input.text, sign_location_input.text);
98 ui_show_warning_dialog("Signed document successfully.");
99 }
100 fz_always(ctx)
101 {
102 pdf_drop_signer(ctx, signer);
103 fz_drop_image(ctx, sign_image);
104 sign_image = NULL;
105 }
106 fz_catch(ctx)
107 {
108 ui_show_warning_dialog("%s", fz_caught_message(ctx));
109 fz_report_error(ctx);
110 ok = 0;
111 }
112 return ok;
113 }
114
115 static void do_clear_signature(void)
116 {
117 fz_try(ctx)
118 {
119 trace_action("widget.clearSignature();\n");
120 pdf_clear_signature(ctx, sig_widget);
121 ui_show_warning_dialog("Signature cleared successfully.");
122 }
123 fz_catch(ctx)
124 {
125 ui_show_warning_dialog("%s", fz_caught_message(ctx));
126 fz_report_error(ctx);
127 }
128 }
129
130 static int image_file_filter(const char *fn)
131 {
132 return !!fz_strstrcase(fn, ".jpg") || !!fz_strstrcase(fn, ".jpeg") || !!fz_strstrcase(fn, ".png");
133 }
134
135 static void signature_appearance_dialog(void);
136
137 static void signature_select_image_dialog(void)
138 {
139 if (ui_open_file(sign_image_filename, "Select an image for use in signature:"))
140 {
141 if (sign_image_filename[0] != 0)
142 {
143 fz_try(ctx)
144 {
145 sign_image = fz_new_image_from_file(ctx, sign_image_filename);
146 sign_flags &= ~PDF_SIGNATURE_SHOW_GRAPHIC_NAME;
147 preview_needs_update = 1;
148 }
149 fz_catch(ctx)
150 {
151 ui_show_warning_dialog("%s", fz_caught_message(ctx));
152 fz_report_error(ctx);
153 ui.dialog = signature_select_image_dialog;
154 }
155 }
156
157 ui.dialog = signature_appearance_dialog;
158 }
159 }
160
161
162 static void signature_appearance_init(void)
163 {
164 if (!sign_reason_input_initialised)
165 {
166 ui_input_init(&sign_reason_input, "");
167 sign_reason_input_initialised = 1;
168 }
169 if (!sign_location_input_initialised)
170 {
171 ui_input_init(&sign_location_input, "");
172 sign_location_input_initialised = 1;
173 }
174 preview_needs_update = 1;
175 }
176
177 static void signature_appearance_dialog(void)
178 {
179 ui_dialog_begin(ui.gridsize*16, (ui.gridsize+4)*16);
180 {
181 int orig_flags = sign_flags;
182 int labels = sign_flags & PDF_SIGNATURE_SHOW_LABELS;
183 int graphic_name = sign_flags & PDF_SIGNATURE_SHOW_GRAPHIC_NAME;
184 int graphic_image = sign_image != NULL;
185 int text_name = sign_flags & PDF_SIGNATURE_SHOW_TEXT_NAME;
186 int dn = sign_flags & PDF_SIGNATURE_SHOW_DN;
187 int date = sign_flags & PDF_SIGNATURE_SHOW_DATE;
188 int logo = sign_flags & PDF_SIGNATURE_SHOW_LOGO;
189 fz_irect preview_rect;
190
191 ui_layout(T, X, NW, ui.padsize*2, ui.padsize);
192
193 {
194 ui_panel_begin(0, ui.gridsize * 6, 0, 0, 0);
195 ui_layout(T, Y, NW, 0, 0);
196
197 ui_label("Preview:");
198 ui_spacer();
199
200 ui_layout(ALL, BOTH, CENTER, 0, 0);
201 preview_rect = ui_pack(0, 0);
202
203 ui_panel_end();
204 }
205
206 ui_spacer();
207
208 {
209 ui_label("Graphic:");
210 ui_checkbox("Name", &graphic_name);
211
212 ui_panel_begin(ui.gridsize * 10, ui.gridsize, 0, 0, 0);
213 ui_layout(L, X, NW, 0, 0);
214 ui_checkbox("Image", &graphic_image);
215
216 ui_spacer();
217
218 if (ui_button("Select image"))
219 {
220 fz_drop_image(ctx, sign_image);
221 sign_image = NULL;
222 ui_init_open_file(".", image_file_filter);
223 ui.dialog = signature_select_image_dialog;
224 }
225 ui_panel_end();
226
227 if (graphic_name && graphic_image)
228 {
229 fz_drop_image(ctx, sign_image);
230 sign_image = NULL;
231 graphic_image = 0;
232 }
233 }
234
235 ui_spacer();
236
237 {
238 ui_label("Text:");
239
240 ui_panel_begin(0, ui.gridsize * 5, 0, 0, 0);
241 {
242 ui_layout(L, Y, NW, 0, 0);
243
244 ui_panel_begin(ui.gridsize * 7, ui.gridsize * 5, 0, 0, 0);
245 {
246 ui_layout(T, Y, NW, 0, 0);
247 ui_checkbox("Labels", &labels);
248 ui_checkbox("Common name", &text_name);
249 ui_checkbox("Distinguished name", &dn);
250 ui_checkbox("Date", &date);
251 ui_checkbox("Logo", &logo);
252 }
253 ui_panel_end();
254
255 ui_panel_begin(ui.gridsize * 5, ui.gridsize * 5, 0, 0, 0);
256 {
257 ui_layout(T, Y, NW, 0, 0);
258
259 ui_panel_begin(ui.gridsize * 5, ui.gridsize, 0, 0, 0);
260 ui_layout(L, X, NW, 0, 0);
261 ui_label("Reason:");
262 ui_spacer();
263 if (ui_input(&sign_reason_input, ui.gridsize * 5, 1))
264 preview_needs_update = 1;
265 ui_panel_end();
266
267 ui_panel_begin(ui.gridsize * 10, ui.gridsize, 0, 0, 0);
268 ui_layout(L, X, NW, 0, 0);
269 ui_label("Location:");
270 ui_spacer();
271 if (ui_input(&sign_location_input, ui.gridsize * 5, 1))
272 preview_needs_update = 1;
273 ui_panel_end();
274 }
275 ui_panel_end();
276 }
277 ui_panel_end();
278
279 if (!text_name && !dn && !date && !strlen(sign_reason_input.text) && !strlen(sign_location_input.text))
280 {
281 labels = 1;
282 text_name = 1;
283 }
284 }
285
286 sign_flags = 0;
287 sign_flags |= labels ? PDF_SIGNATURE_SHOW_LABELS : 0;
288 sign_flags |= dn ? PDF_SIGNATURE_SHOW_DN : 0;
289 sign_flags |= date ? PDF_SIGNATURE_SHOW_DATE : 0;
290 sign_flags |= text_name ? PDF_SIGNATURE_SHOW_TEXT_NAME : 0;
291 sign_flags |= graphic_name ? PDF_SIGNATURE_SHOW_GRAPHIC_NAME : 0;
292 sign_flags |= logo ? PDF_SIGNATURE_SHOW_LOGO : 0;
293
294 if (orig_flags != sign_flags)
295 preview_needs_update = 1;
296
297 if (preview_needs_update)
298 {
299 fz_pixmap *pix;
300 pdf_pkcs7_signer *signer;
301 int w = preview_rect.x1 - preview_rect.x0;
302 int h = preview_rect.y1 - preview_rect.y0;
303
304 signer = pkcs7_openssl_read_pfx(ctx, cert_filename, cert_password.text);
305 pix = pdf_preview_signature_as_pixmap(ctx,
306 w, h, FZ_LANG_UNSET, signer, sign_flags, sign_image,
307 strlen(sign_reason_input.text) ? sign_reason_input.text : NULL,
308 strlen(sign_location_input.text) ? sign_location_input.text : NULL);
309 pdf_drop_signer(ctx, signer);
310 ui_texture_from_pixmap(&preview_tex, pix);
311 fz_drop_pixmap(ctx, pix);
312
313 preview_needs_update = 0;
314 }
315
316 ui_draw_image(&preview_tex, preview_rect.x0, preview_rect.y0);
317
318 ui_layout(B, X, NW, ui.padsize, ui.padsize);
319 ui_panel_begin(0, ui.gridsize, 0, 0, 0);
320 {
321 ui_layout(R, NONE, S, 0, 0);
322 if (ui_button("Cancel"))
323 {
324 fz_drop_image(ctx, sign_image);
325 sign_image = NULL;
326 ui.dialog = NULL;
327 }
328 ui_spacer();
329 if (ui_button("Okay"))
330 {
331 ui.dialog = NULL;
332 do_save_signed_pdf_file();
333 }
334 }
335 ui_panel_end();
336 }
337 ui_dialog_end();
338 }
339
340 static int is_valid_certificate_and_password(void)
341 {
342 fz_try(ctx)
343 {
344 pdf_pkcs7_signer *signer = pkcs7_openssl_read_pfx(ctx, cert_filename, cert_password.text);
345 pdf_drop_signer(ctx, signer);
346 }
347 fz_catch(ctx)
348 return 0;
349 return 1;
350 }
351
352 static void cert_password_dialog(void)
353 {
354 int is;
355 ui_dialog_begin(ui.gridsize*16, (ui.gridsize+4)*3);
356 {
357 ui_layout(T, X, NW, ui.padsize, ui.padsize);
358 ui_label("Password:");
359 is = ui_input(&cert_password, 200, 1);
360
361 ui_layout(B, X, NW, ui.padsize, ui.padsize);
362 ui_panel_begin(0, ui.gridsize, 0, 0, 0);
363 {
364 ui_layout(R, NONE, S, 0, 0);
365 if (ui_button("Cancel"))
366 ui.dialog = NULL;
367 ui_spacer();
368 if (ui_button("Okay") || is == UI_INPUT_ACCEPT)
369 {
370 if (is_valid_certificate_and_password()) {
371 signature_appearance_init();
372 ui.dialog = signature_appearance_dialog;
373 } else {
374 ui_show_warning_dialog("%s", fz_caught_message(ctx));
375 fz_report_error(ctx);
376 }
377 }
378 }
379 ui_panel_end();
380 }
381 ui_dialog_end();
382 }
383
384 static int cert_file_filter(const char *fn)
385 {
386 return !!fz_strstrcase(fn, ".pfx");
387 }
388
389 static void cert_file_dialog(void)
390 {
391 if (ui_open_file(cert_filename, "Select a certificate file to sign with:"))
392 {
393 if (cert_filename[0] != 0)
394 {
395 ui_input_init(&cert_password, "");
396 ui.focus = &cert_password;
397 ui.dialog = cert_password_dialog;
398 }
399 else
400 ui.dialog = NULL;
401 }
402 }
403
404 static void sig_sign_dialog(void)
405 {
406 const char *label = pdf_field_label(ctx, pdf_annot_obj(ctx, sig_widget));
407
408 ui_dialog_begin(ui.gridsize*16, (ui.gridsize+4)*3 + ui.lineheight*10);
409 {
410 ui_layout(T, X, NW, ui.padsize, ui.padsize);
411
412 ui_label("%s", label);
413 ui_spacer();
414
415 ui_label("Would you like to sign this field?");
416
417 ui_layout(B, X, NW, ui.padsize, ui.padsize);
418 ui_panel_begin(0, ui.gridsize, 0, 0, 0);
419 {
420 ui_layout(R, NONE, S, 0, 0);
421 if (ui_button("Cancel") || (!ui.focus && ui.key == KEY_ESCAPE))
422 ui.dialog = NULL;
423 ui_spacer();
424 if (!(pdf_field_flags(ctx, pdf_annot_obj(ctx, sig_widget)) & PDF_FIELD_IS_READ_ONLY))
425 {
426 if (ui_button("Sign"))
427 {
428 fz_strlcpy(cert_filename, filename, sizeof cert_filename);
429 ui_init_open_file(".", cert_file_filter);
430 ui.dialog = cert_file_dialog;
431 }
432 }
433 }
434 ui_panel_end();
435 }
436 ui_dialog_end();
437 }
438
439 static void sig_verify_dialog(void)
440 {
441 const char *label = pdf_field_label(ctx, pdf_annot_obj(ctx, sig_widget));
442
443 ui_dialog_begin(ui.gridsize*16, (ui.gridsize+4)*3 + ui.lineheight*10);
444 {
445 ui_layout(T, X, NW, ui.padsize, ui.padsize);
446
447 ui_label("%s", label);
448 ui_spacer();
449
450 if (sig_readonly)
451 {
452 ui_label("Signature field is read-only.");
453 ui_spacer();
454 }
455
456 ui_label("Distinguished name: %s.", sig_distinguished_name);
457 ui_spacer();
458
459 if (sig_cert_error)
460 ui_label("Certificate error: %s", pdf_signature_error_description(sig_cert_error));
461 else
462 ui_label("Certificate is trusted.");
463
464 ui_spacer();
465
466 if (sig_digest_error)
467 ui_label("Digest error: %s", pdf_signature_error_description(sig_digest_error));
468 else if (sig_valid_until == 0)
469 ui_label("The fields signed by this signature are unchanged.");
470 else if (sig_valid_until == 1)
471 ui_label("This signature was invalidated in the last update by the signed fields being changed.");
472 else if (sig_valid_until == 2)
473 ui_label("This signature was invalidated in the penultimate update by the signed fields being changed.");
474 else
475 ui_label("This signature was invalidated %d updates ago by the signed fields being changed.", sig_valid_until);
476
477 ui_layout(B, X, NW, ui.padsize, ui.padsize);
478 ui_panel_begin(0, ui.gridsize, 0, 0, 0);
479 {
480 ui_layout(L, NONE, S, 0, 0);
481 if (!sig_readonly)
482 {
483 if (ui_button("Clear"))
484 {
485 ui.dialog = NULL;
486 do_clear_signature();
487 }
488 }
489 ui_layout(R, NONE, S, 0, 0);
490 if (ui_button("Close") || (!ui.focus && ui.key == KEY_ESCAPE))
491 ui.dialog = NULL;
492 }
493 ui_panel_end();
494 }
495 ui_dialog_end();
496 }
497
498 static void show_sig_dialog(pdf_annot *widget)
499 {
500 fz_try(ctx)
501 {
502 sig_widget = widget;
503
504 if (pdf_signature_is_signed(ctx, pdf, pdf_annot_obj(ctx, widget)))
505 {
506 pdf_pkcs7_verifier *verifier;
507 pdf_pkcs7_distinguished_name *dn;
508
509 sig_readonly = pdf_widget_is_readonly(ctx, widget);
510
511 sig_valid_until = pdf_validate_signature(ctx, widget);
512
513 verifier = pkcs7_openssl_new_verifier(ctx);
514
515 sig_cert_error = pdf_check_widget_certificate(ctx, verifier, widget);
516 sig_digest_error = pdf_check_widget_digest(ctx, verifier, widget);
517
518 fz_free(ctx, sig_distinguished_name);
519 dn = pdf_signature_get_widget_signatory(ctx, verifier, widget);
520 if (dn)
521 sig_distinguished_name = pdf_signature_format_distinguished_name(ctx, dn);
522 else
523 sig_distinguished_name = fz_strdup(ctx, "Signature information missing.");
524 pdf_signature_drop_distinguished_name(ctx, dn);
525
526 pdf_drop_verifier(ctx, verifier);
527
528 ui.dialog = sig_verify_dialog;
529 }
530 else
531 {
532 ui.dialog = sig_sign_dialog;
533 }
534 }
535 fz_catch(ctx)
536 {
537 ui_show_warning_dialog("%s", fz_caught_message(ctx));
538 fz_report_error(ctx);
539 }
540 }
541
542 static pdf_annot *tx_widget;
543 static struct input tx_input;
544
545 static void tx_dialog(void)
546 {
547 int ff = pdf_annot_field_flags(ctx, tx_widget);
548 const char *label = pdf_annot_field_label(ctx, tx_widget);
549 int tx_h = (ff & PDF_TX_FIELD_IS_MULTILINE) ? 10 : 1;
550 int lbl_h = ui_break_lines((char*)label, NULL, 20, 394, NULL);
551 int is;
552
553 ui_dialog_begin(ui.gridsize*16, (ui.gridsize+4)*3 + ui.lineheight*(tx_h+lbl_h-2));
554 {
555 ui_layout(T, X, NW, ui.padsize, ui.padsize);
556 ui_label("%s", label);
557 tx_input.widget = tx_widget;
558 is = ui_input(&tx_input, 200, tx_h);
559
560 ui_layout(B, X, NW, ui.padsize, ui.padsize);
561 ui_panel_begin(0, ui.gridsize, 0, 0, 0);
562 {
563 ui_layout(R, NONE, S, 0, 0);
564 if (ui_button("Cancel") || (!ui.focus && ui.key == KEY_ESCAPE))
565 ui.dialog = NULL;
566 ui_spacer();
567 if (ui_button("Okay") || is == UI_INPUT_ACCEPT)
568 {
569 if (pdf_set_text_field_value(ctx, tx_widget, tx_input.text))
570 {
571 trace_action("widget.setTextValue(%q);\n", tx_input.text);
572 trace_field_value(tx_widget, tx_input.text);
573 ui.dialog = NULL;
574 }
575 }
576 }
577 ui_panel_end();
578 }
579 ui_dialog_end();
580 }
581
582 void show_tx_dialog(pdf_annot *widget)
583 {
584 ui_input_init(&tx_input, pdf_annot_field_value(ctx, widget));
585 ui.focus = &tx_input;
586 ui.dialog = tx_dialog;
587 tx_widget = widget;
588 }
589
590 static pdf_annot *ch_widget;
591 static void ch_dialog(void)
592 {
593 const char *label;
594 const char *value;
595 char **options;
596 int n, choice;
597 int label_h;
598
599 label = pdf_field_label(ctx, pdf_annot_obj(ctx, ch_widget));
600 label_h = ui_break_lines((char*)label, NULL, 20, 394, NULL);
601 n = pdf_choice_widget_options(ctx, ch_widget, 0, NULL);
602 options = fz_malloc_array(ctx, n, char *);
603 pdf_choice_widget_options(ctx, ch_widget, 0, (const char **)options);
604 value = pdf_field_value(ctx, pdf_annot_obj(ctx, ch_widget));
605
606 ui_dialog_begin(ui.gridsize*16, (ui.gridsize+4)*3 + ui.lineheight*(label_h-1));
607 {
608 ui_layout(T, X, NW, ui.padsize, ui.padsize);
609
610 ui_label("%s", label);
611 choice = ui_select("Widget/Ch", value, (const char **)options, n);
612 if (choice >= 0)
613 {
614 trace_action("widget.setChoiceValue(%q);\n", options[choice]);
615 pdf_set_choice_field_value(ctx, ch_widget, options[choice]);
616 trace_field_value(ch_widget, options[choice]);
617 }
618
619 ui_layout(B, X, NW, ui.padsize, ui.padsize);
620 ui_panel_begin(0, ui.gridsize, 0, 0, 0);
621 {
622 ui_layout(R, NONE, S, 0, 0);
623 if (ui_button("Cancel") || (!ui.focus && ui.key == KEY_ESCAPE))
624 ui.dialog = NULL;
625 ui_spacer();
626 if (ui_button("Okay"))
627 {
628 ui.dialog = NULL;
629 }
630 }
631 ui_panel_end();
632 }
633 ui_dialog_end();
634
635 fz_free(ctx, options);
636 }
637
638 void do_widget_canvas(fz_irect canvas_area)
639 {
640 pdf_annot *widget;
641 fz_rect bounds;
642 fz_irect area;
643 int idx;
644
645 if (!pdf)
646 return;
647
648 for (idx = 0, widget = pdf_first_widget(ctx, page); widget; ++idx, widget = pdf_next_widget(ctx, widget))
649 {
650 bounds = pdf_bound_widget(ctx, widget);
651 bounds = fz_transform_rect(bounds, view_page_ctm);
652 area = fz_irect_from_rect(bounds);
653
654 if (ui_mouse_inside(canvas_area) && ui_mouse_inside(area))
655 {
656 if (!pdf_annot_hot(ctx, widget))
657 {
658 trace_action("page.getWidgets()[%d].eventEnter();\n", idx);
659 pdf_annot_event_enter(ctx, widget);
660 }
661 pdf_set_annot_hot(ctx, widget, 1);
662
663 ui.hot = widget;
664 if (!ui.active && ui.down)
665 {
666 ui.active = widget;
667 trace_action("page.getWidgets()[%d].eventDown();\n", idx);
668 pdf_annot_event_down(ctx, widget);
669 if (ui.selected_annot != widget)
670 {
671 if (ui.selected_annot && pdf_annot_type(ctx, ui.selected_annot) == PDF_ANNOT_WIDGET)
672 {
673 trace_action("widget.eventBlur();\n", idx);
674 pdf_annot_event_blur(ctx, ui.selected_annot);
675 }
676 trace_action("widget = page.getWidgets()[%d];\n", idx);
677 ui_select_annot(pdf_keep_annot(ctx, widget));
678 trace_action("widget.eventFocus();\n");
679 pdf_annot_event_focus(ctx, widget);
680 }
681 }
682 }
683 else
684 {
685 if (pdf_annot_hot(ctx, widget))
686 {
687 trace_action("page.getWidgets()[%d].eventExit();\n", idx);
688 pdf_annot_event_exit(ctx, widget);
689 }
690 pdf_set_annot_hot(ctx, widget, 0);
691 }
692
693 /* Set is_hot and is_active to select current appearance */
694 pdf_set_annot_active(ctx, widget, (ui.active == widget && ui.down));
695
696 if (showform)
697 {
698 glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
699 glEnable(GL_BLEND);
700 glColor4f(0, 0, 1, 0.1f);
701 glRectf(area.x0, area.y0, area.x1, area.y1);
702 glDisable(GL_BLEND);
703 }
704
705 if (ui.active == widget || (!ui.active && ui.hot == widget))
706 {
707 glLineStipple(1, 0xAAAA);
708 glEnable(GL_LINE_STIPPLE);
709 glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ZERO);
710 glEnable(GL_BLEND);
711 glColor4f(1, 1, 1, 1);
712 glBegin(GL_LINE_LOOP);
713 glVertex2f(area.x0-0.5f, area.y0-0.5f);
714 glVertex2f(area.x1+0.5f, area.y0-0.5f);
715 glVertex2f(area.x1+0.5f, area.y1+0.5f);
716 glVertex2f(area.x0-0.5f, area.y1+0.5f);
717 glEnd();
718 glDisable(GL_BLEND);
719 glDisable(GL_LINE_STIPPLE);
720 }
721
722 if (ui.hot == widget && ui.active == widget && !ui.down)
723 {
724 trace_action("widget.eventUp();\n");
725 pdf_annot_event_up(ctx, widget);
726
727 if (pdf_widget_type(ctx, widget) == PDF_WIDGET_TYPE_SIGNATURE)
728 {
729 show_sig_dialog(widget);
730 }
731 else
732 {
733 if (pdf_annot_field_flags(ctx, widget) & PDF_FIELD_IS_READ_ONLY)
734 continue;
735
736 switch (pdf_widget_type(ctx, widget))
737 {
738 default:
739 break;
740 case PDF_WIDGET_TYPE_CHECKBOX:
741 case PDF_WIDGET_TYPE_RADIOBUTTON:
742 trace_action("widget.toggle();\n");
743 pdf_toggle_widget(ctx, widget);
744 break;
745 case PDF_WIDGET_TYPE_TEXT:
746 show_tx_dialog(widget);
747 break;
748 case PDF_WIDGET_TYPE_COMBOBOX:
749 case PDF_WIDGET_TYPE_LISTBOX:
750 ui.dialog = ch_dialog;
751 ch_widget = widget;
752 break;
753 }
754 }
755 }
756 }
757 }