1 | #include "gl-app.h" |
2 | |
3 | #include <string.h> |
4 | #include <stdlib.h> |
5 | #include <stdio.h> |
6 | #include <time.h> |
7 | #include <limits.h> |
8 | |
9 | #ifndef PATH_MAX |
10 | #define PATH_MAX 2048 |
11 | #endif |
12 | |
13 | static int is_draw_mode = 0; |
14 | |
15 | static char save_filename[PATH_MAX]; |
16 | static pdf_write_options save_opts; |
17 | static struct input opwinput; |
18 | static struct input upwinput; |
19 | |
20 | static int pdf_filter(const char *fn) |
21 | { |
22 | const char *extension = strrchr(fn, '.'); |
23 | if (extension && !fz_strcasecmp(extension, ".pdf" )) |
24 | return 1; |
25 | return 0; |
26 | } |
27 | |
28 | static void init_save_pdf_options(void) |
29 | { |
30 | save_opts = pdf_default_write_options; |
31 | save_opts.do_compress = 1; |
32 | save_opts.do_compress_images = 1; |
33 | save_opts.do_compress_fonts = 1; |
34 | } |
35 | |
36 | static const char *cryptalgo_names[] = { |
37 | "Keep" , |
38 | "None" , |
39 | "RC4, 40 bit" , |
40 | "RC4, 128 bit" , |
41 | "AES, 128 bit" , |
42 | "AES, 256 bit" , |
43 | }; |
44 | |
45 | static void save_pdf_options(void) |
46 | { |
47 | const char *cryptalgo = cryptalgo_names[save_opts.do_encrypt]; |
48 | int choice; |
49 | |
50 | ui_layout(T, X, NW, 2, 2); |
51 | ui_label("PDF write options:" ); |
52 | ui_layout(T, X, NW, 4, 2); |
53 | |
54 | ui_checkbox("Incremental" , &save_opts.do_incremental); |
55 | ui_spacer(); |
56 | ui_checkbox("Pretty-print" , &save_opts.do_pretty); |
57 | ui_checkbox("Ascii" , &save_opts.do_ascii); |
58 | ui_checkbox("Decompress" , &save_opts.do_decompress); |
59 | ui_checkbox("Compress" , &save_opts.do_compress); |
60 | ui_checkbox("Compress images" , &save_opts.do_compress_images); |
61 | ui_checkbox("Compress fonts" , &save_opts.do_compress_fonts); |
62 | if (save_opts.do_incremental) |
63 | { |
64 | save_opts.do_garbage = 0; |
65 | save_opts.do_linear = 0; |
66 | save_opts.do_clean = 0; |
67 | save_opts.do_sanitize = 0; |
68 | save_opts.do_encrypt = PDF_ENCRYPT_KEEP; |
69 | } |
70 | else |
71 | { |
72 | ui_spacer(); |
73 | ui_checkbox("Linearize" , &save_opts.do_linear); |
74 | ui_checkbox("Garbage collect" , &save_opts.do_garbage); |
75 | ui_checkbox("Clean syntax" , &save_opts.do_clean); |
76 | ui_checkbox("Sanitize syntax" , &save_opts.do_sanitize); |
77 | |
78 | ui_spacer(); |
79 | ui_label("Encryption:" ); |
80 | choice = ui_select("Encryption" , cryptalgo, cryptalgo_names, nelem(cryptalgo_names)); |
81 | if (choice != -1) |
82 | save_opts.do_encrypt = choice; |
83 | } |
84 | |
85 | if (save_opts.do_encrypt >= PDF_ENCRYPT_RC4_40) |
86 | { |
87 | ui_spacer(); |
88 | ui_label("User password:" ); |
89 | if (ui_input(&upwinput, 32, 1) >= UI_INPUT_EDIT) |
90 | fz_strlcpy(save_opts.upwd_utf8, upwinput.text, nelem(save_opts.upwd_utf8)); |
91 | ui_label("Owner password:" ); |
92 | if (ui_input(&opwinput, 32, 1) >= UI_INPUT_EDIT) |
93 | fz_strlcpy(save_opts.opwd_utf8, opwinput.text, nelem(save_opts.opwd_utf8)); |
94 | } |
95 | } |
96 | |
97 | static void save_pdf_dialog(void) |
98 | { |
99 | ui_input_init(&opwinput, "" ); |
100 | ui_input_init(&upwinput, "" ); |
101 | |
102 | if (ui_save_file(save_filename, save_pdf_options)) |
103 | { |
104 | ui.dialog = NULL; |
105 | if (save_filename[0] != 0) |
106 | { |
107 | if (save_opts.do_garbage) |
108 | save_opts.do_garbage = 2; |
109 | fz_try(ctx) |
110 | { |
111 | pdf_save_document(ctx, pdf, save_filename, &save_opts); |
112 | fz_strlcpy(filename, save_filename, PATH_MAX); |
113 | update_title(); |
114 | } |
115 | fz_catch(ctx) |
116 | { |
117 | ui_show_warning_dialog("%s" , fz_caught_message(ctx)); |
118 | } |
119 | } |
120 | } |
121 | } |
122 | |
123 | void do_save_pdf_file(void) |
124 | { |
125 | if (pdf) |
126 | { |
127 | init_save_pdf_options(); |
128 | ui_init_save_file(filename, pdf_filter); |
129 | ui.dialog = save_pdf_dialog; |
130 | } |
131 | } |
132 | |
133 | static int rects_differ(fz_rect a, fz_rect b, float threshold) |
134 | { |
135 | if (fz_abs(a.x0 - b.x0) > threshold) return 1; |
136 | if (fz_abs(a.y0 - b.y0) > threshold) return 1; |
137 | if (fz_abs(a.x1 - b.x1) > threshold) return 1; |
138 | if (fz_abs(a.y1 - b.y1) > threshold) return 1; |
139 | return 0; |
140 | } |
141 | |
142 | static int points_differ(fz_point a, fz_point b, float threshold) |
143 | { |
144 | if (fz_abs(a.x - b.x) > threshold) return 1; |
145 | if (fz_abs(a.y - b.y) > threshold) return 1; |
146 | return 0; |
147 | } |
148 | |
149 | static const char *getuser(void) |
150 | { |
151 | const char *u; |
152 | u = getenv("USER" ); |
153 | if (!u) u = getenv("USERNAME" ); |
154 | if (!u) u = "user" ; |
155 | return u; |
156 | } |
157 | |
158 | static void new_annot(int type) |
159 | { |
160 | selected_annot = pdf_create_annot(ctx, page, type); |
161 | |
162 | pdf_set_annot_modification_date(ctx, selected_annot, time(NULL)); |
163 | if (pdf_annot_has_author(ctx, selected_annot)) |
164 | pdf_set_annot_author(ctx, selected_annot, getuser()); |
165 | |
166 | pdf_update_appearance(ctx, selected_annot); |
167 | |
168 | switch (type) |
169 | { |
170 | case PDF_ANNOT_INK: |
171 | case PDF_ANNOT_POLYGON: |
172 | case PDF_ANNOT_POLY_LINE: |
173 | case PDF_ANNOT_HIGHLIGHT: |
174 | case PDF_ANNOT_UNDERLINE: |
175 | case PDF_ANNOT_STRIKE_OUT: |
176 | case PDF_ANNOT_SQUIGGLY: |
177 | case PDF_ANNOT_REDACT: |
178 | is_draw_mode = 1; |
179 | break; |
180 | } |
181 | |
182 | render_page(); |
183 | } |
184 | |
185 | static void do_annotate_flags(void) |
186 | { |
187 | char buf[4096]; |
188 | int f = pdf_annot_flags(ctx, selected_annot); |
189 | fz_strlcpy(buf, "Flags:" , sizeof buf); |
190 | if (f & PDF_ANNOT_IS_INVISIBLE) fz_strlcat(buf, " inv" , sizeof buf); |
191 | if (f & PDF_ANNOT_IS_HIDDEN) fz_strlcat(buf, " hidden" , sizeof buf); |
192 | if (f & PDF_ANNOT_IS_PRINT) fz_strlcat(buf, " print" , sizeof buf); |
193 | if (f & PDF_ANNOT_IS_NO_ZOOM) fz_strlcat(buf, " nz" , sizeof buf); |
194 | if (f & PDF_ANNOT_IS_NO_ROTATE) fz_strlcat(buf, " nr" , sizeof buf); |
195 | if (f & PDF_ANNOT_IS_NO_VIEW) fz_strlcat(buf, " nv" , sizeof buf); |
196 | if (f & PDF_ANNOT_IS_READ_ONLY) fz_strlcat(buf, " ro" , sizeof buf); |
197 | if (f & PDF_ANNOT_IS_LOCKED) fz_strlcat(buf, " lock" , sizeof buf); |
198 | if (f & PDF_ANNOT_IS_TOGGLE_NO_VIEW) fz_strlcat(buf, " tnv" , sizeof buf); |
199 | if (f & PDF_ANNOT_IS_LOCKED_CONTENTS) fz_strlcat(buf, " lc" , sizeof buf); |
200 | if (!f) fz_strlcat(buf, " none" , sizeof buf); |
201 | ui_label("%s" , buf); |
202 | } |
203 | |
204 | static const char *color_names[] = { |
205 | "None" , |
206 | "Aqua" , |
207 | "Black" , |
208 | "Blue" , |
209 | "Fuchsia" , |
210 | "Gray" , |
211 | "Green" , |
212 | "Lime" , |
213 | "Maroon" , |
214 | "Navy" , |
215 | "Olive" , |
216 | "Orange" , |
217 | "Purple" , |
218 | "Red" , |
219 | "Silver" , |
220 | "Teal" , |
221 | "White" , |
222 | "Yellow" , |
223 | }; |
224 | |
225 | static unsigned int color_values[] = { |
226 | 0x00000000, /* transparent */ |
227 | 0xff00ffff, /* aqua */ |
228 | 0xff000000, /* black */ |
229 | 0xff0000ff, /* blue */ |
230 | 0xffff00ff, /* fuchsia */ |
231 | 0xff808080, /* gray */ |
232 | 0xff008000, /* green */ |
233 | 0xff00ff00, /* lime */ |
234 | 0xff800000, /* maroon */ |
235 | 0xff000080, /* navy */ |
236 | 0xff808000, /* olive */ |
237 | 0xffffa500, /* orange */ |
238 | 0xff800080, /* purple */ |
239 | 0xffff0000, /* red */ |
240 | 0xffc0c0c0, /* silver */ |
241 | 0xff008080, /* teal */ |
242 | 0xffffffff, /* white */ |
243 | 0xffffff00, /* yellow */ |
244 | }; |
245 | |
246 | static unsigned int hex_from_color(int n, float color[4]) |
247 | { |
248 | float rgb[4]; |
249 | int r, g, b; |
250 | switch (n) |
251 | { |
252 | default: |
253 | return 0; |
254 | case 1: |
255 | r = color[0] * 255; |
256 | return 0xff000000 | (r<<16) | (r<<8) | r; |
257 | case 3: |
258 | r = color[0] * 255; |
259 | g = color[1] * 255; |
260 | b = color[2] * 255; |
261 | return 0xff000000 | (r<<16) | (g<<8) | b; |
262 | case 4: |
263 | fz_convert_color(ctx, fz_device_cmyk(ctx), color, fz_device_rgb(ctx), rgb, NULL, fz_default_color_params); |
264 | r = rgb[0] * 255; |
265 | g = rgb[1] * 255; |
266 | b = rgb[2] * 255; |
267 | return 0xff000000 | (r<<16) | (g<<8) | b; |
268 | } |
269 | } |
270 | |
271 | static const char *name_from_hex(unsigned int hex) |
272 | { |
273 | static char buf[10]; |
274 | int i; |
275 | for (i = 0; i < nelem(color_names); ++i) |
276 | if (color_values[i] == hex) |
277 | return color_names[i]; |
278 | fz_snprintf(buf, sizeof buf, "#%06x" , hex & 0xffffff); |
279 | return buf; |
280 | } |
281 | |
282 | static void do_annotate_color(char *label, |
283 | void (*get_color)(fz_context *ctx, pdf_annot *annot, int *n, float color[4]), |
284 | void (*set_color)(fz_context *ctx, pdf_annot *annot, int n, const float color[4])) |
285 | { |
286 | float color[4]; |
287 | int hex, choice, n; |
288 | get_color(ctx, selected_annot, &n, color); |
289 | ui_label("%s:" , label); |
290 | choice = ui_select(label, name_from_hex(hex_from_color(n, color)), color_names, nelem(color_names)); |
291 | if (choice != -1) |
292 | { |
293 | hex = color_values[choice]; |
294 | if (hex == 0) |
295 | set_color(ctx, selected_annot, 0, color); |
296 | else |
297 | { |
298 | color[0] = ((hex>>16)&0xff) / 255.0f; |
299 | color[1] = ((hex>>8)&0xff) / 255.0f; |
300 | color[2] = ((hex)&0xff) / 255.0f; |
301 | set_color(ctx, selected_annot, 3, color); |
302 | } |
303 | } |
304 | } |
305 | |
306 | static void do_annotate_author(void) |
307 | { |
308 | if (pdf_annot_has_author(ctx, selected_annot)) |
309 | { |
310 | const char *author = pdf_annot_author(ctx, selected_annot); |
311 | if (strlen(author) > 0) |
312 | ui_label("Author: %s" , author); |
313 | } |
314 | } |
315 | |
316 | static void do_annotate_date(void) |
317 | { |
318 | time_t secs = pdf_annot_modification_date(ctx, selected_annot); |
319 | if (secs > 0) |
320 | { |
321 | #ifdef _POSIX_SOURCE |
322 | struct tm tmbuf, *tm = gmtime_r(&secs, &tmbuf); |
323 | #else |
324 | struct tm *tm = gmtime(&secs); |
325 | #endif |
326 | char buf[100]; |
327 | if (tm) |
328 | { |
329 | strftime(buf, sizeof buf, "%Y-%m-%d %H:%M UTC" , tm); |
330 | ui_label("Date: %s" , buf); |
331 | } |
332 | } |
333 | } |
334 | |
335 | static void do_annotate_contents(void) |
336 | { |
337 | static pdf_annot *last_annot = NULL; |
338 | static struct input input; |
339 | const char *contents; |
340 | |
341 | if (selected_annot != last_annot) |
342 | { |
343 | last_annot = selected_annot; |
344 | contents = pdf_annot_contents(ctx, selected_annot); |
345 | ui_input_init(&input, contents); |
346 | } |
347 | |
348 | ui_label("Contents:" ); |
349 | if (ui_input(&input, 0, 5) >= UI_INPUT_EDIT) |
350 | pdf_set_annot_contents(ctx, selected_annot, input.text); |
351 | } |
352 | |
353 | static const char *file_attachment_icons[] = { "Graph" , "Paperclip" , "PushPin" , "Tag" }; |
354 | static const char *sound_icons[] = { "Speaker" , "Mic" }; |
355 | static const char *stamp_icons[] = { |
356 | "Approved" , "AsIs" , "Confidential" , "Departmental" , "Draft" , |
357 | "Experimental" , "Expired" , "Final" , "ForComment" , "ForPublicRelease" , |
358 | "NotApproved" , "NotForPublicRelease" , "Sold" , "TopSecret" }; |
359 | static const char *text_icons[] = { |
360 | "Comment" , "Help" , "Insert" , "Key" , "NewParagraph" , "Note" , "Paragraph" }; |
361 | static const char *line_ending_styles[] = { |
362 | "None" , "Square" , "Circle" , "Diamond" , "OpenArrow" , "ClosedArrow" , "Butt" , |
363 | "ROpenArrow" , "RClosedArrow" , "Slash" }; |
364 | static const char *quadding_names[] = { "Left" , "Center" , "Right" }; |
365 | static const char *font_names[] = { "Cour" , "Helv" , "TiRo" , "Symb" , "ZaDb" , }; |
366 | |
367 | static int should_edit_border(enum pdf_annot_type subtype) |
368 | { |
369 | switch (subtype) { |
370 | default: |
371 | return 0; |
372 | case PDF_ANNOT_FREE_TEXT: |
373 | return 1; |
374 | case PDF_ANNOT_INK: |
375 | case PDF_ANNOT_LINE: |
376 | case PDF_ANNOT_SQUARE: |
377 | case PDF_ANNOT_CIRCLE: |
378 | case PDF_ANNOT_POLYGON: |
379 | case PDF_ANNOT_POLY_LINE: |
380 | return 1; |
381 | } |
382 | } |
383 | |
384 | static int should_edit_color(enum pdf_annot_type subtype) |
385 | { |
386 | switch (subtype) { |
387 | default: |
388 | return 0; |
389 | case PDF_ANNOT_STAMP: |
390 | case PDF_ANNOT_TEXT: |
391 | case PDF_ANNOT_FILE_ATTACHMENT: |
392 | case PDF_ANNOT_SOUND: |
393 | case PDF_ANNOT_CARET: |
394 | return 1; |
395 | case PDF_ANNOT_FREE_TEXT: |
396 | return 1; |
397 | case PDF_ANNOT_INK: |
398 | case PDF_ANNOT_LINE: |
399 | case PDF_ANNOT_SQUARE: |
400 | case PDF_ANNOT_CIRCLE: |
401 | case PDF_ANNOT_POLYGON: |
402 | case PDF_ANNOT_POLY_LINE: |
403 | return 1; |
404 | case PDF_ANNOT_HIGHLIGHT: |
405 | case PDF_ANNOT_UNDERLINE: |
406 | case PDF_ANNOT_STRIKE_OUT: |
407 | case PDF_ANNOT_SQUIGGLY: |
408 | return 1; |
409 | } |
410 | } |
411 | |
412 | static int should_edit_icolor(enum pdf_annot_type subtype) |
413 | { |
414 | switch (subtype) { |
415 | default: |
416 | return 0; |
417 | case PDF_ANNOT_LINE: |
418 | case PDF_ANNOT_SQUARE: |
419 | case PDF_ANNOT_CIRCLE: |
420 | return 1; |
421 | } |
422 | } |
423 | |
424 | void do_annotate_panel(void) |
425 | { |
426 | static struct list annot_list; |
427 | enum pdf_annot_type subtype; |
428 | pdf_annot *annot; |
429 | int n; |
430 | |
431 | int has_redact = 0; |
432 | int was_dirty = pdf->dirty; |
433 | |
434 | ui_layout(T, X, NW, 2, 2); |
435 | |
436 | if (ui_popup("CreateAnnotPopup" , "Create..." , 1, 15)) |
437 | { |
438 | if (ui_popup_item("Text" )) new_annot(PDF_ANNOT_TEXT); |
439 | if (ui_popup_item("FreeText" )) new_annot(PDF_ANNOT_FREE_TEXT); |
440 | if (ui_popup_item("Stamp" )) new_annot(PDF_ANNOT_STAMP); |
441 | if (ui_popup_item("Caret" )) new_annot(PDF_ANNOT_CARET); |
442 | if (ui_popup_item("Ink" )) new_annot(PDF_ANNOT_INK); |
443 | if (ui_popup_item("Square" )) new_annot(PDF_ANNOT_SQUARE); |
444 | if (ui_popup_item("Circle" )) new_annot(PDF_ANNOT_CIRCLE); |
445 | if (ui_popup_item("Line" )) new_annot(PDF_ANNOT_LINE); |
446 | if (ui_popup_item("Polygon" )) new_annot(PDF_ANNOT_POLYGON); |
447 | if (ui_popup_item("PolyLine" )) new_annot(PDF_ANNOT_POLY_LINE); |
448 | if (ui_popup_item("Highlight" )) new_annot(PDF_ANNOT_HIGHLIGHT); |
449 | if (ui_popup_item("Underline" )) new_annot(PDF_ANNOT_UNDERLINE); |
450 | if (ui_popup_item("StrikeOut" )) new_annot(PDF_ANNOT_STRIKE_OUT); |
451 | if (ui_popup_item("Squiggly" )) new_annot(PDF_ANNOT_SQUIGGLY); |
452 | if (ui_popup_item("Redact" )) new_annot(PDF_ANNOT_REDACT); |
453 | ui_popup_end(); |
454 | } |
455 | |
456 | n = 0; |
457 | for (annot = pdf_first_annot(ctx, page); annot; annot = pdf_next_annot(ctx, annot)) |
458 | ++n; |
459 | |
460 | ui_list_begin(&annot_list, n, 0, ui.lineheight * 10 + 4); |
461 | for (annot = pdf_first_annot(ctx, page); annot; annot = pdf_next_annot(ctx, annot)) |
462 | { |
463 | char buf[256]; |
464 | int num = pdf_to_num(ctx, annot->obj); |
465 | subtype = pdf_annot_type(ctx, annot); |
466 | fz_snprintf(buf, sizeof buf, "%d: %s" , num, pdf_string_from_annot_type(ctx, subtype)); |
467 | if (ui_list_item(&annot_list, annot->obj, buf, selected_annot == annot)) |
468 | selected_annot = annot; |
469 | if (subtype == PDF_ANNOT_REDACT) |
470 | has_redact = 1; |
471 | } |
472 | ui_list_end(&annot_list); |
473 | |
474 | if (selected_annot && (subtype = pdf_annot_type(ctx, selected_annot)) != PDF_ANNOT_WIDGET) |
475 | { |
476 | fz_rect rect; |
477 | fz_irect irect; |
478 | int n, choice; |
479 | pdf_obj *obj; |
480 | |
481 | if (ui_button("Delete" )) |
482 | { |
483 | pdf_delete_annot(ctx, page, selected_annot); |
484 | selected_annot = NULL; |
485 | render_page(); |
486 | return; |
487 | } |
488 | |
489 | ui_spacer(); |
490 | |
491 | /* common annotation properties */ |
492 | |
493 | rect = pdf_annot_rect(ctx, selected_annot); |
494 | irect = fz_irect_from_rect(rect); |
495 | ui_label("Rect: %d %d %d %d" , irect.x0, irect.y0, irect.x1, irect.y1); |
496 | |
497 | do_annotate_flags(); |
498 | do_annotate_author(); |
499 | do_annotate_date(); |
500 | |
501 | obj = pdf_dict_get(ctx, selected_annot->obj, PDF_NAME(Popup)); |
502 | if (obj) |
503 | ui_label("Popup: %d 0 R" , pdf_to_num(ctx, obj)); |
504 | |
505 | ui_spacer(); |
506 | |
507 | do_annotate_contents(); |
508 | |
509 | ui_spacer(); |
510 | |
511 | if (subtype == PDF_ANNOT_FREE_TEXT) |
512 | { |
513 | int font_choice, color_choice, size_changed; |
514 | int q = pdf_annot_quadding(ctx, selected_annot); |
515 | const char *text_font; |
516 | static float text_size_f, text_color[3]; |
517 | static int text_size; |
518 | ui_label("Text Alignment:" ); |
519 | choice = ui_select("Q" , quadding_names[q], quadding_names, nelem(quadding_names)); |
520 | if (choice != -1) |
521 | pdf_set_annot_quadding(ctx, selected_annot, choice); |
522 | |
523 | pdf_annot_default_appearance(ctx, selected_annot, &text_font, &text_size_f, text_color); |
524 | text_size = text_size_f; |
525 | |
526 | ui_label("Text Font:" ); |
527 | font_choice = ui_select("DA/Font" , text_font, font_names, nelem(font_names)); |
528 | ui_label("Text Size: %d" , text_size); |
529 | size_changed = ui_slider(&text_size, 8, 36, 256); |
530 | ui_label("Text Color:" ); |
531 | color_choice = ui_select("DA/Color" , name_from_hex(hex_from_color(3, text_color)), color_names+1, nelem(color_names)-1); |
532 | if (font_choice != -1 || color_choice != -1 || size_changed) |
533 | { |
534 | if (font_choice != -1) |
535 | text_font = font_names[font_choice]; |
536 | if (color_choice != -1) |
537 | { |
538 | text_color[0] = ((color_values[color_choice+1]>>16) & 0xff) / 255.0f; |
539 | text_color[1] = ((color_values[color_choice+1]>>8) & 0xff) / 255.0f; |
540 | text_color[2] = ((color_values[color_choice+1]) & 0xff) / 255.0f; |
541 | } |
542 | pdf_set_annot_default_appearance(ctx, selected_annot, text_font, text_size, text_color); |
543 | } |
544 | ui_spacer(); |
545 | } |
546 | |
547 | if (subtype == PDF_ANNOT_LINE) |
548 | { |
549 | enum pdf_line_ending s, e; |
550 | int s_choice, e_choice; |
551 | |
552 | pdf_annot_line_ending_styles(ctx, selected_annot, &s, &e); |
553 | |
554 | ui_label("Line Start:" ); |
555 | s_choice = ui_select("LE0" , line_ending_styles[s], line_ending_styles, nelem(line_ending_styles)); |
556 | |
557 | ui_label("Line End:" ); |
558 | e_choice = ui_select("LE1" , line_ending_styles[e], line_ending_styles, nelem(line_ending_styles)); |
559 | |
560 | if (s_choice != -1 || e_choice != -1) |
561 | { |
562 | if (s_choice != -1) s = s_choice; |
563 | if (e_choice != -1) e = e_choice; |
564 | pdf_set_annot_line_ending_styles(ctx, selected_annot, s, e); |
565 | } |
566 | } |
567 | |
568 | if (pdf_annot_has_icon_name(ctx, selected_annot)) |
569 | { |
570 | const char *name = pdf_annot_icon_name(ctx, selected_annot); |
571 | ui_label("Icon:" ); |
572 | switch (pdf_annot_type(ctx, selected_annot)) |
573 | { |
574 | default: |
575 | break; |
576 | case PDF_ANNOT_TEXT: |
577 | choice = ui_select("Icon" , name, text_icons, nelem(text_icons)); |
578 | if (choice != -1) |
579 | pdf_set_annot_icon_name(ctx, selected_annot, text_icons[choice]); |
580 | break; |
581 | case PDF_ANNOT_FILE_ATTACHMENT: |
582 | choice = ui_select("Icon" , name, file_attachment_icons, nelem(file_attachment_icons)); |
583 | if (choice != -1) |
584 | pdf_set_annot_icon_name(ctx, selected_annot, file_attachment_icons[choice]); |
585 | break; |
586 | case PDF_ANNOT_SOUND: |
587 | choice = ui_select("Icon" , name, sound_icons, nelem(sound_icons)); |
588 | if (choice != -1) |
589 | pdf_set_annot_icon_name(ctx, selected_annot, sound_icons[choice]); |
590 | break; |
591 | case PDF_ANNOT_STAMP: |
592 | choice = ui_select("Icon" , name, stamp_icons, nelem(stamp_icons)); |
593 | if (choice != -1) |
594 | pdf_set_annot_icon_name(ctx, selected_annot, stamp_icons[choice]); |
595 | break; |
596 | } |
597 | } |
598 | |
599 | if (should_edit_border(subtype)) |
600 | { |
601 | static int border; |
602 | border = pdf_annot_border(ctx, selected_annot); |
603 | ui_label("Border: %d" , border); |
604 | if (ui_slider(&border, 0, 12, 100)) |
605 | pdf_set_annot_border(ctx, selected_annot, border); |
606 | } |
607 | |
608 | if (should_edit_color(subtype)) |
609 | do_annotate_color("Color" , pdf_annot_color, pdf_set_annot_color); |
610 | if (should_edit_icolor(subtype)) |
611 | do_annotate_color("Interior Color" , pdf_annot_interior_color, pdf_set_annot_interior_color); |
612 | |
613 | if (subtype == PDF_ANNOT_HIGHLIGHT) |
614 | { |
615 | static int opacity; |
616 | opacity = pdf_annot_opacity(ctx, selected_annot) * 255; |
617 | ui_label("Opacity:" ); |
618 | if (ui_slider(&opacity, 0, 255, 256)) |
619 | pdf_set_annot_opacity(ctx, selected_annot, opacity / 255.0f); |
620 | } |
621 | |
622 | if (pdf_annot_has_open(ctx, selected_annot)) |
623 | { |
624 | int is_open = pdf_annot_is_open(ctx, selected_annot); |
625 | int start_is_open = is_open; |
626 | ui_checkbox("Open" , &is_open); |
627 | if (start_is_open != is_open) |
628 | pdf_set_annot_is_open(ctx, selected_annot, is_open); |
629 | } |
630 | |
631 | ui_spacer(); |
632 | |
633 | if (pdf_annot_has_quad_points(ctx, selected_annot)) |
634 | { |
635 | if (is_draw_mode) |
636 | { |
637 | n = pdf_annot_quad_point_count(ctx, selected_annot); |
638 | ui_label("QuadPoints: %d" , n); |
639 | if (ui_button("Clear" )) |
640 | pdf_clear_annot_quad_points(ctx, selected_annot); |
641 | if (ui_button("Done" )) |
642 | is_draw_mode = 0; |
643 | } |
644 | else |
645 | { |
646 | if (ui_button("Edit" )) |
647 | is_draw_mode = 1; |
648 | } |
649 | } |
650 | |
651 | if (pdf_annot_has_vertices(ctx, selected_annot)) |
652 | { |
653 | if (is_draw_mode) |
654 | { |
655 | n = pdf_annot_vertex_count(ctx, selected_annot); |
656 | ui_label("Vertices: %d" , n); |
657 | if (ui_button("Clear" )) |
658 | pdf_clear_annot_vertices(ctx, selected_annot); |
659 | if (ui_button("Done" )) |
660 | is_draw_mode = 0; |
661 | } |
662 | else |
663 | { |
664 | if (ui_button("Edit" )) |
665 | is_draw_mode = 1; |
666 | } |
667 | } |
668 | |
669 | if (pdf_annot_has_ink_list(ctx, selected_annot)) |
670 | { |
671 | if (is_draw_mode) |
672 | { |
673 | n = pdf_annot_ink_list_count(ctx, selected_annot); |
674 | ui_label("InkList: %d strokes" , n); |
675 | if (ui_button("Clear" )) |
676 | pdf_clear_annot_ink_list(ctx, selected_annot); |
677 | if (ui_button("Done" )) |
678 | is_draw_mode = 0; |
679 | } |
680 | else |
681 | { |
682 | if (ui_button("Edit" )) |
683 | is_draw_mode = 1; |
684 | } |
685 | } |
686 | |
687 | if (selected_annot && selected_annot->needs_new_ap) |
688 | { |
689 | pdf_update_appearance(ctx, selected_annot); |
690 | render_page(); |
691 | } |
692 | } |
693 | |
694 | ui_layout(B, X, NW, 2, 2); |
695 | |
696 | if (ui_button("Save PDF..." )) |
697 | do_save_pdf_file(); |
698 | |
699 | if (has_redact) |
700 | { |
701 | if (ui_button("Redact" )) |
702 | { |
703 | selected_annot = NULL; |
704 | pdf_redact_page(ctx, pdf, page, NULL); |
705 | load_page(); |
706 | render_page(); |
707 | } |
708 | } |
709 | |
710 | if (was_dirty != pdf->dirty) |
711 | update_title(); |
712 | } |
713 | |
714 | static void do_edit_icon(fz_irect canvas_area, fz_irect area, fz_rect *rect) |
715 | { |
716 | static fz_point start_pt; |
717 | static float w, h; |
718 | static int moving = 0; |
719 | |
720 | if (ui_mouse_inside(canvas_area) && ui_mouse_inside(area)) |
721 | { |
722 | ui.hot = selected_annot; |
723 | if (!ui.active && ui.down) |
724 | { |
725 | ui.active = selected_annot; |
726 | start_pt.x = rect->x0; |
727 | start_pt.y = rect->y0; |
728 | w = rect->x1 - rect->x0; |
729 | h = rect->y1 - rect->y0; |
730 | moving = 1; |
731 | } |
732 | } |
733 | |
734 | if (ui.active == selected_annot && moving) |
735 | { |
736 | rect->x0 = start_pt.x + (ui.x - ui.down_x); |
737 | rect->y0 = start_pt.y + (ui.y - ui.down_y); |
738 | |
739 | /* Clamp to fit on page */ |
740 | rect->x0 = fz_clamp(rect->x0, view_page_area.x0, view_page_area.x1-w); |
741 | rect->y0 = fz_clamp(rect->y0, view_page_area.y0, view_page_area.y1-h); |
742 | rect->x1 = rect->x0 + w; |
743 | rect->y1 = rect->y0 + h; |
744 | |
745 | /* cancel on right click */ |
746 | if (ui.right) |
747 | moving = 0; |
748 | |
749 | /* Commit movement on mouse-up */ |
750 | if (!ui.down) |
751 | { |
752 | fz_point dp = { rect->x0 - start_pt.x, rect->y0 - start_pt.y }; |
753 | moving = 0; |
754 | if (fz_abs(dp.x) > 0.1f || fz_abs(dp.y) > 0.1f) |
755 | { |
756 | fz_rect trect = pdf_annot_rect(ctx, selected_annot); |
757 | dp = fz_transform_vector(dp, view_page_inv_ctm); |
758 | trect.x0 += dp.x; trect.x1 += dp.x; |
759 | trect.y0 += dp.y; trect.y1 += dp.y; |
760 | pdf_set_annot_rect(ctx, selected_annot, trect); |
761 | } |
762 | } |
763 | } |
764 | } |
765 | |
766 | static void do_edit_rect(fz_irect canvas_area, fz_irect area, fz_rect *rect) |
767 | { |
768 | enum { |
769 | ER_N=1, ER_E=2, ER_S=4, ER_W=8, |
770 | ER_NONE = 0, |
771 | ER_NW = ER_N|ER_W, |
772 | ER_NE = ER_N|ER_E, |
773 | ER_SW = ER_S|ER_W, |
774 | ER_SE = ER_S|ER_E, |
775 | ER_MOVE = ER_N|ER_E|ER_S|ER_W, |
776 | }; |
777 | static fz_rect start_rect; |
778 | static int state = ER_NONE; |
779 | |
780 | area = fz_expand_irect(area, 5); |
781 | if (ui_mouse_inside(canvas_area) && ui_mouse_inside(area)) |
782 | { |
783 | ui.hot = selected_annot; |
784 | if (!ui.active && ui.down) |
785 | { |
786 | ui.active = selected_annot; |
787 | start_rect = *rect; |
788 | state = ER_NONE; |
789 | if (ui.x <= area.x0 + 10) state |= ER_W; |
790 | if (ui.x >= area.x1 - 10) state |= ER_E; |
791 | if (ui.y <= area.y0 + 10) state |= ER_N; |
792 | if (ui.y >= area.y1 - 10) state |= ER_S; |
793 | if (!state) state = ER_MOVE; |
794 | } |
795 | } |
796 | |
797 | if (ui.active == selected_annot && state != ER_NONE) |
798 | { |
799 | *rect = start_rect; |
800 | if (state & ER_W) rect->x0 += (ui.x - ui.down_x); |
801 | if (state & ER_E) rect->x1 += (ui.x - ui.down_x); |
802 | if (state & ER_N) rect->y0 += (ui.y - ui.down_y); |
803 | if (state & ER_S) rect->y1 += (ui.y - ui.down_y); |
804 | if (rect->x1 < rect->x0) { float t = rect->x1; rect->x1 = rect->x0; rect->x0 = t; } |
805 | if (rect->y1 < rect->y0) { float t = rect->y1; rect->y1 = rect->y0; rect->y0 = t; } |
806 | if (rect->x1 < rect->x0 + 10) rect->x1 = rect->x0 + 10; |
807 | if (rect->y1 < rect->y0 + 10) rect->y1 = rect->y0 + 10; |
808 | |
809 | /* cancel on right click */ |
810 | if (ui.right) |
811 | state = ER_NONE; |
812 | |
813 | /* commit on mouse-up */ |
814 | if (!ui.down) |
815 | { |
816 | state = ER_NONE; |
817 | if (rects_differ(start_rect, *rect, 1)) |
818 | { |
819 | fz_rect trect = fz_transform_rect(*rect, view_page_inv_ctm); |
820 | pdf_set_annot_rect(ctx, selected_annot, trect); |
821 | } |
822 | } |
823 | } |
824 | } |
825 | |
826 | static void do_edit_line(fz_irect canvas_area, fz_irect area, fz_rect *rect) |
827 | { |
828 | enum { EL_NONE, EL_A=1, EL_B=2, EL_MOVE=EL_A|EL_B }; |
829 | static fz_point start_a, start_b; |
830 | static int state = EL_NONE; |
831 | fz_irect a_grab, b_grab; |
832 | fz_point a, b; |
833 | float lw; |
834 | |
835 | area = fz_expand_irect(area, 5); |
836 | if (ui_mouse_inside(canvas_area) && ui_mouse_inside(area)) |
837 | { |
838 | ui.hot = selected_annot; |
839 | if (!ui.active && ui.down) |
840 | { |
841 | ui.active = selected_annot; |
842 | pdf_annot_line(ctx, selected_annot, &start_a, &start_b); |
843 | start_a = fz_transform_point(start_a, view_page_ctm); |
844 | start_b = fz_transform_point(start_b, view_page_ctm); |
845 | a_grab = fz_make_irect(start_a.x, start_a.y, start_a.x, start_a.y); |
846 | b_grab = fz_make_irect(start_b.x, start_b.y, start_b.x, start_b.y); |
847 | a_grab = fz_expand_irect(a_grab, 10); |
848 | b_grab = fz_expand_irect(b_grab, 10); |
849 | state = EL_NONE; |
850 | if (ui_mouse_inside(a_grab)) state |= EL_A; |
851 | if (ui_mouse_inside(b_grab)) state |= EL_B; |
852 | if (!state) state = EL_MOVE; |
853 | } |
854 | } |
855 | |
856 | if (ui.active == selected_annot && state != 0) |
857 | { |
858 | a = start_a; |
859 | b = start_b; |
860 | if (state & EL_A) { a.x += (ui.x - ui.down_x); a.y += (ui.y - ui.down_y); } |
861 | if (state & EL_B) { b.x += (ui.x - ui.down_x); b.y += (ui.y - ui.down_y); } |
862 | |
863 | glBegin(GL_LINES); |
864 | glColor4f(1, 0, 0, 1); |
865 | glVertex2f(a.x, a.y); |
866 | glVertex2f(b.x, b.y); |
867 | glEnd(); |
868 | |
869 | rect->x0 = fz_min(a.x, b.x); |
870 | rect->y0 = fz_min(a.y, b.y); |
871 | rect->x1 = fz_max(a.x, b.x); |
872 | rect->y1 = fz_max(a.y, b.y); |
873 | lw = pdf_annot_border(ctx, selected_annot); |
874 | *rect = fz_expand_rect(*rect, fz_matrix_expansion(view_page_ctm) * lw); |
875 | |
876 | /* cancel on right click */ |
877 | if (ui.right) |
878 | state = EL_NONE; |
879 | |
880 | /* commit on mouse-up */ |
881 | if (!ui.down) |
882 | { |
883 | state = EL_NONE; |
884 | if (points_differ(start_a, a, 1) || points_differ(start_b, b, 1)) |
885 | { |
886 | a = fz_transform_point(a, view_page_inv_ctm); |
887 | b = fz_transform_point(b, view_page_inv_ctm); |
888 | pdf_set_annot_line(ctx, selected_annot, a, b); |
889 | } |
890 | } |
891 | } |
892 | } |
893 | |
894 | static void do_edit_polygon(fz_irect canvas_area, int close) |
895 | { |
896 | static int drawing = 0; |
897 | fz_point a, p; |
898 | |
899 | if (ui_mouse_inside(canvas_area) && ui_mouse_inside(view_page_area)) |
900 | { |
901 | ui.hot = selected_annot; |
902 | if (!ui.active || ui.active == selected_annot) |
903 | ui.cursor = GLUT_CURSOR_CROSSHAIR; |
904 | if (!ui.active && ui.down) |
905 | { |
906 | ui.active = selected_annot; |
907 | drawing = 1; |
908 | } |
909 | } |
910 | |
911 | if (ui.active == selected_annot && drawing) |
912 | { |
913 | int n = pdf_annot_vertex_count(ctx, selected_annot); |
914 | if (n > 0) |
915 | { |
916 | p = pdf_annot_vertex(ctx, selected_annot, n-1); |
917 | p = fz_transform_point(p, view_page_ctm); |
918 | if (close) |
919 | { |
920 | a = pdf_annot_vertex(ctx, selected_annot, 0); |
921 | a = fz_transform_point(a, view_page_ctm); |
922 | } |
923 | glBegin(GL_LINE_STRIP); |
924 | glColor4f(1, 0, 0, 1); |
925 | glVertex2f(p.x, p.y); |
926 | glVertex2f(ui.x, ui.y); |
927 | if (close) |
928 | glVertex2f(a.x, a.y); |
929 | glEnd(); |
930 | } |
931 | |
932 | glColor4f(1, 0, 0, 1); |
933 | glPointSize(4); |
934 | glBegin(GL_POINTS); |
935 | glVertex2f(ui.x, ui.y); |
936 | glEnd(); |
937 | |
938 | /* cancel on right click */ |
939 | if (ui.right) |
940 | drawing = 0; |
941 | |
942 | /* commit point on mouse-up */ |
943 | if (!ui.down) |
944 | { |
945 | fz_point p = fz_transform_point_xy(ui.x, ui.y, view_page_inv_ctm); |
946 | pdf_add_annot_vertex(ctx, selected_annot, p); |
947 | drawing = 0; |
948 | } |
949 | } |
950 | } |
951 | |
952 | static void do_edit_ink(fz_irect canvas_area) |
953 | { |
954 | static int drawing = 0; |
955 | static fz_point p[1000]; |
956 | static int n, last_x, last_y; |
957 | int i; |
958 | |
959 | if (ui_mouse_inside(canvas_area) && ui_mouse_inside(view_page_area)) |
960 | { |
961 | ui.hot = selected_annot; |
962 | if (!ui.active || ui.active == selected_annot) |
963 | ui.cursor = GLUT_CURSOR_CROSSHAIR; |
964 | if (!ui.active && ui.down) |
965 | { |
966 | ui.active = selected_annot; |
967 | drawing = 1; |
968 | n = 0; |
969 | last_x = INT_MIN; |
970 | last_y = INT_MIN; |
971 | } |
972 | } |
973 | |
974 | if (ui.active == selected_annot && drawing) |
975 | { |
976 | if (n < nelem(p) && (ui.x != last_x || ui.y != last_y)) |
977 | { |
978 | p[n].x = fz_clamp(ui.x, view_page_area.x0, view_page_area.x1); |
979 | p[n].y = fz_clamp(ui.y, view_page_area.y0, view_page_area.y1); |
980 | ++n; |
981 | } |
982 | last_x = ui.x; |
983 | last_y = ui.y; |
984 | |
985 | if (n > 1) |
986 | { |
987 | glBegin(GL_LINE_STRIP); |
988 | glColor4f(1, 0, 0, 1); |
989 | for (i = 0; i < n; ++i) |
990 | glVertex2f(p[i].x, p[i].y); |
991 | glEnd(); |
992 | } |
993 | |
994 | /* cancel on right click */ |
995 | if (ui.right) |
996 | { |
997 | drawing = 0; |
998 | n = 0; |
999 | } |
1000 | |
1001 | /* commit stroke on mouse-up */ |
1002 | if (!ui.down) |
1003 | { |
1004 | if (n > 1) |
1005 | { |
1006 | for (i = 0; i < n; ++i) |
1007 | p[i] = fz_transform_point(p[i], view_page_inv_ctm); |
1008 | pdf_add_annot_ink_list(ctx, selected_annot, n, p); |
1009 | } |
1010 | drawing = 0; |
1011 | n = 0; |
1012 | } |
1013 | } |
1014 | } |
1015 | |
1016 | static void do_edit_quad_points(void) |
1017 | { |
1018 | static fz_point pt = { 0, 0 }; |
1019 | static int marking = 0; |
1020 | fz_quad hits[1000]; |
1021 | int i, n; |
1022 | |
1023 | if (ui_mouse_inside(view_page_area)) |
1024 | { |
1025 | ui.hot = selected_annot; |
1026 | if (!ui.active || ui.active == selected_annot) |
1027 | ui.cursor = GLUT_CURSOR_TEXT; |
1028 | if (!ui.active && ui.down) |
1029 | { |
1030 | ui.active = selected_annot; |
1031 | marking = 1; |
1032 | pt.x = ui.x; |
1033 | pt.y = ui.y; |
1034 | } |
1035 | } |
1036 | |
1037 | if (ui.active == selected_annot && marking) |
1038 | { |
1039 | fz_point page_a = { pt.x, pt.y }; |
1040 | fz_point page_b = { ui.x, ui.y }; |
1041 | |
1042 | page_a = fz_transform_point(page_a, view_page_inv_ctm); |
1043 | page_b = fz_transform_point(page_b, view_page_inv_ctm); |
1044 | |
1045 | n = fz_highlight_selection(ctx, page_text, page_a, page_b, hits, nelem(hits)); |
1046 | |
1047 | glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ZERO); /* invert destination color */ |
1048 | glEnable(GL_BLEND); |
1049 | |
1050 | glColor4f(1, 1, 1, 1); |
1051 | glBegin(GL_QUADS); |
1052 | for (i = 0; i < n; ++i) |
1053 | { |
1054 | fz_quad thit = fz_transform_quad(hits[i], view_page_ctm); |
1055 | glVertex2f(thit.ul.x, thit.ul.y); |
1056 | glVertex2f(thit.ur.x, thit.ur.y); |
1057 | glVertex2f(thit.lr.x, thit.lr.y); |
1058 | glVertex2f(thit.ll.x, thit.ll.y); |
1059 | } |
1060 | glEnd(); |
1061 | |
1062 | glDisable(GL_BLEND); |
1063 | |
1064 | /* cancel on right click */ |
1065 | if (ui.right) |
1066 | marking = 0; |
1067 | |
1068 | if (!ui.down) |
1069 | { |
1070 | if (n > 0) |
1071 | { |
1072 | pdf_clear_annot_quad_points(ctx, selected_annot); |
1073 | for (i = 0; i < n; ++i) |
1074 | pdf_add_annot_quad_point(ctx, selected_annot, hits[i]); |
1075 | } |
1076 | marking = 0; |
1077 | } |
1078 | } |
1079 | } |
1080 | |
1081 | void do_annotate_canvas(fz_irect canvas_area) |
1082 | { |
1083 | fz_rect bounds; |
1084 | fz_irect area; |
1085 | pdf_annot *annot; |
1086 | const void *nothing = ui.hot; |
1087 | |
1088 | int was_dirty = pdf->dirty; |
1089 | |
1090 | for (annot = pdf_first_annot(ctx, page); annot; annot = pdf_next_annot(ctx, annot)) |
1091 | { |
1092 | enum pdf_annot_type subtype = pdf_annot_type(ctx, annot); |
1093 | |
1094 | bounds = pdf_bound_annot(ctx, annot); |
1095 | bounds = fz_transform_rect(bounds, view_page_ctm); |
1096 | area = fz_irect_from_rect(bounds); |
1097 | |
1098 | if (ui_mouse_inside(canvas_area) && ui_mouse_inside(area)) |
1099 | { |
1100 | ui.hot = annot; |
1101 | if (!ui.active && ui.down) |
1102 | { |
1103 | if (selected_annot != annot) |
1104 | { |
1105 | if (!selected_annot && !showannotate) |
1106 | toggle_annotate(); |
1107 | ui.active = annot; |
1108 | selected_annot = annot; |
1109 | } |
1110 | } |
1111 | } |
1112 | |
1113 | if (annot == selected_annot) |
1114 | { |
1115 | switch (subtype) |
1116 | { |
1117 | default: |
1118 | break; |
1119 | |
1120 | /* Popup window */ |
1121 | case PDF_ANNOT_POPUP: |
1122 | do_edit_rect(canvas_area, area, &bounds); |
1123 | break; |
1124 | |
1125 | /* Icons */ |
1126 | case PDF_ANNOT_TEXT: |
1127 | case PDF_ANNOT_CARET: |
1128 | case PDF_ANNOT_FILE_ATTACHMENT: |
1129 | case PDF_ANNOT_SOUND: |
1130 | do_edit_icon(canvas_area, area, &bounds); |
1131 | break; |
1132 | |
1133 | case PDF_ANNOT_STAMP: |
1134 | do_edit_rect(canvas_area, area, &bounds); |
1135 | break; |
1136 | |
1137 | case PDF_ANNOT_FREE_TEXT: |
1138 | do_edit_rect(canvas_area, area, &bounds); |
1139 | break; |
1140 | |
1141 | /* Drawings */ |
1142 | case PDF_ANNOT_LINE: |
1143 | do_edit_line(canvas_area, area, &bounds); |
1144 | break; |
1145 | case PDF_ANNOT_CIRCLE: |
1146 | case PDF_ANNOT_SQUARE: |
1147 | do_edit_rect(canvas_area, area, &bounds); |
1148 | break; |
1149 | case PDF_ANNOT_POLYGON: |
1150 | if (is_draw_mode) |
1151 | do_edit_polygon(canvas_area, 1); |
1152 | break; |
1153 | case PDF_ANNOT_POLY_LINE: |
1154 | if (is_draw_mode) |
1155 | do_edit_polygon(canvas_area, 0); |
1156 | break; |
1157 | |
1158 | case PDF_ANNOT_INK: |
1159 | if (is_draw_mode) |
1160 | do_edit_ink(canvas_area); |
1161 | break; |
1162 | |
1163 | case PDF_ANNOT_HIGHLIGHT: |
1164 | case PDF_ANNOT_UNDERLINE: |
1165 | case PDF_ANNOT_STRIKE_OUT: |
1166 | case PDF_ANNOT_SQUIGGLY: |
1167 | case PDF_ANNOT_REDACT: |
1168 | if (is_draw_mode) |
1169 | do_edit_quad_points(); |
1170 | break; |
1171 | } |
1172 | |
1173 | glLineStipple(1, 0xAAAA); |
1174 | glEnable(GL_LINE_STIPPLE); |
1175 | glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ZERO); |
1176 | glEnable(GL_BLEND); |
1177 | glColor4f(1, 1, 1, 1); |
1178 | glBegin(GL_LINE_LOOP); |
1179 | area = fz_irect_from_rect(bounds); |
1180 | glVertex2f(area.x0-0.5f, area.y0-0.5f); |
1181 | glVertex2f(area.x1+0.5f, area.y0-0.5f); |
1182 | glVertex2f(area.x1+0.5f, area.y1+0.5f); |
1183 | glVertex2f(area.x0-0.5f, area.y1+0.5f); |
1184 | glEnd(); |
1185 | glDisable(GL_BLEND); |
1186 | glDisable(GL_LINE_STIPPLE); |
1187 | |
1188 | if (annot->needs_new_ap) |
1189 | { |
1190 | pdf_update_appearance(ctx, annot); |
1191 | render_page(); |
1192 | } |
1193 | } |
1194 | } |
1195 | |
1196 | if (ui_mouse_inside(canvas_area) && ui.down) |
1197 | { |
1198 | if (!ui.active && ui.hot == nothing) |
1199 | selected_annot = NULL; |
1200 | } |
1201 | |
1202 | if (ui.right) |
1203 | is_draw_mode = 0; |
1204 | |
1205 | if (was_dirty != pdf->dirty) |
1206 | update_title(); |
1207 | } |
1208 | |