gl-form.c 20 KB

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