Remove, shot in the dark, attempt to pacify GH CI robot.
[llpp.git] / main.ml
blobe379c6f75ee14fed558c918d02dd1452391fb149
1 open Utils
2 open Config
3 open Uiutils
5 module U = struct
6 let dopen = '\023'
7 let cs = '\024'
8 let freepage = '\025'
9 let freetile = '\026'
10 let search = '\027'
11 let geometry = '\028'
12 let reqlayout = '\029'
13 let page = '\030'
14 let tile = '\031'
15 let trimset = '\032'
16 let settrim = '\033'
17 let sliceh = '\034'
18 let interrupt = '\035'
19 let pgscale h = truncate (float h *. conf.pgscale)
20 let nogeomcmds = function | s, [] -> emptystr s | _ -> false
21 let maxy () = !S.maxy - if conf.maxhfit then !S.winh else 0
22 let scalecolor c = let c = c *. conf.colorscale in (c, c, c)
23 let panbound x = bound x (- !S.w) !S.winw
24 let pagevisible layout n = List.exists (fun l -> l.pageno = n) layout
25 let add_to_y_and_clamp inc = bound (!S.y + inc) 0 @@ maxy ()
26 end
28 let debugrect (x0, y0, x1, y1, x2, y2, x3, y3) =
29 dolog {|rect {
30 x0,y0=(% f, % f)
31 x1,y1=(% f, % f)
32 x2,y2=(% f, % f)
33 x3,y3=(% f, % f)
34 }|} x0 y0 x1 y1 x2 y2 x3 y3
36 let hscrollh () =
37 if ((conf.scrollb land scrollbhv != 0) && (!S.w > !S.winw))
38 || !S.uioh#alwaysscrolly
39 then conf.scrollbw
40 else 0
42 let setfontsize n =
43 fstate.fontsize <- n;
44 fstate.wwidth <- Ffi.measurestr fstate.fontsize "w";
45 fstate.maxrows <- (!S.winh - fstate.fontsize - 1) / (fstate.fontsize + 1)
47 let showtext c s =
48 S.text := Printf.sprintf "%c%s" c s;
49 Glutils.postRedisplay "showtext"
51 let adderrmsg src msg =
52 Buffer.add_string S.errmsgs msg;
53 S.newerrmsgs := true;
54 Glutils.postRedisplay src
56 let settextfmt fmt = Printf.kprintf (fun s -> S.text := s) fmt
57 let impmsg fmt = Printf.ksprintf (fun s -> showtext '!' s) fmt
58 let adderrfmt src fmt = Printf.ksprintf (fun s -> adderrmsg src s) fmt
60 let launchpath () =
61 if emptystr conf.pathlauncher
62 then adderrmsg "path launcher" "command set"
63 else
64 let cmd = Str.global_replace Re.percent !S.path conf.pathlauncher in
65 match spawn cmd [] with
66 | exception exn ->
67 adderrfmt "spawn" "failed to execute `%s': %s" cmd @@ exntos exn
68 | _pid -> ()
70 let getopaque pageno = Hashtbl.find S.pagemap (pageno, !S.gen)
72 let pagetranslatepoint l x y =
73 let dy = y - l.pagedispy in
74 let y = dy + l.pagey in
75 let dx = x - l.pagedispx in
76 let x = dx + l.pagex in
77 (x, y)
79 let onppundermouse g x y d =
80 let rec f = function
81 | [] -> d
82 | l :: rest ->
83 match getopaque l.pageno with
84 | exception Not_found -> f rest
85 | opaque ->
86 let x0 = l.pagedispx in
87 let x1 = x0 + l.pagevw in
88 let y0 = l.pagedispy in
89 let y1 = y0 + l.pagevh in
90 if y >= y0 && y <= y1 && x >= x0 && x <= x1
91 then
92 let px, py = pagetranslatepoint l x y in
93 match g opaque l px py with
94 | Some res -> res
95 | None -> f rest
96 else f rest
98 f !S.layout
100 let getunder x y =
101 let g opaque l px py =
102 if !S.bzoom
103 then (
104 match Ffi.rectofblock opaque px py with
105 | Some [|x0;x1;y0;y1|] ->
106 let rect = (x0, y0, x1, y0, x1, y1, x0, y1) in
107 let color = (0.0, 0.0, 1.0 /. (l.pageno mod 3 |> float), 0.5) in
108 S.rects := [l.pageno, color, rect];
109 Glutils.postRedisplay "getunder";
110 | _ -> ()
112 let under = Ffi.whatsunder opaque px py in
113 if under = Unone then None else Some under
115 onppundermouse g x y Unone
117 let unproject x y =
118 let g opaque l x y =
119 match Ffi.unproject opaque x y with
120 | Some (x, y) -> Some (Some (opaque, l.pageno, x, y))
121 | None -> None
123 onppundermouse g x y None
125 let pipesel opaque cmd =
126 if Ffi.hassel opaque
127 then
128 pipef ~closew:false "pipesel"
129 (fun w ->
130 Ffi.copysel w opaque;
131 Glutils.postRedisplay "pipesel"
132 ) cmd
134 let paxunder x y =
135 let g opaque l px py =
136 if Ffi.markunder opaque px py conf.paxmark
137 then
138 Some (fun () ->
139 match getopaque l.pageno with
140 | exception Not_found -> ()
141 | opaque -> pipesel opaque conf.paxcmd
143 else None
145 Glutils.postRedisplay "paxunder";
146 if conf.paxmark = Mark_page
147 then
148 List.iter (fun l ->
149 match getopaque l.pageno with
150 | exception Not_found -> ()
151 | opaque -> Ffi.clearmark opaque) !S.layout;
152 S.roamf := onppundermouse g x y (fun () -> impmsg "whoopsie daisy")
154 let undertext = function
155 | Unone -> "none"
156 | Ulinkuri s -> s
157 | Utext s -> "font: " ^ s
158 | Utextannot (opaque, slinkindex) ->
159 "text annotation: " ^ Ffi.gettextannot opaque slinkindex
160 | Ufileannot (opaque, slinkindex) ->
161 "file annotation: " ^ Ffi.getfileannot opaque slinkindex
163 let updateunder x y =
164 match getunder x y with
165 | Unone -> Wsi.setcursor Wsi.CURSOR_INHERIT
166 | Ulinkuri uri ->
167 if conf.underinfo then showtext 'u' ("ri: " ^ uri);
168 Wsi.setcursor Wsi.CURSOR_INFO
169 | Utext s ->
170 if conf.underinfo then showtext 'f' ("ont: " ^ s);
171 Wsi.setcursor Wsi.CURSOR_TEXT
172 | Utextannot _ ->
173 if conf.underinfo then showtext 't' "ext annotation";
174 Wsi.setcursor Wsi.CURSOR_INFO
175 | Ufileannot _ ->
176 if conf.underinfo then showtext 'f' "ile annotation";
177 Wsi.setcursor Wsi.CURSOR_INFO
179 let showlinktype under =
180 if conf.underinfo && under != Unone
181 then showtext ' ' @@ undertext under
183 let intentry_with_suffix text key =
184 let text =
185 match [@warning "-fragile-match"] key with
186 | Keys.Ascii ('0'..'9' as c) -> addchar text c
187 | Keys.Ascii ('k' | 'm' | 'g' | 'K' | 'M' | 'G' as c) ->
188 addchar text @@ Char.lowercase_ascii c
189 | _ ->
190 S.text := "invalid key";
191 text
193 TEcont text
195 let wcmd cmd fmt =
196 let b = Buffer.create 16 in
197 Printf.kbprintf
198 (fun b ->
199 Buffer.add_char b cmd;
200 let b = Buffer.to_bytes b in
201 Ffi.wcmd !S.ss b @@ Bytes.length b
202 ) b fmt
204 let wcmd1 cmd opaque =
205 let s = Opaque.to_string opaque in
206 let l = String.length s in
207 let b = Bytes.create (l+1) in
208 Bytes.set b l cmd;
209 Bytes.blit_string s 0 b 0 l;
210 Ffi.wcmd !S.ss b @@ l + 1
212 let layoutN ((columns, coverA, coverB), b) x y sw sh =
213 let rec fold accu n =
214 if n = Array.length b
215 then accu
216 else
217 let pdimno, dx, vy, (_, w, h, xoff) = b.(n) in
218 if (vy - y) > sh
219 && (n = coverA - 1
220 || n = !S.pagecount - coverB
221 || (n - coverA) mod columns = columns - 1)
222 then accu
223 else
224 let accu =
225 if vy + h > y
226 then
227 let pagey = max 0 (y - vy) in
228 let pagedispy = if pagey > 0 then 0 else vy - y in
229 let pagedispx, pagex =
230 let pdx =
231 if n = coverA - 1 || n = !S.pagecount - coverB
232 then x + (sw - w) / 2
233 else dx + xoff + x
235 if pdx < 0
236 then 0, -pdx
237 else pdx, 0
239 let pagevw =
240 let vw = sw - pagedispx in
241 let pw = w - pagex in
242 min vw pw
244 let pagevh = min (h - pagey) (sh - pagedispy) in
245 if pagevw > 0 && pagevh > 0
246 then
247 { pageno = n
248 ; pagecol = 0 ; pagedimno = pdimno ; pagew = w ; pageh = h
249 ; pagex ; pagey ; pagevw ; pagevh ; pagedispx ; pagedispy
250 } :: accu
251 else accu
252 else accu
254 fold accu (n+1)
256 if Array.length b = 0
257 then []
258 else List.rev (fold [] (page_of_y y))
260 let layoutS (columns, b) x y sw sh =
261 let rec fold accu n =
262 if n = Array.length b
263 then accu
264 else
265 let pdimno, px, vy, (_, pagew, pageh, xoff) = b.(n) in
266 if (vy - y) > sh
267 then accu
268 else
269 let accu =
270 if vy + pageh > y
271 then
272 let x = xoff + x in
273 let pagey = max 0 (y - vy) in
274 let pagedispy = if pagey > 0 then 0 else vy - y in
275 let pagedispx, pagex =
276 if px = 0
277 then (
278 if x < 0
279 then 0, -x
280 else x, 0
282 else (
283 let px = px - x in
284 if px < 0
285 then -px, 0
286 else 0, px
289 let pagecolw = pagew/columns in
290 let pagedispx =
291 if pagecolw < sw
292 then pagedispx + ((sw - pagecolw) / 2)
293 else pagedispx
295 let pagevw =
296 let vw = sw - pagedispx in
297 let pw = pagew - pagex in
298 min vw pw
300 let pagevw = min pagevw pagecolw in
301 let pagevh = min (pageh - pagey) (sh - pagedispy) in
302 if pagevw > 0 && pagevh > 0
303 then
304 { pageno = n/columns
305 ; pagedimno = pdimno
306 ; pagecol = n mod columns
307 ; pagew ; pageh ; pagex ; pagey ; pagedispx ; pagedispy
308 ; pagevw ; pagevh
309 } :: accu
310 else accu
311 else accu
313 fold accu (n+1)
315 List.rev (fold [] 0)
317 let layout x y sw sh =
318 if U.nogeomcmds !S.geomcmds
319 then
320 match conf.columns with
321 | Csingle b -> layoutN ((1, 0, 0), b) x y sw sh
322 | Cmulti c -> layoutN c x y sw sh
323 | Csplit s -> layoutS s x y sw sh
324 else []
326 let itertiles l f =
327 let tilex = l.pagex mod conf.tilew in
328 let tiley = l.pagey mod conf.tileh in
330 let col = l.pagex / conf.tilew in
331 let row = l.pagey / conf.tileh in
333 let rec rowloop row y0 dispy h =
334 if h != 0
335 then
336 let dh = conf.tileh - y0 in
337 let dh = min h dh in
338 let rec colloop col x0 dispx w =
339 if w != 0
340 then
341 let dw = conf.tilew - x0 in
342 let dw = min w dw in
343 f col row dispx dispy x0 y0 dw dh;
344 colloop (col+1) 0 (dispx+dw) (w-dw)
346 colloop col tilex l.pagedispx l.pagevw;
347 rowloop (row+1) 0 (dispy+dh) (h-dh)
349 if l.pagevw > 0 && l.pagevh > 0
350 then rowloop row tiley l.pagedispy l.pagevh
352 let gettileopaque l col row =
353 let key = l.pageno, !S.gen, conf.colorspace,
354 conf.angle, l.pagew, l.pageh, col, row in
355 Hashtbl.find_opt S.tilemap key
357 let puttileopaque l col row gen colorspace angle opaque size elapsed =
358 let key = l.pageno, gen, colorspace, angle, l.pagew, l.pageh, col, row in
359 Hashtbl.add S.tilemap key (opaque, size, elapsed)
361 let drawtiles l color =
362 let texe e = if conf.invert then GlTex.env (`mode e) in
363 GlDraw.color color;
364 Ffi.begintiles ();
365 let f col row x y tilex tiley w h =
366 match gettileopaque l col row with
367 | Some (opaque, _, t) ->
368 let params = x, y, w, h, tilex, tiley in
369 texe `blend;
370 Ffi.drawtile params opaque;
371 texe `modulate;
372 if conf.debug
373 then (
374 Ffi.endtiles ();
375 let s = Printf.sprintf "%d[%d,%d] %f sec" l.pageno col row t in
376 let w = Ffi.measurestr fstate.fontsize s in
377 GlDraw.color (0.0, 0.0, 0.0);
378 Glutils.filledrect
379 (float (x-2))
380 (float (y-2))
381 (float (x+2) +. w)
382 (float (y + fstate.fontsize + 2));
383 GlDraw.color color;
384 Glutils.drawstring fstate.fontsize x (y + fstate.fontsize - 1) s;
385 Ffi.begintiles ();
388 | None ->
389 Ffi.endtiles ();
390 let w = let lw = !S.winw - x in min lw w
391 and h = let lh = !S.winh - y in min lh h in
392 texe `blend;
393 GlDraw.color (0.8, 0.8, 0.8);
394 Glutils.filledrect (float x) (float y) (float (x+w)) (float (y+h));
395 texe `modulate;
396 if w > 128 && h > fstate.fontsize + 10
397 then (
398 let c = if conf.invert then 1.0 else 0.0 in
399 GlDraw.color (c, c, c);
400 let c, r =
401 if conf.verbose
402 then (col*conf.tilew, row*conf.tileh)
403 else col, row
405 Glutils.drawstringf fstate.fontsize x y
406 "Loading %d [%d,%d]" l.pageno c r;
408 GlDraw.color color;
409 Ffi.begintiles ();
411 itertiles l f;
412 Ffi.endtiles ()
414 let tilevisible1 l x y =
415 let ax0 = l.pagex
416 and ax1 = l.pagex + l.pagevw
417 and ay0 = l.pagey
418 and ay1 = l.pagey + l.pagevh in
420 let bx0 = x
421 and by0 = y in
422 let bx1 = min (bx0 + conf.tilew) l.pagew
423 and by1 = min (by0 + conf.tileh) l.pageh in
425 let rx0 = max ax0 bx0
426 and ry0 = max ay0 by0
427 and rx1 = min ax1 bx1
428 and ry1 = min ay1 by1 in
430 let nonemptyintersection = rx1 > rx0 && ry1 > ry0 in
431 nonemptyintersection
433 let tilevisible layout n x y =
434 let rec findpageinlayout m = function
435 | l :: rest when l.pageno = n ->
436 tilevisible1 l x y || (
437 match conf.columns with
438 | Csplit (c, _) when c > m -> findpageinlayout (m+1) rest
439 | Csplit _ | Csingle _ | Cmulti _ -> false
441 | _ :: rest -> findpageinlayout 0 rest
442 | [] -> false
444 findpageinlayout 0 layout
446 let tileready l x y =
447 tilevisible1 l x y &&
448 gettileopaque l (x/conf.tilew) (y/conf.tileh) != None
450 let tilepage n p layout =
451 let rec loop = function
452 | l :: rest ->
453 if l.pageno = n
454 then
455 let f col row _ _ _ _ _ _ =
456 if !S.currently = Idle
457 then
458 match gettileopaque l col row with
459 | Some _ -> ()
460 | None ->
461 let x = col*conf.tilew
462 and y = row*conf.tileh in
463 let w =
464 let w = l.pagew - x in
465 min w conf.tilew
467 let h =
468 let h = l.pageh - y in
469 min h conf.tileh
471 wcmd U.tile "%s %d %d %d %d" (Opaque.to_string p) x y w h;
472 S.currently :=
473 Tiling (
474 l, p, conf.colorspace, conf.angle,
475 !S.gen, col, row, conf.tilew, conf.tileh
478 itertiles l f;
479 else loop rest
481 | [] -> ()
483 if U.nogeomcmds !S.geomcmds
484 then loop layout
486 let preloadlayout x y sw sh =
487 let y = if y < sh then 0 else y - sh in
488 let x = min 0 (x + sw) in
489 let h = sh*3 in
490 let w = sw*3 in
491 layout x y w h
493 let load pages =
494 let rec loop pages =
495 if !S.currently = Idle
496 then
497 match pages with
498 | l :: rest ->
499 begin match getopaque l.pageno with
500 | exception Not_found ->
501 wcmd U.page "%d %d" l.pageno l.pagedimno;
502 S.currently := Loading (l, !S.gen);
503 | opaque ->
504 tilepage l.pageno opaque pages;
505 loop rest
507 | _ -> ()
509 if U.nogeomcmds !S.geomcmds
510 then loop pages
512 let preload pages =
513 load pages;
514 if conf.preload && !S.currently = Idle
515 then load (preloadlayout !S.x !S.y !S.winw !S.winh)
517 let layoutready layout =
518 let exception E in
519 let rec fold ls =
520 match ls with
521 | [] -> true
522 | l :: rest ->
523 let foo col row _ _ _ _ _ _ =
524 match gettileopaque l col row with
525 | Some _ -> ()
526 | None -> raise E
528 match itertiles l foo with
529 | () -> fold rest
530 | exception E -> false
532 fold layout
534 let gotoxy x y =
535 let y = bound y 0 !S.maxy in
536 let y, layout =
537 let layout = layout x y !S.winw !S.winh in
538 Glutils.postRedisplay "gotoxy ready";
539 y, layout
541 S.x := x;
542 S.y := y;
543 S.layout := layout;
544 begin match !S.mode with
545 | LinkNav ln ->
546 begin match ln with
547 | Ltexact (pageno, linkno) ->
548 let rec loop = function
549 | [] ->
550 S.lnava := Some (pageno, linkno);
551 S.mode := LinkNav (Ltgendir 0)
552 | l :: _ when l.pageno = pageno ->
553 begin match getopaque pageno with
554 | exception Not_found ->
555 S.mode := LinkNav (Ltnotready (pageno, 0))
556 | opaque ->
557 let x0, y0, x1, y1 = Ffi.getlinkrect opaque linkno in
558 if not (x0 >= l.pagex && x1 <= l.pagex + l.pagevw
559 && y0 >= l.pagey && y1 <= l.pagey + l.pagevh)
560 then S.mode := LinkNav (Ltgendir 0)
562 | _ :: rest -> loop rest
564 loop layout
565 | Ltnotready _ | Ltgendir _ -> ()
567 | Birdseye _ | Textentry _ | View -> ()
568 end;
569 begin match !S.mode with
570 | Birdseye (conf, leftx, pageno, hooverpageno, anchor) ->
571 if not (U.pagevisible layout pageno)
572 then (
573 match !S.layout with
574 | [] -> ()
575 | l :: _ ->
576 S.mode := Birdseye (conf, leftx, l.pageno, hooverpageno, anchor)
578 | LinkNav lt ->
579 begin match lt with
580 | Ltnotready (_, dir)
581 | Ltgendir dir ->
582 let linknav =
583 let rec loop = function
584 | [] -> lt
585 | l :: rest ->
586 match getopaque l.pageno with
587 | exception Not_found -> Ltnotready (l.pageno, dir)
588 | opaque ->
589 let link =
590 let ld =
591 if dir = 0
592 then LDfirstvisible (l.pagex, l.pagey, dir)
593 else if dir > 0 then LDfirst else LDlast
595 Ffi.findlink opaque ld
597 match link with
598 | Lnotfound -> loop rest
599 | Lfound n ->
600 showlinktype (Ffi.getlink opaque n);
601 Ltexact (l.pageno, n)
603 loop !S.layout
605 S.mode := LinkNav linknav
606 | Ltexact _ -> ()
608 | Textentry _ | View -> ()
609 end;
610 preload layout;
611 if conf.updatecurs
612 then (
613 let mx, my = !S.mpos in
614 updateunder mx my;
617 let conttiling pageno opaque =
618 tilepage pageno opaque
619 (if conf.preload
620 then preloadlayout !S.x !S.y !S.winw !S.winh
621 else !S.layout)
623 let gotoxy x y =
624 if not conf.verbose then S.text := E.s;
625 gotoxy x y
627 let getanchory (n, top, dtop) =
628 let y, h = getpageyh n in
629 if conf.presentation
630 then
631 let ips = calcips h in
632 y + truncate (top*.float h -. dtop*.float ips) + ips;
633 else y + truncate (top*.float h -. dtop*.float conf.interpagespace)
635 let addnav () = S.nav := { past = getanchor () :: !S.nav.past; future = []; }
637 let gotopage n top =
638 let y, h = getpageyh n in
639 let y = y + (truncate (top *. float h)) in
640 gotoxy !S.x y
642 let gotopage1 n top =
643 let y = getpagey n in
644 let y = y + top in
645 gotoxy !S.x y
647 let invalidate s f =
648 Glutils.redisplay := false;
649 S.layout := [];
650 S.pdims := [];
651 S.rects := [];
652 S.rects1 := [];
653 match !S.geomcmds with
654 | ps, [] when emptystr ps ->
655 f ();
656 S.geomcmds := s, [];
657 | ps, [] -> S.geomcmds := ps, [s, f];
658 | ps, (s', _) :: rest when s' = s -> S.geomcmds := ps, ((s, f) :: rest);
659 | ps, cmds -> S.geomcmds := ps, ((s, f) :: cmds)
661 let flushpages () =
662 Hashtbl.iter (fun _ opaque -> wcmd1 U.freepage opaque) S.pagemap;
663 Hashtbl.clear S.pagemap
665 let flushtiles () =
666 if not (Queue.is_empty S.tilelru)
667 then (
668 Queue.iter (fun (k, p, s) ->
669 wcmd1 U.freetile p;
670 S.memused := !S.memused - s;
671 Hashtbl.remove S.tilemap k;
672 ) S.tilelru;
673 !S.uioh#infochanged Memused;
674 Queue.clear S.tilelru;
676 load !S.layout
678 let stateh h =
679 let h = truncate (float h*.conf.zoom) in
680 let d = conf.interpagespace lsl (if conf.presentation then 1 else 0) in
681 h - d
683 let fillhelp () =
684 S.help :=
685 let sl = keystostrlist conf in
686 let rec loop accu =
687 function | [] -> accu
688 | s :: rest -> loop ((s, 0, None) :: accu) rest
689 in Help.makehelp conf.urilauncher
690 @ (("", 0, None) :: loop [] sl) |> Array.of_list
692 let titlify path =
693 if emptystr path
694 then path
695 else
696 (if emptystr !S.origin then path else !S.origin)
697 |> Filename.basename |> Ffi.mbtoutf8
699 let settitle title =
700 conf.title <- title;
701 if not !S.ignoredoctitlte
702 then Wsi.settitle @@ title ^ " - llpp"
704 let opendoc path password =
705 S.path := path;
706 S.password := password;
707 S.gen := !S.gen + 1;
708 S.docinfo := [];
709 S.outlines := [||];
711 flushpages ();
712 Ffi.setaalevel conf.aalevel;
713 Ffi.setpapercolor conf.papercolor;
714 Ffi.setdcf conf.dcf;
716 settitle @@ titlify path;
717 wcmd U.dopen "%d %d %d %d %s\000%s\000%s\000"
718 (btod conf.usedoccss) conf.rlw conf.rlh conf.rlem path password conf.css;
719 invalidate "reqlayout"
720 (fun () ->
721 wcmd U.reqlayout " %d %d %d %s\000"
722 conf.angle (FMTE.to_int conf.fitmodel)
723 (stateh !S.winh) !S.nameddest
725 fillhelp ()
727 let reload () =
728 S.anchor := getanchor ();
729 S.reload := Some (!S.x, !S.y, now ());
730 opendoc !S.path !S.password
732 let docolumns columns =
733 match columns with
734 | Csingle _ ->
735 let a = Array.make !S.pagecount (-1, -1, -1, (-1, -1, -1, -1)) in
736 let rec loop pageno pdimno pdim y ph pdims =
737 if pageno != !S.pagecount
738 then
739 let pdimno, ((_, w, h, xoff) as pdim), pdims =
740 match pdims with
741 | ((pageno', _, _, _) as pdim) :: rest when pageno' = pageno ->
742 pdimno+1, pdim, rest
743 | _ ->
744 pdimno, pdim, pdims
746 let x = max 0 (((!S.winw - w) / 2) - xoff) in
747 let y =
748 y + (if conf.presentation
749 then (if pageno = 0 then calcips h else calcips ph + calcips h)
750 else (if pageno = 0 then 0 else conf.interpagespace))
752 a.(pageno) <- (pdimno, x, y, pdim);
753 loop (pageno+1) pdimno pdim (y + h) h pdims
755 loop 0 ~-1 (-1,-1,-1,-1) 0 0 !S.pdims;
756 conf.columns <- Csingle a;
758 | Cmulti ((columns, coverA, coverB), _) ->
759 let a = Array.make !S.pagecount (-1, -1, -1, (-1, -1, -1, -1)) in
760 let rec loop pageno pdimno pdim x y rowh pdims =
761 let rec fixrow m =
762 if m >= pageno
763 then
764 let (pdimno, x, y, ((_, _, h, _) as pdim)) = a.(m) in
765 if h < rowh
766 then a.(m) <- (pdimno, x, y + (rowh - h) / 2, pdim);
767 fixrow (m+1)
769 if pageno = !S.pagecount
770 then fixrow (((pageno - 1) / columns) * columns)
771 else
772 let pdimno, ((_, w, h, xoff) as pdim), pdims =
773 match pdims with
774 | ((pageno', _, _, _) as pdim) :: rest when pageno' = pageno ->
775 pdimno+1, pdim, rest
776 | _ -> pdimno, pdim, pdims
778 let x, y, rowh' =
779 if pageno = coverA - 1 || pageno = !S.pagecount - coverB
780 then (
781 let x = (!S.winw - w) / 2 in
782 let ips =
783 if conf.presentation then calcips h else conf.interpagespace in
784 x, y + ips + rowh, h
786 else (
787 if (pageno - coverA) mod columns = 0
788 then (
789 let x = max 0 (!S.winw - !S.w) / 2 in
790 let y =
791 if conf.presentation
792 then
793 let ips = calcips h in
794 y + (if pageno = 0 then 0 else calcips rowh + ips)
795 else y + (if pageno = 0 then 0 else conf.interpagespace)
797 x, y + rowh, h
799 else x, y, max rowh h
802 let y =
803 if pageno > 1 && (pageno - coverA) mod columns = 0
804 then (
805 let y =
806 if pageno = columns && conf.presentation
807 then (
808 let ips = calcips rowh in
809 for i = 0 to pred columns
811 let (pdimno, x, y, pdim) = a.(i) in
812 a.(i) <- (pdimno, x, y+ips, pdim)
813 done;
814 y+ips;
816 else y
818 fixrow (pageno - columns);
821 else y
823 a.(pageno) <- (pdimno, x, y, pdim);
824 let x = x + w + xoff*2 + conf.interpagespace in
825 loop (pageno+1) pdimno pdim x y rowh' pdims
827 loop 0 ~-1 (-1,-1,-1,-1) 0 0 0 !S.pdims;
828 conf.columns <- Cmulti ((columns, coverA, coverB), a);
830 | Csplit (c, _) ->
831 let a = Array.make (!S.pagecount*c) (-1, -1, -1, (-1, -1, -1, -1)) in
832 let rec loop pageno pdimno pdim y pdims =
833 if pageno != !S.pagecount
834 then
835 let pdimno, ((_, w, h, _) as pdim), pdims =
836 match pdims with
837 | ((pageno', _, _, _) as pdim) :: rest when pageno' = pageno ->
838 pdimno+1, pdim, rest
839 | _ -> pdimno, pdim, pdims
841 let cw = w / c in
842 let rec loop1 n x y =
843 if n = c then y else (
844 a.(pageno*c + n) <- (pdimno, x, y, pdim);
845 loop1 (n+1) (x+cw) (y + h + conf.interpagespace)
848 let y = loop1 0 0 y in
849 loop (pageno+1) pdimno pdim y pdims
851 loop 0 ~-1 (-1,-1,-1,-1) 0 !S.pdims;
852 conf.columns <- Csplit (c, a)
854 let represent () =
855 docolumns conf.columns;
856 S.maxy := calcheight ();
857 if !S.reprf == noreprf
858 then (
859 match !S.mode with
860 | Birdseye (_, _, pageno, _, _) ->
861 let y, h = getpageyh pageno in
862 let top = (!S.winh - h) / 2 in
863 gotoxy !S.x (max 0 (y - top))
864 | Textentry _ | View | LinkNav _ ->
865 let y = getanchory !S.anchor in
866 let y = min y (!S.maxy - !S.winh) in
867 gotoxy !S.x y;
869 else (
870 !S.reprf ();
871 S.reprf := noreprf;
874 let reshape ?(firsttime=false) w h =
875 GlDraw.viewport ~x:0 ~y:0 ~w ~h;
876 if not firsttime && U.nogeomcmds !S.geomcmds
877 then S.anchor := getanchor ();
879 S.winw := w;
880 let w = truncate (float w *. conf.zoom) in
881 let w = max w 2 in
882 S.winh := h;
883 setfontsize fstate.fontsize;
884 GlMat.mode `modelview;
885 GlMat.load_identity ();
887 GlMat.mode `projection;
888 GlMat.load_identity ();
889 GlMat.rotate ~x:1.0 ~angle:180.0 ();
890 GlMat.translate ~x:~-.1.0 ~y:~-.1.0 ();
891 GlMat.scale3 (2.0 /. float !S.winw, 2.0 /. float !S.winh, 1.0);
893 let relx =
894 if conf.zoom <= 1.0
895 then 0.0
896 else float !S.x /. float !S.w
898 invalidate "geometry"
899 (fun () ->
900 S.w := w;
901 if not firsttime
902 then S.x := truncate (relx *. float w);
903 let w =
904 match conf.columns with
905 | Csingle _ -> w
906 | Cmulti ((c, _, _), _) -> (w - (c-1)*conf.interpagespace) / c
907 | Csplit (c, _) -> w * c
909 wcmd U.geometry "%d %d %d" w (stateh h) (FMTE.to_int conf.fitmodel)
912 let gctiles () =
913 let len = Queue.length S.tilelru in
914 let layout = lazy (if conf.preload
915 then preloadlayout !S.x !S.y !S.winw !S.winh
916 else !S.layout) in
917 let rec loop qpos =
918 if !S.memused > conf.memlimit
919 then (
920 if qpos < len
921 then
922 let (k, p, s) as lruitem = Queue.pop S.tilelru in
923 let n, gen, colorspace, angle, pagew, pageh, col, row = k in
924 let (_, pw, ph, _) = getpagedim n in
925 if gen = !S.gen
926 && colorspace = conf.colorspace
927 && angle = conf.angle
928 && pagew = pw
929 && pageh = ph
930 && (
931 let x = col*conf.tilew and y = row*conf.tileh in
932 tilevisible (Lazy.force_val layout) n x y
934 then Queue.push lruitem S.tilelru
935 else (
936 wcmd1 U.freetile p;
937 S.memused := !S.memused - s;
938 !S.uioh#infochanged Memused;
939 Hashtbl.remove S.tilemap k;
941 loop (qpos+1)
944 loop 0
946 let onpagerect pageno f =
947 let b =
948 match conf.columns with
949 | Cmulti (_, b) -> b
950 | Csingle b -> b
951 | Csplit (_, b) -> b
953 if pageno >= 0 && pageno < Array.length b
954 then
955 let (_, _, _, (_, w, h, _)) = b.(pageno) in
956 f w h
958 let gotopagexy1 pageno x y =
959 let _,w1,h1,leftx = getpagedim pageno in
960 let top = y /. (float h1) in
961 let left = x /. (float w1) in
962 let py, w, h = getpageywh pageno in
963 let wh = !S.winh in
964 let x = left *. (float w) in
965 let x = leftx + !S.x + truncate x in
966 let sx =
967 if x < 0 || x >= !S.winw
968 then !S.x - x
969 else !S.x
971 let pdy = truncate (top *. float h) in
972 let y' = py + pdy in
973 let dy = y' - !S.y in
974 let sy =
975 if x != !S.x || not (dy > 0 && dy < wh)
976 then (
977 if conf.presentation
978 then
979 if abs (py - y') > wh
980 then y'
981 else py
982 else y';
984 else !S.y
986 if !S.x != sx || !S.y != sy
987 then gotoxy sx sy
988 else gotoxy !S.x !S.y
990 let gotopagexy pageno x y =
991 match !S.mode with
992 | Birdseye _ -> gotopage pageno 0.0
993 | Textentry _ | View | LinkNav _ -> gotopagexy1 pageno x y
995 let getpassword () =
996 let passcmd = getenvdef "LLPP_ASKPASS" conf.passcmd in
997 if emptystr passcmd
998 then (adderrmsg "askpass" "ask password program not set"; E.s)
999 else getcmdoutput (adderrfmt passcmd "failed to obrain password: %s") passcmd
1001 let pgoto opaque pageno x y =
1002 let pdimno = getpdimno pageno in
1003 let x, y = Ffi.project opaque pageno pdimno x y in
1004 gotopagexy pageno x y
1006 let act cmds =
1007 (* dolog1 "%S" cmds; *)
1008 let spl = splitatchar cmds ' ' in
1009 let scan s fmt f =
1010 try Scanf.sscanf s fmt f
1011 with exn ->
1012 dolog1 "error scanning %S: %s" cmds @@ exntos exn;
1013 exit 1
1015 let addoutline outline =
1016 match !S.currently with
1017 | Outlining outlines -> S.currently := Outlining (outline :: outlines)
1018 | Idle -> S.currently := Outlining [outline]
1019 | Loading _ | Tiling _ ->
1020 dolog1 "invalid outlining state";
1021 logcurrently !S.currently
1023 match spl with
1024 | "clear", "" ->
1025 S.pdims := [];
1026 !S.uioh#infochanged Pdim;
1028 | "clearrects", "" ->
1029 S.rects := !S.rects1;
1030 Glutils.postRedisplay "clearrects";
1032 | "continue", args ->
1033 let n = scan args "%u" (fun n -> n) in
1034 S.pagecount := n;
1035 begin match !S.currently with
1036 | Outlining l ->
1037 S.currently := Idle;
1038 S.outlines := Array.of_list (List.rev l)
1039 | Idle | Loading _ | Tiling _ -> ()
1040 end;
1042 let cur, cmds = !S.geomcmds in
1043 if emptystr cur then error "empty geomcmd";
1045 begin match List.rev cmds with
1046 | [] ->
1047 S.geomcmds := E.s, [];
1048 represent ();
1049 | (s, f) :: rest ->
1050 f ();
1051 S.geomcmds := s, List.rev rest;
1052 end;
1053 Glutils.postRedisplay "continue";
1055 | "vmsg", args ->
1056 if conf.verbose then showtext ' ' args
1058 | "emsg", args ->
1059 if not !S.redirstderr
1060 then Format.eprintf "%s@." args
1061 else (
1062 Buffer.add_string S.errmsgs args;
1063 Buffer.add_char S.errmsgs '\n';
1064 if not !S.newerrmsgs
1065 then (
1066 S.newerrmsgs := true;
1067 Glutils.postRedisplay "error message";
1071 | "progress", args ->
1072 let progress, text =
1073 scan args "%f %n"
1074 (fun f pos -> f, String.sub args pos (String.length args - pos))
1076 S.text := text;
1077 S.progress := progress;
1078 Glutils.postRedisplay "progress"
1080 | "match", args ->
1081 let pageno, n, x0, y0, x1, y1, x2, y2, x3, y3 =
1082 scan args "%u %d %f %f %f %f %f %f %f %f"
1083 (fun p n x0 y0 x1 y1 x2 y2 x3 y3 ->
1084 (p, n, x0, y0, x1, y1, x2, y2, x3, y3))
1086 if n = 0
1087 then (
1088 let y = (getpagey pageno) + truncate y0 in
1089 let x =
1090 if (!S.x < - truncate x0) || (!S.x > !S.winw - truncate x1)
1091 then !S.winw/2 - truncate (x0 /. 2. +. x1 /. 2.)
1092 else !S.x
1094 addnav ();
1095 gotoxy x y;
1097 let color = (0.0, 0.0, (if n = 0 then 1.0 else 0.5), 0.5) in
1098 S.rects1 :=
1099 (pageno, color, (x0, y0, x1, y1, x2, y2, x3, y3)) :: !S.rects1
1101 | "page", args ->
1102 let pageopaques, t = scan args "%s %f" (fun p t -> p, t) in
1103 let pageopaque = Opaque.of_string pageopaques in
1104 begin match !S.currently with
1105 | Loading (l, gen) ->
1106 vlog "page %d took %f sec" l.pageno t;
1107 Hashtbl.replace S.pagemap (l.pageno, gen) pageopaque;
1108 let preloadedpages =
1109 if conf.preload
1110 then preloadlayout !S.x !S.y !S.winw !S.winh
1111 else !S.layout
1113 let evict () =
1114 let set = List.fold_left (fun s l -> IntSet.add l.pageno s)
1115 IntSet.empty preloadedpages
1117 let evictedpages =
1118 Hashtbl.fold (fun ((pageno, _) as key) opaque accu ->
1119 if not (IntSet.mem pageno set)
1120 then (
1121 wcmd1 U.freepage opaque;
1122 key :: accu
1124 else accu
1125 ) S.pagemap []
1127 List.iter (Hashtbl.remove S.pagemap) evictedpages;
1129 evict ();
1130 S.currently := Idle;
1131 if gen = !S.gen
1132 then (
1133 tilepage l.pageno pageopaque !S.layout;
1134 load !S.layout;
1135 load preloadedpages;
1136 let visible = U.pagevisible !S.layout l.pageno in
1137 if visible
1138 then (
1139 match !S.mode with
1140 | LinkNav (Ltnotready (pageno, dir)) ->
1141 if pageno = l.pageno
1142 then (
1143 let link =
1144 let ld =
1145 if dir = 0
1146 then LDfirstvisible (l.pagex, l.pagey, dir)
1147 else if dir > 0 then LDfirst else LDlast
1149 Ffi.findlink pageopaque ld
1151 match link with
1152 | Lnotfound -> ()
1153 | Lfound n ->
1154 showlinktype (Ffi.getlink pageopaque n);
1155 S.mode := LinkNav (Ltexact (l.pageno, n))
1157 | LinkNav (Ltgendir _)
1158 | LinkNav (Ltexact _)
1159 | View
1160 | Birdseye _
1161 | Textentry _ -> ()
1164 if visible && layoutready !S.layout
1165 then Glutils.postRedisplay "page";
1168 | Idle | Tiling _ | Outlining _ ->
1169 dolog1 "Inconsistent loading state";
1170 logcurrently !S.currently;
1171 exit 1
1174 | "tile" , args ->
1175 let (x, y, opaques, size, t) =
1176 scan args "%u %u %s %u %f" (fun x y p size t -> (x, y, p, size, t))
1178 let opaque = Opaque.of_string opaques in
1179 begin match !S.currently with
1180 | Tiling (l, pageopaque, cs, angle, gen, col, row, tilew, tileh) ->
1181 vlog "tile %d [%d,%d] took %f sec" l.pageno col row t;
1182 if tilew != conf.tilew || tileh != conf.tileh
1183 then (
1184 wcmd1 U.freetile opaque;
1185 S.currently := Idle;
1186 load !S.layout;
1188 else (
1189 puttileopaque l col row gen cs angle opaque size t;
1190 S.memused := !S.memused + size;
1191 !S.uioh#infochanged Memused;
1192 gctiles ();
1193 Queue.push ((l.pageno, gen, cs, angle, l.pagew, l.pageh, col, row),
1194 opaque, size) S.tilelru;
1196 S.currently := Idle;
1197 if gen = !S.gen
1198 && conf.colorspace = cs
1199 && conf.angle = angle
1200 && tilevisible !S.layout l.pageno x y
1201 then conttiling l.pageno pageopaque;
1203 preload !S.layout;
1204 if gen = !S.gen
1205 && conf.colorspace = cs
1206 && conf.angle = angle
1207 && tilevisible !S.layout l.pageno x y
1208 && layoutready !S.layout
1209 then Glutils.postRedisplay "tile nothrottle";
1212 | Idle | Loading _ | Outlining _ ->
1213 dolog1 "Inconsistent tiling state";
1214 logcurrently !S.currently;
1215 exit 1
1218 | "pdim", args ->
1219 let (n, w, h, _) as pdim =
1220 scan args "%u %d %d %d" (fun n x w h -> n, w, h, x)
1222 let pdim =
1223 match conf.fitmodel with
1224 | FitWidth -> pdim
1225 | FitPage | FitProportional ->
1226 match conf.columns with
1227 | Csplit _ -> (n, w, h, 0)
1228 | Csingle _ | Cmulti _ -> pdim
1230 S.pdims := pdim :: !S.pdims;
1231 !S.uioh#infochanged Pdim
1233 | "o", args ->
1234 let (l, n, t, h, pos) =
1235 scan args "%u %u %d %u %n" (fun l n t h pos -> l, n, t, h, pos)
1237 let s = String.sub args pos (String.length args - pos) in
1238 addoutline (s, l, Oanchor (n, float t /. float h, 0.0))
1240 | "ou", args ->
1241 let (l, len, pos) = scan args "%u %u %n" (fun l len pos -> l, len, pos) in
1242 let s = String.sub args pos len in
1243 let pos2 = pos + len + 1 in
1244 let uri = String.sub args pos2 (String.length args - pos2) in
1245 addoutline (s, l, Ouri uri)
1247 | "on", args ->
1248 let (l, pos) = scan args "%u %n" (fun l pos -> l, pos) in
1249 let s = String.sub args pos (String.length args - pos) in
1250 addoutline (s, l, Onone)
1252 | "a", args ->
1253 let (n, l, t) = scan args "%u %d %d" (fun n l t -> n, l, t) in
1254 S.reprf := (fun () -> gotopagexy n (float l) (float t))
1256 | "info", args ->
1257 let s =
1258 match splitatchar args '\t' with
1259 | "Title", "" ->
1260 settitle @@ Filename.basename !S.path;
1262 | "Title", v ->
1263 settitle v;
1264 args
1265 | _, "" -> E.s
1266 | c, v ->
1267 if let len = String.length c in
1268 len > 6 && ((String.sub c (len-4) 4) = "date")
1269 then (
1270 if String.length v >= 7 && v.[0] = 'D' && v.[1] = ':'
1271 then
1272 let b = Buffer.create 10 in
1273 Printf.bprintf b "%s\t" c;
1274 let sub p l c =
1276 Buffer.add_substring b v p l;
1277 Buffer.add_char b c;
1278 with exn -> Buffer.add_string b @@ exntos exn
1280 sub 2 4 '/';
1281 sub 6 2 '/';
1282 sub 8 2 ' ';
1283 sub 10 2 ':';
1284 sub 12 2 ':';
1285 sub 14 2 ' ';
1286 Printf.bprintf b "[%s]" v;
1287 Buffer.contents b
1288 else args
1290 else args
1292 if nonemptystr s then S.docinfo := (1, s) :: !S.docinfo
1294 | "infoend", "" ->
1295 S.docinfo := List.rev !S.docinfo;
1296 !S.uioh#infochanged Docinfo
1298 | "pass", args ->
1299 if args = "fail"
1300 then adderrmsg "pass" "Wrong password";
1301 let password = getpassword () in
1302 if emptystr password
1303 then error "document is password protected"
1304 else opendoc !S.path password
1306 | _ -> error "unknown cmd `%S'" cmds
1308 let onhist cb =
1309 let rc = cb.rc in
1310 let action = function
1311 | HCprev -> cbget cb ~-1
1312 | HCnext -> cbget cb 1
1313 | HCfirst -> cbget cb ~-(cb.rc)
1314 | HClast -> cbget cb (cb.len - 1 - cb.rc)
1315 and cancel () = cb.rc <- rc
1316 in (action, cancel)
1318 let search pattern forward =
1319 match conf.columns with
1320 | Csplit _ -> impmsg "searching does not work properly in split columns mode"
1321 | Csingle _ | Cmulti _ ->
1322 if nonemptystr pattern
1323 then
1324 let pn, py =
1325 match !S.layout with
1326 | [] -> 0, 0
1327 | l :: _ -> l.pageno, (l.pagey + if forward then 0 else 0*l.pagevh)
1329 S.rects1 := [];
1330 wcmd U.search "%d %d %d %d,%s\000"
1331 (btod conf.icase) pn py (btod forward) pattern
1333 let intentry text key =
1334 let text =
1335 if emptystr text && key = Keys.Ascii '-'
1336 then addchar text '-'
1337 else
1338 match [@warning "-fragile-match"] key with
1339 | Keys.Ascii ('0'..'9' as c) -> addchar text c
1340 | _ ->
1341 S.text := "invalid key";
1342 text
1344 TEcont text
1346 let linknact f s =
1347 if nonemptystr s
1348 then
1349 let rec loop off = function
1350 | [] -> ()
1351 | l :: rest ->
1352 match getopaque l.pageno with
1353 | exception Not_found -> loop off rest
1354 | opaque ->
1355 let n = Ffi.getlinkn opaque conf.hcs s off in
1356 if n <= 0
1357 then loop n rest
1358 else Ffi.getlink opaque (n-1) |> f
1360 loop 0 !S.layout
1362 let linknentry text = function [@warning "-fragile-match"]
1363 | Keys.Ascii c ->
1364 let text = addchar text c in
1365 linknact (fun under -> S.text := undertext under) text;
1366 TEcont text
1367 | key ->
1368 settextfmt "invalid key %s" @@ Keys.to_string key;
1369 TEcont text
1371 let textentry text key = match [@warning "-fragile-match"] key with
1372 | Keys.Ascii c -> TEcont (addchar text c)
1373 | Keys.Code c -> TEcont (text ^ Ffi.toutf8 c)
1374 | _ -> TEcont text
1376 let reqlayout angle fitmodel =
1377 if U.nogeomcmds !S.geomcmds
1378 then S.anchor := getanchor ();
1379 conf.angle <- angle mod 360;
1380 if conf.angle != 0
1381 then (
1382 match !S.mode with
1383 | LinkNav _ -> S.mode := View
1384 | Birdseye _ | Textentry _ | View -> ()
1386 conf.fitmodel <- fitmodel;
1387 invalidate "reqlayout"
1388 (fun () -> wcmd U.reqlayout "%d %d %d"
1389 conf.angle (FMTE.to_int conf.fitmodel) (stateh !S.winh))
1391 let settrim trimmargins trimfuzz =
1392 if U.nogeomcmds !S.geomcmds
1393 then S.anchor := getanchor ();
1394 conf.trimmargins <- trimmargins;
1395 conf.trimfuzz <- trimfuzz;
1396 let x0, y0, x1, y1 = trimfuzz in
1397 invalidate "settrim"
1398 (fun () -> wcmd U.settrim "%d %d %d %d %d"
1399 (btod conf.trimmargins) x0 y0 x1 y1);
1400 flushpages ()
1402 let setzoom zoom =
1403 let zoom = max 0.0001 zoom in
1404 if zoom <> conf.zoom
1405 then (
1406 S.prevzoom := (conf.zoom, !S.x);
1407 conf.zoom <- zoom;
1408 reshape !S.winw !S.winh;
1409 settextfmt "zoom is now %-5.2f" (zoom *. 100.0);
1412 let pivotzoom ?(vw=min !S.w !S.winw)
1413 ?(vh=min (!S.maxy - !S.y) !S.winh)
1414 ?(x=vw/2) ?(y=vh/2) zoom =
1415 let w = float !S.w /. zoom in
1416 let hw = w /. 2.0 in
1417 let ratio = float vh /. float vw in
1418 let hh = hw *. ratio in
1419 let x0 = float x -. hw +. !S.xf and y0 = float y -. hh +. !S.yf in
1420 let xf, xr = modf x0 and yf, yr = modf y0 in
1421 S.xf := xf;
1422 S.yf := yf;
1423 gotoxy (!S.x - truncate xr) (!S.y + truncate yr);
1424 setzoom zoom
1426 let pivotzoom ?vw ?vh ?x ?y zoom =
1427 if U.nogeomcmds !S.geomcmds
1428 then
1429 if zoom > 1.0
1430 then pivotzoom ?vw ?vh ?x ?y zoom
1431 else setzoom zoom
1433 let setcolumns mode columns coverA coverB =
1434 S.prevcolumns := Some (conf.columns, conf.zoom);
1435 if columns < 0
1436 then (
1437 if isbirdseye mode
1438 then impmsg "split mode doesn't work in bird's eye"
1439 else (
1440 conf.columns <- Csplit (-columns, E.a);
1441 S.x := 0;
1442 conf.zoom <- 1.0;
1445 else (
1446 if columns < 2
1447 then (
1448 conf.columns <- Csingle E.a;
1449 S.x := 0;
1450 setzoom 1.0;
1452 else (
1453 conf.columns <- Cmulti ((columns, coverA, coverB), E.a);
1454 conf.zoom <- 1.0;
1457 reshape !S.winw !S.winh
1459 let resetmstate () =
1460 S.mstate := Mnone;
1461 Wsi.setcursor Wsi.CURSOR_INHERIT
1463 let enterbirdseye () =
1464 let zoom = float conf.thumbw /. float !S.winw in
1465 let birdseyepageno =
1466 let cy = !S.winh / 2 in
1467 let fold = function
1468 | [] -> 0
1469 | l :: rest ->
1470 let rec fold best = function
1471 | [] -> best.pageno
1472 | l :: rest ->
1473 let d = cy - (l.pagedispy + l.pagevh/2)
1474 and dbest = cy - (best.pagedispy + best.pagevh/2) in
1475 if abs d < abs dbest
1476 then fold l rest
1477 else best.pageno
1478 in fold l rest
1480 fold !S.layout
1482 S.mode :=
1483 Birdseye (
1484 { conf with zoom = conf.zoom },
1485 !S.x, birdseyepageno, -1, getanchor ()
1487 resetmstate ();
1488 conf.zoom <- zoom;
1489 conf.presentation <- false;
1490 conf.interpagespace <- 10;
1491 conf.hlinks <- false;
1492 conf.fitmodel <- FitPage;
1493 S.x := 0;
1494 conf.columns <- (
1495 match conf.beyecolumns with
1496 | Some c ->
1497 conf.zoom <- 1.0;
1498 Cmulti ((c, 0, 0), E.a)
1499 | None -> Csingle E.a
1501 if conf.verbose
1502 then settextfmt "birds eye on (zoom %3.1f%%)" (100.0*.zoom);
1503 reshape !S.winw !S.winh
1505 let leavebirdseye (c, leftx, pageno, _, anchor) goback =
1506 S.mode := View;
1507 conf.zoom <- c.zoom;
1508 conf.presentation <- c.presentation;
1509 conf.interpagespace <- c.interpagespace;
1510 conf.hlinks <- c.hlinks;
1511 conf.fitmodel <- c.fitmodel;
1512 conf.beyecolumns <- (
1513 match conf.columns with
1514 | Cmulti ((c, _, _), _) -> Some c
1515 | Csingle _ -> None
1516 | Csplit _ -> error "leaving bird's eye split mode"
1518 conf.columns <- (
1519 match c.columns with
1520 | Cmulti (c, _) -> Cmulti (c, E.a)
1521 | Csingle _ -> Csingle E.a
1522 | Csplit (c, _) -> Csplit (c, E.a)
1524 if conf.verbose
1525 then settextfmt "bird's eye off (zoom %3.1f%%)" (100.0*.conf.zoom);
1526 reshape !S.winw !S.winh;
1527 S.anchor := if goback then anchor else (pageno, 0.0, 1.0);
1528 S.x := leftx
1530 let togglebirdseye () =
1531 match !S.mode with
1532 | Birdseye vals -> leavebirdseye vals true
1533 | View -> enterbirdseye ()
1534 | Textentry _ | LinkNav _ -> ()
1536 let upbirdseye incr (conf, leftx, pageno, hooverpageno, anchor) =
1537 let pageno = max 0 (pageno - incr) in
1538 let rec loop = function
1539 | [] -> gotopage1 pageno 0
1540 | l :: _ when l.pageno = pageno ->
1541 if l.pagedispy >= 0 && l.pagey = 0
1542 then Glutils.postRedisplay "upbirdseye"
1543 else gotopage1 pageno 0
1544 | _ :: rest -> loop rest
1546 loop !S.layout;
1547 S.text := E.s;
1548 S.mode := Birdseye (conf, leftx, pageno, hooverpageno, anchor)
1550 let downbirdseye incr (conf, leftx, pageno, hooverpageno, anchor) =
1551 let pageno = min (!S.pagecount - 1) (pageno + incr) in
1552 S.mode := Birdseye (conf, leftx, pageno, hooverpageno, anchor);
1553 let rec loop = function
1554 | [] ->
1555 let y, h = getpageyh pageno in
1556 let dy = (y - !S.y) - (!S.winh - h - conf.interpagespace) in
1557 gotoxy !S.x (U.add_to_y_and_clamp dy)
1558 | l :: _ when l.pageno = pageno ->
1559 if l.pagevh != l.pageh
1560 then
1561 let inc = l.pageh - l.pagevh + conf.interpagespace in
1562 gotoxy !S.x (U.add_to_y_and_clamp inc)
1563 else Glutils.postRedisplay "downbirdseye"
1564 | _ :: rest -> loop rest
1566 loop !S.layout;
1567 S.text := E.s
1569 let optentry mode _ key =
1570 match [@warning "-fragile-match"] key with
1571 | Keys.Ascii 'C' ->
1572 let ondone s =
1574 let n, a, b = multicolumns_of_string s in
1575 setcolumns mode n a b;
1576 with exn -> settextfmt "bad columns `%s': %s" s @@ exntos exn
1578 TEswitch ("columns: ", E.s, None, textentry, ondone, true)
1580 | Keys.Ascii 'Z' ->
1581 let ondone s =
1583 let zoom = float (int_of_string s) /. 100.0 in
1584 pivotzoom zoom
1585 with exn -> settextfmt "bad integer `%s': %s" s @@ exntos exn
1587 TEswitch ("zoom: ", E.s, None, intentry, ondone, true)
1589 | Keys.Ascii 'i' ->
1590 conf.icase <- not conf.icase;
1591 TEdone ("case insensitive search " ^ (onoffs conf.icase))
1593 | Keys.Ascii 'v' ->
1594 conf.verbose <- not conf.verbose;
1595 TEdone ("verbose " ^ (onoffs conf.verbose))
1597 | Keys.Ascii 'd' ->
1598 conf.debug <- not conf.debug;
1599 TEdone ("debug " ^ (onoffs conf.debug))
1601 | Keys.Ascii 'f' ->
1602 conf.underinfo <- not conf.underinfo;
1603 TEdone ("underinfo " ^ onoffs conf.underinfo)
1605 | Keys.Ascii 'T' ->
1606 settrim (not conf.trimmargins) conf.trimfuzz;
1607 TEdone ("trim margins " ^ onoffs conf.trimmargins)
1609 | Keys.Ascii 'I' ->
1610 conf.invert <- not conf.invert;
1611 TEdone ("invert colors " ^ onoffs conf.invert)
1613 | Keys.Ascii 'x' ->
1614 let ondone s =
1615 cbput !S.hists.sel s;
1616 conf.selcmd <- s;
1618 TEswitch ("selection command: ", E.s, Some (onhist !S.hists.sel),
1619 textentry, ondone, true)
1621 | Keys.Ascii 'M' ->
1622 if conf.pax == None
1623 then conf.pax <- Some 0.0
1624 else conf.pax <- None;
1625 TEdone ("PAX " ^ onoffs (conf.pax != None))
1627 | (Keys.Ascii c) ->
1628 settextfmt "bad option %d `%c'" (Char.code c) c;
1629 TEstop
1631 | _ -> TEcont !S.text
1633 class outlinelistview ~zebra ~source =
1634 let settext autonarrow s =
1635 S.text :=
1636 if autonarrow
1637 then
1638 let ss = source#statestr in
1639 if emptystr ss then "[" ^ s ^ "]" else "{" ^ ss ^ "} [" ^ s ^ "]"
1640 else s
1642 object (self)
1643 inherit listview
1644 ~zebra
1645 ~helpmode:false
1646 ~source:(source :> lvsource)
1647 ~trusted:false
1648 ~modehash:(findkeyhash conf "outline")
1649 as super
1651 val m_autonarrow = false
1653 method! key key mask =
1654 let maxrows =
1655 if emptystr !S.text
1656 then fstate.maxrows
1657 else fstate.maxrows - 2
1659 let calcfirst first active =
1660 if active > first
1661 then
1662 let rows = active - first in
1663 if rows > maxrows then active - maxrows else first
1664 else active
1666 let navigate incr =
1667 let active = m_active + incr in
1668 let active = bound active 0 (source#getitemcount - 1) in
1669 let first = calcfirst m_first active in
1670 Glutils.postRedisplay "outline navigate";
1671 coe {< m_active = active; m_first = first >}
1673 let navscroll first =
1674 let active =
1675 let dist = m_active - first in
1676 if dist < 0
1677 then first
1678 else (
1679 if dist < maxrows
1680 then m_active
1681 else first + maxrows
1684 Glutils.postRedisplay "outline navscroll";
1685 coe {< m_first = first; m_active = active >}
1687 let ctrl = Wsi.withctrl mask in
1688 let open Keys in
1689 match Wsi.ks2kt key with
1690 | Ascii 'a' when ctrl ->
1691 let text =
1692 if m_autonarrow
1693 then (
1694 source#denarrow;
1697 else (
1698 let pattern = source#renarrow in
1699 if nonemptystr m_qsearch
1700 then (source#narrow m_qsearch; m_qsearch)
1701 else pattern
1704 settext (not m_autonarrow) text;
1705 Glutils.postRedisplay "toggle auto narrowing";
1706 coe {< m_first = 0; m_active = 0; m_autonarrow = not m_autonarrow >}
1707 | Ascii '/' when emptystr m_qsearch && not m_autonarrow ->
1708 settext true E.s;
1709 Glutils.postRedisplay "toggle auto narrowing";
1710 coe {< m_first = 0; m_active = 0; m_autonarrow = true >}
1711 | Ascii 'n' when ctrl ->
1712 source#narrow m_qsearch;
1713 if not m_autonarrow
1714 then source#add_narrow_pattern m_qsearch;
1715 Glutils.postRedisplay "outline ctrl-n";
1716 coe {< m_first = 0; m_active = 0 >}
1717 | Ascii 'S' when ctrl ->
1718 let active = source#calcactive (getanchor ()) in
1719 let first = firstof m_first active in
1720 Glutils.postRedisplay "outline ctrl-s";
1721 coe {< m_first = first; m_active = active >}
1722 | Ascii 'u' when ctrl ->
1723 Glutils.postRedisplay "outline ctrl-u";
1724 if m_autonarrow && nonemptystr m_qsearch
1725 then (
1726 ignore (source#renarrow);
1727 settext m_autonarrow E.s;
1728 coe {< m_first = 0; m_active = 0; m_qsearch = E.s >}
1730 else (
1731 source#del_narrow_pattern;
1732 let pattern = source#renarrow in
1733 let text =
1734 if emptystr pattern then E.s else "Narrowed to " ^ pattern
1736 settext m_autonarrow text;
1737 coe {< m_first = 0; m_active = 0; m_qsearch = E.s >}
1739 | Ascii 'l' when ctrl ->
1740 let first = max 0 (m_active - (fstate.maxrows / 2)) in
1741 Glutils.postRedisplay "outline ctrl-l";
1742 coe {< m_first = first >}
1744 | Ascii '\t' when m_autonarrow ->
1745 if nonemptystr m_qsearch
1746 then (
1747 Glutils.postRedisplay "outline list view tab";
1748 source#add_narrow_pattern m_qsearch;
1749 settext true E.s;
1750 coe {< m_qsearch = E.s >}
1752 else coe self
1753 | Escape when m_autonarrow ->
1754 if nonemptystr m_qsearch
1755 then source#add_narrow_pattern m_qsearch;
1756 super#key key mask
1757 | Enter when m_autonarrow ->
1758 if nonemptystr m_qsearch
1759 then source#add_narrow_pattern m_qsearch;
1760 super#key key mask
1761 | (Ascii _ | Code _) when m_autonarrow ->
1762 let pattern = m_qsearch ^ Ffi.toutf8 key in
1763 Glutils.postRedisplay "outlinelistview autonarrow add";
1764 source#narrow pattern;
1765 settext true pattern;
1766 coe {< m_first = 0; m_active = 0; m_qsearch = pattern >}
1767 | Backspace when m_autonarrow ->
1768 if emptystr m_qsearch
1769 then coe self
1770 else
1771 let pattern = withoutlastutf8 m_qsearch in
1772 Glutils.postRedisplay "outlinelistview autonarrow backspace";
1773 ignore (source#renarrow);
1774 source#narrow pattern;
1775 settext true pattern;
1776 coe {< m_first = 0; m_active = 0; m_qsearch = pattern >}
1777 | Up when ctrl -> navscroll (max 0 (m_first-1))
1778 | Down when ctrl -> navscroll (min (source#getitemcount-1) (m_first+1))
1779 | Up -> navigate ~-1
1780 | Down -> navigate 1
1781 | Prior -> navigate ~-(fstate.maxrows)
1782 | Next -> navigate fstate.maxrows
1783 | Right ->
1784 (if ctrl
1785 then (
1786 Glutils.postRedisplay "outline ctrl right";
1787 {< m_pan = m_pan + 1 >}
1789 else (
1790 if Wsi.withshift mask
1791 then self#nextcurlevel 1
1792 else self#updownlevel 1
1793 )) |> coe
1794 | Left ->
1795 (if ctrl
1796 then (
1797 Glutils.postRedisplay "outline ctrl left";
1798 {< m_pan = m_pan - 1 >}
1800 else (
1801 if Wsi.withshift mask
1802 then self#nextcurlevel ~-1
1803 else self#updownlevel ~-1
1804 )) |> coe
1805 | Home ->
1806 Glutils.postRedisplay "outline home";
1807 coe {< m_first = 0; m_active = 0 >}
1808 | End ->
1809 let active = source#getitemcount - 1 in
1810 let first = max 0 (active - fstate.maxrows) in
1811 Glutils.postRedisplay "outline end";
1812 coe {< m_active = active; m_first = first >}
1813 | Delete|Escape|Insert|Enter|Ascii _|Code _|Ctrl _|Backspace|Fn _ ->
1814 super#key key mask
1817 let genhistoutlines () =
1818 Config.gethist ()
1819 |> List.sort (fun (_, c1, _, _, _, _) (_, c2, _, _, _, _) ->
1820 compare c2.lastvisit c1.lastvisit)
1821 |> List.map (fun ((path, c, _, _, _, origin) as hist) ->
1822 let path = if nonemptystr origin then origin else path in
1823 let base = Ffi.mbtoutf8 @@ Filename.basename path in
1824 (base ^ "\000" ^ c.title, 1, Ohistory hist)
1827 let gotohist (path, c, bookmarks, x, anchor, origin) =
1828 Config.save leavebirdseye;
1829 S.anchor := anchor;
1830 S.bookmarks := bookmarks;
1831 S.origin := origin;
1832 S.x := x;
1833 setconf conf c;
1834 let x0, y0, x1, y1 = conf.trimfuzz in
1835 wcmd U.trimset "%d %d %d %d %d" (btod conf.trimmargins) x0 y0 x1 y1;
1836 Wsi.reshape c.cwinw c.cwinh;
1837 opendoc path origin;
1838 setzoom c.zoom
1840 let describe_layout layout =
1841 let d =
1842 match layout with
1843 | [] -> "Page 0"
1844 | l :: [] -> Printf.sprintf "Page %d" (l.pageno+1)
1845 | l :: rest ->
1846 let rangestr a b =
1847 if a.pageno = b.pageno then Printf.sprintf "%d" (a.pageno+1)
1848 else Printf.sprintf "%d%s%d" (a.pageno+1)
1849 (if a.pageno+1 = b.pageno then ", " else Utf8syms.ellipsis)
1850 (b.pageno+1)
1852 let rec fold s la lb = function
1853 | [] -> Printf.sprintf "%s %s" s (rangestr la lb)
1854 | l :: rest when l.pageno = succ lb.pageno -> fold s la l rest
1855 | l :: rest -> fold (s ^ " " ^ rangestr la lb ^ ",") l l rest
1857 fold "Pages" l l rest
1859 let percent =
1860 let maxy = U.maxy () in
1861 if maxy <= 0
1862 then 100.
1863 else 100. *. (float !S.y /. float maxy)
1865 Printf.sprintf "%s of %d [%.2f%%]" d !S.pagecount percent
1867 let setpresentationmode v =
1868 let n = page_of_y !S.y in
1869 S.anchor := (n, 0.0, 1.0);
1870 conf.presentation <- v;
1871 if conf.fitmodel = FitPage
1872 then reqlayout conf.angle conf.fitmodel;
1873 represent ()
1875 let infomenu =
1876 let modehash = lazy (findkeyhash conf "info") in (fun source ->
1877 S.text := E.s;
1878 new listview ~zebra:false ~helpmode:false ~source
1879 ~trusted:true ~modehash:(Lazy.force_val modehash) |> coe)
1881 let enterinfomode =
1882 let btos b = if b then Utf8syms.radical else E.s in
1883 let showextended = ref false in
1884 let showcolors = ref false in
1885 let showcommands = ref false in
1886 let showrefl = ref false in
1887 let leave mode _ = S.mode := mode in
1888 let src = object
1889 val mutable m_l = []
1890 val mutable m_a = E.a
1891 val mutable m_prev_uioh = nouioh
1892 val mutable m_prev_mode = View
1894 inherit lvsourcebase
1896 method reset prev_mode prev_uioh =
1897 m_a <- Array.of_list (List.rev m_l);
1898 m_l <- [];
1899 m_prev_mode <- prev_mode;
1900 m_prev_uioh <- prev_uioh;
1902 method int name get set =
1903 m_l <-
1904 (name, `int get, 1,
1905 Some (fun u ->
1906 let ondone s =
1907 try set (int_of_string s)
1908 with exn -> settextfmt "bad integer `%s': %s" s @@ exntos exn
1910 S.text := E.s;
1911 let te = (name ^ ": ", E.s, None, intentry, ondone, true) in
1912 S.mode := Textentry (te, leave m_prev_mode);
1914 )) :: m_l
1916 method int_with_suffix name get set =
1917 m_l <-
1918 (name, `intws get, 1,
1919 Some (fun u ->
1920 let ondone s =
1921 try set (int_of_string_with_suffix s)
1922 with exn -> settextfmt "bad integer `%s': %s" s @@ exntos exn
1924 S.text := E.s;
1925 let te = (name ^ ": ", E.s, None, intentry_with_suffix,
1926 ondone, true) in
1927 S.mode := Textentry (te, leave m_prev_mode);
1929 )) :: m_l
1931 method bool ?(offset=1) ?(btos=btos) name get set =
1932 m_l <- (name, `bool (btos, get), offset,
1933 Some (fun u -> set (not (get ())); u)) :: m_l
1935 method color name get set =
1936 m_l <-
1937 (name, `color get, 1,
1938 Some (fun u ->
1939 let invalid = (nan, nan, nan) in
1940 let ondone s =
1941 let c =
1942 try color_of_string s
1943 with exn -> settextfmt "bad color `%s': %s" s @@ exntos exn;
1944 invalid
1946 if c <> invalid
1947 then set c;
1949 let te = (name ^ ": ", E.s, None, textentry, ondone, true) in
1950 S.text := color_to_string (get ());
1951 S.mode := Textentry (te, leave m_prev_mode);
1953 )) :: m_l
1955 method string name get set =
1956 m_l <-
1957 (name, `string get, 1,
1958 Some (fun u ->
1959 let ondone s = set s in
1960 let te = (String.trim name ^ ": ", E.s, None,
1961 textentry, ondone, true) in
1962 S.mode := Textentry (te, leave m_prev_mode);
1964 )) :: m_l
1966 method colorspace name get set =
1967 m_l <-
1968 (name, `string get, 1,
1969 Some (fun _ ->
1970 let source = object
1971 inherit lvsourcebase
1973 initializer
1974 m_active <- CSTE.to_int conf.colorspace;
1975 m_first <- 0;
1977 method getitemcount =
1978 Array.length CSTE.names
1979 method getitem n =
1980 (CSTE.names.(n), 0)
1981 method exit ~uioh ~cancel ~active ~first ~pan =
1982 ignore (uioh, first, pan);
1983 if not cancel then set active;
1984 None
1985 method hasaction _ = true
1988 infomenu source
1989 )) :: m_l
1991 method paxmark name get set =
1992 m_l <-
1993 (name, `string get, 1,
1994 Some (fun _ ->
1995 let source = object
1996 inherit lvsourcebase
1998 initializer
1999 m_active <- MTE.to_int conf.paxmark;
2000 m_first <- 0;
2002 method getitemcount = Array.length MTE.names
2003 method getitem n = (MTE.names.(n), 0)
2004 method exit ~uioh ~cancel ~active ~first ~pan =
2005 ignore (uioh, first, pan);
2006 if not cancel then set active;
2007 None
2008 method hasaction _ = true
2011 infomenu source
2012 )) :: m_l
2014 method fitmodel name get set =
2015 m_l <-
2016 (name, `string get, 1,
2017 Some (fun _ ->
2018 let source = object
2019 inherit lvsourcebase
2021 initializer
2022 m_active <- FMTE.to_int conf.fitmodel;
2023 m_first <- 0;
2025 method getitemcount = Array.length FMTE.names
2026 method getitem n = (FMTE.names.(n), 0)
2027 method exit ~uioh ~cancel ~active ~first ~pan =
2028 ignore (uioh, first, pan);
2029 if not cancel then set active;
2030 None
2031 method hasaction _ = true
2034 infomenu source
2035 )) :: m_l
2037 method caption s offset =
2038 m_l <- (s, `empty, offset, None) :: m_l
2040 method caption2 s f offset =
2041 m_l <- (s, `string f, offset, None) :: m_l
2043 method getitemcount = Array.length m_a
2045 method getitem n =
2046 let tostr = function
2047 | `int f -> string_of_int (f ())
2048 | `intws f -> string_with_suffix_of_int (f ())
2049 | `string f -> f ()
2050 | `color f -> color_to_string (f ())
2051 | `bool (btos, f) -> btos (f ())
2052 | `empty -> E.s
2054 let name, t, offset, _ = m_a.(n) in
2055 ((let s = tostr t in
2056 if nonemptystr s
2057 then Printf.sprintf "%s\t%s" name s
2058 else name),
2059 offset)
2061 method exit ~uioh ~cancel ~active ~first ~pan =
2062 let uiohopt =
2063 if not cancel
2064 then (
2065 let uioh =
2066 match m_a.(active) with
2067 | _, _, _, Some f -> f uioh
2068 | _, _, _, None -> uioh
2070 Some uioh
2072 else None
2074 m_active <- active;
2075 m_first <- first;
2076 m_pan <- pan;
2077 uiohopt
2079 method hasaction n =
2080 match m_a.(n) with
2081 | _, _, _, Some _ -> true
2082 | _, _, _, None -> false
2084 initializer m_active <- 1
2087 let rec fillsrc prevmode prevuioh =
2088 let sep () = src#caption E.s 0 in
2089 let bad v exn = settextfmt "bad color `%s': %s" v @@ exntos exn in
2090 let colorp name get set =
2091 src#string name
2092 (fun () -> color_to_string (get ()))
2093 (fun v ->
2094 try set @@ color_of_string v
2095 with exn -> bad v exn
2098 let rgba name get set =
2099 src#string name
2100 (fun () -> get () |> rgba_to_string)
2101 (fun v ->
2102 try set @@ rgba_of_string v
2103 with exn -> bad v exn
2106 let oldmode = !S.mode in
2107 let birdseye = isbirdseye !S.mode in
2109 src#caption (if birdseye then "Setup (Bird's eye)" else "Setup") 0;
2111 src#bool "presentation mode"
2112 (fun () -> conf.presentation)
2113 (fun v -> setpresentationmode v);
2115 src#bool "ignore case in searches"
2116 (fun () -> conf.icase)
2117 (fun v -> conf.icase <- v);
2119 src#bool "preload"
2120 (fun () -> conf.preload)
2121 (fun v -> conf.preload <- v);
2123 src#bool "highlight links"
2124 (fun () -> conf.hlinks)
2125 (fun v -> conf.hlinks <- v);
2127 src#bool "under info"
2128 (fun () -> conf.underinfo)
2129 (fun v -> conf.underinfo <- v);
2131 src#fitmodel "fit model"
2132 (fun () -> FMTE.to_string conf.fitmodel)
2133 (fun v -> reqlayout conf.angle (FMTE.of_int v));
2135 src#bool "trim margins"
2136 (fun () -> conf.trimmargins)
2137 (fun v -> settrim v conf.trimfuzz; fillsrc prevmode prevuioh);
2139 sep ();
2140 src#int "inter-page space"
2141 (fun () -> conf.interpagespace)
2142 (fun n ->
2143 conf.interpagespace <- n;
2144 docolumns conf.columns;
2145 let pageno, py =
2146 match !S.layout with
2147 | [] -> 0, 0
2148 | l :: _ -> l.pageno, l.pagey
2150 S.maxy :=- calcheight ();
2151 gotoxy !S.x (py + getpagey pageno)
2154 src#int "page bias"
2155 (fun () -> conf.pagebias)
2156 (fun v -> conf.pagebias <- v);
2158 src#int "scroll step"
2159 (fun () -> conf.scrollstep)
2160 (fun n -> conf.scrollstep <- n);
2162 src#int "horizontal scroll step"
2163 (fun () -> conf.hscrollstep)
2164 (fun v -> conf.hscrollstep <- v);
2166 src#int "auto scroll step"
2167 (fun () ->
2168 match !S.autoscroll with
2169 | Some step -> step
2170 | _ -> conf.autoscrollstep)
2171 (fun n ->
2172 let n = boundastep !S.winh n in
2173 if !S.autoscroll <> None
2174 then S.autoscroll := Some n;
2175 conf.autoscrollstep <- n);
2177 src#int "zoom"
2178 (fun () -> truncate (conf.zoom *. 100.))
2179 (fun v -> pivotzoom ((float v) /. 100.));
2181 src#int "rotation"
2182 (fun () -> conf.angle)
2183 (fun v -> reqlayout v conf.fitmodel);
2185 src#int "scroll bar width"
2186 (fun () -> conf.scrollbw)
2187 (fun v ->
2188 conf.scrollbw <- v;
2189 reshape !S.winw !S.winh;
2192 src#int "scroll handle height"
2193 (fun () -> conf.scrollh)
2194 (fun v -> conf.scrollh <- v;);
2196 src#int "thumbnail width"
2197 (fun () -> conf.thumbw)
2198 (fun v ->
2199 conf.thumbw <- min 4096 v;
2200 match oldmode with
2201 | Birdseye beye ->
2202 leavebirdseye beye false;
2203 enterbirdseye ()
2204 | Textentry _ | View | LinkNav _ -> ()
2207 let mode = !S.mode in
2208 src#string "columns"
2209 (fun () ->
2210 match conf.columns with
2211 | Csingle _ -> "1"
2212 | Cmulti (multi, _) -> multicolumns_to_string multi
2213 | Csplit (count, _) -> "-" ^ string_of_int count
2215 (fun v ->
2216 let n, a, b = multicolumns_of_string v in
2217 setcolumns mode n a b);
2219 sep ();
2220 src#caption "Pixmap cache" 0;
2221 src#int_with_suffix "size (advisory)"
2222 (fun () -> conf.memlimit)
2223 (fun v -> conf.memlimit <- v);
2225 src#caption2 "used"
2226 (fun () ->
2227 Printf.sprintf "%s bytes, %d tiles"
2228 (string_with_suffix_of_int !S.memused)
2229 (Hashtbl.length S.tilemap)) 1;
2231 sep ();
2232 src#caption "Layout" 0;
2233 src#caption2 "Dimension"
2234 (fun () -> Printf.sprintf "%dx%d (virtual %dx%d)"
2235 !S.winw !S.winh
2236 !S.w !S.maxy)
2238 if conf.debug
2239 then src#caption2 "Position" (fun () ->
2240 Printf.sprintf "%dx%d" !S.x !S.y
2242 else src#caption2 "Position" (fun () -> describe_layout !S.layout) 1;
2244 sep ();
2245 let btos b = Utf8syms.(if b then lguillemet else rguillemet) in
2246 src#bool ~offset:0 ~btos "Extended parameters"
2247 (fun () -> !showextended)
2248 (fun v -> showextended := v; fillsrc prevmode prevuioh);
2249 if !showextended
2250 then (
2251 src#bool "update cursor"
2252 (fun () -> conf.updatecurs)
2253 (fun v -> conf.updatecurs <- v);
2254 src#bool "scroll-bar on the left"
2255 (fun () -> conf.leftscroll)
2256 (fun v -> conf.leftscroll <- v);
2257 src#bool "verbose"
2258 (fun () -> conf.verbose)
2259 (fun v -> conf.verbose <- v);
2260 src#bool "invert colors"
2261 (fun () -> conf.invert)
2262 (fun v -> conf.invert <- v);
2263 src#bool "max fit"
2264 (fun () -> conf.maxhfit)
2265 (fun v -> conf.maxhfit <- v);
2266 src#bool "pax mode"
2267 (fun () -> conf.pax != None)
2268 (fun v ->
2269 if v
2270 then conf.pax <- Some (now ())
2271 else conf.pax <- None);
2272 src#string "tile size"
2273 (fun () -> Printf.sprintf "%dx%d" conf.tilew conf.tileh)
2274 (fun v ->
2276 let w, h = Scanf.sscanf v "%dx%d" (fun w h -> w, h) in
2277 conf.tilew <- max 64 w;
2278 conf.tileh <- max 64 h;
2279 flushtiles ();
2280 with exn -> settextfmt "bad tile size `%s': %s" v @@ exntos exn);
2281 src#int "texture count"
2282 (fun () -> conf.texcount)
2283 (fun v ->
2284 if Ffi.realloctexts v
2285 then conf.texcount <- v
2286 else impmsg "failed to set texture count please retry later");
2287 src#int "slice height"
2288 (fun () -> conf.sliceheight)
2289 (fun v ->
2290 conf.sliceheight <- v;
2291 wcmd U.sliceh "%d" conf.sliceheight);
2292 src#int "anti-aliasing level"
2293 (fun () -> conf.aalevel)
2294 (fun v ->
2295 conf.aalevel <- bound v 0 8;
2296 S.anchor := getanchor ();
2297 opendoc !S.path !S.password);
2298 src#string "page scroll scaling factor"
2299 (fun () -> string_of_float conf.pgscale)
2300 (fun v ->
2301 try conf.pgscale <- float_of_string v
2302 with exn ->
2303 S.text :=
2304 Printf.sprintf "bad page scroll scaling factor `%s': %s" v
2305 @@ exntos exn);
2306 src#int "ui font size"
2307 (fun () -> fstate.fontsize)
2308 (fun v -> setfontsize (bound v 5 100));
2309 src#int "hint font size"
2310 (fun () -> conf.hfsize)
2311 (fun v -> conf.hfsize <- bound v 5 100);
2312 src#string "hint chars"
2313 (fun () -> conf.hcs)
2314 (fun v ->
2316 validatehcs v;
2317 conf.hcs <- v
2318 with exn ->
2319 S.text :=
2320 Printf.sprintf "invalid hint chars %S: %s" v (exntos exn));
2321 src#string "trim fuzz"
2322 (fun () -> irect_to_string conf.trimfuzz)
2323 (fun v ->
2325 conf.trimfuzz <- irect_of_string v;
2326 if conf.trimmargins
2327 then settrim true conf.trimfuzz;
2328 with exn -> settextfmt "bad irect `%s': %s" v @@ exntos exn);
2329 src#bool ~btos "external commands"
2330 (fun () -> !showcommands)
2331 (fun v -> showcommands := v; fillsrc prevmode prevuioh);
2332 if !showcommands
2333 then (
2334 src#string " uri launcher"
2335 (fun () -> conf.urilauncher)
2336 (fun v -> conf.urilauncher <- v);
2337 src#string " path launcher"
2338 (fun () -> conf.pathlauncher)
2339 (fun v -> conf.pathlauncher <- v);
2340 src#string " selection"
2341 (fun () -> conf.selcmd)
2342 (fun v -> conf.selcmd <- v);
2343 src#string " synctex"
2344 (fun () -> conf.stcmd)
2345 (fun v -> conf.stcmd <- v);
2346 src#string " pax"
2347 (fun () -> conf.paxcmd)
2348 (fun v -> conf.paxcmd <- v);
2349 src#string " ask password"
2350 (fun () -> conf.passcmd)
2351 (fun v -> conf.passcmd <- v);
2352 src#string " save path"
2353 (fun () -> conf.savecmd)
2354 (fun v -> conf.savecmd <- v);
2356 src#colorspace "color space"
2357 (fun () -> CSTE.to_string conf.colorspace)
2358 (fun v ->
2359 conf.colorspace <- CSTE.of_int v;
2360 wcmd U.cs "%d" v;
2361 load !S.layout);
2362 src#paxmark "pax mark method"
2363 (fun () -> MTE.to_string conf.paxmark)
2364 (fun v -> conf.paxmark <- MTE.of_int v);
2365 src#bool "mouse wheel scrolls pages"
2366 (fun () -> conf.wheelbypage)
2367 (fun v -> conf.wheelbypage <- v);
2368 src#bool "open remote links in a new instance"
2369 (fun () -> conf.riani)
2370 (fun v -> conf.riani <- v);
2371 src#bool "edit annotations inline"
2372 (fun () -> conf.annotinline)
2373 (fun v -> conf.annotinline <- v);
2374 src#bool "coarse positioning in presentation mode"
2375 (fun () -> conf.coarseprespos)
2376 (fun v -> conf.coarseprespos <- v);
2377 src#bool "use document CSS"
2378 (fun () -> conf.usedoccss)
2379 (fun v ->
2380 conf.usedoccss <- v;
2381 S.anchor := getanchor ();
2382 opendoc !S.path !S.password);
2383 src#bool ~btos "colors"
2384 (fun () -> !showcolors)
2385 (fun v -> showcolors := v; fillsrc prevmode prevuioh);
2386 if !showcolors
2387 then (
2388 colorp " background"
2389 (fun () -> conf.bgcolor)
2390 (fun v -> conf.bgcolor <- v);
2391 rgba " paper"
2392 (fun () -> conf.papercolor)
2393 (fun v ->
2394 conf.papercolor <- v;
2395 Ffi.setpapercolor conf.papercolor;
2396 flushtiles ();
2398 rgba " scrollbar"
2399 (fun () -> conf.sbarcolor)
2400 (fun v -> conf.sbarcolor <- v);
2401 rgba " scrollbar handle"
2402 (fun () -> conf.sbarhndlcolor)
2403 (fun v -> conf.sbarhndlcolor <- v);
2404 rgba " texture"
2405 (fun () -> conf.texturecolor)
2406 (fun v ->
2407 GlTex.env (`color v);
2408 conf.texturecolor <- v;
2410 src#string " scale"
2411 (fun () -> string_of_float conf.colorscale)
2412 (fun v -> conf.colorscale <- bound (float_of_string v) 0.0 1.0);
2414 src#bool ~btos "reflowable layout"
2415 (fun () -> !showrefl)
2416 (fun v -> showrefl := v; fillsrc prevmode prevuioh);
2417 if !showrefl
2418 then (
2419 src#int " width"
2420 (fun () -> conf.rlw)
2421 (fun v -> conf.rlw <- v; reload ());
2422 src#int " height"
2423 (fun () -> conf.rlh)
2424 (fun v -> conf.rlh <- v; reload ());
2425 src#int " em"
2426 (fun () -> conf.rlem)
2427 (fun v -> conf.rlem <- v; reload ());
2431 sep ();
2432 src#caption "Document" 0;
2433 List.iter (fun (_, s) -> src#caption s 1) !S.docinfo;
2434 src#caption2 "Pages" (fun () -> string_of_int !S.pagecount) 1;
2435 src#caption2 "Dimensions"
2436 (fun () -> string_of_int (List.length !S.pdims)) 1;
2437 if nonemptystr conf.css
2438 then src#caption2 "CSS" (fun () -> conf.css) 1;
2439 if conf.trimmargins
2440 then (
2441 sep ();
2442 src#caption "Trimmed margins" 0;
2443 src#caption2 "Dimensions"
2444 (fun () -> string_of_int (List.length !S.pdims)) 1;
2447 sep ();
2448 src#caption "OpenGL" 0;
2449 src#caption ("Vendor\t" ^ GlMisc.get_string `vendor) 1;
2450 src#caption ("Renderer\t" ^ GlMisc.get_string `renderer) 1;
2452 sep ();
2453 src#caption "Location" 0;
2454 if nonemptystr !S.origin
2455 then src#caption ("Origin\t" ^ Ffi.mbtoutf8 !S.origin) 1;
2456 src#caption ("Path\t" ^ Ffi.mbtoutf8 !S.path) 1;
2457 if nonemptystr conf.dcf
2458 then src#caption ("DCF\t" ^ Ffi.mbtoutf8 conf.dcf) 1;
2460 src#reset prevmode prevuioh;
2462 fun () -> (
2463 S.text := E.s;
2464 resetmstate ();
2465 let prevmode = !S.mode
2466 and prevuioh = !S.uioh in
2467 fillsrc prevmode prevuioh;
2468 let source = (src :> lvsource) in
2469 let modehash = findkeyhash conf "info" in
2470 object (self)
2471 inherit listview ~zebra:false ~helpmode:false
2472 ~source ~trusted:true ~modehash as super
2473 val mutable m_prevmemused = 0
2474 method! infochanged = function
2475 | Memused ->
2476 if m_prevmemused != !S.memused
2477 then (
2478 m_prevmemused <- !S.memused;
2479 Glutils.postRedisplay "memusedchanged";
2481 | Pdim -> Glutils.postRedisplay "pdimchanged"
2482 | Docinfo -> fillsrc prevmode prevuioh
2483 method! key key mask =
2484 if not (Wsi.withctrl mask)
2485 then
2486 match [@warning "-fragile-match"] Wsi.ks2kt key with
2487 | Keys.Left -> coe (self#updownlevel ~-1)
2488 | Keys.Right -> coe (self#updownlevel 1)
2489 | _ -> super#key key mask
2490 else super#key key mask
2491 end |> setuioh;
2492 Glutils.postRedisplay "info";
2495 let enterhelpmode =
2496 let source = object
2497 inherit lvsourcebase
2498 method getitemcount = Array.length !S.help
2499 method getitem n =
2500 let s, l, _ = !S.help.(n) in
2501 (s, l)
2503 method exit ~uioh ~cancel ~active ~first ~pan =
2504 let optuioh =
2505 if not cancel
2506 then (
2507 match !S.help.(active) with
2508 | _, _, Some f -> Some (f uioh)
2509 | _, _, None -> Some uioh
2511 else None
2513 m_active <- active;
2514 m_first <- first;
2515 m_pan <- pan;
2516 optuioh
2518 method hasaction n =
2519 match !S.help.(n) with
2520 | _, _, Some _ -> true
2521 | _, _, None -> false
2523 initializer m_active <- -1
2525 in fun () ->
2526 let modehash = findkeyhash conf "help" in
2527 resetmstate ();
2528 new listview ~zebra:false ~helpmode:true
2529 ~source ~trusted:true ~modehash |> setuioh;
2530 Glutils.postRedisplay "help"
2532 let entermsgsmode =
2533 let msgsource = object
2534 inherit lvsourcebase
2535 val mutable m_items = E.a
2537 method getitemcount = 1 + Array.length m_items
2539 method getitem n =
2540 if n = 0
2541 then "[Clear]", 0
2542 else m_items.(n-1), 0
2544 method exit ~uioh ~cancel ~active ~first ~pan =
2545 ignore uioh;
2546 if not cancel
2547 then (
2548 if active = 0
2549 then Buffer.clear S.errmsgs;
2551 m_active <- active;
2552 m_first <- first;
2553 m_pan <- pan;
2554 None
2556 method hasaction n =
2557 n = 0
2559 method reset =
2560 S.newerrmsgs := false;
2561 let l = Str.split Re.crlf (Buffer.contents S.errmsgs) in
2562 m_items <- Array.of_list l
2564 initializer m_active <- 0
2567 fun () ->
2568 S.text := E.s;
2569 resetmstate ();
2570 msgsource#reset;
2571 let source = (msgsource :> lvsource) in
2572 let modehash = findkeyhash conf "listview" in
2573 object
2574 inherit listview ~zebra:false ~helpmode:false
2575 ~source ~trusted:false ~modehash as super
2576 method! display =
2577 if !S.newerrmsgs
2578 then msgsource#reset;
2579 super#display
2580 end |> setuioh;
2581 Glutils.postRedisplay "msgs"
2583 let getusertext s =
2584 let editor = getenvdef "EDITOR" E.s in
2585 if emptystr editor
2586 then E.s
2587 else
2588 let tmppath = Filename.temp_file "llpp" "note" in
2589 if nonemptystr s
2590 then (
2591 let oc = open_out tmppath in
2592 output_string oc s;
2593 close_out oc;
2595 let execstr = editor ^ " " ^ tmppath in
2596 let eret r = Printf.ksprintf (fun s -> adderrmsg "gtut:eret" s; r) in
2597 let s =
2598 match spawn execstr [] with
2599 | exception exn -> eret E.s "spawn(%S) failed: %s" execstr @@ exntos exn
2600 | pid ->
2601 match Unix.waitpid [] pid with
2602 | exception exn -> eret E.s "waitpid(%d) failed: %s" pid @@ exntos exn
2603 | (_pid, status) ->
2604 match status with
2605 | Unix.WEXITED 0 -> filecontents tmppath
2606 | Unix.WEXITED n ->
2607 eret E.s "editor process(%s) exited abnormally: %d" execstr n
2608 | Unix.WSIGNALED n ->
2609 eret E.s "editor process(%s) was killed by signal %d" execstr n
2610 | Unix.WSTOPPED n ->
2611 eret E.s "editor(%s) process was stopped by signal %d" execstr n
2613 match Unix.unlink tmppath with
2614 | exception exn -> eret s "failed to ulink %S: %s" tmppath @@ exntos exn
2615 | () -> s
2617 let enterannotmode opaque slinkindex =
2618 let msgsource = object
2619 inherit lvsourcebase
2620 val mutable m_text = E.s
2621 val mutable m_items = E.a
2623 method getitemcount = Array.length m_items
2625 method getitem n =
2626 let label, _func = m_items.(n) in
2627 label, 0
2629 method exit ~uioh ~cancel ~active ~first ~pan =
2630 ignore (uioh, first, pan);
2631 if not cancel
2632 then (
2633 let _label, func = m_items.(active) in
2634 func ()
2636 None
2638 method hasaction n = nonemptystr @@ fst m_items.(n)
2640 method reset s =
2641 let rec split accu b i =
2642 let p = b+i in
2643 if p = String.length s
2644 then (String.sub s b (p-b), fun () -> ()) :: accu
2645 else
2646 if (i > 70 && s.[p] = ' ') || s.[p] = '\r' || s.[p] = '\n'
2647 then
2648 let ss = if i = 0 then E.s else String.sub s b i in
2649 split ((ss, fun () -> ())::accu) (p+1) 0
2650 else split accu b (i+1)
2652 let cleanup () =
2653 wcmd1 U.freepage opaque;
2654 let keys =
2655 Hashtbl.fold (fun key opaque' accu ->
2656 if opaque' = opaque'
2657 then key :: accu else accu) S.pagemap []
2659 List.iter (Hashtbl.remove S.pagemap) keys;
2660 flushtiles ();
2661 gotoxy !S.x !S.y
2663 let dele () =
2664 Ffi.delannot opaque slinkindex;
2665 cleanup ();
2667 let edit inline () =
2668 let update s =
2669 if emptystr s
2670 then dele ()
2671 else (
2672 Ffi.modannot opaque slinkindex s;
2673 cleanup ();
2676 if inline
2677 then
2678 let mode = !S.mode in
2679 let te = ("annotation: ", m_text, None, textentry, update, true) in
2680 S.mode := Textentry (te, fun _ -> S.mode := mode);
2681 S.text := E.s;
2682 enttext ();
2683 else getusertext m_text |> update
2685 m_text <- s;
2686 m_items <-
2687 ( "[Copy]", fun () -> selstring conf.selcmd m_text)
2688 :: ("[Delete]", dele)
2689 :: ("[Edit]", edit conf.annotinline)
2690 :: (E.s, fun () -> ())
2691 :: split [] 0 0 |> List.rev |> Array.of_list
2693 initializer m_active <- 0
2696 S.text := E.s;
2697 let s = Ffi.gettextannot opaque slinkindex in
2698 resetmstate ();
2699 msgsource#reset s;
2700 let source = (msgsource :> lvsource) in
2701 let modehash = findkeyhash conf "listview" in
2702 object inherit listview ~zebra:false
2703 ~helpmode:false ~source ~trusted:false ~modehash
2704 end |> setuioh;
2705 Glutils.postRedisplay "enterannotmode"
2707 let gotoremote spec =
2708 let filename, dest = splitatchar spec '#' in
2709 let getpath filename =
2710 let path =
2711 if nonemptystr filename
2712 then
2713 if Filename.is_relative filename
2714 then
2715 let dir = Filename.dirname !S.path in
2716 let dir =
2717 if Filename.is_implicit dir
2718 then Filename.concat (Sys.getcwd ()) dir
2719 else dir
2721 Filename.concat dir filename
2722 else filename
2723 else E.s
2725 if Sys.file_exists path
2726 then path
2727 else E.s
2729 let path = getpath filename in
2730 if emptystr path
2731 then adderrfmt "gotoremote/getpath" "failed getpath for %S\n" filename
2732 else
2733 let dospawn lcmd =
2734 if conf.riani
2735 then
2736 let cmd = Lazy.force_val lcmd in
2737 match spawn cmd with
2738 | exception exn -> dolog "failed to execute `%s': %s" cmd @@ exntos exn
2739 | _pid -> ()
2740 else
2741 let anchor = getanchor () in
2742 let ranchor = !S.path, !S.password, anchor, !S.origin in
2743 S.origin := E.s;
2744 S.ranchors := ranchor :: !S.ranchors;
2745 opendoc path E.s;
2747 if substratis spec 0 "page="
2748 then
2749 match Scanf.sscanf spec "page=%d" (fun n -> n) with
2750 | exception exn ->
2751 adderrfmt "error parsing remote destination" "%s %s" spec @@ exntos exn
2752 | pageno ->
2753 S.anchor := (pageno, 0.0, 0.0);
2754 dospawn @@ lazy (Printf.sprintf "%s -page %d %S"
2755 !S.selfexec pageno path);
2756 else (
2757 S.nameddest := dest;
2758 dospawn @@ lazy (!S.selfexec ^ " " ^ path ^ " -dest " ^ dest)
2761 let gotounder = function
2762 | Ulinkuri s when Ffi.isexternallink s ->
2763 if substratis s 0 "file://"
2764 then gotoremote @@ String.sub s 7 (String.length s - 7)
2765 else Help.gotouri conf.urilauncher s
2766 | Ulinkuri s ->
2767 let pageno, x, y = Ffi.uritolocation s in
2768 addnav ();
2769 gotopagexy pageno x y
2770 | Utext _ | Unone -> ()
2771 | Utextannot (opaque, slinkindex) -> enterannotmode opaque slinkindex
2772 | Ufileannot (opaque, slinkindex) ->
2773 if emptystr conf.savecmd
2774 then adderrmsg "savepath-command is empty"
2775 "don't know where to save attachment"
2776 else
2777 let filename = Ffi.getfileannot opaque slinkindex in
2778 let savecmd = Str.global_replace Re.percent filename conf.savecmd in
2779 let path =
2780 getcmdoutput
2781 (adderrfmt savecmd
2782 "failed to obtain path to the saved attachment: %s") savecmd
2784 Ffi.savefileannot opaque slinkindex path
2786 let gotooutline (_, _, kind) =
2787 match kind with
2788 | Onone -> ()
2789 | Oanchor ((pageno, y, _) as anchor) ->
2790 addnav ();
2791 gotoxy !S.x @@
2792 getanchory (if conf.presentation then (pageno, y, 1.0) else anchor)
2793 | Ouri uri -> gotounder (Ulinkuri uri)
2794 | Olaunch cmd -> error "gotounder (Ulaunch %S)" cmd
2795 | Oremote (remote, pageno) ->
2796 error "gotounder (Uremote (%S,%d) )" remote pageno
2797 | Ohistory hist -> gotohist hist
2798 | Oremotedest (path, dest) ->
2799 error "gotounder (Uremotedest (%S, %S))" path dest
2801 class outlinesoucebase fetchoutlines = object (self)
2802 inherit lvsourcebase
2803 val mutable m_items = E.a
2804 val mutable m_minfo = E.a
2805 val mutable m_orig_items = E.a
2806 val mutable m_orig_minfo = E.a
2807 val mutable m_narrow_patterns = []
2808 val mutable m_gen = -1
2810 method getitemcount = Array.length m_items
2812 method getitem n =
2813 let s, n, _ = m_items.(n) in
2814 (s, n+0)
2816 method exit ~(uioh:uioh) ~cancel ~active ~(first:int) ~pan : uioh option =
2817 ignore (uioh, first);
2818 let items, minfo =
2819 if m_narrow_patterns = []
2820 then m_orig_items, m_orig_minfo
2821 else m_items, m_minfo
2823 m_pan <- pan;
2824 if not cancel
2825 then (
2826 m_items <- items;
2827 m_minfo <- minfo;
2828 gotooutline m_items.(active);
2830 else (
2831 m_items <- items;
2832 m_minfo <- minfo;
2834 None
2836 method hasaction (_:int) = true
2838 method greetmsg =
2839 if Array.length m_items != Array.length m_orig_items
2840 then
2841 let s =
2842 match m_narrow_patterns with
2843 | one :: [] -> one
2844 | many -> String.concat Utf8syms.ellipsis (List.rev many)
2846 "Narrowed to " ^ s ^ " (ctrl-u to restore)"
2847 else E.s
2849 method statestr =
2850 match m_narrow_patterns with
2851 | [] -> E.s
2852 | one :: [] -> one
2853 | head :: _ -> Utf8syms.ellipsis ^ head
2855 method narrow pattern =
2856 match Str.regexp_case_fold pattern with
2857 | exception _ -> ()
2858 | re ->
2859 let rec loop accu minfo n =
2860 if n = -1
2861 then (
2862 m_items <- Array.of_list accu;
2863 m_minfo <- Array.of_list minfo;
2865 else
2866 let (s, _, _) as o = m_items.(n) in
2867 let accu, minfo =
2868 match Str.search_forward re s 0 with
2869 | exception Not_found -> accu, minfo
2870 | first -> o :: accu, (first, Str.match_end ()) :: minfo
2872 loop accu minfo (n-1)
2874 loop [] [] (Array.length m_items - 1)
2876 method! getminfo = m_minfo
2878 method denarrow =
2879 m_orig_items <- fetchoutlines ();
2880 m_minfo <- m_orig_minfo;
2881 m_items <- m_orig_items
2883 method add_narrow_pattern pattern =
2884 m_narrow_patterns <- pattern :: m_narrow_patterns
2886 method del_narrow_pattern =
2887 match m_narrow_patterns with
2888 | _ :: rest -> m_narrow_patterns <- rest
2889 | [] -> ()
2891 method renarrow =
2892 self#denarrow;
2893 match m_narrow_patterns with
2894 | pattern :: [] -> self#narrow pattern; pattern
2895 | list ->
2896 List.fold_left (fun accu pattern ->
2897 self#narrow pattern;
2898 pattern ^ Utf8syms.ellipsis ^ accu) E.s list
2900 method calcactive (_:anchor) = 0
2902 method reset anchor items =
2903 if !S.gen != m_gen
2904 then (
2905 m_orig_items <- items;
2906 m_items <- items;
2907 m_narrow_patterns <- [];
2908 m_minfo <- E.a;
2909 m_orig_minfo <- E.a;
2910 m_gen <- !S.gen;
2912 else (
2913 if items != m_orig_items
2914 then (
2915 m_orig_items <- items;
2916 if m_narrow_patterns == []
2917 then m_items <- items;
2920 let active = self#calcactive anchor in
2921 m_active <- active;
2922 m_first <- firstof m_first active
2925 let outlinesource fetchoutlines = object
2926 inherit outlinesoucebase fetchoutlines
2927 method! calcactive anchor =
2928 let rely = getanchory anchor in
2929 let rec loop n best bestd =
2930 if n = Array.length m_items
2931 then best
2932 else
2933 let _, _, kind = m_items.(n) in
2934 match kind with
2935 | Oanchor anchor ->
2936 let orely = getanchory anchor in
2937 let d = abs (orely - rely) in
2938 if d < bestd
2939 then loop (n+1) n d
2940 else loop (n+1) best bestd
2941 | Onone | Oremote _ | Olaunch _
2942 | Oremotedest _ | Ouri _ | Ohistory _ ->
2943 loop (n+1) best bestd
2945 loop 0 ~-1 max_int
2948 let enteroutlinemode, enterbookmarkmode, enterhistmode =
2949 let fetchoutlines sourcetype () =
2950 match sourcetype with
2951 | `bookmarks -> Array.of_list !S.bookmarks
2952 | `outlines -> !S.outlines
2953 | `history -> genhistoutlines () |> Array.of_list
2955 let so = outlinesource (fetchoutlines `outlines) in
2956 let sb = outlinesource (fetchoutlines `bookmarks) in
2957 let sh = outlinesource (fetchoutlines `history) in
2958 let mkselector sourcetype source =
2959 (fun emptymsg ->
2960 let outlines = fetchoutlines sourcetype () in
2961 if Array.length outlines = 0
2962 then showtext ' ' emptymsg
2963 else (
2964 resetmstate ();
2965 Wsi.setcursor Wsi.CURSOR_INHERIT;
2966 let anchor = getanchor () in
2967 source#reset anchor outlines;
2968 S.text := source#greetmsg;
2969 new outlinelistview ~zebra:(sourcetype=`history) ~source |> setuioh;
2970 Glutils.postRedisplay "enter selector";
2974 let mkenter src errmsg s = fun () -> mkselector src s errmsg in
2975 ( mkenter `outlines "document has no outline" so
2976 , mkenter `bookmarks "document has no bookmarks (yet)" sb
2977 , mkenter `history "history is empty" sh )
2979 let addbookmark title a =
2980 let b = List.filter (fun (title', _, _) -> title <> title') !S.bookmarks in
2981 S.bookmarks := (title, 0, Oanchor a) :: b
2983 let quickbookmark ?title () =
2984 match !S.layout with
2985 | [] -> ()
2986 | l :: _ ->
2987 let title =
2988 match title with
2989 | None ->
2990 Unix.(
2991 let tm = localtime (now ()) in
2992 Printf.sprintf
2993 "Quick (page %d) (bookmarked on %02d/%02d/%d at %02d:%02d)"
2994 (l.pageno+1)
2995 tm.tm_mday (tm.tm_mon+1) (tm.tm_year+1900) tm.tm_hour tm.tm_min
2997 | Some title -> title
2999 addbookmark title (getanchor1 l)
3001 let setautoscrollspeed step goingdown =
3002 let incr = max 1 ((abs step) / 2) in
3003 let incr = if goingdown then incr else -incr in
3004 let astep = boundastep !S.winh (step + incr) in
3005 S.autoscroll := Some astep
3007 let canpan () =
3008 match conf.columns with
3009 | Csplit _ -> true
3010 | Csingle _ | Cmulti _ -> !S.x != 0 || conf.zoom > 1.0
3012 let existsinrow pageno (columns, coverA, coverB) p =
3013 let last = ((pageno - coverA) mod columns) + columns in
3014 let rec any = function
3015 | [] -> false
3016 | l :: rest ->
3017 if l.pageno = coverA - 1 || l.pageno = !S.pagecount - coverB
3018 then p l
3019 else (
3020 if not (p l)
3021 then (if l.pageno = last then false else any rest)
3022 else true
3025 any !S.layout
3027 let nextpage () =
3028 match !S.layout with
3029 | [] ->
3030 let pageno = page_of_y !S.y in
3031 gotoxy !S.x (getpagey (pageno+1))
3032 | l :: rest ->
3033 match conf.columns with
3034 | Csingle _ ->
3035 if conf.presentation && rest == [] && l.pageh > l.pagey + l.pagevh
3036 then
3037 let y = U.add_to_y_and_clamp (U.pgscale !S.winh) in
3038 gotoxy !S.x y
3039 else
3040 let pageno = min (l.pageno+1) (!S.pagecount-1) in
3041 gotoxy !S.x (getpagey pageno)
3042 | Cmulti ((c, _, _) as cl, _) ->
3043 if conf.presentation
3044 && (existsinrow l.pageno cl
3045 (fun l -> l.pageh > l.pagey + l.pagevh))
3046 then
3047 let y = U.add_to_y_and_clamp (U.pgscale !S.winh) in
3048 gotoxy !S.x y
3049 else
3050 let pageno = min (l.pageno+c) (!S.pagecount-1) in
3051 gotoxy !S.x (getpagey pageno)
3052 | Csplit (n, _) ->
3053 if l.pageno < !S.pagecount - 1 || l.pagecol < n - 1
3054 then
3055 let pagey, pageh = getpageyh l.pageno in
3056 let pagey = pagey + pageh * l.pagecol in
3057 let ips = if l.pagecol = 0 then 0 else conf.interpagespace in
3058 gotoxy !S.x (pagey + pageh + ips)
3060 let prevpage () =
3061 match !S.layout with
3062 | [] ->
3063 let pageno = page_of_y !S.y in
3064 gotoxy !S.x (getpagey (pageno-1))
3065 | l :: _ ->
3066 match conf.columns with
3067 | Csingle _ ->
3068 if conf.presentation && l.pagey != 0
3069 then gotoxy !S.x (U.add_to_y_and_clamp (U.pgscale ~-(!S.winh)))
3070 else
3071 let pageno = max 0 (l.pageno-1) in
3072 gotoxy !S.x (getpagey pageno)
3073 | Cmulti ((c, _, coverB) as cl, _) ->
3074 if conf.presentation &&
3075 (existsinrow l.pageno cl (fun l -> l.pagey != 0))
3076 then gotoxy !S.x (U.add_to_y_and_clamp (U.pgscale ~-(!S.winh)))
3077 else
3078 let decr =
3079 if l.pageno = !S.pagecount - coverB
3080 then 1
3081 else c
3083 let pageno = max 0 (l.pageno-decr) in
3084 gotoxy !S.x (getpagey pageno)
3085 | Csplit (n, _) ->
3086 let y =
3087 if l.pagecol = 0
3088 then
3089 if l.pageno = 0
3090 then l.pagey
3091 else
3092 let pageno = max 0 (l.pageno-1) in
3093 let pagey, pageh = getpageyh pageno in
3094 pagey + (n-1)*pageh
3095 else
3096 let pagey, pageh = getpageyh l.pageno in
3097 pagey + pageh * (l.pagecol-1) - conf.interpagespace
3099 gotoxy !S.x y
3101 let save () =
3102 if emptystr conf.savecmd
3103 then adderrmsg "savepath-command is empty"
3104 "don't know where to save modified document"
3105 else
3106 let savecmd = Str.global_replace Re.percent !S.path conf.savecmd in
3107 let path =
3108 getcmdoutput
3109 (adderrfmt savecmd "failed to obtain path to the saved copy: %s")
3110 savecmd
3112 if nonemptystr path
3113 then
3114 let tmp = path ^ ".tmp" in
3115 Ffi.savedoc tmp;
3116 Unix.rename tmp path
3118 let viewkeyboard key mask =
3119 let enttext te =
3120 let mode = !S.mode in
3121 S.mode := Textentry (te, fun _ -> S.mode := mode);
3122 S.text := E.s;
3123 enttext ();
3124 Glutils.postRedisplay "view:enttext"
3125 and histback () =
3126 match !S.nav.past with
3127 | [] -> ()
3128 | prev :: prest ->
3129 S.nav := { past = prest ; future = getanchor () :: !S.nav.future; };
3130 gotoxy !S.x (getanchory prev)
3132 let ctrl = Wsi.withctrl mask in
3133 let open Keys in
3134 match Wsi.ks2kt key with
3135 | Ascii 'Q' -> exit 0
3136 | Ascii 'z' ->
3137 let yloc f =
3138 match List.rev !S.rects with
3139 | [] -> ()
3140 | (pageno, _, (_, y0, _, y1, _, y2, _, y3)) :: _ ->
3141 f pageno (y0, y1, y2, y3)
3142 and fsel f (y0, y1, y2, y3) = f y0 y1 |> f y2 |> f y3 |> truncate in
3143 let ondone msg = S.text := msg
3144 and zmod _ _ k =
3145 match [@warning "-fragile-match"] k with
3146 | Keys.Ascii 'z' ->
3147 let f pageno ys =
3148 let miny = fsel min ys in
3149 let hh = (fsel max ys - miny)/2 in
3150 gotopage1 pageno (miny + hh - !S.winh/2)
3152 yloc f;
3153 TEdone "center"
3154 | Keys.Ascii 't' ->
3155 let f pageno ys = gotopage1 pageno @@ fsel min ys in
3156 yloc f;
3157 TEdone "top"
3158 | Keys.Ascii 'b' ->
3159 let f pageno ys = gotopage1 pageno (fsel max ys - !S.winh) in
3160 yloc f;
3161 TEdone "bottom"
3162 | _ -> TEstop
3164 enttext (": ", E.s, None, zmod !S.mode, ondone, true)
3165 | Ascii 'W' ->
3166 if Ffi.hasunsavedchanges ()
3167 then save ()
3168 | Insert ->
3169 if conf.angle mod 360 = 0 && not (isbirdseye !S.mode)
3170 then (
3171 S.mode := (
3172 match !S.lnava with
3173 | None -> LinkNav (Ltgendir 0)
3174 | Some pn -> LinkNav (Ltexact pn)
3176 gotoxy !S.x !S.y;
3178 else impmsg "keyboard link navigation does not work under rotation"
3179 | Escape | Ascii 'q' ->
3180 begin match !S.mstate with
3181 | Mzoomrect _ ->
3182 resetmstate ();
3183 Glutils.postRedisplay "kill rect";
3184 | Msel _
3185 | Mpan _
3186 | Mscrolly | Mscrollx
3187 | Mzoom _
3188 | Mnone ->
3189 begin match !S.mode with
3190 | LinkNav ln ->
3191 begin match ln with
3192 | Ltexact pl -> S.lnava := Some pl
3193 | Ltgendir _ | Ltnotready _ -> S.lnava := None
3194 end;
3195 S.mode := View;
3196 Glutils.postRedisplay "esc leave linknav"
3197 | Birdseye _ | Textentry _ | View ->
3198 match !S.ranchors with
3199 | [] -> raise Quit
3200 | (path, password, anchor, origin) :: rest ->
3201 S.ranchors := rest;
3202 S.anchor := anchor;
3203 S.origin := origin;
3204 S.nameddest := E.s;
3205 opendoc path password
3206 end;
3207 end;
3208 | Ascii 'o' -> enteroutlinemode ()
3209 | Ascii 'u' ->
3210 S.rects := [];
3211 S.text := E.s;
3212 Hashtbl.iter (fun _ opaque -> Ffi.clearmark opaque) S.pagemap;
3213 Glutils.postRedisplay "dehighlight";
3214 | Ascii (('/' | '?') as c) ->
3215 let ondone isforw s =
3216 cbput !S.hists.pat s;
3217 S.searchpattern := s;
3218 search s isforw
3220 enttext (String.make 1 c, E.s, Some (onhist !S.hists.pat),
3221 textentry, ondone (c = '/'), true)
3222 | Ascii '+' | Ascii '=' when ctrl ->
3223 let incr = if conf.zoom +. 0.01 > 0.1 then 0.1 else 0.01 in
3224 pivotzoom (conf.zoom +. incr)
3225 | Ascii '+' ->
3226 let ondone s =
3227 let n =
3228 try int_of_string s with exn ->
3229 S.text := Printf.sprintf "bad integer `%s': %s" s @@ exntos exn;
3230 max_int
3232 if n != max_int
3233 then (
3234 conf.pagebias <- n;
3235 S.text := "page bias is now " ^ string_of_int n;
3238 enttext ("page bias: ", E.s, None, intentry, ondone, true)
3239 | Ascii '-' when ctrl ->
3240 let decr = if conf.zoom -. 0.1 < 0.1 then 0.01 else 0.1 in
3241 pivotzoom (max 0.01 (conf.zoom -. decr))
3242 | Ascii '-' ->
3243 let ondone msg = S.text := msg in
3244 enttext ("option: ", E.s, None,
3245 optentry !S.mode, ondone, true)
3246 | Ascii '0' when ctrl ->
3247 if conf.zoom = 1.0
3248 then gotoxy 0 !S.y
3249 else setzoom 1.0
3250 | Ascii ('1'|'2' as c) when ctrl && conf.fitmodel != FitPage ->
3251 let cols =
3252 match conf.columns with
3253 | Csingle _ | Cmulti _ -> 1
3254 | Csplit (n, _) -> n
3256 let h = !S.winh -
3257 conf.interpagespace lsl (if conf.presentation then 1 else 0)
3259 let zoom = Ffi.zoomforh !S.winw h 0 cols in
3260 if zoom > 0.0 && (c = '2' || zoom < 1.0)
3261 then setzoom zoom
3262 | Ascii '3' when ctrl ->
3263 let fm =
3264 match conf.fitmodel with
3265 | FitWidth -> FitProportional
3266 | FitProportional -> FitPage
3267 | FitPage -> FitWidth
3269 S.text := "fit model: " ^ FMTE.to_string fm;
3270 reqlayout conf.angle fm
3271 | Ascii '4' when ctrl ->
3272 let zoom = Ffi.getmaxw () /. float !S.winw in
3273 if zoom > 0.0 then setzoom zoom
3274 | Fn 9 | Ascii '9' when ctrl -> togglebirdseye ()
3275 | Ascii ('0'..'9' as c) when not ctrl ->
3276 let ondone s =
3277 let n =
3278 try int_of_string s with exn ->
3279 adderrfmt "int_of_string" "`%s': %s" s @@ exntos exn;
3282 if n >= 0
3283 then (
3284 addnav ();
3285 cbput !S.hists.pag (string_of_int n);
3286 gotopage1 (n + conf.pagebias - 1) 0;
3289 let pageentry text = function [@warning "-fragile-match"]
3290 | Keys.Ascii 'g' -> TEdone text
3291 | key -> intentry text key
3293 enttext (":", String.make 1 c, Some (onhist !S.hists.pag),
3294 pageentry, ondone, true)
3295 | Ascii 'b' ->
3296 conf.scrollb <- if conf.scrollb = 0 then (scrollbvv lor scrollbhv) else 0;
3297 Glutils.postRedisplay "toggle scrollbar";
3298 | Ascii 'B' ->
3299 S.bzoom := not !S.bzoom;
3300 S.rects := [];
3301 showtext ' ' ("block zoom " ^ onoffs !S.bzoom)
3302 | Ascii 'l' ->
3303 conf.hlinks <- not conf.hlinks;
3304 S.text := "highlightlinks " ^ onoffs conf.hlinks;
3305 Glutils.postRedisplay "toggle highlightlinks"
3306 | Ascii 'F' ->
3307 if conf.angle mod 360 = 0
3308 then (
3309 S.glinks := true;
3310 let mode = !S.mode in
3311 let te = ("goto: ", E.s, None, linknentry, linknact gotounder, false) in
3312 S.mode := Textentry (te, (fun _ -> S.glinks := false; S.mode := mode));
3313 S.text := E.s;
3314 Glutils.postRedisplay "view:linkent(F)"
3316 else impmsg "hint mode does not work under rotation"
3317 | Ascii 'y' ->
3318 S.glinks := true;
3319 let mode = !S.mode in
3320 let te = ("copy: ", E.s, None, linknentry,
3321 linknact (fun under -> selstring conf.selcmd (undertext under)),
3322 false) in
3323 S.mode := Textentry (te, (fun _ -> S.glinks := false; S.mode := mode));
3324 S.text := E.s;
3325 Glutils.postRedisplay "view:linkent"
3326 | Ascii 'a' ->
3327 begin match !S.autoscroll with
3328 | Some step ->
3329 conf.autoscrollstep <- step;
3330 S.autoscroll := None
3331 | None -> S.autoscroll := Some conf.autoscrollstep
3333 | Ascii 'p' when ctrl -> launchpath ()
3334 | Ascii 'P' ->
3335 setpresentationmode (not conf.presentation);
3336 showtext ' ' ("presentation mode " ^ onoffs conf.presentation)
3337 | Ascii 'f' ->
3338 if List.mem Wsi.Fullscreen !S.winstate
3339 then Wsi.reshape conf.cwinw conf.cwinh
3340 else Wsi.fullscreen ()
3341 | Ascii ('p'|'N') -> search !S.searchpattern false
3342 | Ascii 'n' | Fn 3 -> search !S.searchpattern true
3343 | Ascii 't' ->
3344 begin match !S.layout with
3345 | [] -> ()
3346 | l :: _ -> gotoxy !S.x (getpagey l.pageno)
3348 | Ascii ' ' -> nextpage ()
3349 | Delete -> prevpage ()
3350 | Ascii '=' -> showtext ' ' (describe_layout !S.layout);
3351 | Ascii 'w' ->
3352 begin match !S.layout with
3353 | [] -> ()
3354 | l :: _ ->
3355 Wsi.reshape l.pagew l.pageh;
3356 Glutils.postRedisplay "w"
3358 | Ascii '\'' -> enterbookmarkmode ()
3359 | Ascii 'i' -> enterinfomode ()
3360 | Ascii 'e' when Buffer.length S.errmsgs > 0 -> entermsgsmode ()
3361 | Ascii 'm' ->
3362 let ondone s =
3363 match !S.layout with
3364 | l :: _ when nonemptystr s -> addbookmark s @@ getanchor1 l
3365 | _ -> ()
3367 enttext ("bookmark: ", E.s, None, textentry, ondone, true)
3368 | Ascii '~' ->
3369 quickbookmark ();
3370 showtext ' ' "Quick bookmark added";
3371 | Ascii 'x' -> !S.roamf ()
3372 | Ascii ('<'|'>' as c) ->
3373 reqlayout (conf.angle + (if c = '>' then 30 else -30)) conf.fitmodel
3374 | Ascii ('['|']' as c) ->
3375 conf.colorscale <-
3376 bound (conf.colorscale +. (if c = ']' then 0.1 else -0.1)) 0.0 1.0;
3377 Glutils.postRedisplay "brightness";
3378 | Ascii 'c' when !S.mode = View ->
3379 if Wsi.withalt mask
3380 then (
3381 if conf.zoom > 1.0
3382 then
3383 let m = (!S.winw - !S.w) / 2 in
3384 gotoxy m !S.y
3386 else
3387 let (c, a, b), z =
3388 match !S.prevcolumns with
3389 | None -> (1, 0, 0), 1.0
3390 | Some (columns, z) ->
3391 let cab =
3392 match columns with
3393 | Csplit (c, _) -> -c, 0, 0
3394 | Cmulti ((c, a, b), _) -> c, a, b
3395 | Csingle _ -> 1, 0, 0
3397 cab, z
3399 setcolumns View c a b;
3400 setzoom z
3401 | Down | Up when ctrl && Wsi.withshift mask ->
3402 let zoom, x = !S.prevzoom in
3403 setzoom zoom;
3404 S.x := x;
3405 | Up ->
3406 begin match !S.autoscroll with
3407 | None ->
3408 begin match !S.mode with
3409 | Birdseye beye -> upbirdseye 1 beye
3410 | Textentry _ | View | LinkNav _ ->
3411 if ctrl
3412 then gotoxy !S.x (U.add_to_y_and_clamp ~-(!S.winh/2))
3413 else (
3414 if not (Wsi.withshift mask) && conf.presentation
3415 then prevpage ()
3416 else gotoxy !S.x (U.add_to_y_and_clamp (-conf.scrollstep))
3419 | Some n -> setautoscrollspeed n false
3421 | Down ->
3422 begin match !S.autoscroll with
3423 | None ->
3424 begin match !S.mode with
3425 | Birdseye beye -> downbirdseye 1 beye
3426 | Textentry _ | View | LinkNav _ ->
3427 if ctrl
3428 then gotoxy !S.x (U.add_to_y_and_clamp (!S.winh/2))
3429 else (
3430 if not (Wsi.withshift mask) && conf.presentation
3431 then nextpage ()
3432 else gotoxy !S.x (U.add_to_y_and_clamp (conf.scrollstep))
3435 | Some n -> setautoscrollspeed n true
3437 | Ascii 'H' -> enterhistmode ()
3438 | Fn 1 when Wsi.withalt mask -> enterhistmode ()
3439 | Fn 1 -> enterhelpmode ()
3440 | Left | Right when not (Wsi.withalt mask) ->
3441 if canpan ()
3442 then
3443 let dx =
3444 if ctrl
3445 then !S.winw / 2
3446 else conf.hscrollstep
3448 let dx =
3449 let pv = Wsi.ks2kt key in
3450 if pv = Keys.Left then dx else -dx
3452 gotoxy (U.panbound (!S.x + dx)) !S.y
3453 else (
3454 S.text := E.s;
3455 Glutils.postRedisplay "left/right"
3457 | Prior ->
3458 let y =
3459 if ctrl
3460 then
3461 match !S.layout with
3462 | [] -> !S.y
3463 | l :: _ -> !S.y - l.pagey
3464 else U.add_to_y_and_clamp (U.pgscale ~- !S.winh)
3466 gotoxy !S.x y
3467 | Next ->
3468 let y =
3469 if ctrl
3470 then
3471 match List.rev !S.layout with
3472 | [] -> !S.y
3473 | l :: _ -> getpagey l.pageno
3474 else U.add_to_y_and_clamp (U.pgscale !S.winh)
3476 gotoxy !S.x y
3477 | Ascii 'g' | Home ->
3478 addnav ();
3479 gotoxy 0 0
3480 | Ascii 'G' | End ->
3481 addnav ();
3482 gotoxy 0 (U.add_to_y_and_clamp !S.maxy)
3483 | Right when Wsi.withalt mask ->
3484 (match !S.nav.future with
3485 | [] -> ()
3486 | next :: frest ->
3487 S.nav := { past = getanchor () :: !S.nav.past; future = frest; };
3488 gotoxy !S.x (getanchory next)
3490 | Left when Wsi.withalt mask -> histback ()
3491 | Backspace -> histback ()
3492 | Ascii 'r' -> reload ()
3493 | Ascii 'v' when conf.debug ->
3494 S.rects := [];
3495 List.iter (fun l ->
3496 match getopaque l.pageno with
3497 | exception Not_found -> ()
3498 | opaque ->
3499 let x0, y0, x1, y1 = Ffi.pagebbox opaque in
3500 let rect = (float x0, float y0,
3501 float x1, float y0,
3502 float x1, float y1,
3503 float x0, float y1) in
3504 debugrect rect;
3505 let color = (0.0, 0.0, 1.0 /. (l.pageno mod 3 |> float), 0.5) in
3506 S.rects := (l.pageno, color, rect) :: !S.rects;
3507 ) !S.layout;
3508 Glutils.postRedisplay "v";
3509 | Ascii '|' ->
3510 let mode = !S.mode in
3511 let cmd = ref E.s in
3512 let onleave = function
3513 | Cancel -> S.mode := mode
3514 | Confirm ->
3515 List.iter (fun l ->
3516 match getopaque l.pageno with
3517 | exception Not_found -> ()
3518 | opaque -> pipesel opaque !cmd) !S.layout;
3519 S.mode := mode
3521 let ondone s =
3522 cbput !S.hists.sel s;
3523 cmd := s
3525 let te =
3526 "| ", !cmd, Some (onhist !S.hists.sel), textentry, ondone, true
3528 Glutils.postRedisplay "|";
3529 S.mode := Textentry (te, onleave);
3530 | (Ascii _|Fn _|Enter|Left|Right|Code _|Ctrl _) ->
3531 vlog "huh? %s" (Wsi.keyname key)
3533 let linknavkeyboard key mask linknav =
3534 let pv = Wsi.ks2kt key in
3535 let getpage pageno =
3536 let rec loop = function
3537 | [] -> None
3538 | l :: _ when l.pageno = pageno -> Some l
3539 | _ :: rest -> loop rest
3540 in loop !S.layout
3542 let doexact (pageno, n) =
3543 match getopaque pageno, getpage pageno with
3544 | opaque, Some l ->
3545 if pv = Keys.Enter
3546 then
3547 let under = Ffi.getlink opaque n in
3548 Glutils.postRedisplay "link gotounder";
3549 gotounder under;
3550 S.mode := View;
3551 else
3552 let opt, dir =
3553 let open Keys in
3554 match pv with
3555 | Home -> Some (Ffi.findlink opaque LDfirst), -1
3556 | End -> Some (Ffi.findlink opaque LDlast), 1
3557 | Left -> Some (Ffi.findlink opaque (LDleft n)), -1
3558 | Right -> Some (Ffi.findlink opaque (LDright n)), 1
3559 | Up -> Some (Ffi.findlink opaque (LDup n)), -1
3560 | Down -> Some (Ffi.findlink opaque (LDdown n)), 1
3561 | Delete|Escape|Insert|Enter|Next|Prior|Ascii _
3562 | Code _|Fn _|Ctrl _|Backspace -> None, 0
3564 let pwl l dir =
3565 begin match Ffi.findpwl l.pageno dir with
3566 | Pwlnotfound -> ()
3567 | Pwl pageno ->
3568 let notfound dir =
3569 S.mode := LinkNav (Ltgendir dir);
3570 let y, h = getpageyh pageno in
3571 let y =
3572 if dir < 0
3573 then y + h - !S.winh
3574 else y
3576 gotoxy !S.x y
3578 begin match getopaque pageno, getpage pageno with
3579 | opaque, Some _ ->
3580 let link =
3581 let ld = if dir > 0 then LDfirst else LDlast in
3582 Ffi.findlink opaque ld
3584 begin match link with
3585 | Lfound m ->
3586 showlinktype (Ffi.getlink opaque m);
3587 S.mode := LinkNav (Ltexact (pageno, m));
3588 Glutils.postRedisplay "linknav jpage";
3589 | Lnotfound -> notfound dir
3590 end;
3591 | _ | exception Not_found -> notfound dir
3592 end;
3593 end;
3595 begin match opt with
3596 | Some Lnotfound -> pwl l dir;
3597 | Some (Lfound m) ->
3598 if m = n
3599 then pwl l dir
3600 else (
3601 let _, y0, _, y1 = Ffi.getlinkrect opaque m in
3602 if y0 < l.pagey
3603 then gotopage1 l.pageno y0
3604 else (
3605 let d = fstate.fontsize + 1 in
3606 if y1 - l.pagey > l.pagevh - d
3607 then gotopage1 l.pageno (y1 - !S.winh + d)
3608 else Glutils.postRedisplay "linknav";
3610 showlinktype (Ffi.getlink opaque m);
3611 S.mode := LinkNav (Ltexact (l.pageno, m));
3614 | None -> viewkeyboard key mask
3615 end;
3616 | _ | exception Not_found -> viewkeyboard key mask
3618 if pv = Keys.Insert
3619 then (
3620 begin match linknav with
3621 | Ltexact pa -> S.lnava := Some pa
3622 | Ltgendir _ | Ltnotready _ -> ()
3623 end;
3624 S.mode := View;
3625 Glutils.postRedisplay "leave linknav"
3627 else
3628 match linknav with
3629 | Ltgendir _ | Ltnotready _ -> viewkeyboard key mask
3630 | Ltexact exact -> doexact exact
3632 let keyboard key mask =
3633 if (key = Char.code 'g' && Wsi.withctrl mask) && not (istextentry !S.mode)
3634 then wcmd U.interrupt ""
3635 else !S.uioh#key key mask |> setuioh
3637 let birdseyekeyboard key mask
3638 ((oconf, leftx, pageno, hooverpageno, anchor) as beye) =
3639 let incr =
3640 match conf.columns with
3641 | Csingle _ -> 1
3642 | Cmulti ((c, _, _), _) -> c
3643 | Csplit _ -> error "bird's eye split mode"
3645 let pgh layout = List.fold_left
3646 (fun m l -> max l.pageh m) !S.winh layout in
3647 let open Keys in
3648 match Wsi.ks2kt key with
3649 | Ascii 'l' when Wsi.withctrl mask ->
3650 let y, h = getpageyh pageno in
3651 let top = (!S.winh - h) / 2 in
3652 gotoxy !S.x (max 0 (y - top))
3653 | Enter -> leavebirdseye beye false
3654 | Escape -> leavebirdseye beye true
3655 | Up -> upbirdseye incr beye
3656 | Down -> downbirdseye incr beye
3657 | Left -> upbirdseye 1 beye
3658 | Right -> downbirdseye 1 beye
3660 | Prior ->
3661 begin match !S.layout with
3662 | l :: _ ->
3663 if l.pagey != 0
3664 then (
3665 S.mode := Birdseye (oconf, leftx, l.pageno, hooverpageno, anchor);
3666 gotopage1 l.pageno 0;
3668 else (
3669 let layout = layout !S.x (!S.y - !S.winh)
3670 !S.winw
3671 (pgh !S.layout) in
3672 match layout with
3673 | [] -> gotoxy !S.x (U.add_to_y_and_clamp ~- !S.winh)
3674 | l :: _ ->
3675 S.mode := Birdseye (oconf, leftx, l.pageno, hooverpageno, anchor);
3676 gotopage1 l.pageno 0
3679 | [] -> gotoxy !S.x (U.add_to_y_and_clamp ~- !S.winh)
3680 end;
3682 | Next ->
3683 begin match List.rev !S.layout with
3684 | l :: _ ->
3685 let layout = layout !S.x
3686 (!S.y + (pgh !S.layout))
3687 !S.winw !S.winh in
3688 begin match layout with
3689 | [] ->
3690 let incr = l.pageh - l.pagevh in
3691 if incr = 0
3692 then (
3693 S.mode :=
3694 Birdseye (
3695 oconf, leftx, !S.pagecount - 1, hooverpageno, anchor
3697 Glutils.postRedisplay "birdseye pagedown";
3699 else
3700 gotoxy !S.x (U.add_to_y_and_clamp (incr + conf.interpagespace*2));
3702 | l :: _ ->
3703 S.mode := Birdseye (oconf, leftx, l.pageno, hooverpageno, anchor);
3704 gotopage1 l.pageno 0;
3707 | [] -> gotoxy !S.x (U.add_to_y_and_clamp !S.winh)
3708 end;
3710 | Home ->
3711 S.mode := Birdseye (oconf, leftx, 0, hooverpageno, anchor);
3712 gotopage1 0 0
3714 | End ->
3715 let pageno = !S.pagecount - 1 in
3716 S.mode := Birdseye (oconf, leftx, pageno, hooverpageno, anchor);
3717 if not (U.pagevisible !S.layout pageno)
3718 then
3719 let h =
3720 match List.rev !S.pdims with
3721 | [] -> !S.winh
3722 | (_, _, h, _) :: _ -> h
3724 gotoxy
3725 !S.x
3726 (max 0 (getpagey pageno - (!S.winh - h - conf.interpagespace)))
3727 else Glutils.postRedisplay "birdseye end";
3729 | Delete|Insert|Ascii _|Code _|Ctrl _|Fn _|Backspace -> viewkeyboard key mask
3731 let drawpage l =
3732 let color =
3733 match !S.mode with
3734 | Textentry _ -> U.scalecolor 0.4
3735 | LinkNav _ | View -> U.scalecolor 1.0
3736 | Birdseye (_, _, pageno, hooverpageno, _) ->
3737 if l.pageno = hooverpageno
3738 then U.scalecolor 0.9
3739 else (
3740 if l.pageno = pageno
3741 then (
3742 let c = U.scalecolor 1.0 in
3743 GlDraw.color c;
3744 GlDraw.line_width 3.0;
3745 let dispx = l.pagedispx in
3746 Glutils.linerect
3747 (float (dispx-1)) (float (l.pagedispy-1))
3748 (float (dispx+l.pagevw+1))
3749 (float (l.pagedispy+l.pagevh+1));
3750 GlDraw.line_width 1.0;
3753 else U.scalecolor 0.8
3756 drawtiles l color
3758 let postdrawpage l linkindexbase =
3759 match getopaque l.pageno with
3760 | exception Not_found -> 0
3761 | opaque ->
3762 if tileready l l.pagex l.pagey
3763 then
3764 let x = l.pagedispx - l.pagex
3765 and y = l.pagedispy - l.pagey in
3766 let hlmask =
3767 match conf.columns with
3768 | Csingle _ | Cmulti _ ->
3769 (if conf.hlinks then 1 else 0)
3770 + (if !S.glinks
3771 && not (isbirdseye !S.mode) then 2 else 0)
3772 | Csplit _ -> 0
3774 let s =
3775 match !S.mode with
3776 | Textentry ((_, s, _, _, _, _), _) when !S.glinks -> s
3777 | Textentry _
3778 | Birdseye _
3779 | View
3780 | LinkNav _ -> E.s
3782 let n =
3783 Ffi.postprocess opaque hlmask x y
3784 (linkindexbase, s, conf.hfsize, conf.hcs) in
3785 if n < 0
3786 then (Glutils.redisplay := not @@ hasdata !S.ss; 0)
3787 else n
3788 else 0
3790 let scrollindicator () =
3791 let sbw, ph, sh = !S.uioh#scrollph in
3792 let sbh, pw, sw = !S.uioh#scrollpw in
3794 let x0,x1,hx0 =
3795 if conf.leftscroll
3796 then (0, sbw, sbw)
3797 else ((!S.winw - sbw), !S.winw, 0)
3800 Gl.enable `blend;
3801 GlFunc.blend_func ~src:`src_alpha ~dst:`one_minus_src_alpha;
3802 let (r, g, b, alpha) = conf.sbarcolor in
3803 GlDraw.color (r, g, b) ~alpha;
3804 Glutils.filledrect (float x0) 0. (float x1) (float !S.winh);
3805 Glutils.filledrect
3806 (float hx0) (float (!S.winh - sbh))
3807 (float (hx0 + !S.winw)) (float !S.winh);
3808 let (r, g, b, alpha) = conf.sbarhndlcolor in
3809 GlDraw.color (r, g, b) ~alpha;
3811 Glutils.filledrect (float x0) ph (float x1) (ph +. sh);
3812 let pw = pw +. float hx0 in
3813 Glutils.filledrect pw (float (!S.winh - sbh)) (pw +. sw) (float !S.winh);
3814 Gl.disable `blend
3816 let showsel () =
3817 match !S.mstate with
3818 | Mnone | Mscrolly | Mscrollx | Mpan _ | Mzoom _ | Mzoomrect _ -> ()
3819 | Msel ((x0, y0), (x1, y1)) ->
3820 let identify opaque l px py = Some (opaque, l.pageno, px, py) in
3821 let o0,n0,px0,py0 =
3822 onppundermouse identify x0 y0 (Opaque.of_string E.s, -1, 0, 0) in
3823 let _o1,n1,px1,py1 =
3824 onppundermouse identify x1 y1 (Opaque.of_string E.s, -1, 0, 0) in
3825 if n0 != -1 && n0 = n1 then Ffi.seltext o0 (px0, py0, px1, py1)
3827 let showrects = function
3828 | [] -> ()
3829 | rects ->
3830 Gl.enable `blend;
3831 GlDraw.color (0.0, 0.0, 1.0) ~alpha:0.5;
3832 GlFunc.blend_func ~src:`src_alpha ~dst:`one_minus_src_alpha;
3833 List.iter
3834 (fun (pageno, c, (x0, y0, x1, y1, x2, y2, x3, y3)) ->
3835 List.iter (fun l ->
3836 if l.pageno = pageno
3837 then
3838 let dx = float (l.pagedispx - l.pagex) in
3839 let dy = float (l.pagedispy - l.pagey) in
3840 let r, g, b, alpha = c in
3841 GlDraw.color (r, g, b) ~alpha;
3842 Glutils.filledrect2
3843 (x0+.dx) (y0+.dy)
3844 (x1+.dx) (y1+.dy)
3845 (x3+.dx) (y3+.dy)
3846 (x2+.dx) (y2+.dy);
3847 ) !S.layout
3848 ) rects;
3849 Gl.disable `blend
3851 let display () =
3852 let sc (r, g, b) = let s = conf.colorscale in (r *. s, g *. s, b *. s) in
3853 GlDraw.color (sc conf.bgcolor);
3854 GlClear.color (sc conf.bgcolor);
3855 GlClear.clear [`color];
3856 List.iter drawpage !S.layout;
3857 let rects =
3858 match !S.mode with
3859 | LinkNav (Ltgendir _) | LinkNav (Ltnotready _)
3860 | Birdseye _
3861 | Textentry _
3862 | View -> !S.rects
3863 | LinkNav (Ltexact (pageno, linkno)) ->
3864 match getopaque pageno with
3865 | exception Not_found -> !S.rects
3866 | opaque ->
3867 let x0, y0, x1, y1 = Ffi.getlinkrect opaque linkno in
3868 let color =
3869 if conf.invert
3870 then (1.0, 1.0, 1.0, 0.5)
3871 else (0.0, 0.0, 0.5, 0.5)
3873 (pageno, color,
3874 (float x0, float y0,
3875 float x1, float y0,
3876 float x1, float y1,
3877 float x0, float y1)
3878 ) :: !S.rects
3880 showrects rects;
3881 let rec postloop linkindexbase = function
3882 | l :: rest ->
3883 let linkindexbase = linkindexbase + postdrawpage l linkindexbase in
3884 postloop linkindexbase rest
3885 | [] -> ()
3887 showsel ();
3888 postloop 0 !S.layout;
3889 !S.uioh#display;
3890 begin match !S.mstate with
3891 | Mzoomrect ((x0, y0), (x1, y1)) ->
3892 Gl.enable `blend;
3893 GlDraw.color (0.3, 0.3, 0.3) ~alpha:0.5;
3894 GlFunc.blend_func ~src:`src_alpha ~dst:`one_minus_src_alpha;
3895 Glutils.filledrect (float x0) (float y0) (float x1) (float y1);
3896 Gl.disable `blend;
3897 | Msel _
3898 | Mpan _
3899 | Mscrolly | Mscrollx
3900 | Mzoom _
3901 | Mnone -> ()
3902 end;
3903 enttext ();
3904 scrollindicator ();
3906 if conf.pgscale > 0.0
3907 then (
3908 let yh = conf.pgscale *. float !S.winh in
3909 match !S.layout with
3910 | _ :: [] ->
3911 Gl.enable `blend;
3912 GlDraw.color (0.1, 0.1, 0.1) ~alpha:0.5;
3913 GlFunc.blend_func ~src:`src_alpha ~dst:`one_minus_src_alpha;
3914 let x0 = 0.0
3915 and y0 = yh -. 3.0 in
3916 let x1 = float !S.winw
3917 and y1 = yh +. 3.0 in
3918 Glutils.filledrect x0 y0 x1 y1;
3919 Gl.disable `blend;
3920 | _ -> ()
3922 Wsi.swapb ()
3924 let display () =
3925 match !S.reload with
3926 | Some (x, y, t) ->
3927 if x != !S.x || y != !S.y || abs_float @@ now () -. t > 0.5
3928 || (!S.layout != [] && layoutready !S.layout)
3929 then (
3930 S.reload := None;
3931 display ()
3933 | None -> display ()
3935 let zoomrect x y x1 y1 =
3936 let x0 = min x x1
3937 and x1 = max x x1
3938 and y0 = min y y1 in
3939 let zoom = (float !S.w) /. float (x1 - x0) in
3940 let margin =
3941 let simple () =
3942 if !S.w < !S.winw
3943 then (!S.winw - !S.w) / 2
3944 else 0
3946 match conf.fitmodel with
3947 | FitWidth | FitProportional -> simple ()
3948 | FitPage ->
3949 match conf.columns with
3950 | Csplit _ ->
3951 onppundermouse (fun _ l _ _ -> Some l.pagedispx) x0 y0 x0
3952 | Cmulti _ | Csingle _ -> simple ()
3954 gotoxy ((!S.x + margin) - x0) (!S.y + y0);
3955 S.anchor := getanchor ();
3956 setzoom zoom;
3957 resetmstate ()
3959 let annot inline x y =
3960 match unproject x y with
3961 | Some (opaque, n, ux, uy) ->
3962 let add text =
3963 Ffi.addannot opaque ux uy text;
3964 wcmd1 U.freepage opaque;
3965 Hashtbl.remove S.pagemap (n, !S.gen);
3966 flushtiles ();
3967 gotoxy !S.x !S.y
3969 if inline
3970 then
3971 let mode = !S.mode in
3972 let te = ("annotation: ", E.s, None, textentry, add, true) in
3973 S.mode := Textentry (te, fun _ -> S.mode := mode);
3974 S.text := E.s;
3975 enttext ();
3976 Glutils.postRedisplay "annot"
3977 else add @@ getusertext E.s
3978 | _ -> ()
3980 let zoomblock x y =
3981 let g opaque l px py =
3982 match Ffi.rectofblock opaque px py with
3983 | Some a ->
3984 let x0 = a.(0) -. 20. in
3985 let x1 = a.(1) +. 20. in
3986 let y0 = a.(2) -. 20. in
3987 let zoom = (float !S.w) /. (x1 -. x0) in
3988 let pagey = getpagey l.pageno in
3989 let margin = (!S.w - l.pagew)/2 in
3990 let nx = -truncate x0 - margin in
3991 gotoxy nx (pagey + truncate y0);
3992 S.anchor := getanchor ();
3993 setzoom zoom;
3994 None
3995 | None -> None
3997 match conf.columns with
3998 | Csplit _ ->
3999 impmsg "block zooming does not work properly in split columns mode"
4000 | Cmulti _ | Csingle _ -> onppundermouse g x y ()
4002 let scrollx x =
4003 let winw = !S.winw - 1 in
4004 let s = float x /. float winw in
4005 let destx = truncate (float (!S.w + winw) *. s) in
4006 gotoxy (winw - destx) !S.y;
4007 S.mstate := Mscrollx
4009 let scrolly y =
4010 let s = float y /. float !S.winh in
4011 let desty = truncate (s *. float (U.maxy ())) in
4012 gotoxy !S.x desty;
4013 S.mstate := Mscrolly
4015 let viewmulticlick clicks x y mask =
4016 let g opaque l px py =
4017 let mark =
4018 match clicks with
4019 | 2 -> Mark_word
4020 | 3 -> Mark_line
4021 | 4 -> Mark_block
4022 | _ -> Mark_page
4024 if Ffi.markunder opaque px py mark
4025 then (
4026 Some (fun () ->
4027 let dopipe cmd =
4028 match getopaque l.pageno with
4029 | exception Not_found -> ()
4030 | opaque -> pipesel opaque cmd
4032 S.roamf := (fun () -> dopipe conf.paxcmd);
4033 if not (Wsi.withctrl mask) then dopipe conf.selcmd;
4036 else None
4038 Glutils.postRedisplay "viewmulticlick";
4039 onppundermouse g x y (fun () -> impmsg "nothing to select") ()
4041 let canselect () =
4042 match conf.columns with
4043 | Csplit _ -> false
4044 | Csingle _ | Cmulti _ -> conf.angle mod 360 = 0
4046 let viewmouse button down x y mask =
4047 match button with
4048 | n when (n == 4 || n == 5) && not (Wsi.withshift mask) && not down ->
4049 if Wsi.withctrl mask
4050 then (
4051 let incr =
4052 if n = 5
4053 then if conf.zoom +. 0.01 > 0.1 then 0.1 else 0.01
4054 else if conf.zoom -. 0.1 < 0.1 then -0.01 else -0.1
4056 let fx, fy =
4057 match !S.mstate with
4058 | Mzoom (oldn, _, pos) when n = oldn -> pos
4059 | Mzoomrect _ | Mnone | Mpan _
4060 | Msel _ | Mscrollx | Mscrolly | Mzoom _ -> (x, y)
4062 let zoom = conf.zoom -. incr in
4063 S.mstate := Mzoom (n, 0, (x, y));
4064 if false && abs (fx - x) > 5 || abs (fy - y) > 5
4065 then pivotzoom ~x ~y zoom
4066 else pivotzoom zoom
4068 else (
4069 match !S.autoscroll with
4070 | Some step -> setautoscrollspeed step (n=4)
4071 | None ->
4072 if conf.wheelbypage || conf.presentation
4073 then (
4074 if n = 4
4075 then prevpage ()
4076 else nextpage ()
4078 else
4079 let incr = if n = 4 then -conf.scrollstep else conf.scrollstep in
4080 let incr = incr * 2 in
4081 let y = U.add_to_y_and_clamp incr in
4082 gotoxy !S.x y
4085 | n when (n = 4 || n = 5 || n = 6 || n = 7) && not down && canpan () ->
4086 let x = U.panbound
4087 (!S.x + (if n = 5 || n = 7 then -2 else 2) * conf.hscrollstep)
4089 gotoxy x !S.y
4091 | 1 when Wsi.withshift mask ->
4092 S.mstate := Mnone;
4093 if not down
4094 then (
4095 match unproject x y with
4096 | None -> ()
4097 | Some (_, pageno, ux, uy) ->
4098 let cmd = Printf.sprintf "%s %s %d %d %d" conf.stcmd !S.path
4099 pageno ux uy
4101 match spawn cmd [] with
4102 | exception exn ->
4103 adderrfmt "spawn" "execution of synctex command(%S) failed: %S"
4104 conf.stcmd @@ exntos exn
4105 | _pid -> ()
4108 | 1 when Wsi.withctrl mask ->
4109 if down
4110 then (
4111 Wsi.setcursor Wsi.CURSOR_FLEUR;
4112 S.mstate := Mpan (x, y)
4114 else S.mstate := Mnone
4116 | 3 ->
4117 if down
4118 then (
4119 if Wsi.withshift mask
4120 then (
4121 annot conf.annotinline x y;
4122 Glutils.postRedisplay "addannot"
4124 else
4125 let p = (x, y) in
4126 Wsi.setcursor Wsi.CURSOR_CYCLE;
4127 S.mstate := Mzoomrect (p, p)
4129 else (
4130 match !S.mstate with
4131 | Mzoomrect ((x0, y0), _) ->
4132 if abs (x-x0) > 10 && abs (y - y0) > 10
4133 then zoomrect x0 y0 x y
4134 else (
4135 resetmstate ();
4136 Glutils.postRedisplay "kill accidental zoom rect";
4138 | Msel _
4139 | Mpan _
4140 | Mscrolly | Mscrollx
4141 | Mzoom _
4142 | Mnone -> resetmstate ()
4145 | 1 when vscrollhit x ->
4146 if down
4147 then
4148 let _, position, sh = !S.uioh#scrollph in
4149 if y > truncate position && y < truncate (position +. sh)
4150 then S.mstate := Mscrolly
4151 else scrolly y
4152 else S.mstate := Mnone
4154 | 1 when y > !S.winh - hscrollh () ->
4155 if down
4156 then
4157 let _, position, sw = !S.uioh#scrollpw in
4158 if x > truncate position && x < truncate (position +. sw)
4159 then S.mstate := Mscrollx
4160 else scrollx x
4161 else S.mstate := Mnone
4163 | 1 when !S.bzoom -> if not down then zoomblock x y
4165 | 1 ->
4166 let dest = if down then getunder x y else Unone in
4167 begin match dest with
4168 | Ulinkuri _ -> gotounder dest
4169 | Unone when down ->
4170 Wsi.setcursor Wsi.CURSOR_FLEUR;
4171 S.mstate := Mpan (x, y);
4172 | Utextannot (opaque, slinkindex) -> enterannotmode opaque slinkindex
4173 | Unone | Utext _ | Ufileannot _ ->
4174 if down
4175 then (
4176 if canselect ()
4177 then (
4178 S.mstate := Msel ((x, y), (x, y));
4179 Glutils.postRedisplay "mouse select";
4182 else (
4183 match !S.mstate with
4184 | Mnone -> ()
4185 | Mzoom _ | Mscrollx | Mscrolly -> S.mstate := Mnone
4186 | Mzoomrect ((x0, y0), _) -> zoomrect x0 y0 x y
4187 | Mpan _ ->
4188 Wsi.setcursor Wsi.CURSOR_INHERIT;
4189 S.mstate := Mnone
4190 | Msel ((x0, y0), (x1, y1)) ->
4191 let rec loop = function
4192 | [] -> ()
4193 | l :: rest ->
4194 let inside =
4195 let a0 = l.pagedispy in
4196 let a1 = a0 + l.pagevh in
4197 let b0 = l.pagedispx in
4198 let b1 = b0 + l.pagevw in
4199 ((y0 >= a0 && y0 <= a1) || (y1 >= a0 && y1 <= a1))
4200 && ((x0 >= b0 && x0 <= b1) || (x1 >= b0 && x1 <= b1))
4202 if inside
4203 then
4204 match getopaque l.pageno with
4205 | exception Not_found -> ()
4206 | opaque ->
4207 let dosel cmd () =
4208 pipef ~closew:false "Msel"
4209 (fun w ->
4210 Ffi.copysel w opaque;
4211 Glutils.postRedisplay "Msel") cmd
4213 dosel conf.selcmd ();
4214 S.roamf := dosel conf.paxcmd;
4215 else loop rest
4217 loop !S.layout;
4218 resetmstate ();
4221 | _ -> ()
4223 let birdseyemouse button down x y mask
4224 (conf, leftx, _, hooverpageno, anchor) =
4225 match button with
4226 | 1 when down ->
4227 let rec loop = function
4228 | [] -> ()
4229 | l :: rest ->
4230 if y > l.pagedispy && y < l.pagedispy + l.pagevh
4231 && x > l.pagedispx && x < l.pagedispx + l.pagevw
4232 then
4233 leavebirdseye (conf, leftx, l.pageno, hooverpageno, anchor) false
4234 else loop rest
4236 loop !S.layout
4237 | 3 -> ()
4238 | _ -> viewmouse button down x y mask
4240 let uioh = object
4241 method display = ()
4242 method infochanged _ = ()
4244 method key key mask =
4245 begin match !S.mode with
4246 | Textentry textentry -> textentrykeyboard key mask textentry
4247 | Birdseye birdseye -> birdseyekeyboard key mask birdseye
4248 | View -> viewkeyboard key mask
4249 | LinkNav linknav -> linknavkeyboard key mask linknav
4250 end;
4251 !S.uioh
4253 method button button bstate x y mask =
4254 begin match !S.mode with
4255 | LinkNav _ | View -> viewmouse button bstate x y mask
4256 | Birdseye beye -> birdseyemouse button bstate x y mask beye
4257 | Textentry _ -> ()
4258 end;
4259 !S.uioh
4261 method multiclick clicks x y mask =
4262 begin match !S.mode with
4263 | LinkNav _ | View -> viewmulticlick clicks x y mask
4264 | Birdseye _ | Textentry _ -> ()
4265 end;
4266 !S.uioh
4268 method motion x y =
4269 begin match !S.mode with
4270 | Textentry _ -> ()
4271 | View | Birdseye _ | LinkNav _ ->
4272 match !S.mstate with
4273 | Mzoom _ | Mnone -> ()
4274 | Mpan (x0, y0) ->
4275 let dx = x - x0
4276 and dy = y0 - y in
4277 S.mstate := Mpan (x, y);
4278 let x = if canpan () then U.panbound (!S.x + dx) else !S.x in
4279 let y = U.add_to_y_and_clamp dy in
4280 gotoxy x y
4282 | Msel (a, _) ->
4283 S.mstate := Msel (a, (x, y));
4284 Glutils.postRedisplay "motion select";
4286 | Mscrolly ->
4287 let y = min !S.winh (max 0 y) in
4288 scrolly y
4290 | Mscrollx ->
4291 let x = min !S.winw (max 0 x) in
4292 scrollx x
4294 | Mzoomrect (p0, _) ->
4295 S.mstate := Mzoomrect (p0, (x, y));
4296 Glutils.postRedisplay "motion zoomrect";
4297 end;
4298 !S.uioh
4300 method pmotion x y =
4301 begin match !S.mode with
4302 | Birdseye (conf, leftx, pageno, hooverpageno, anchor) ->
4303 let rec loop = function
4304 | [] ->
4305 if hooverpageno != -1
4306 then (
4307 S.mode := Birdseye (conf, leftx, pageno, -1, anchor);
4308 Glutils.postRedisplay "pmotion birdseye no hoover";
4310 | l :: rest ->
4311 if y > l.pagedispy && y < l.pagedispy + l.pagevh
4312 && x > l.pagedispx && x < l.pagedispx + l.pagevw
4313 then (
4314 S.mode := Birdseye (conf, leftx, pageno, l.pageno, anchor);
4315 Glutils.postRedisplay "pmotion birdseye hoover";
4317 else loop rest
4319 loop !S.layout
4321 | Textentry _ -> ()
4323 | LinkNav _ | View ->
4324 match !S.mstate with
4325 | Mpan _ | Msel _ | Mzoom _ | Mscrolly | Mscrollx | Mzoomrect _ -> ()
4326 | Mnone ->
4327 updateunder x y;
4328 if canselect ()
4329 then
4330 match conf.pax with
4331 | None -> ()
4332 | Some past ->
4333 let now = now () in
4334 let delta = now -. past in
4335 if delta > 0.01
4336 then paxunder x y
4337 else conf.pax <- Some now
4338 end;
4339 !S.uioh
4341 method scrollph =
4342 let maxy = U.maxy () in
4343 let p, h =
4344 if maxy = 0
4345 then 0.0, float !S.winh
4346 else scrollph !S.y maxy
4348 vscrollw (), p, h
4350 method scrollpw =
4351 let fwinw = float (!S.winw - vscrollw ()) in
4352 let sw =
4353 let sw = fwinw /. float !S.w in
4354 let sw = fwinw *. sw in
4355 max sw (float conf.scrollh)
4357 let position =
4358 let maxx = !S.w + !S.winw in
4359 let x = !S.winw - !S.x in
4360 let percent = float x /. float maxx in
4361 (fwinw -. sw) *. percent
4363 hscrollh (), position, sw
4365 method modehash =
4366 let modename =
4367 match !S.mode with
4368 | LinkNav _ -> "links"
4369 | Textentry _ -> "textentry"
4370 | Birdseye _ -> "birdseye"
4371 | View -> "view"
4373 findkeyhash conf modename
4375 method eformsgs = true
4376 method alwaysscrolly = false
4377 method scroll dx dy =
4378 let x = if canpan () then U.panbound (!S.x + dx) else !S.x in
4379 gotoxy x (U.add_to_y_and_clamp (2 * dy));
4380 !S.uioh
4381 method zoom z x y =
4382 pivotzoom ~x ~y (conf.zoom *. exp z);
4385 let ract cmds =
4386 let cl = splitatchar cmds ' ' in
4387 let scan s fmt f =
4388 try Scanf.sscanf s fmt f
4389 with exn -> adderrfmt "remote exec" "error processing '%S': %s\n"
4390 cmds @@ exntos exn
4392 let rectx s pageno (r, g, b, a) x0 y0 x1 y1 =
4393 vlog "%s page %d color (%f %f %f %f) x0,y0,x1,y1 = %f %f %f %f"
4394 s pageno r g b a x0 y0 x1 y1;
4395 onpagerect
4396 pageno
4397 (fun w h ->
4398 let _,w1,h1,_ = getpagedim pageno in
4399 let sw = float w1 /. float w
4400 and sh = float h1 /. float h in
4401 let x0s = x0 *. sw
4402 and x1s = x1 *. sw
4403 and y0s = y0 *. sh
4404 and y1s = y1 *. sh in
4405 let rect = (x0s,y0s,x1s,y0s,x1s,y1s,x0s,y1s) in
4406 let color = (r, g, b, a) in
4407 if conf.verbose then debugrect rect;
4408 S.rects := (pageno, color, rect) :: !S.rects;
4409 Glutils.postRedisplay s;
4412 match cl with
4413 | "reload", "" -> reload ()
4414 | "goto", args ->
4415 scan args "%u %f %f"
4416 (fun pageno x y ->
4417 let cmd, _ = !S.geomcmds in
4418 if emptystr cmd
4419 then gotopagexy pageno x y
4420 else
4421 let f prevf () =
4422 gotopagexy pageno x y;
4423 prevf ()
4425 S.reprf := f !S.reprf
4427 | "goto1", args -> scan args "%u %f" gotopage
4428 | "gotor", args -> scan args "%S" gotoremote
4429 | "rect", args ->
4430 scan args "%u %u %f %f %f %f"
4431 (fun pageno c x0 y0 x1 y1 ->
4432 let color = (0.0, 0.0, 1.0 /. float c, 0.5) in
4433 rectx "rect" pageno color x0 y0 x1 y1;
4435 | "pgoto", args ->
4436 scan args "%u %f %f"
4437 (fun pageno x y ->
4438 let optopaque =
4439 match getopaque pageno with
4440 | exception Not_found -> Opaque.of_string E.s
4441 | opaque -> opaque
4443 pgoto optopaque pageno x y;
4444 let rec fixx = function
4445 | [] -> ()
4446 | l :: rest ->
4447 if l.pageno = pageno
4448 then gotoxy (!S.x - l.pagedispx) !S.y
4449 else fixx rest
4451 let layout =
4452 let mult =
4453 match conf.columns with
4454 | Csingle _ | Csplit _ -> 1
4455 | Cmulti ((n, _, _), _) -> n
4457 layout 0 !S.y (!S.winw * mult) !S.winh
4459 fixx layout
4461 | "activatewin", "" -> Wsi.activatewin ()
4462 | "quit", "" -> raise Quit
4463 | "keys", keys ->
4464 begin try
4465 let l = Config.keys_of_string keys in
4466 List.iter (fun (k, m) -> keyboard k m) l
4467 with exn -> adderrfmt "error processing keys" "`%S': %s\n"
4468 cmds @@ exntos exn
4470 | _ ->
4471 adderrfmt "remote command"
4472 "error processing remote command: %S\n" cmds
4474 let remote =
4475 let scratch = Bytes.create 80 in
4476 let buf = Buffer.create 80 in
4477 fun fd ->
4478 match tempfailureretry (Unix.read fd scratch 0) 80 with
4479 | exception Unix.Unix_error (Unix.EAGAIN, _, _) -> None
4480 | 0 ->
4481 Unix.close fd;
4482 if Buffer.length buf > 0
4483 then (
4484 let s = Buffer.contents buf in
4485 Buffer.clear buf;
4486 ract s;
4488 None
4489 | n ->
4490 let rec eat ppos =
4491 let nlpos =
4492 match Bytes.index_from scratch ppos '\n' with
4493 | exception Not_found -> -1
4494 | pos -> if pos >= n then -1 else pos
4496 if nlpos >= 0
4497 then (
4498 Buffer.add_subbytes buf scratch ppos (nlpos-ppos);
4499 let s = Buffer.contents buf in
4500 Buffer.clear buf;
4501 ract s;
4502 eat (nlpos+1);
4504 else (
4505 Buffer.add_subbytes buf scratch ppos (n-ppos);
4506 Some fd
4508 in eat 0
4510 let remoteopen path =
4511 try Some (Unix.openfile path [Unix.O_NONBLOCK; Unix.O_RDONLY] 0o0)
4512 with exn ->
4513 adderrfmt "remoteopen" "error opening %S: %s" path @@ exntos exn;
4514 None
4516 let () =
4517 vlogf := (fun s -> if conf.verbose then print_endline s else ignore s);
4518 S.redirstderr := not @@ Unix.isatty Unix.stderr;
4519 let gc = ref false in
4520 let rcmdpath = ref E.s in
4521 let dcfpath = ref E.s in
4522 let pageno = ref None in
4523 let openlast = ref false in
4524 let doreap = ref false in
4525 let csspath = ref None in
4526 S.selfexec := Sys.executable_name;
4527 let spec =
4528 [("-p", Arg.Set_string S.password, "<password> Set password");
4529 ("-f", Arg.String
4530 (fun s ->
4531 S.fontpath := s;
4532 S.selfexec := !S.selfexec ^ " -f " ^ Filename.quote s;
4533 ), "<path> Set path to the user interface font");
4534 ("-c", Arg.String
4535 (fun s ->
4536 S.selfexec := !S.selfexec ^ " -c " ^ Filename.quote s;
4537 S.confpath := s), "<path> Set path to the configuration file");
4538 ("-last", Arg.Set openlast, " Open last document");
4539 ("-page", Arg.Int (fun pageno1 -> pageno := Some (pageno1-1)),
4540 "<page-number> Jump to page");
4541 ("-dest", Arg.Set_string S.nameddest, "<dest-name> Set named destination");
4542 ("-remote", Arg.Set_string rcmdpath, "<path> Set path to the remote fifo");
4543 ("-gc", Arg.Set gc, " Collect garbage");
4544 ("-v",
4545 Arg.Unit (fun () ->
4546 Printf.printf "%s\nconfiguration file: %s\n" (Help.version ())
4547 Config.defconfpath;
4548 exit 0), " Print version and exit");
4549 ("-css", Arg.String (fun s -> csspath := Some s),
4550 "<path> Set path to the style sheet to use with EPUB/HTML");
4551 ("-origin", Arg.Set_string S.origin, "<origin> <undocumented>");
4552 ("-no-title", Arg.Set S.ignoredoctitlte, " Ignore document title");
4553 ("-dcf", Arg.Set_string dcfpath, "<path> <undocumented>");
4554 ("-flip-stderr-redirection",
4555 Arg.Unit (fun () -> S.redirstderr := not !S.redirstderr),
4556 " <undocumented>");
4559 Arg.parse (Arg.align spec) (fun s -> S.path := s)
4560 ("Usage: " ^ Sys.argv.(0) ^ " [options] some.pdf\nOptions:");
4562 let histmode = emptystr !S.path && not !openlast in
4564 if !gc
4565 then (
4566 Config.gc ();
4567 if histmode then exit 0;
4570 if not (Config.load !openlast)
4571 then dolog "failed to load configuration";
4573 if nonemptystr !dcfpath
4574 then conf.dcf <- !dcfpath;
4576 begin match !pageno with
4577 | Some pageno -> S.anchor := (pageno, 0.0, 0.0)
4578 | None -> ()
4579 end;
4581 fillhelp ();
4582 let mu =
4583 object (self)
4584 val mutable m_clicks = 0
4585 val mutable m_click_x = 0
4586 val mutable m_click_y = 0
4587 val mutable m_lastclicktime = infinity
4589 method private cleanup =
4590 S.roamf := noroamf;
4591 Hashtbl.iter (fun _ opaque -> Ffi.clearmark opaque) S.pagemap
4592 method expose = Glutils.postRedisplay "expose"
4593 method visible v =
4594 let name =
4595 match v with
4596 | Wsi.Unobscured -> "unobscured"
4597 | Wsi.PartiallyObscured -> "partiallyobscured"
4598 | Wsi.FullyObscured -> "fullyobscured"
4600 vlog "visibility change %s" name
4601 method display = display ()
4602 method map mapped = vlog "mapped %b" mapped
4603 method reshape w h =
4604 self#cleanup;
4605 reshape w h
4606 method mouse b d x y m =
4607 (* http://blogs.msdn.com/b/oldnewthing/archive/2004/10/18/243925.aspx *)
4608 m_click_x <- x;
4609 setuioh @@
4610 if d && canselect ()
4611 then (
4612 m_click_y <- y;
4613 if b = 1
4614 then (
4615 let t = now () in
4616 if abs x - m_click_x > 10
4617 || abs y - m_click_y > 10
4618 || abs_float (t -. m_lastclicktime) > 0.3
4619 then m_clicks <- 0;
4620 m_clicks <- m_clicks + 1;
4621 m_lastclicktime <- t;
4622 if m_clicks = 1
4623 then (
4624 self#cleanup;
4625 Glutils.postRedisplay "cleanup";
4626 !S.uioh#button b d x y m
4628 else !S.uioh#multiclick m_clicks x y m
4630 else (
4631 self#cleanup;
4632 m_clicks <- 0;
4633 m_lastclicktime <- infinity;
4634 !S.uioh#button b d x y m
4637 else !S.uioh#button b d x y m
4638 method motion x y =
4639 S.mpos := (x, y);
4640 !S.uioh#motion x y |> setuioh
4641 method pmotion x y =
4642 S.mpos := (x, y);
4643 !S.uioh#pmotion x y |> setuioh
4644 method key k m =
4645 vlog "k=%#x m=%#x" k m;
4646 let mascm = m land (
4647 Wsi.altmask + Wsi.shiftmask + Wsi.ctrlmask + Wsi.metamask
4648 ) in
4649 let keyboard k m =
4650 let x = !S.x and y = !S.y in
4651 keyboard k m;
4652 if x != !S.x || y != !S.y then self#cleanup
4654 match !S.keystate with
4655 | KSnone ->
4656 let km = k, mascm in
4657 begin
4658 match
4659 let modehash = !S.uioh#modehash in
4660 try Hashtbl.find modehash km
4661 with Not_found ->
4662 try Hashtbl.find (findkeyhash conf "global") km
4663 with Not_found -> KMinsrt (k, m)
4664 with
4665 | KMinsrt (k, m) -> keyboard k m
4666 | KMinsrl l -> List.iter (fun (k, m) -> keyboard k m) l
4667 | KMmulti (l, r) -> S.keystate := KSinto (l, r)
4669 | KSinto ((k', m') :: [], insrt) when k'=k && m' land mascm = m' ->
4670 List.iter (fun (k, m) -> keyboard k m) insrt;
4671 S.keystate := KSnone
4672 | KSinto ((k', m') :: keys, insrt) when k'=k && m' land mascm = m' ->
4673 S.keystate := KSinto (keys, insrt)
4674 | KSinto _ -> S.keystate := KSnone
4675 method enter x y =
4676 S.mpos := (x, y);
4677 !S.uioh#pmotion x y |> setuioh
4678 method leave = S.mpos := (-1, -1)
4679 method winstate wsl = S.winstate := wsl
4680 method quit : 'a. 'a = raise Quit
4681 method scroll dx dy =
4682 !S.uioh#scroll dx dy |> setuioh
4683 method zoom z x y = !S.uioh#zoom z x y
4684 method opendoc path =
4685 S.mode := View;
4686 setuioh uioh;
4687 Glutils.postRedisplay "opendoc";
4688 opendoc path !S.password
4691 let wsfd, winw, winh = Wsi.init mu conf.cwinw conf.cwinh in
4692 S.wsfd := wsfd;
4694 let cs, ss =
4695 match Unix.socketpair Unix.PF_UNIX Unix.SOCK_STREAM 0 with
4696 | exception exn ->
4697 dolog "socketpair failed: %s" @@ exntos exn;
4698 exit 1
4699 | (r, w) ->
4700 Unix.set_close_on_exec r;
4701 Unix.set_close_on_exec w;
4702 r, w
4705 begin match !csspath with
4706 | None -> ()
4707 | Some "" -> conf.css <- E.s
4708 | Some path ->
4709 let css = filecontents path in
4710 let l = String.length css in
4711 conf.css <-
4712 if l > 1 && substratis css (l-2) "\r\n"
4713 then String.sub css 0 (l-2)
4714 else (if l > 0 && css.[l-1] = '\n' then String.sub css 0 (l-1) else css)
4715 end;
4716 S.stderr := Ffi.init cs (
4717 conf.angle, conf.fitmodel, (conf.trimmargins, conf.trimfuzz),
4718 conf.texcount, conf.sliceheight, conf.mustoresize,
4719 conf.colorspace, !S.fontpath, !S.redirstderr
4721 List.iter GlArray.enable [`texture_coord; `vertex];
4722 GlTex.env (`color conf.texturecolor);
4723 S.ss := ss;
4724 reshape ~firsttime:true winw winh;
4725 setuioh uioh;
4726 if histmode
4727 then (Wsi.settitle "previously visited - llpp"; enterhistmode ())
4728 else opendoc !S.path !S.password;
4729 display ();
4730 Wsi.mapwin ();
4731 Wsi.setcursor Wsi.CURSOR_INHERIT;
4732 Sys.set_signal Sys.sighup (Sys.Signal_handle (fun _ -> reload ()));
4734 let rec reap () =
4735 match Unix.waitpid [Unix.WNOHANG] ~-1 with
4736 | exception (Unix.Unix_error (Unix.ECHILD, _, _)) -> ()
4737 | exception exn -> dolog "Unix.waitpid: %s" @@ exntos exn
4738 | 0, _ -> ()
4739 | _pid, _status -> reap ()
4741 Sys.set_signal Sys.sigchld (Sys.Signal_handle (fun _ -> doreap := true));
4743 let optrfd =
4744 ref (if nonemptystr !rcmdpath then remoteopen !rcmdpath else None)
4746 dologf := (adderrfmt "stderr" "%s\n");
4748 let fdl =
4749 let l = [!S.ss; !S.wsfd] in if !S.redirstderr then !S.stderr :: l else l
4751 let rec loop deadline =
4752 if !doreap
4753 then (
4754 doreap := false;
4755 reap ()
4757 let r =
4758 match !optrfd with
4759 | None -> fdl
4760 | Some fd -> fd :: fdl
4762 if !Glutils.redisplay
4763 then (
4764 Glutils.redisplay := false;
4765 display ();
4767 let timeout =
4768 let now = now () in
4769 if deadline > now
4770 then (
4771 if deadline = infinity
4772 then ~-.1.0
4773 else max 0.0 (deadline -. now)
4775 else 0.0
4777 let r, _, _ =
4778 try Unix.select r [] [] timeout
4779 with Unix.Unix_error (Unix.EINTR, _, _) -> [], [], []
4781 begin match r with
4782 | [] ->
4783 let newdeadline =
4784 match !S.autoscroll with
4785 | Some step when step != 0 ->
4786 let y = !S.y + step in
4787 let fy = if conf.maxhfit then !S.winh else 0 in
4788 let y =
4789 if y < 0
4790 then !S.maxy - fy
4791 else
4792 if y >= !S.maxy - fy
4793 then 0
4794 else y
4796 gotoxy !S.x y;
4797 deadline +. 0.01
4798 | _ -> infinity
4800 loop newdeadline
4802 | l ->
4803 let rec checkfds = function
4804 | [] -> ()
4805 | fd :: rest when fd = !S.ss ->
4806 let cmd = Ffi.rcmd !S.ss in
4807 act cmd;
4808 checkfds rest
4810 | fd :: rest when fd = !S.wsfd ->
4811 Wsi.readresp fd;
4812 checkfds rest
4814 | fd :: rest when fd = !S.stderr ->
4815 let b = Bytes.create 80 in
4816 begin match Unix.read fd b 0 80 with
4817 | exception Unix.Unix_error (Unix.EINTR, _, _) -> ()
4818 | exception exn -> adderrmsg "Unix.read exn" @@ exntos exn
4819 | 0 -> ()
4820 | n -> adderrmsg "stderr" @@ Bytes.sub_string b 0 n
4821 end;
4822 checkfds rest
4824 | fd :: rest when Some fd = !optrfd ->
4825 begin match remote fd with
4826 | None -> optrfd := remoteopen !rcmdpath;
4827 | opt -> optrfd := opt
4828 end;
4829 checkfds rest
4831 | _ :: rest ->
4832 adderrmsg "mainloop" "select returned unknown descriptor";
4833 checkfds rest
4835 checkfds l;
4836 let newdeadline =
4837 match !S.autoscroll with
4838 | Some step when step != 0 ->
4839 if deadline = infinity
4840 then now () +. 0.01
4841 else deadline
4842 | _ -> infinity
4844 loop newdeadline
4845 end;
4847 match loop infinity with
4848 | exception Quit ->
4849 (match Buffer.length S.errmsgs with
4850 | 0 -> ()
4851 | n ->
4852 match Unix.write Unix.stdout (Buffer.to_bytes S.errmsgs) 0 n with
4853 | exception _ | _ -> ());
4854 Config.save leavebirdseye;
4855 if Ffi.hasunsavedchanges ()
4856 then save ()
4857 | _ -> error "umpossible - infinity reached"