Change shuffstr a bit and show it verbatim too
[llpp.git] / main.ml
blobc60a68f4c2ed530bd73dde34bb7ee90d5dd95554
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 clamp incr = bound (!S.y + incr) 0 @@ maxy ()
23 let scalecolor c = let c = c *. conf.colorscale in (c, c, c)
24 let panbound x = bound x (- !S.w) !S.winw
25 let pagevisible layout n = List.exists (fun l -> l.pageno = n) layout
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 pipef ~closew:false "pipesel"
128 (fun w ->
129 Ffi.copysel w opaque;
130 Glutils.postRedisplay "pipesel"
131 ) cmd
133 let paxunder x y =
134 let g opaque l px py =
135 if Ffi.markunder opaque px py conf.paxmark
136 then
137 Some (fun () ->
138 match getopaque l.pageno with
139 | exception Not_found -> ()
140 | opaque -> pipesel opaque conf.paxcmd
142 else None
144 Glutils.postRedisplay "paxunder";
145 if conf.paxmark = Mark_page
146 then
147 List.iter (fun l ->
148 match getopaque l.pageno with
149 | exception Not_found -> ()
150 | opaque -> Ffi.clearmark opaque) !S.layout;
151 S.roamf := onppundermouse g x y (fun () -> impmsg "whoopsie daisy")
153 let undertext = function
154 | Unone -> "none"
155 | Ulinkuri s -> s
156 | Utext s -> "font: " ^ s
157 | Utextannot (opaque, slinkindex) ->
158 "text annotation: " ^ Ffi.gettextannot opaque slinkindex
159 | Ufileannot (opaque, slinkindex) ->
160 "file annotation: " ^ Ffi.getfileannot opaque slinkindex
162 let updateunder x y =
163 match getunder x y with
164 | Unone -> Wsi.setcursor Wsi.CURSOR_INHERIT
165 | Ulinkuri uri ->
166 if conf.underinfo then showtext 'u' ("ri: " ^ uri);
167 Wsi.setcursor Wsi.CURSOR_INFO
168 | Utext s ->
169 if conf.underinfo then showtext 'f' ("ont: " ^ s);
170 Wsi.setcursor Wsi.CURSOR_TEXT
171 | Utextannot _ ->
172 if conf.underinfo then showtext 't' "ext annotation";
173 Wsi.setcursor Wsi.CURSOR_INFO
174 | Ufileannot _ ->
175 if conf.underinfo then showtext 'f' "ile annotation";
176 Wsi.setcursor Wsi.CURSOR_INFO
178 let showlinktype under =
179 if conf.underinfo && under != Unone
180 then showtext ' ' @@ undertext under
182 let intentry_with_suffix text key =
183 let text =
184 match [@warning "-fragile-match"] key with
185 | Keys.Ascii ('0'..'9' as c) -> addchar text c
186 | Keys.Ascii ('k' | 'm' | 'g' | 'K' | 'M' | 'G' as c) ->
187 addchar text @@ Char.lowercase_ascii c
188 | _ ->
189 S.text := "invalid key";
190 text
192 TEcont text
194 let wcmd cmd fmt =
195 let b = Buffer.create 16 in
196 Printf.kbprintf
197 (fun b ->
198 Buffer.add_char b cmd;
199 let b = Buffer.to_bytes b in
200 Ffi.wcmd !S.ss b @@ Bytes.length b
201 ) b fmt
203 let wcmd1 cmd opaque =
204 let s = Opaque.to_string opaque in
205 let l = String.length s in
206 let b = Bytes.create (l+1) in
207 Bytes.set b l cmd;
208 Bytes.blit_string s 0 b 0 l;
209 Ffi.wcmd !S.ss b @@ l + 1
211 let layoutN ((columns, coverA, coverB), b) x y sw sh =
212 let rec fold accu n =
213 if n = Array.length b
214 then accu
215 else
216 let pdimno, dx, vy, (_, w, h, xoff) = b.(n) in
217 if (vy - y) > sh
218 && (n = coverA - 1
219 || n = !S.pagecount - coverB
220 || (n - coverA) mod columns = columns - 1)
221 then accu
222 else
223 let accu =
224 if vy + h > y
225 then
226 let pagey = max 0 (y - vy) in
227 let pagedispy = if pagey > 0 then 0 else vy - y in
228 let pagedispx, pagex =
229 let pdx =
230 if n = coverA - 1 || n = !S.pagecount - coverB
231 then x + (sw - w) / 2
232 else dx + xoff + x
234 if pdx < 0
235 then 0, -pdx
236 else pdx, 0
238 let pagevw =
239 let vw = sw - pagedispx in
240 let pw = w - pagex in
241 min vw pw
243 let pagevh = min (h - pagey) (sh - pagedispy) in
244 if pagevw > 0 && pagevh > 0
245 then
246 { pageno = n
247 ; pagecol = 0 ; pagedimno = pdimno ; pagew = w ; pageh = h
248 ; pagex ; pagey ; pagevw ; pagevh ; pagedispx ; pagedispy
249 } :: accu
250 else accu
251 else accu
253 fold accu (n+1)
255 if Array.length b = 0
256 then []
257 else List.rev (fold [] (page_of_y y))
259 let layoutS (columns, b) x y sw sh =
260 let rec fold accu n =
261 if n = Array.length b
262 then accu
263 else
264 let pdimno, px, vy, (_, pagew, pageh, xoff) = b.(n) in
265 if (vy - y) > sh
266 then accu
267 else
268 let accu =
269 if vy + pageh > y
270 then
271 let x = xoff + x in
272 let pagey = max 0 (y - vy) in
273 let pagedispy = if pagey > 0 then 0 else vy - y in
274 let pagedispx, pagex =
275 if px = 0
276 then (
277 if x < 0
278 then 0, -x
279 else x, 0
281 else (
282 let px = px - x in
283 if px < 0
284 then -px, 0
285 else 0, px
288 let pagecolw = pagew/columns in
289 let pagedispx =
290 if pagecolw < sw
291 then pagedispx + ((sw - pagecolw) / 2)
292 else pagedispx
294 let pagevw =
295 let vw = sw - pagedispx in
296 let pw = pagew - pagex in
297 min vw pw
299 let pagevw = min pagevw pagecolw in
300 let pagevh = min (pageh - pagey) (sh - pagedispy) in
301 if pagevw > 0 && pagevh > 0
302 then
303 { pageno = n/columns
304 ; pagedimno = pdimno
305 ; pagecol = n mod columns
306 ; pagew ; pageh ; pagex ; pagey ; pagedispx ; pagedispy
307 ; pagevw ; pagevh
308 } :: accu
309 else accu
310 else accu
312 fold accu (n+1)
314 List.rev (fold [] 0)
316 let layout x y sw sh =
317 if U.nogeomcmds !S.geomcmds
318 then
319 match conf.columns with
320 | Csingle b -> layoutN ((1, 0, 0), b) x y sw sh
321 | Cmulti c -> layoutN c x y sw sh
322 | Csplit s -> layoutS s x y sw sh
323 else []
325 let itertiles l f =
326 let tilex = l.pagex mod conf.tilew in
327 let tiley = l.pagey mod conf.tileh in
329 let col = l.pagex / conf.tilew in
330 let row = l.pagey / conf.tileh in
332 let rec rowloop row y0 dispy h =
333 if h != 0
334 then
335 let dh = conf.tileh - y0 in
336 let dh = min h dh in
337 let rec colloop col x0 dispx w =
338 if w != 0
339 then
340 let dw = conf.tilew - x0 in
341 let dw = min w dw in
342 f col row dispx dispy x0 y0 dw dh;
343 colloop (col+1) 0 (dispx+dw) (w-dw)
345 colloop col tilex l.pagedispx l.pagevw;
346 rowloop (row+1) 0 (dispy+dh) (h-dh)
348 if l.pagevw > 0 && l.pagevh > 0
349 then rowloop row tiley l.pagedispy l.pagevh
351 let gettileopaque l col row =
352 let key = l.pageno, !S.gen, conf.colorspace,
353 conf.angle, l.pagew, l.pageh, col, row in
354 Hashtbl.find_opt S.tilemap key
356 let puttileopaque l col row gen colorspace angle opaque size elapsed =
357 let key = l.pageno, gen, colorspace, angle, l.pagew, l.pageh, col, row in
358 Hashtbl.add S.tilemap key (opaque, size, elapsed)
360 let drawtiles l color =
361 let texe e = if conf.invert then GlTex.env (`mode e) in
362 GlDraw.color color;
363 Ffi.begintiles ();
364 let f col row x y tilex tiley w h =
365 match gettileopaque l col row with
366 | Some (opaque, _, t) ->
367 let params = x, y, w, h, tilex, tiley in
368 texe `blend;
369 Ffi.drawtile params opaque;
370 texe `modulate;
371 if conf.debug
372 then (
373 Ffi.endtiles ();
374 let s = Printf.sprintf "%d[%d,%d] %f sec" l.pageno col row t in
375 let w = Ffi.measurestr fstate.fontsize s in
376 GlDraw.color (0.0, 0.0, 0.0);
377 Glutils.filledrect
378 (float (x-2))
379 (float (y-2))
380 (float (x+2) +. w)
381 (float (y + fstate.fontsize + 2));
382 GlDraw.color color;
383 Glutils.drawstring fstate.fontsize x (y + fstate.fontsize - 1) s;
384 Ffi.begintiles ();
387 | None ->
388 Ffi.endtiles ();
389 let w = let lw = !S.winw - x in min lw w
390 and h = let lh = !S.winh - y in min lh h in
391 texe `blend;
392 GlDraw.color (0.8, 0.8, 0.8);
393 Glutils.filledrect (float x) (float y) (float (x+w)) (float (y+h));
394 texe `modulate;
395 if w > 128 && h > fstate.fontsize + 10
396 then (
397 let c = if conf.invert then 1.0 else 0.0 in
398 GlDraw.color (c, c, c);
399 let c, r =
400 if conf.verbose
401 then (col*conf.tilew, row*conf.tileh)
402 else col, row
404 Glutils.drawstringf fstate.fontsize x y
405 "Loading %d [%d,%d]" l.pageno c r;
407 GlDraw.color color;
408 Ffi.begintiles ();
410 itertiles l f;
411 Ffi.endtiles ()
413 let tilevisible1 l x y =
414 let ax0 = l.pagex
415 and ax1 = l.pagex + l.pagevw
416 and ay0 = l.pagey
417 and ay1 = l.pagey + l.pagevh in
419 let bx0 = x
420 and by0 = y in
421 let bx1 = min (bx0 + conf.tilew) l.pagew
422 and by1 = min (by0 + conf.tileh) l.pageh in
424 let rx0 = max ax0 bx0
425 and ry0 = max ay0 by0
426 and rx1 = min ax1 bx1
427 and ry1 = min ay1 by1 in
429 let nonemptyintersection = rx1 > rx0 && ry1 > ry0 in
430 nonemptyintersection
432 let tilevisible layout n x y =
433 let rec findpageinlayout m = function
434 | l :: rest when l.pageno = n ->
435 tilevisible1 l x y || (
436 match conf.columns with
437 | Csplit (c, _) when c > m -> findpageinlayout (m+1) rest
438 | Csplit _ | Csingle _ | Cmulti _ -> false
440 | _ :: rest -> findpageinlayout 0 rest
441 | [] -> false
443 findpageinlayout 0 layout
445 let tileready l x y =
446 tilevisible1 l x y &&
447 gettileopaque l (x/conf.tilew) (y/conf.tileh) != None
449 let tilepage n p layout =
450 let rec loop = function
451 | l :: rest ->
452 if l.pageno = n
453 then
454 let f col row _ _ _ _ _ _ =
455 if !S.currently = Idle
456 then
457 match gettileopaque l col row with
458 | Some _ -> ()
459 | None ->
460 let x = col*conf.tilew
461 and y = row*conf.tileh in
462 let w =
463 let w = l.pagew - x in
464 min w conf.tilew
466 let h =
467 let h = l.pageh - y in
468 min h conf.tileh
470 wcmd U.tile "%s %d %d %d %d" (Opaque.to_string p) x y w h;
471 S.currently :=
472 Tiling (
473 l, p, conf.colorspace, conf.angle,
474 !S.gen, col, row, conf.tilew, conf.tileh
477 itertiles l f;
478 else loop rest
480 | [] -> ()
482 if U.nogeomcmds !S.geomcmds
483 then loop layout
485 let preloadlayout x y sw sh =
486 let y = if y < sh then 0 else y - sh in
487 let x = min 0 (x + sw) in
488 let h = sh*3 in
489 let w = sw*3 in
490 layout x y w h
492 let load pages =
493 let rec loop pages =
494 if !S.currently = Idle
495 then
496 match pages with
497 | l :: rest ->
498 begin match getopaque l.pageno with
499 | exception Not_found ->
500 wcmd U.page "%d %d" l.pageno l.pagedimno;
501 S.currently := Loading (l, !S.gen);
502 | opaque ->
503 tilepage l.pageno opaque pages;
504 loop rest
506 | _ -> ()
508 if U.nogeomcmds !S.geomcmds
509 then loop pages
511 let preload pages =
512 load pages;
513 if conf.preload && !S.currently = Idle
514 then load (preloadlayout !S.x !S.y !S.winw !S.winh)
516 let layoutready layout =
517 let exception E in
518 let rec fold ls =
519 match ls with
520 | [] -> true
521 | l :: rest ->
522 let foo col row _ _ _ _ _ _ =
523 match gettileopaque l col row with
524 | Some _ -> ()
525 | None -> raise E
527 match itertiles l foo with
528 | () -> fold rest
529 | exception E -> false
531 fold layout
533 let gotoxy x y =
534 let y = bound y 0 !S.maxy in
535 let y, layout =
536 let layout = layout x y !S.winw !S.winh in
537 Glutils.postRedisplay "gotoxy ready";
538 y, layout
540 S.x := x;
541 S.y := y;
542 S.layout := layout;
543 begin match !S.mode with
544 | LinkNav ln ->
545 begin match ln with
546 | Ltexact (pageno, linkno) ->
547 let rec loop = function
548 | [] ->
549 S.lnava := Some (pageno, linkno);
550 S.mode := LinkNav (Ltgendir 0)
551 | l :: _ when l.pageno = pageno ->
552 begin match getopaque pageno with
553 | exception Not_found ->
554 S.mode := LinkNav (Ltnotready (pageno, 0))
555 | opaque ->
556 let x0, y0, x1, y1 = Ffi.getlinkrect opaque linkno in
557 if not (x0 >= l.pagex && x1 <= l.pagex + l.pagevw
558 && y0 >= l.pagey && y1 <= l.pagey + l.pagevh)
559 then S.mode := LinkNav (Ltgendir 0)
561 | _ :: rest -> loop rest
563 loop layout
564 | Ltnotready _ | Ltgendir _ -> ()
566 | Birdseye _ | Textentry _ | View -> ()
567 end;
568 begin match !S.mode with
569 | Birdseye (conf, leftx, pageno, hooverpageno, anchor) ->
570 if not (U.pagevisible layout pageno)
571 then (
572 match !S.layout with
573 | [] -> ()
574 | l :: _ ->
575 S.mode := Birdseye (conf, leftx, l.pageno, hooverpageno, anchor)
577 | LinkNav lt ->
578 begin match lt with
579 | Ltnotready (_, dir)
580 | Ltgendir dir ->
581 let linknav =
582 let rec loop = function
583 | [] -> lt
584 | l :: rest ->
585 match getopaque l.pageno with
586 | exception Not_found -> Ltnotready (l.pageno, dir)
587 | opaque ->
588 let link =
589 let ld =
590 if dir = 0
591 then LDfirstvisible (l.pagex, l.pagey, dir)
592 else if dir > 0 then LDfirst else LDlast
594 Ffi.findlink opaque ld
596 match link with
597 | Lnotfound -> loop rest
598 | Lfound n ->
599 showlinktype (Ffi.getlink opaque n);
600 Ltexact (l.pageno, n)
602 loop !S.layout
604 S.mode := LinkNav linknav
605 | Ltexact _ -> ()
607 | Textentry _ | View -> ()
608 end;
609 preload layout;
610 if conf.updatecurs
611 then (
612 let mx, my = !S.mpos in
613 updateunder mx my;
616 let conttiling pageno opaque =
617 tilepage pageno opaque
618 (if conf.preload
619 then preloadlayout !S.x !S.y !S.winw !S.winh
620 else !S.layout)
622 let gotoxy x y =
623 if not conf.verbose then S.text := E.s;
624 gotoxy x y
626 let getanchory (n, top, dtop) =
627 let y, h = getpageyh n in
628 if conf.presentation
629 then
630 let ips = calcips h in
631 y + truncate (top*.float h -. dtop*.float ips) + ips;
632 else y + truncate (top*.float h -. dtop*.float conf.interpagespace)
634 let addnav () = S.nav := { past = getanchor () :: !S.nav.past; future = []; }
636 let gotopage n top =
637 let y, h = getpageyh n in
638 let y = y + (truncate (top *. float h)) in
639 gotoxy !S.x y
641 let gotopage1 n top =
642 let y = getpagey n in
643 let y = y + top in
644 gotoxy !S.x y
646 let invalidate s f =
647 Glutils.redisplay := false;
648 S.layout := [];
649 S.pdims := [];
650 S.rects := [];
651 S.rects1 := [];
652 match !S.geomcmds with
653 | ps, [] when emptystr ps ->
654 f ();
655 S.geomcmds := s, [];
656 | ps, [] -> S.geomcmds := ps, [s, f];
657 | ps, (s', _) :: rest when s' = s -> S.geomcmds := ps, ((s, f) :: rest);
658 | ps, cmds -> S.geomcmds := ps, ((s, f) :: cmds)
660 let flushpages () =
661 Hashtbl.iter (fun _ opaque -> wcmd1 U.freepage opaque) S.pagemap;
662 Hashtbl.clear S.pagemap
664 let flushtiles () =
665 if not (Queue.is_empty S.tilelru)
666 then (
667 Queue.iter (fun (k, p, s) ->
668 wcmd1 U.freetile p;
669 S.memused := !S.memused - s;
670 Hashtbl.remove S.tilemap k;
671 ) S.tilelru;
672 !S.uioh#infochanged Memused;
673 Queue.clear S.tilelru;
675 load !S.layout
677 let stateh h =
678 let h = truncate (float h*.conf.zoom) in
679 let d = conf.interpagespace lsl (if conf.presentation then 1 else 0) in
680 h - d
682 let fillhelp () =
683 S.help :=
684 let sl = keystostrlist conf in
685 let rec loop accu =
686 function | [] -> accu
687 | s :: rest -> loop ((s, 0, None) :: accu) rest
688 in Help.makehelp conf.urilauncher
689 @ (("", 0, None) :: loop [] sl) |> Array.of_list
691 let titlify path =
692 if emptystr path
693 then path
694 else
695 (if emptystr !S.origin then path else !S.origin)
696 |> Filename.basename |> Ffi.mbtoutf8
698 let settitle title =
699 conf.title <- title;
700 if not !S.ignoredoctitlte
701 then Wsi.settitle @@ title ^ " - llpp"
703 let opendoc path password =
704 S.path := path;
705 S.password := password;
706 S.gen := !S.gen + 1;
707 S.docinfo := [];
708 S.outlines := [||];
710 flushpages ();
711 Ffi.setaalevel conf.aalevel;
712 Ffi.setpapercolor conf.papercolor;
713 Ffi.setdcf conf.dcf;
715 settitle @@ titlify path;
716 wcmd U.dopen "%d %d %d %d %s\000%s\000%s\000"
717 (btod conf.usedoccss) conf.rlw conf.rlh conf.rlem path password conf.css;
718 invalidate "reqlayout"
719 (fun () ->
720 wcmd U.reqlayout " %d %d %d %s\000"
721 conf.angle (FMTE.to_int conf.fitmodel)
722 (stateh !S.winh) !S.nameddest
724 fillhelp ()
726 let reload () =
727 S.anchor := getanchor ();
728 S.reload := Some (!S.x, !S.y, now ());
729 opendoc !S.path !S.password
731 let docolumns columns =
732 match columns with
733 | Csingle _ ->
734 let a = Array.make !S.pagecount (-1, -1, -1, (-1, -1, -1, -1)) in
735 let rec loop pageno pdimno pdim y ph pdims =
736 if pageno != !S.pagecount
737 then
738 let pdimno, ((_, w, h, xoff) as pdim), pdims =
739 match pdims with
740 | ((pageno', _, _, _) as pdim) :: rest when pageno' = pageno ->
741 pdimno+1, pdim, rest
742 | _ ->
743 pdimno, pdim, pdims
745 let x = max 0 (((!S.winw - w) / 2) - xoff) in
746 let y =
747 y + (if conf.presentation
748 then (if pageno = 0 then calcips h else calcips ph + calcips h)
749 else (if pageno = 0 then 0 else conf.interpagespace))
751 a.(pageno) <- (pdimno, x, y, pdim);
752 loop (pageno+1) pdimno pdim (y + h) h pdims
754 loop 0 ~-1 (-1,-1,-1,-1) 0 0 !S.pdims;
755 conf.columns <- Csingle a;
757 | Cmulti ((columns, coverA, coverB), _) ->
758 let a = Array.make !S.pagecount (-1, -1, -1, (-1, -1, -1, -1)) in
759 let rec loop pageno pdimno pdim x y rowh pdims =
760 let rec fixrow m =
761 if m >= pageno
762 then
763 let (pdimno, x, y, ((_, _, h, _) as pdim)) = a.(m) in
764 if h < rowh
765 then a.(m) <- (pdimno, x, y + (rowh - h) / 2, pdim);
766 fixrow (m+1)
768 if pageno = !S.pagecount
769 then fixrow (((pageno - 1) / columns) * columns)
770 else
771 let pdimno, ((_, w, h, xoff) as pdim), pdims =
772 match pdims with
773 | ((pageno', _, _, _) as pdim) :: rest when pageno' = pageno ->
774 pdimno+1, pdim, rest
775 | _ -> pdimno, pdim, pdims
777 let x, y, rowh' =
778 if pageno = coverA - 1 || pageno = !S.pagecount - coverB
779 then (
780 let x = (!S.winw - w) / 2 in
781 let ips =
782 if conf.presentation then calcips h else conf.interpagespace in
783 x, y + ips + rowh, h
785 else (
786 if (pageno - coverA) mod columns = 0
787 then (
788 let x = max 0 (!S.winw - !S.w) / 2 in
789 let y =
790 if conf.presentation
791 then
792 let ips = calcips h in
793 y + (if pageno = 0 then 0 else calcips rowh + ips)
794 else y + (if pageno = 0 then 0 else conf.interpagespace)
796 x, y + rowh, h
798 else x, y, max rowh h
801 let y =
802 if pageno > 1 && (pageno - coverA) mod columns = 0
803 then (
804 let y =
805 if pageno = columns && conf.presentation
806 then (
807 let ips = calcips rowh in
808 for i = 0 to pred columns
810 let (pdimno, x, y, pdim) = a.(i) in
811 a.(i) <- (pdimno, x, y+ips, pdim)
812 done;
813 y+ips;
815 else y
817 fixrow (pageno - columns);
820 else y
822 a.(pageno) <- (pdimno, x, y, pdim);
823 let x = x + w + xoff*2 + conf.interpagespace in
824 loop (pageno+1) pdimno pdim x y rowh' pdims
826 loop 0 ~-1 (-1,-1,-1,-1) 0 0 0 !S.pdims;
827 conf.columns <- Cmulti ((columns, coverA, coverB), a);
829 | Csplit (c, _) ->
830 let a = Array.make (!S.pagecount*c) (-1, -1, -1, (-1, -1, -1, -1)) in
831 let rec loop pageno pdimno pdim y pdims =
832 if pageno != !S.pagecount
833 then
834 let pdimno, ((_, w, h, _) as pdim), pdims =
835 match pdims with
836 | ((pageno', _, _, _) as pdim) :: rest when pageno' = pageno ->
837 pdimno+1, pdim, rest
838 | _ -> pdimno, pdim, pdims
840 let cw = w / c in
841 let rec loop1 n x y =
842 if n = c then y else (
843 a.(pageno*c + n) <- (pdimno, x, y, pdim);
844 loop1 (n+1) (x+cw) (y + h + conf.interpagespace)
847 let y = loop1 0 0 y in
848 loop (pageno+1) pdimno pdim y pdims
850 loop 0 ~-1 (-1,-1,-1,-1) 0 !S.pdims;
851 conf.columns <- Csplit (c, a)
853 let represent () =
854 docolumns conf.columns;
855 S.maxy := calcheight ();
856 if !S.reprf == noreprf
857 then (
858 match !S.mode with
859 | Birdseye (_, _, pageno, _, _) ->
860 let y, h = getpageyh pageno in
861 let top = (!S.winh - h) / 2 in
862 gotoxy !S.x (max 0 (y - top))
863 | Textentry _ | View | LinkNav _ ->
864 let y = getanchory !S.anchor in
865 let y = min y (!S.maxy - !S.winh) in
866 gotoxy !S.x y;
868 else (
869 !S.reprf ();
870 S.reprf := noreprf;
873 let reshape ?(firsttime=false) w h =
874 GlDraw.viewport ~x:0 ~y:0 ~w ~h;
875 if not firsttime && U.nogeomcmds !S.geomcmds
876 then S.anchor := getanchor ();
878 S.winw := w;
879 let w = truncate (float w *. conf.zoom) in
880 let w = max w 2 in
881 S.winh := h;
882 setfontsize fstate.fontsize;
883 GlMat.mode `modelview;
884 GlMat.load_identity ();
886 GlMat.mode `projection;
887 GlMat.load_identity ();
888 GlMat.rotate ~x:1.0 ~angle:180.0 ();
889 GlMat.translate ~x:~-.1.0 ~y:~-.1.0 ();
890 GlMat.scale3 (2.0 /. float !S.winw, 2.0 /. float !S.winh, 1.0);
892 let relx =
893 if conf.zoom <= 1.0
894 then 0.0
895 else float !S.x /. float !S.w
897 invalidate "geometry"
898 (fun () ->
899 S.w := w;
900 if not firsttime
901 then S.x := truncate (relx *. float w);
902 let w =
903 match conf.columns with
904 | Csingle _ -> w
905 | Cmulti ((c, _, _), _) -> (w - (c-1)*conf.interpagespace) / c
906 | Csplit (c, _) -> w * c
908 wcmd U.geometry "%d %d %d" w (stateh h) (FMTE.to_int conf.fitmodel)
911 let gctiles () =
912 let len = Queue.length S.tilelru in
913 let layout = lazy (if conf.preload
914 then preloadlayout !S.x !S.y !S.winw !S.winh
915 else !S.layout) in
916 let rec loop qpos =
917 if !S.memused > conf.memlimit
918 then (
919 if qpos < len
920 then
921 let (k, p, s) as lruitem = Queue.pop S.tilelru in
922 let n, gen, colorspace, angle, pagew, pageh, col, row = k in
923 let (_, pw, ph, _) = getpagedim n in
924 if gen = !S.gen
925 && colorspace = conf.colorspace
926 && angle = conf.angle
927 && pagew = pw
928 && pageh = ph
929 && (
930 let x = col*conf.tilew and y = row*conf.tileh in
931 tilevisible (Lazy.force_val layout) n x y
933 then Queue.push lruitem S.tilelru
934 else (
935 wcmd1 U.freetile p;
936 S.memused := !S.memused - s;
937 !S.uioh#infochanged Memused;
938 Hashtbl.remove S.tilemap k;
940 loop (qpos+1)
943 loop 0
945 let onpagerect pageno f =
946 let b =
947 match conf.columns with
948 | Cmulti (_, b) -> b
949 | Csingle b -> b
950 | Csplit (_, b) -> b
952 if pageno >= 0 && pageno < Array.length b
953 then
954 let (_, _, _, (_, w, h, _)) = b.(pageno) in
955 f w h
957 let gotopagexy1 pageno x y =
958 let _,w1,h1,leftx = getpagedim pageno in
959 let top = y /. (float h1) in
960 let left = x /. (float w1) in
961 let py, w, h = getpageywh pageno in
962 let wh = !S.winh in
963 let x = left *. (float w) in
964 let x = leftx + !S.x + truncate x in
965 let sx =
966 if x < 0 || x >= !S.winw
967 then !S.x - x
968 else !S.x
970 let pdy = truncate (top *. float h) in
971 let y' = py + pdy in
972 let dy = y' - !S.y in
973 let sy =
974 if x != !S.x || not (dy > 0 && dy < wh)
975 then (
976 if conf.presentation
977 then
978 if abs (py - y') > wh
979 then y'
980 else py
981 else y';
983 else !S.y
985 if !S.x != sx || !S.y != sy
986 then gotoxy sx sy
987 else gotoxy !S.x !S.y
989 let gotopagexy pageno x y =
990 match !S.mode with
991 | Birdseye _ -> gotopage pageno 0.0
992 | Textentry _ | View | LinkNav _ -> gotopagexy1 pageno x y
994 let getpassword () =
995 let passcmd = getenvdef "LLPP_ASKPASS" conf.passcmd in
996 if emptystr passcmd
997 then (adderrmsg "askpass" "ask password program not set"; E.s)
998 else getcmdoutput (adderrfmt passcmd "failed to obrain password: %s") passcmd
1000 let pgoto opaque pageno x y =
1001 let pdimno = getpdimno pageno in
1002 let x, y = Ffi.project opaque pageno pdimno x y in
1003 gotopagexy pageno x y
1005 let act cmds =
1006 (* dolog1 "%S" cmds; *)
1007 let spl = splitatchar cmds ' ' in
1008 let scan s fmt f =
1009 try Scanf.sscanf s fmt f
1010 with exn ->
1011 dolog1 "error scanning %S: %s" cmds @@ exntos exn;
1012 exit 1
1014 let addoutline outline =
1015 match !S.currently with
1016 | Outlining outlines -> S.currently := Outlining (outline :: outlines)
1017 | Idle -> S.currently := Outlining [outline]
1018 | Loading _ | Tiling _ ->
1019 dolog1 "invalid outlining state";
1020 logcurrently !S.currently
1022 match spl with
1023 | "clear", "" ->
1024 S.pdims := [];
1025 !S.uioh#infochanged Pdim;
1027 | "clearrects", "" ->
1028 S.rects := !S.rects1;
1029 Glutils.postRedisplay "clearrects";
1031 | "continue", args ->
1032 let n = scan args "%u" (fun n -> n) in
1033 S.pagecount := n;
1034 begin match !S.currently with
1035 | Outlining l ->
1036 S.currently := Idle;
1037 S.outlines := Array.of_list (List.rev l)
1038 | Idle | Loading _ | Tiling _ -> ()
1039 end;
1041 let cur, cmds = !S.geomcmds in
1042 if emptystr cur then error "empty geomcmd";
1044 begin match List.rev cmds with
1045 | [] ->
1046 S.geomcmds := E.s, [];
1047 represent ();
1048 | (s, f) :: rest ->
1049 f ();
1050 S.geomcmds := s, List.rev rest;
1051 end;
1052 Glutils.postRedisplay "continue";
1054 | "vmsg", args ->
1055 if conf.verbose then showtext ' ' args
1057 | "emsg", args ->
1058 Buffer.add_string S.errmsgs args;
1059 Buffer.add_char S.errmsgs '\n';
1060 if not !S.newerrmsgs
1061 then (
1062 S.newerrmsgs := true;
1063 Glutils.postRedisplay "error message";
1066 | "progress", args ->
1067 let progress, text =
1068 scan args "%f %n"
1069 (fun f pos -> f, String.sub args pos (String.length args - pos))
1071 S.text := text;
1072 S.progress := progress;
1073 Glutils.postRedisplay "progress"
1075 | "match", args ->
1076 let pageno, n, x0, y0, x1, y1, x2, y2, x3, y3 =
1077 scan args "%u %d %f %f %f %f %f %f %f %f"
1078 (fun p n x0 y0 x1 y1 x2 y2 x3 y3 ->
1079 (p, n, x0, y0, x1, y1, x2, y2, x3, y3))
1081 if n = 0
1082 then (
1083 let y = (getpagey pageno) + truncate y0 in
1084 let x =
1085 if (!S.x < - truncate x0) || (!S.x > !S.winw - truncate x1)
1086 then !S.winw/2 - truncate (x0 /. 2. +. x1 /. 2.)
1087 else !S.x
1089 addnav ();
1090 gotoxy x y;
1092 let color = (0.0, 0.0, (if n = 0 then 1.0 else 0.5), 0.5) in
1093 S.rects1 :=
1094 (pageno, color, (x0, y0, x1, y1, x2, y2, x3, y3)) :: !S.rects1
1096 | "page", args ->
1097 let pageopaques, t = scan args "%s %f" (fun p t -> p, t) in
1098 let pageopaque = Opaque.of_string pageopaques in
1099 begin match !S.currently with
1100 | Loading (l, gen) ->
1101 vlog "page %d took %f sec" l.pageno t;
1102 Hashtbl.replace S.pagemap (l.pageno, gen) pageopaque;
1103 let preloadedpages =
1104 if conf.preload
1105 then preloadlayout !S.x !S.y !S.winw !S.winh
1106 else !S.layout
1108 let evict () =
1109 let set = List.fold_left (fun s l -> IntSet.add l.pageno s)
1110 IntSet.empty preloadedpages
1112 let evictedpages =
1113 Hashtbl.fold (fun ((pageno, _) as key) opaque accu ->
1114 if not (IntSet.mem pageno set)
1115 then (
1116 wcmd1 U.freepage opaque;
1117 key :: accu
1119 else accu
1120 ) S.pagemap []
1122 List.iter (Hashtbl.remove S.pagemap) evictedpages;
1124 evict ();
1125 S.currently := Idle;
1126 if gen = !S.gen
1127 then (
1128 tilepage l.pageno pageopaque !S.layout;
1129 load !S.layout;
1130 load preloadedpages;
1131 let visible = U.pagevisible !S.layout l.pageno in
1132 if visible
1133 then (
1134 match !S.mode with
1135 | LinkNav (Ltnotready (pageno, dir)) ->
1136 if pageno = l.pageno
1137 then (
1138 let link =
1139 let ld =
1140 if dir = 0
1141 then LDfirstvisible (l.pagex, l.pagey, dir)
1142 else if dir > 0 then LDfirst else LDlast
1144 Ffi.findlink pageopaque ld
1146 match link with
1147 | Lnotfound -> ()
1148 | Lfound n ->
1149 showlinktype (Ffi.getlink pageopaque n);
1150 S.mode := LinkNav (Ltexact (l.pageno, n))
1152 | LinkNav (Ltgendir _)
1153 | LinkNav (Ltexact _)
1154 | View
1155 | Birdseye _
1156 | Textentry _ -> ()
1159 if visible && layoutready !S.layout
1160 then Glutils.postRedisplay "page";
1163 | Idle | Tiling _ | Outlining _ ->
1164 dolog1 "Inconsistent loading state";
1165 logcurrently !S.currently;
1166 exit 1
1169 | "tile" , args ->
1170 let (x, y, opaques, size, t) =
1171 scan args "%u %u %s %u %f" (fun x y p size t -> (x, y, p, size, t))
1173 let opaque = Opaque.of_string opaques in
1174 begin match !S.currently with
1175 | Tiling (l, pageopaque, cs, angle, gen, col, row, tilew, tileh) ->
1176 vlog "tile %d [%d,%d] took %f sec" l.pageno col row t;
1177 if tilew != conf.tilew || tileh != conf.tileh
1178 then (
1179 wcmd1 U.freetile opaque;
1180 S.currently := Idle;
1181 load !S.layout;
1183 else (
1184 puttileopaque l col row gen cs angle opaque size t;
1185 S.memused := !S.memused + size;
1186 !S.uioh#infochanged Memused;
1187 gctiles ();
1188 Queue.push ((l.pageno, gen, cs, angle, l.pagew, l.pageh, col, row),
1189 opaque, size) S.tilelru;
1191 S.currently := Idle;
1192 if gen = !S.gen
1193 && conf.colorspace = cs
1194 && conf.angle = angle
1195 && tilevisible !S.layout l.pageno x y
1196 then conttiling l.pageno pageopaque;
1198 preload !S.layout;
1199 if gen = !S.gen
1200 && conf.colorspace = cs
1201 && conf.angle = angle
1202 && tilevisible !S.layout l.pageno x y
1203 && layoutready !S.layout
1204 then Glutils.postRedisplay "tile nothrottle";
1207 | Idle | Loading _ | Outlining _ ->
1208 dolog1 "Inconsistent tiling state";
1209 logcurrently !S.currently;
1210 exit 1
1213 | "pdim", args ->
1214 let (n, w, h, _) as pdim =
1215 scan args "%u %d %d %d" (fun n x w h -> n, w, h, x)
1217 let pdim =
1218 match conf.fitmodel with
1219 | FitWidth -> pdim
1220 | FitPage | FitProportional ->
1221 match conf.columns with
1222 | Csplit _ -> (n, w, h, 0)
1223 | Csingle _ | Cmulti _ -> pdim
1225 S.pdims := pdim :: !S.pdims;
1226 !S.uioh#infochanged Pdim
1228 | "o", args ->
1229 let (l, n, t, h, pos) =
1230 scan args "%u %u %d %u %n" (fun l n t h pos -> l, n, t, h, pos)
1232 let s = String.sub args pos (String.length args - pos) in
1233 addoutline (s, l, Oanchor (n, float t /. float h, 0.0))
1235 | "ou", args ->
1236 let (l, len, pos) = scan args "%u %u %n" (fun l len pos -> l, len, pos) in
1237 let s = String.sub args pos len in
1238 let pos2 = pos + len + 1 in
1239 let uri = String.sub args pos2 (String.length args - pos2) in
1240 addoutline (s, l, Ouri uri)
1242 | "on", args ->
1243 let (l, pos) = scan args "%u %n" (fun l pos -> l, pos) in
1244 let s = String.sub args pos (String.length args - pos) in
1245 addoutline (s, l, Onone)
1247 | "a", args ->
1248 let (n, l, t) = scan args "%u %d %d" (fun n l t -> n, l, t) in
1249 S.reprf := (fun () -> gotopagexy n (float l) (float t))
1251 | "info", args ->
1252 let s =
1253 match splitatchar args '\t' with
1254 | "Title", "" ->
1255 settitle @@ Filename.basename !S.path;
1257 | "Title", v ->
1258 settitle v;
1259 args
1260 | _, "" -> E.s
1261 | c, v ->
1262 if let len = String.length c in
1263 len > 6 && ((String.sub c (len-4) 4) = "date")
1264 then (
1265 if String.length v >= 7 && v.[0] = 'D' && v.[1] = ':'
1266 then
1267 let b = Buffer.create 10 in
1268 Printf.bprintf b "%s\t" c;
1269 let sub p l c =
1271 Buffer.add_substring b v p l;
1272 Buffer.add_char b c;
1273 with exn -> Buffer.add_string b @@ exntos exn
1275 sub 2 4 '/';
1276 sub 6 2 '/';
1277 sub 8 2 ' ';
1278 sub 10 2 ':';
1279 sub 12 2 ':';
1280 sub 14 2 ' ';
1281 Printf.bprintf b "[%s]" v;
1282 Buffer.contents b
1283 else args
1285 else args
1287 if nonemptystr s then S.docinfo := (1, s) :: !S.docinfo
1289 | "infoend", "" ->
1290 S.docinfo := List.rev !S.docinfo;
1291 !S.uioh#infochanged Docinfo
1293 | "pass", args ->
1294 if args = "fail"
1295 then adderrmsg "pass" "Wrong password";
1296 let password = getpassword () in
1297 if emptystr password
1298 then error "document is password protected"
1299 else opendoc !S.path password
1301 | _ -> error "unknown cmd `%S'" cmds
1303 let onhist cb =
1304 let rc = cb.rc in
1305 let action = function
1306 | HCprev -> cbget cb ~-1
1307 | HCnext -> cbget cb 1
1308 | HCfirst -> cbget cb ~-(cb.rc)
1309 | HClast -> cbget cb (cb.len - 1 - cb.rc)
1310 and cancel () = cb.rc <- rc
1311 in (action, cancel)
1313 let search pattern forward =
1314 match conf.columns with
1315 | Csplit _ -> impmsg "searching does not work properly in split columns mode"
1316 | Csingle _ | Cmulti _ ->
1317 if nonemptystr pattern
1318 then
1319 let pn, py =
1320 match !S.layout with
1321 | [] -> 0, 0
1322 | l :: _ -> l.pageno, (l.pagey + if forward then 0 else 0*l.pagevh)
1324 S.rects1 := [];
1325 wcmd U.search "%d %d %d %d,%s\000"
1326 (btod conf.icase) pn py (btod forward) pattern
1328 let intentry text key =
1329 let text =
1330 if emptystr text && key = Keys.Ascii '-'
1331 then addchar text '-'
1332 else
1333 match [@warning "-fragile-match"] key with
1334 | Keys.Ascii ('0'..'9' as c) -> addchar text c
1335 | _ ->
1336 S.text := "invalid key";
1337 text
1339 TEcont text
1341 let linknact f s =
1342 if nonemptystr s
1343 then
1344 let rec loop off = function
1345 | [] -> ()
1346 | l :: rest ->
1347 match getopaque l.pageno with
1348 | exception Not_found -> loop off rest
1349 | opaque ->
1350 let n = Ffi.getlinkn opaque conf.hcs s off in
1351 if n <= 0
1352 then loop n rest
1353 else Ffi.getlink opaque (n-1) |> f
1355 loop 0 !S.layout
1357 let linknentry text = function [@warning "-fragile-match"]
1358 | Keys.Ascii c ->
1359 let text = addchar text c in
1360 linknact (fun under -> S.text := undertext under) text;
1361 TEcont text
1362 | key ->
1363 settextfmt "invalid key %s" @@ Keys.to_string key;
1364 TEcont text
1366 let textentry text key = match [@warning "-fragile-match"] key with
1367 | Keys.Ascii c -> TEcont (addchar text c)
1368 | Keys.Code c -> TEcont (text ^ Ffi.toutf8 c)
1369 | _ -> TEcont text
1371 let reqlayout angle fitmodel =
1372 if U.nogeomcmds !S.geomcmds
1373 then S.anchor := getanchor ();
1374 conf.angle <- angle mod 360;
1375 if conf.angle != 0
1376 then (
1377 match !S.mode with
1378 | LinkNav _ -> S.mode := View
1379 | Birdseye _ | Textentry _ | View -> ()
1381 conf.fitmodel <- fitmodel;
1382 invalidate "reqlayout"
1383 (fun () -> wcmd U.reqlayout "%d %d %d"
1384 conf.angle (FMTE.to_int conf.fitmodel) (stateh !S.winh))
1386 let settrim trimmargins trimfuzz =
1387 if U.nogeomcmds !S.geomcmds
1388 then S.anchor := getanchor ();
1389 conf.trimmargins <- trimmargins;
1390 conf.trimfuzz <- trimfuzz;
1391 let x0, y0, x1, y1 = trimfuzz in
1392 invalidate "settrim"
1393 (fun () -> wcmd U.settrim "%d %d %d %d %d"
1394 (btod conf.trimmargins) x0 y0 x1 y1);
1395 flushpages ()
1397 let setzoom zoom =
1398 let zoom = max 0.0001 zoom in
1399 if zoom <> conf.zoom
1400 then (
1401 S.prevzoom := (conf.zoom, !S.x);
1402 conf.zoom <- zoom;
1403 reshape !S.winw !S.winh;
1404 settextfmt "zoom is now %-5.2f" (zoom *. 100.0);
1407 let pivotzoom ?(vw=min !S.w !S.winw)
1408 ?(vh=min (!S.maxy - !S.y) !S.winh)
1409 ?(x=vw/2) ?(y=vh/2) zoom =
1410 let w = float !S.w /. zoom in
1411 let hw = w /. 2.0 in
1412 let ratio = float vh /. float vw in
1413 let hh = hw *. ratio in
1414 let x0 = float x -. hw +. !S.xf and y0 = float y -. hh +. !S.yf in
1415 let xf, xr = modf x0 and yf, yr = modf y0 in
1416 S.xf := xf;
1417 S.yf := yf;
1418 gotoxy (!S.x - truncate xr) (!S.y + truncate yr);
1419 setzoom zoom
1421 let pivotzoom ?vw ?vh ?x ?y zoom =
1422 if U.nogeomcmds !S.geomcmds
1423 then
1424 if zoom > 1.0
1425 then pivotzoom ?vw ?vh ?x ?y zoom
1426 else setzoom zoom
1428 let setcolumns mode columns coverA coverB =
1429 S.prevcolumns := Some (conf.columns, conf.zoom);
1430 if columns < 0
1431 then (
1432 if isbirdseye mode
1433 then impmsg "split mode doesn't work in bird's eye"
1434 else (
1435 conf.columns <- Csplit (-columns, E.a);
1436 S.x := 0;
1437 conf.zoom <- 1.0;
1440 else (
1441 if columns < 2
1442 then (
1443 conf.columns <- Csingle E.a;
1444 S.x := 0;
1445 setzoom 1.0;
1447 else (
1448 conf.columns <- Cmulti ((columns, coverA, coverB), E.a);
1449 conf.zoom <- 1.0;
1452 reshape !S.winw !S.winh
1454 let resetmstate () =
1455 S.mstate := Mnone;
1456 Wsi.setcursor Wsi.CURSOR_INHERIT
1458 let enterbirdseye () =
1459 let zoom = float conf.thumbw /. float !S.winw in
1460 let birdseyepageno =
1461 let cy = !S.winh / 2 in
1462 let fold = function
1463 | [] -> 0
1464 | l :: rest ->
1465 let rec fold best = function
1466 | [] -> best.pageno
1467 | l :: rest ->
1468 let d = cy - (l.pagedispy + l.pagevh/2)
1469 and dbest = cy - (best.pagedispy + best.pagevh/2) in
1470 if abs d < abs dbest
1471 then fold l rest
1472 else best.pageno
1473 in fold l rest
1475 fold !S.layout
1477 S.mode :=
1478 Birdseye (
1479 { conf with zoom = conf.zoom },
1480 !S.x, birdseyepageno, -1, getanchor ()
1482 resetmstate ();
1483 conf.zoom <- zoom;
1484 conf.presentation <- false;
1485 conf.interpagespace <- 10;
1486 conf.hlinks <- false;
1487 conf.fitmodel <- FitPage;
1488 S.x := 0;
1489 conf.columns <- (
1490 match conf.beyecolumns with
1491 | Some c ->
1492 conf.zoom <- 1.0;
1493 Cmulti ((c, 0, 0), E.a)
1494 | None -> Csingle E.a
1496 if conf.verbose
1497 then settextfmt "birds eye on (zoom %3.1f%%)" (100.0*.zoom);
1498 reshape !S.winw !S.winh
1500 let leavebirdseye (c, leftx, pageno, _, anchor) goback =
1501 S.mode := View;
1502 conf.zoom <- c.zoom;
1503 conf.presentation <- c.presentation;
1504 conf.interpagespace <- c.interpagespace;
1505 conf.hlinks <- c.hlinks;
1506 conf.fitmodel <- c.fitmodel;
1507 conf.beyecolumns <- (
1508 match conf.columns with
1509 | Cmulti ((c, _, _), _) -> Some c
1510 | Csingle _ -> None
1511 | Csplit _ -> error "leaving bird's eye split mode"
1513 conf.columns <- (
1514 match c.columns with
1515 | Cmulti (c, _) -> Cmulti (c, E.a)
1516 | Csingle _ -> Csingle E.a
1517 | Csplit (c, _) -> Csplit (c, E.a)
1519 if conf.verbose
1520 then settextfmt "bird's eye off (zoom %3.1f%%)" (100.0*.conf.zoom);
1521 reshape !S.winw !S.winh;
1522 S.anchor := if goback then anchor else (pageno, 0.0, 1.0);
1523 S.x := leftx
1525 let togglebirdseye () =
1526 match !S.mode with
1527 | Birdseye vals -> leavebirdseye vals true
1528 | View -> enterbirdseye ()
1529 | Textentry _ | LinkNav _ -> ()
1531 let upbirdseye incr (conf, leftx, pageno, hooverpageno, anchor) =
1532 let pageno = max 0 (pageno - incr) in
1533 let rec loop = function
1534 | [] -> gotopage1 pageno 0
1535 | l :: _ when l.pageno = pageno ->
1536 if l.pagedispy >= 0 && l.pagey = 0
1537 then Glutils.postRedisplay "upbirdseye"
1538 else gotopage1 pageno 0
1539 | _ :: rest -> loop rest
1541 loop !S.layout;
1542 S.text := E.s;
1543 S.mode := Birdseye (conf, leftx, pageno, hooverpageno, anchor)
1545 let downbirdseye incr (conf, leftx, pageno, hooverpageno, anchor) =
1546 let pageno = min (!S.pagecount - 1) (pageno + incr) in
1547 S.mode := Birdseye (conf, leftx, pageno, hooverpageno, anchor);
1548 let rec loop = function
1549 | [] ->
1550 let y, h = getpageyh pageno in
1551 let dy = (y - !S.y) - (!S.winh - h - conf.interpagespace) in
1552 gotoxy !S.x (U.clamp dy)
1553 | l :: _ when l.pageno = pageno ->
1554 if l.pagevh != l.pageh
1555 then gotoxy !S.x (U.clamp (l.pageh - l.pagevh + conf.interpagespace))
1556 else Glutils.postRedisplay "downbirdseye"
1557 | _ :: rest -> loop rest
1559 loop !S.layout;
1560 S.text := E.s
1562 let optentry mode _ key =
1563 match [@warning "-fragile-match"] key with
1564 | Keys.Ascii 'C' ->
1565 let ondone s =
1567 let n, a, b = multicolumns_of_string s in
1568 setcolumns mode n a b;
1569 with exn -> settextfmt "bad columns `%s': %s" s @@ exntos exn
1571 TEswitch ("columns: ", E.s, None, textentry, ondone, true)
1573 | Keys.Ascii 'Z' ->
1574 let ondone s =
1576 let zoom = float (int_of_string s) /. 100.0 in
1577 pivotzoom zoom
1578 with exn -> settextfmt "bad integer `%s': %s" s @@ exntos exn
1580 TEswitch ("zoom: ", E.s, None, intentry, ondone, true)
1582 | Keys.Ascii 'i' ->
1583 conf.icase <- not conf.icase;
1584 TEdone ("case insensitive search " ^ (onoffs conf.icase))
1586 | Keys.Ascii 'v' ->
1587 conf.verbose <- not conf.verbose;
1588 TEdone ("verbose " ^ (onoffs conf.verbose))
1590 | Keys.Ascii 'd' ->
1591 conf.debug <- not conf.debug;
1592 TEdone ("debug " ^ (onoffs conf.debug))
1594 | Keys.Ascii 'f' ->
1595 conf.underinfo <- not conf.underinfo;
1596 TEdone ("underinfo " ^ onoffs conf.underinfo)
1598 | Keys.Ascii 'T' ->
1599 settrim (not conf.trimmargins) conf.trimfuzz;
1600 TEdone ("trim margins " ^ onoffs conf.trimmargins)
1602 | Keys.Ascii 'I' ->
1603 conf.invert <- not conf.invert;
1604 TEdone ("invert colors " ^ onoffs conf.invert)
1606 | Keys.Ascii 'x' ->
1607 let ondone s =
1608 cbput !S.hists.sel s;
1609 conf.selcmd <- s;
1611 TEswitch ("selection command: ", E.s, Some (onhist !S.hists.sel),
1612 textentry, ondone, true)
1614 | Keys.Ascii 'M' ->
1615 if conf.pax == None
1616 then conf.pax <- Some 0.0
1617 else conf.pax <- None;
1618 TEdone ("PAX " ^ onoffs (conf.pax != None))
1620 | (Keys.Ascii c) ->
1621 settextfmt "bad option %d `%c'" (Char.code c) c;
1622 TEstop
1624 | _ -> TEcont !S.text
1626 class outlinelistview ~zebra ~source =
1627 let settext autonarrow s =
1628 S.text :=
1629 if autonarrow
1630 then
1631 let ss = source#statestr in
1632 if emptystr ss then "[" ^ s ^ "]" else "{" ^ ss ^ "} [" ^ s ^ "]"
1633 else s
1635 object (self)
1636 inherit listview
1637 ~zebra
1638 ~helpmode:false
1639 ~source:(source :> lvsource)
1640 ~trusted:false
1641 ~modehash:(findkeyhash conf "outline")
1642 as super
1644 val m_autonarrow = false
1646 method! key key mask =
1647 let maxrows =
1648 if emptystr !S.text
1649 then fstate.maxrows
1650 else fstate.maxrows - 2
1652 let calcfirst first active =
1653 if active > first
1654 then
1655 let rows = active - first in
1656 if rows > maxrows then active - maxrows else first
1657 else active
1659 let navigate incr =
1660 let active = m_active + incr in
1661 let active = bound active 0 (source#getitemcount - 1) in
1662 let first = calcfirst m_first active in
1663 Glutils.postRedisplay "outline navigate";
1664 coe {< m_active = active; m_first = first >}
1666 let navscroll first =
1667 let active =
1668 let dist = m_active - first in
1669 if dist < 0
1670 then first
1671 else (
1672 if dist < maxrows
1673 then m_active
1674 else first + maxrows
1677 Glutils.postRedisplay "outline navscroll";
1678 coe {< m_first = first; m_active = active >}
1680 let ctrl = Wsi.withctrl mask in
1681 let open Keys in
1682 match Wsi.ks2kt key with
1683 | Ascii 'a' when ctrl ->
1684 let text =
1685 if m_autonarrow
1686 then (
1687 source#denarrow;
1690 else (
1691 let pattern = source#renarrow in
1692 if nonemptystr m_qsearch
1693 then (source#narrow m_qsearch; m_qsearch)
1694 else pattern
1697 settext (not m_autonarrow) text;
1698 Glutils.postRedisplay "toggle auto narrowing";
1699 coe {< m_first = 0; m_active = 0; m_autonarrow = not m_autonarrow >}
1700 | Ascii '/' when emptystr m_qsearch && not m_autonarrow ->
1701 settext true E.s;
1702 Glutils.postRedisplay "toggle auto narrowing";
1703 coe {< m_first = 0; m_active = 0; m_autonarrow = true >}
1704 | Ascii 'n' when ctrl ->
1705 source#narrow m_qsearch;
1706 if not m_autonarrow
1707 then source#add_narrow_pattern m_qsearch;
1708 Glutils.postRedisplay "outline ctrl-n";
1709 coe {< m_first = 0; m_active = 0 >}
1710 | Ascii 'S' when ctrl ->
1711 let active = source#calcactive (getanchor ()) in
1712 let first = firstof m_first active in
1713 Glutils.postRedisplay "outline ctrl-s";
1714 coe {< m_first = first; m_active = active >}
1715 | Ascii 'u' when ctrl ->
1716 Glutils.postRedisplay "outline ctrl-u";
1717 if m_autonarrow && nonemptystr m_qsearch
1718 then (
1719 ignore (source#renarrow);
1720 settext m_autonarrow E.s;
1721 coe {< m_first = 0; m_active = 0; m_qsearch = E.s >}
1723 else (
1724 source#del_narrow_pattern;
1725 let pattern = source#renarrow in
1726 let text =
1727 if emptystr pattern then E.s else "Narrowed to " ^ pattern
1729 settext m_autonarrow text;
1730 coe {< m_first = 0; m_active = 0; m_qsearch = E.s >}
1732 | Ascii 'l' when ctrl ->
1733 let first = max 0 (m_active - (fstate.maxrows / 2)) in
1734 Glutils.postRedisplay "outline ctrl-l";
1735 coe {< m_first = first >}
1737 | Ascii '\t' when m_autonarrow ->
1738 if nonemptystr m_qsearch
1739 then (
1740 Glutils.postRedisplay "outline list view tab";
1741 source#add_narrow_pattern m_qsearch;
1742 settext true E.s;
1743 coe {< m_qsearch = E.s >}
1745 else coe self
1746 | Escape when m_autonarrow ->
1747 if nonemptystr m_qsearch
1748 then source#add_narrow_pattern m_qsearch;
1749 super#key key mask
1750 | Enter when m_autonarrow ->
1751 if nonemptystr m_qsearch
1752 then source#add_narrow_pattern m_qsearch;
1753 super#key key mask
1754 | (Ascii _ | Code _) when m_autonarrow ->
1755 let pattern = m_qsearch ^ Ffi.toutf8 key in
1756 Glutils.postRedisplay "outlinelistview autonarrow add";
1757 source#narrow pattern;
1758 settext true pattern;
1759 coe {< m_first = 0; m_active = 0; m_qsearch = pattern >}
1760 | Backspace when m_autonarrow ->
1761 if emptystr m_qsearch
1762 then coe self
1763 else
1764 let pattern = withoutlastutf8 m_qsearch in
1765 Glutils.postRedisplay "outlinelistview autonarrow backspace";
1766 ignore (source#renarrow);
1767 source#narrow pattern;
1768 settext true pattern;
1769 coe {< m_first = 0; m_active = 0; m_qsearch = pattern >}
1770 | Up when ctrl -> navscroll (max 0 (m_first-1))
1771 | Down when ctrl -> navscroll (min (source#getitemcount-1) (m_first+1))
1772 | Up -> navigate ~-1
1773 | Down -> navigate 1
1774 | Prior -> navigate ~-(fstate.maxrows)
1775 | Next -> navigate fstate.maxrows
1776 | Right ->
1777 (if ctrl
1778 then (
1779 Glutils.postRedisplay "outline ctrl right";
1780 {< m_pan = m_pan + 1 >}
1782 else (
1783 if Wsi.withshift mask
1784 then self#nextcurlevel 1
1785 else self#updownlevel 1
1786 )) |> coe
1787 | Left ->
1788 (if ctrl
1789 then (
1790 Glutils.postRedisplay "outline ctrl left";
1791 {< m_pan = m_pan - 1 >}
1793 else (
1794 if Wsi.withshift mask
1795 then self#nextcurlevel ~-1
1796 else self#updownlevel ~-1
1797 )) |> coe
1798 | Home ->
1799 Glutils.postRedisplay "outline home";
1800 coe {< m_first = 0; m_active = 0 >}
1801 | End ->
1802 let active = source#getitemcount - 1 in
1803 let first = max 0 (active - fstate.maxrows) in
1804 Glutils.postRedisplay "outline end";
1805 coe {< m_active = active; m_first = first >}
1806 | Delete|Escape|Insert|Enter|Ascii _|Code _|Ctrl _|Backspace|Fn _ ->
1807 super#key key mask
1810 let genhistoutlines () =
1811 Config.gethist ()
1812 |> List.sort (fun (_, c1, _, _, _, _) (_, c2, _, _, _, _) ->
1813 compare c2.lastvisit c1.lastvisit)
1814 |> List.map (fun ((path, c, _, _, _, origin) as hist) ->
1815 let path = if nonemptystr origin then origin else path in
1816 let base = Ffi.mbtoutf8 @@ Filename.basename path in
1817 (base ^ "\000" ^ c.title, 1, Ohistory hist)
1820 let gotohist (path, c, bookmarks, x, anchor, origin) =
1821 Config.save leavebirdseye;
1822 S.anchor := anchor;
1823 S.bookmarks := bookmarks;
1824 S.origin := origin;
1825 S.x := x;
1826 setconf conf c;
1827 let x0, y0, x1, y1 = conf.trimfuzz in
1828 wcmd U.trimset "%d %d %d %d %d" (btod conf.trimmargins) x0 y0 x1 y1;
1829 Wsi.reshape c.cwinw c.cwinh;
1830 opendoc path origin;
1831 setzoom c.zoom
1833 let describe_layout layout =
1834 let d =
1835 match layout with
1836 | [] -> "Page 0"
1837 | l :: [] -> Printf.sprintf "Page %d" (l.pageno+1)
1838 | l :: rest ->
1839 let rangestr a b =
1840 if a.pageno = b.pageno then Printf.sprintf "%d" (a.pageno+1)
1841 else Printf.sprintf "%d%s%d" (a.pageno+1)
1842 (if a.pageno+1 = b.pageno then ", " else Utf8syms.ellipsis)
1843 (b.pageno+1)
1845 let rec fold s la lb = function
1846 | [] -> Printf.sprintf "%s %s" s (rangestr la lb)
1847 | l :: rest when l.pageno = succ lb.pageno -> fold s la l rest
1848 | l :: rest -> fold (s ^ " " ^ rangestr la lb ^ ",") l l rest
1850 fold "Pages" l l rest
1852 let percent =
1853 let maxy = U.maxy () in
1854 if maxy <= 0
1855 then 100.
1856 else 100. *. (float !S.y /. float maxy)
1858 Printf.sprintf "%s of %d [%.2f%%]" d !S.pagecount percent
1860 let setpresentationmode v =
1861 let n = page_of_y !S.y in
1862 S.anchor := (n, 0.0, 1.0);
1863 conf.presentation <- v;
1864 if conf.fitmodel = FitPage
1865 then reqlayout conf.angle conf.fitmodel;
1866 represent ()
1868 let infomenu =
1869 let modehash = lazy (findkeyhash conf "info") in (fun source ->
1870 S.text := E.s;
1871 new listview ~zebra:false ~helpmode:false ~source
1872 ~trusted:true ~modehash:(Lazy.force_val modehash) |> coe)
1874 let enterinfomode =
1875 let btos b = if b then Utf8syms.radical else E.s in
1876 let showextended = ref false in
1877 let showcolors = ref false in
1878 let showcommands = ref false in
1879 let showrefl = ref false in
1880 let leave mode _ = S.mode := mode in
1881 let src = object
1882 val mutable m_l = []
1883 val mutable m_a = E.a
1884 val mutable m_prev_uioh = nouioh
1885 val mutable m_prev_mode = View
1887 inherit lvsourcebase
1889 method reset prev_mode prev_uioh =
1890 m_a <- Array.of_list (List.rev m_l);
1891 m_l <- [];
1892 m_prev_mode <- prev_mode;
1893 m_prev_uioh <- prev_uioh;
1895 method int name get set =
1896 m_l <-
1897 (name, `int get, 1,
1898 Some (fun u ->
1899 let ondone s =
1900 try set (int_of_string s)
1901 with exn -> settextfmt "bad integer `%s': %s" s @@ exntos exn
1903 S.text := E.s;
1904 let te = (name ^ ": ", E.s, None, intentry, ondone, true) in
1905 S.mode := Textentry (te, leave m_prev_mode);
1907 )) :: m_l
1909 method int_with_suffix name get set =
1910 m_l <-
1911 (name, `intws get, 1,
1912 Some (fun u ->
1913 let ondone s =
1914 try set (int_of_string_with_suffix s)
1915 with exn -> settextfmt "bad integer `%s': %s" s @@ exntos exn
1917 S.text := E.s;
1918 let te = (name ^ ": ", E.s, None, intentry_with_suffix,
1919 ondone, true) in
1920 S.mode := Textentry (te, leave m_prev_mode);
1922 )) :: m_l
1924 method bool ?(offset=1) ?(btos=btos) name get set =
1925 m_l <- (name, `bool (btos, get), offset,
1926 Some (fun u -> set (not (get ())); u)) :: m_l
1928 method color name get set =
1929 m_l <-
1930 (name, `color get, 1,
1931 Some (fun u ->
1932 let invalid = (nan, nan, nan) in
1933 let ondone s =
1934 let c =
1935 try color_of_string s
1936 with exn -> settextfmt "bad color `%s': %s" s @@ exntos exn;
1937 invalid
1939 if c <> invalid
1940 then set c;
1942 let te = (name ^ ": ", E.s, None, textentry, ondone, true) in
1943 S.text := color_to_string (get ());
1944 S.mode := Textentry (te, leave m_prev_mode);
1946 )) :: m_l
1948 method string name get set =
1949 m_l <-
1950 (name, `string get, 1,
1951 Some (fun u ->
1952 let ondone s = set s in
1953 let te = (String.trim name ^ ": ", E.s, None,
1954 textentry, ondone, true) in
1955 S.mode := Textentry (te, leave m_prev_mode);
1957 )) :: m_l
1959 method colorspace name get set =
1960 m_l <-
1961 (name, `string get, 1,
1962 Some (fun _ ->
1963 let source = object
1964 inherit lvsourcebase
1966 initializer
1967 m_active <- CSTE.to_int conf.colorspace;
1968 m_first <- 0;
1970 method getitemcount =
1971 Array.length CSTE.names
1972 method getitem n =
1973 (CSTE.names.(n), 0)
1974 method exit ~uioh ~cancel ~active ~first ~pan =
1975 ignore (uioh, first, pan);
1976 if not cancel then set active;
1977 None
1978 method hasaction _ = true
1981 infomenu source
1982 )) :: m_l
1984 method paxmark name get set =
1985 m_l <-
1986 (name, `string get, 1,
1987 Some (fun _ ->
1988 let source = object
1989 inherit lvsourcebase
1991 initializer
1992 m_active <- MTE.to_int conf.paxmark;
1993 m_first <- 0;
1995 method getitemcount = Array.length MTE.names
1996 method getitem n = (MTE.names.(n), 0)
1997 method exit ~uioh ~cancel ~active ~first ~pan =
1998 ignore (uioh, first, pan);
1999 if not cancel then set active;
2000 None
2001 method hasaction _ = true
2004 infomenu source
2005 )) :: m_l
2007 method fitmodel name get set =
2008 m_l <-
2009 (name, `string get, 1,
2010 Some (fun _ ->
2011 let source = object
2012 inherit lvsourcebase
2014 initializer
2015 m_active <- FMTE.to_int conf.fitmodel;
2016 m_first <- 0;
2018 method getitemcount = Array.length FMTE.names
2019 method getitem n = (FMTE.names.(n), 0)
2020 method exit ~uioh ~cancel ~active ~first ~pan =
2021 ignore (uioh, first, pan);
2022 if not cancel then set active;
2023 None
2024 method hasaction _ = true
2027 infomenu source
2028 )) :: m_l
2030 method caption s offset =
2031 m_l <- (s, `empty, offset, None) :: m_l
2033 method caption2 s f offset =
2034 m_l <- (s, `string f, offset, None) :: m_l
2036 method getitemcount = Array.length m_a
2038 method getitem n =
2039 let tostr = function
2040 | `int f -> string_of_int (f ())
2041 | `intws f -> string_with_suffix_of_int (f ())
2042 | `string f -> f ()
2043 | `color f -> color_to_string (f ())
2044 | `bool (btos, f) -> btos (f ())
2045 | `empty -> E.s
2047 let name, t, offset, _ = m_a.(n) in
2048 ((let s = tostr t in
2049 if nonemptystr s
2050 then Printf.sprintf "%s\t%s" name s
2051 else name),
2052 offset)
2054 method exit ~uioh ~cancel ~active ~first ~pan =
2055 let uiohopt =
2056 if not cancel
2057 then (
2058 let uioh =
2059 match m_a.(active) with
2060 | _, _, _, Some f -> f uioh
2061 | _, _, _, None -> uioh
2063 Some uioh
2065 else None
2067 m_active <- active;
2068 m_first <- first;
2069 m_pan <- pan;
2070 uiohopt
2072 method hasaction n =
2073 match m_a.(n) with
2074 | _, _, _, Some _ -> true
2075 | _, _, _, None -> false
2077 initializer m_active <- 1
2080 let rec fillsrc prevmode prevuioh =
2081 let sep () = src#caption E.s 0 in
2082 let bad v exn = settextfmt "bad color `%s': %s" v @@ exntos exn in
2083 let colorp name get set =
2084 src#string name
2085 (fun () -> color_to_string (get ()))
2086 (fun v ->
2087 try set @@ color_of_string v
2088 with exn -> bad v exn
2091 let rgba name get set =
2092 src#string name
2093 (fun () -> get () |> rgba_to_string)
2094 (fun v ->
2095 try set @@ rgba_of_string v
2096 with exn -> bad v exn
2099 let oldmode = !S.mode in
2100 let birdseye = isbirdseye !S.mode in
2102 src#caption (if birdseye then "Setup (Bird's eye)" else "Setup") 0;
2104 src#bool "presentation mode"
2105 (fun () -> conf.presentation)
2106 (fun v -> setpresentationmode v);
2108 src#bool "ignore case in searches"
2109 (fun () -> conf.icase)
2110 (fun v -> conf.icase <- v);
2112 src#bool "preload"
2113 (fun () -> conf.preload)
2114 (fun v -> conf.preload <- v);
2116 src#bool "highlight links"
2117 (fun () -> conf.hlinks)
2118 (fun v -> conf.hlinks <- v);
2120 src#bool "under info"
2121 (fun () -> conf.underinfo)
2122 (fun v -> conf.underinfo <- v);
2124 src#fitmodel "fit model"
2125 (fun () -> FMTE.to_string conf.fitmodel)
2126 (fun v -> reqlayout conf.angle (FMTE.of_int v));
2128 src#bool "trim margins"
2129 (fun () -> conf.trimmargins)
2130 (fun v -> settrim v conf.trimfuzz; fillsrc prevmode prevuioh);
2132 sep ();
2133 src#int "inter-page space"
2134 (fun () -> conf.interpagespace)
2135 (fun n ->
2136 conf.interpagespace <- n;
2137 docolumns conf.columns;
2138 let pageno, py =
2139 match !S.layout with
2140 | [] -> 0, 0
2141 | l :: _ -> l.pageno, l.pagey
2143 S.maxy :=- calcheight ();
2144 gotoxy !S.x (py + getpagey pageno)
2147 src#int "page bias"
2148 (fun () -> conf.pagebias)
2149 (fun v -> conf.pagebias <- v);
2151 src#int "scroll step"
2152 (fun () -> conf.scrollstep)
2153 (fun n -> conf.scrollstep <- n);
2155 src#int "horizontal scroll step"
2156 (fun () -> conf.hscrollstep)
2157 (fun v -> conf.hscrollstep <- v);
2159 src#int "auto scroll step"
2160 (fun () ->
2161 match !S.autoscroll with
2162 | Some step -> step
2163 | _ -> conf.autoscrollstep)
2164 (fun n ->
2165 let n = boundastep !S.winh n in
2166 if !S.autoscroll <> None
2167 then S.autoscroll := Some n;
2168 conf.autoscrollstep <- n);
2170 src#int "zoom"
2171 (fun () -> truncate (conf.zoom *. 100.))
2172 (fun v -> pivotzoom ((float v) /. 100.));
2174 src#int "rotation"
2175 (fun () -> conf.angle)
2176 (fun v -> reqlayout v conf.fitmodel);
2178 src#int "scroll bar width"
2179 (fun () -> conf.scrollbw)
2180 (fun v ->
2181 conf.scrollbw <- v;
2182 reshape !S.winw !S.winh;
2185 src#int "scroll handle height"
2186 (fun () -> conf.scrollh)
2187 (fun v -> conf.scrollh <- v;);
2189 src#int "thumbnail width"
2190 (fun () -> conf.thumbw)
2191 (fun v ->
2192 conf.thumbw <- min 4096 v;
2193 match oldmode with
2194 | Birdseye beye ->
2195 leavebirdseye beye false;
2196 enterbirdseye ()
2197 | Textentry _ | View | LinkNav _ -> ()
2200 let mode = !S.mode in
2201 src#string "columns"
2202 (fun () ->
2203 match conf.columns with
2204 | Csingle _ -> "1"
2205 | Cmulti (multi, _) -> multicolumns_to_string multi
2206 | Csplit (count, _) -> "-" ^ string_of_int count
2208 (fun v ->
2209 let n, a, b = multicolumns_of_string v in
2210 setcolumns mode n a b);
2212 sep ();
2213 src#caption "Pixmap cache" 0;
2214 src#int_with_suffix "size (advisory)"
2215 (fun () -> conf.memlimit)
2216 (fun v -> conf.memlimit <- v);
2218 src#caption2 "used"
2219 (fun () ->
2220 Printf.sprintf "%s bytes, %d tiles"
2221 (string_with_suffix_of_int !S.memused)
2222 (Hashtbl.length S.tilemap)) 1;
2224 sep ();
2225 src#caption "Layout" 0;
2226 src#caption2 "Dimension"
2227 (fun () -> Printf.sprintf "%dx%d (virtual %dx%d)"
2228 !S.winw !S.winh
2229 !S.w !S.maxy)
2231 if conf.debug
2232 then src#caption2 "Position" (fun () ->
2233 Printf.sprintf "%dx%d" !S.x !S.y
2235 else src#caption2 "Position" (fun () -> describe_layout !S.layout) 1;
2237 sep ();
2238 let btos b = Utf8syms.(if b then lguillemet else rguillemet) in
2239 src#bool ~offset:0 ~btos "Extended parameters"
2240 (fun () -> !showextended)
2241 (fun v -> showextended := v; fillsrc prevmode prevuioh);
2242 if !showextended
2243 then (
2244 src#bool "update cursor"
2245 (fun () -> conf.updatecurs)
2246 (fun v -> conf.updatecurs <- v);
2247 src#bool "scroll-bar on the left"
2248 (fun () -> conf.leftscroll)
2249 (fun v -> conf.leftscroll <- v);
2250 src#bool "verbose"
2251 (fun () -> conf.verbose)
2252 (fun v -> conf.verbose <- v);
2253 src#bool "invert colors"
2254 (fun () -> conf.invert)
2255 (fun v -> conf.invert <- v);
2256 src#bool "max fit"
2257 (fun () -> conf.maxhfit)
2258 (fun v -> conf.maxhfit <- v);
2259 src#bool "pax mode"
2260 (fun () -> conf.pax != None)
2261 (fun v ->
2262 if v
2263 then conf.pax <- Some (now ())
2264 else conf.pax <- None);
2265 src#string "uri launcher"
2266 (fun () -> conf.urilauncher)
2267 (fun v -> conf.urilauncher <- v);
2268 src#string "path launcher"
2269 (fun () -> conf.pathlauncher)
2270 (fun v -> conf.pathlauncher <- v);
2271 src#string "tile size"
2272 (fun () -> Printf.sprintf "%dx%d" conf.tilew conf.tileh)
2273 (fun v ->
2275 let w, h = Scanf.sscanf v "%dx%d" (fun w h -> w, h) in
2276 conf.tilew <- max 64 w;
2277 conf.tileh <- max 64 h;
2278 flushtiles ();
2279 with exn -> settextfmt "bad tile size `%s': %s" v @@ exntos exn);
2280 src#int "texture count"
2281 (fun () -> conf.texcount)
2282 (fun v ->
2283 if Ffi.realloctexts v
2284 then conf.texcount <- v
2285 else impmsg "failed to set texture count please retry later");
2286 src#int "slice height"
2287 (fun () -> conf.sliceheight)
2288 (fun v ->
2289 conf.sliceheight <- v;
2290 wcmd U.sliceh "%d" conf.sliceheight);
2291 src#int "anti-aliasing level"
2292 (fun () -> conf.aalevel)
2293 (fun v ->
2294 conf.aalevel <- bound v 0 8;
2295 S.anchor := getanchor ();
2296 opendoc !S.path !S.password);
2297 src#string "page scroll scaling factor"
2298 (fun () -> string_of_float conf.pgscale)
2299 (fun v ->
2300 try conf.pgscale <- float_of_string v
2301 with exn ->
2302 S.text :=
2303 Printf.sprintf "bad page scroll scaling factor `%s': %s" v
2304 @@ exntos exn);
2305 src#int "ui font size"
2306 (fun () -> fstate.fontsize)
2307 (fun v -> setfontsize (bound v 5 100));
2308 src#int "hint font size"
2309 (fun () -> conf.hfsize)
2310 (fun v -> conf.hfsize <- bound v 5 100);
2311 src#string "hint chars"
2312 (fun () -> conf.hcs)
2313 (fun v ->
2315 validatehcs v;
2316 conf.hcs <- v
2317 with exn ->
2318 S.text :=
2319 Printf.sprintf "invalid hint chars %S: %s" v (exntos exn));
2320 src#string "trim fuzz"
2321 (fun () -> irect_to_string conf.trimfuzz)
2322 (fun v ->
2324 conf.trimfuzz <- irect_of_string v;
2325 if conf.trimmargins
2326 then settrim true conf.trimfuzz;
2327 with exn -> settextfmt "bad irect `%s': %s" v @@ exntos exn);
2328 src#bool ~btos "external commands"
2329 (fun () -> !showcommands)
2330 (fun v -> showcommands := v; fillsrc prevmode prevuioh);
2331 if !showcommands
2332 then (
2333 src#string " selection"
2334 (fun () -> conf.selcmd)
2335 (fun v -> conf.selcmd <- v);
2336 src#string " synctex"
2337 (fun () -> conf.stcmd)
2338 (fun v -> conf.stcmd <- v);
2339 src#string " pax"
2340 (fun () -> conf.paxcmd)
2341 (fun v -> conf.paxcmd <- v);
2342 src#string " ask password"
2343 (fun () -> conf.passcmd)
2344 (fun v -> conf.passcmd <- v);
2345 src#string " save path"
2346 (fun () -> conf.savecmd)
2347 (fun v -> conf.savecmd <- v);
2349 src#colorspace "color space"
2350 (fun () -> CSTE.to_string conf.colorspace)
2351 (fun v ->
2352 conf.colorspace <- CSTE.of_int v;
2353 wcmd U.cs "%d" v;
2354 load !S.layout);
2355 src#paxmark "pax mark method"
2356 (fun () -> MTE.to_string conf.paxmark)
2357 (fun v -> conf.paxmark <- MTE.of_int v);
2358 src#bool "mouse wheel scrolls pages"
2359 (fun () -> conf.wheelbypage)
2360 (fun v -> conf.wheelbypage <- v);
2361 src#bool "open remote links in a new instance"
2362 (fun () -> conf.riani)
2363 (fun v -> conf.riani <- v);
2364 src#bool "edit annotations inline"
2365 (fun () -> conf.annotinline)
2366 (fun v -> conf.annotinline <- v);
2367 src#bool "coarse positioning in presentation mode"
2368 (fun () -> conf.coarseprespos)
2369 (fun v -> conf.coarseprespos <- v);
2370 src#bool "use document CSS"
2371 (fun () -> conf.usedoccss)
2372 (fun v ->
2373 conf.usedoccss <- v;
2374 S.anchor := getanchor ();
2375 opendoc !S.path !S.password);
2376 src#bool ~btos "colors"
2377 (fun () -> !showcolors)
2378 (fun v -> showcolors := v; fillsrc prevmode prevuioh);
2379 if !showcolors
2380 then (
2381 colorp " background"
2382 (fun () -> conf.bgcolor)
2383 (fun v -> conf.bgcolor <- v);
2384 rgba " paper"
2385 (fun () -> conf.papercolor)
2386 (fun v ->
2387 conf.papercolor <- v;
2388 Ffi.setpapercolor conf.papercolor;
2389 flushtiles ();
2391 rgba " scrollbar"
2392 (fun () -> conf.sbarcolor)
2393 (fun v -> conf.sbarcolor <- v);
2394 rgba " scrollbar handle"
2395 (fun () -> conf.sbarhndlcolor)
2396 (fun v -> conf.sbarhndlcolor <- v);
2397 rgba " texture"
2398 (fun () -> conf.texturecolor)
2399 (fun v ->
2400 GlTex.env (`color v);
2401 conf.texturecolor <- v;
2403 src#string " scale"
2404 (fun () -> string_of_float conf.colorscale)
2405 (fun v -> conf.colorscale <- bound (float_of_string v) 0.0 1.0);
2407 src#bool ~btos "reflowable layout"
2408 (fun () -> !showrefl)
2409 (fun v -> showrefl := v; fillsrc prevmode prevuioh);
2410 if !showrefl
2411 then (
2412 src#int " width"
2413 (fun () -> conf.rlw)
2414 (fun v -> conf.rlw <- v; reload ());
2415 src#int " height"
2416 (fun () -> conf.rlh)
2417 (fun v -> conf.rlh <- v; reload ());
2418 src#int " em"
2419 (fun () -> conf.rlem)
2420 (fun v -> conf.rlem <- v; reload ());
2424 sep ();
2425 src#caption "Document" 0;
2426 List.iter (fun (_, s) -> src#caption s 1) !S.docinfo;
2427 src#caption2 "Pages" (fun () -> string_of_int !S.pagecount) 1;
2428 src#caption2 "Dimensions"
2429 (fun () -> string_of_int (List.length !S.pdims)) 1;
2430 if nonemptystr conf.css
2431 then src#caption2 "CSS" (fun () -> conf.css) 1;
2432 if conf.trimmargins
2433 then (
2434 sep ();
2435 src#caption "Trimmed margins" 0;
2436 src#caption2 "Dimensions"
2437 (fun () -> string_of_int (List.length !S.pdims)) 1;
2440 sep ();
2441 src#caption "OpenGL" 0;
2442 src#caption ("Vendor\t" ^ GlMisc.get_string `vendor) 1;
2443 src#caption ("Renderer\t" ^ GlMisc.get_string `renderer) 1;
2445 sep ();
2446 src#caption "Location" 0;
2447 if nonemptystr !S.origin
2448 then src#caption ("Origin\t" ^ Ffi.mbtoutf8 !S.origin) 1;
2449 src#caption ("Path\t" ^ Ffi.mbtoutf8 !S.path) 1;
2450 if nonemptystr conf.dcf
2451 then src#caption ("DCF\t" ^ Ffi.mbtoutf8 conf.dcf) 1;
2453 src#reset prevmode prevuioh;
2455 fun () -> (
2456 S.text := E.s;
2457 resetmstate ();
2458 let prevmode = !S.mode
2459 and prevuioh = !S.uioh in
2460 fillsrc prevmode prevuioh;
2461 let source = (src :> lvsource) in
2462 let modehash = findkeyhash conf "info" in
2463 object (self)
2464 inherit listview ~zebra:false ~helpmode:false
2465 ~source ~trusted:true ~modehash as super
2466 val mutable m_prevmemused = 0
2467 method! infochanged = function
2468 | Memused ->
2469 if m_prevmemused != !S.memused
2470 then (
2471 m_prevmemused <- !S.memused;
2472 Glutils.postRedisplay "memusedchanged";
2474 | Pdim -> Glutils.postRedisplay "pdimchanged"
2475 | Docinfo -> fillsrc prevmode prevuioh
2476 method! key key mask =
2477 if not (Wsi.withctrl mask)
2478 then
2479 match [@warning "-fragile-match"] Wsi.ks2kt key with
2480 | Keys.Left -> coe (self#updownlevel ~-1)
2481 | Keys.Right -> coe (self#updownlevel 1)
2482 | _ -> super#key key mask
2483 else super#key key mask
2484 end |> setuioh;
2485 Glutils.postRedisplay "info";
2488 let enterhelpmode =
2489 let source = object
2490 inherit lvsourcebase
2491 method getitemcount = Array.length !S.help
2492 method getitem n =
2493 let s, l, _ = !S.help.(n) in
2494 (s, l)
2496 method exit ~uioh ~cancel ~active ~first ~pan =
2497 let optuioh =
2498 if not cancel
2499 then (
2500 match !S.help.(active) with
2501 | _, _, Some f -> Some (f uioh)
2502 | _, _, None -> Some uioh
2504 else None
2506 m_active <- active;
2507 m_first <- first;
2508 m_pan <- pan;
2509 optuioh
2511 method hasaction n =
2512 match !S.help.(n) with
2513 | _, _, Some _ -> true
2514 | _, _, None -> false
2516 initializer m_active <- -1
2518 in fun () ->
2519 let modehash = findkeyhash conf "help" in
2520 resetmstate ();
2521 new listview ~zebra:false ~helpmode:true
2522 ~source ~trusted:true ~modehash |> setuioh;
2523 Glutils.postRedisplay "help"
2525 let entermsgsmode =
2526 let msgsource = object
2527 inherit lvsourcebase
2528 val mutable m_items = E.a
2530 method getitemcount = 1 + Array.length m_items
2532 method getitem n =
2533 if n = 0
2534 then "[Clear]", 0
2535 else m_items.(n-1), 0
2537 method exit ~uioh ~cancel ~active ~first ~pan =
2538 ignore uioh;
2539 if not cancel
2540 then (
2541 if active = 0
2542 then Buffer.clear S.errmsgs;
2544 m_active <- active;
2545 m_first <- first;
2546 m_pan <- pan;
2547 None
2549 method hasaction n =
2550 n = 0
2552 method reset =
2553 S.newerrmsgs := false;
2554 let l = Str.split Re.crlf (Buffer.contents S.errmsgs) in
2555 m_items <- Array.of_list l
2557 initializer m_active <- 0
2560 fun () ->
2561 S.text := E.s;
2562 resetmstate ();
2563 msgsource#reset;
2564 let source = (msgsource :> lvsource) in
2565 let modehash = findkeyhash conf "listview" in
2566 object
2567 inherit listview ~zebra:false ~helpmode:false
2568 ~source ~trusted:false ~modehash as super
2569 method! display =
2570 if !S.newerrmsgs
2571 then msgsource#reset;
2572 super#display
2573 end |> setuioh;
2574 Glutils.postRedisplay "msgs"
2576 let getusertext s =
2577 let editor = getenvdef "EDITOR" E.s in
2578 if emptystr editor
2579 then E.s
2580 else
2581 let tmppath = Filename.temp_file "llpp" "note" in
2582 if nonemptystr s
2583 then (
2584 let oc = open_out tmppath in
2585 output_string oc s;
2586 close_out oc;
2588 let execstr = editor ^ " " ^ tmppath in
2589 let eret r = Printf.ksprintf (fun s -> adderrmsg "gtut:eret" s; r) in
2590 let s =
2591 match spawn execstr [] with
2592 | exception exn -> eret E.s "spawn(%S) failed: %s" execstr @@ exntos exn
2593 | pid ->
2594 match Unix.waitpid [] pid with
2595 | exception exn -> eret E.s "waitpid(%d) failed: %s" pid @@ exntos exn
2596 | (_pid, status) ->
2597 match status with
2598 | Unix.WEXITED 0 -> filecontents tmppath
2599 | Unix.WEXITED n ->
2600 eret E.s "editor process(%s) exited abnormally: %d" execstr n
2601 | Unix.WSIGNALED n ->
2602 eret E.s "editor process(%s) was killed by signal %d" execstr n
2603 | Unix.WSTOPPED n ->
2604 eret E.s "editor(%s) process was stopped by signal %d" execstr n
2606 match Unix.unlink tmppath with
2607 | exception exn -> eret s "failed to ulink %S: %s" tmppath @@ exntos exn
2608 | () -> s
2610 let enterannotmode opaque slinkindex =
2611 let msgsource = object
2612 inherit lvsourcebase
2613 val mutable m_text = E.s
2614 val mutable m_items = E.a
2616 method getitemcount = Array.length m_items
2618 method getitem n =
2619 let label, _func = m_items.(n) in
2620 label, 0
2622 method exit ~uioh ~cancel ~active ~first ~pan =
2623 ignore (uioh, first, pan);
2624 if not cancel
2625 then (
2626 let _label, func = m_items.(active) in
2627 func ()
2629 None
2631 method hasaction n = nonemptystr @@ fst m_items.(n)
2633 method reset s =
2634 let rec split accu b i =
2635 let p = b+i in
2636 if p = String.length s
2637 then (String.sub s b (p-b), fun () -> ()) :: accu
2638 else
2639 if (i > 70 && s.[p] = ' ') || s.[p] = '\r' || s.[p] = '\n'
2640 then
2641 let ss = if i = 0 then E.s else String.sub s b i in
2642 split ((ss, fun () -> ())::accu) (p+1) 0
2643 else split accu b (i+1)
2645 let cleanup () =
2646 wcmd1 U.freepage opaque;
2647 let keys =
2648 Hashtbl.fold (fun key opaque' accu ->
2649 if opaque' = opaque'
2650 then key :: accu else accu) S.pagemap []
2652 List.iter (Hashtbl.remove S.pagemap) keys;
2653 flushtiles ();
2654 gotoxy !S.x !S.y
2656 let dele () =
2657 Ffi.delannot opaque slinkindex;
2658 cleanup ();
2660 let edit inline () =
2661 let update s =
2662 if emptystr s
2663 then dele ()
2664 else (
2665 Ffi.modannot opaque slinkindex s;
2666 cleanup ();
2669 if inline
2670 then
2671 let mode = !S.mode in
2672 let te = ("annotation: ", m_text, None, textentry, update, true) in
2673 S.mode := Textentry (te, fun _ -> S.mode := mode);
2674 S.text := E.s;
2675 enttext ();
2676 else getusertext m_text |> update
2678 m_text <- s;
2679 m_items <-
2680 ( "[Copy]", fun () -> selstring conf.selcmd m_text)
2681 :: ("[Delete]", dele)
2682 :: ("[Edit]", edit conf.annotinline)
2683 :: (E.s, fun () -> ())
2684 :: split [] 0 0 |> List.rev |> Array.of_list
2686 initializer m_active <- 0
2689 S.text := E.s;
2690 let s = Ffi.gettextannot opaque slinkindex in
2691 resetmstate ();
2692 msgsource#reset s;
2693 let source = (msgsource :> lvsource) in
2694 let modehash = findkeyhash conf "listview" in
2695 object inherit listview ~zebra:false
2696 ~helpmode:false ~source ~trusted:false ~modehash
2697 end |> setuioh;
2698 Glutils.postRedisplay "enterannotmode"
2700 let gotoremote spec =
2701 let filename, dest = splitatchar spec '#' in
2702 let getpath filename =
2703 let path =
2704 if nonemptystr filename
2705 then
2706 if Filename.is_relative filename
2707 then
2708 let dir = Filename.dirname !S.path in
2709 let dir =
2710 if Filename.is_implicit dir
2711 then Filename.concat (Sys.getcwd ()) dir
2712 else dir
2714 Filename.concat dir filename
2715 else filename
2716 else E.s
2718 if Sys.file_exists path
2719 then path
2720 else E.s
2722 let path = getpath filename in
2723 if emptystr path
2724 then adderrfmt "gotoremote/getpath" "failed getpath for %S\n" filename
2725 else
2726 let dospawn lcmd =
2727 if conf.riani
2728 then
2729 let cmd = Lazy.force_val lcmd in
2730 match spawn cmd with
2731 | exception exn -> dolog "failed to execute `%s': %s" cmd @@ exntos exn
2732 | _pid -> ()
2733 else
2734 let anchor = getanchor () in
2735 let ranchor = !S.path, !S.password, anchor, !S.origin in
2736 S.origin := E.s;
2737 S.ranchors := ranchor :: !S.ranchors;
2738 opendoc path E.s;
2740 if substratis spec 0 "page="
2741 then
2742 match Scanf.sscanf spec "page=%d" (fun n -> n) with
2743 | exception exn ->
2744 adderrfmt "error parsing remote destination" "%s %s" spec @@ exntos exn
2745 | pageno ->
2746 S.anchor := (pageno, 0.0, 0.0);
2747 dospawn @@ lazy (Printf.sprintf "%s -page %d %S"
2748 !S.selfexec pageno path);
2749 else (
2750 S.nameddest := dest;
2751 dospawn @@ lazy (!S.selfexec ^ " " ^ path ^ " -dest " ^ dest)
2754 let gotounder = function
2755 | Ulinkuri s when Ffi.isexternallink s ->
2756 if substratis s 0 "file://"
2757 then gotoremote @@ String.sub s 7 (String.length s - 7)
2758 else Help.gotouri conf.urilauncher s
2759 | Ulinkuri s ->
2760 let pageno, x, y = Ffi.uritolocation s in
2761 addnav ();
2762 gotopagexy pageno x y
2763 | Utext _ | Unone -> ()
2764 | Utextannot (opaque, slinkindex) -> enterannotmode opaque slinkindex
2765 | Ufileannot (opaque, slinkindex) ->
2766 if emptystr conf.savecmd
2767 then adderrmsg "savepath-command is empty"
2768 "don't know where to save attachment"
2769 else
2770 let filename = Ffi.getfileannot opaque slinkindex in
2771 let savecmd = Str.global_replace Re.percent filename conf.savecmd in
2772 let path =
2773 getcmdoutput
2774 (adderrfmt savecmd
2775 "failed to obtain path to the saved attachment: %s") savecmd
2777 Ffi.savefileannot opaque slinkindex path
2779 let gotooutline (_, _, kind) =
2780 match kind with
2781 | Onone -> ()
2782 | Oanchor ((pageno, y, _) as anchor) ->
2783 addnav ();
2784 gotoxy !S.x @@
2785 getanchory (if conf.presentation then (pageno, y, 1.0) else anchor)
2786 | Ouri uri -> gotounder (Ulinkuri uri)
2787 | Olaunch cmd -> error "gotounder (Ulaunch %S)" cmd
2788 | Oremote (remote, pageno) ->
2789 error "gotounder (Uremote (%S,%d) )" remote pageno
2790 | Ohistory hist -> gotohist hist
2791 | Oremotedest (path, dest) ->
2792 error "gotounder (Uremotedest (%S, %S))" path dest
2794 class outlinesoucebase fetchoutlines = object (self)
2795 inherit lvsourcebase
2796 val mutable m_items = E.a
2797 val mutable m_minfo = E.a
2798 val mutable m_orig_items = E.a
2799 val mutable m_orig_minfo = E.a
2800 val mutable m_narrow_patterns = []
2801 val mutable m_gen = -1
2803 method getitemcount = Array.length m_items
2805 method getitem n =
2806 let s, n, _ = m_items.(n) in
2807 (s, n+0)
2809 method exit ~(uioh:uioh) ~cancel ~active ~(first:int) ~pan : uioh option =
2810 ignore (uioh, first);
2811 let items, minfo =
2812 if m_narrow_patterns = []
2813 then m_orig_items, m_orig_minfo
2814 else m_items, m_minfo
2816 m_pan <- pan;
2817 if not cancel
2818 then (
2819 m_items <- items;
2820 m_minfo <- minfo;
2821 gotooutline m_items.(active);
2823 else (
2824 m_items <- items;
2825 m_minfo <- minfo;
2827 None
2829 method hasaction (_:int) = true
2831 method greetmsg =
2832 if Array.length m_items != Array.length m_orig_items
2833 then
2834 let s =
2835 match m_narrow_patterns with
2836 | one :: [] -> one
2837 | many -> String.concat Utf8syms.ellipsis (List.rev many)
2839 "Narrowed to " ^ s ^ " (ctrl-u to restore)"
2840 else E.s
2842 method statestr =
2843 match m_narrow_patterns with
2844 | [] -> E.s
2845 | one :: [] -> one
2846 | head :: _ -> Utf8syms.ellipsis ^ head
2848 method narrow pattern =
2849 match Str.regexp_case_fold pattern with
2850 | exception _ -> ()
2851 | re ->
2852 let rec loop accu minfo n =
2853 if n = -1
2854 then (
2855 m_items <- Array.of_list accu;
2856 m_minfo <- Array.of_list minfo;
2858 else
2859 let (s, _, _) as o = m_items.(n) in
2860 let accu, minfo =
2861 match Str.search_forward re s 0 with
2862 | exception Not_found -> accu, minfo
2863 | first -> o :: accu, (first, Str.match_end ()) :: minfo
2865 loop accu minfo (n-1)
2867 loop [] [] (Array.length m_items - 1)
2869 method! getminfo = m_minfo
2871 method denarrow =
2872 m_orig_items <- fetchoutlines ();
2873 m_minfo <- m_orig_minfo;
2874 m_items <- m_orig_items
2876 method add_narrow_pattern pattern =
2877 m_narrow_patterns <- pattern :: m_narrow_patterns
2879 method del_narrow_pattern =
2880 match m_narrow_patterns with
2881 | _ :: rest -> m_narrow_patterns <- rest
2882 | [] -> ()
2884 method renarrow =
2885 self#denarrow;
2886 match m_narrow_patterns with
2887 | pattern :: [] -> self#narrow pattern; pattern
2888 | list ->
2889 List.fold_left (fun accu pattern ->
2890 self#narrow pattern;
2891 pattern ^ Utf8syms.ellipsis ^ accu) E.s list
2893 method calcactive (_:anchor) = 0
2895 method reset anchor items =
2896 if !S.gen != m_gen
2897 then (
2898 m_orig_items <- items;
2899 m_items <- items;
2900 m_narrow_patterns <- [];
2901 m_minfo <- E.a;
2902 m_orig_minfo <- E.a;
2903 m_gen <- !S.gen;
2905 else (
2906 if items != m_orig_items
2907 then (
2908 m_orig_items <- items;
2909 if m_narrow_patterns == []
2910 then m_items <- items;
2913 let active = self#calcactive anchor in
2914 m_active <- active;
2915 m_first <- firstof m_first active
2918 let outlinesource fetchoutlines = object
2919 inherit outlinesoucebase fetchoutlines
2920 method! calcactive anchor =
2921 let rely = getanchory anchor in
2922 let rec loop n best bestd =
2923 if n = Array.length m_items
2924 then best
2925 else
2926 let _, _, kind = m_items.(n) in
2927 match kind with
2928 | Oanchor anchor ->
2929 let orely = getanchory anchor in
2930 let d = abs (orely - rely) in
2931 if d < bestd
2932 then loop (n+1) n d
2933 else loop (n+1) best bestd
2934 | Onone | Oremote _ | Olaunch _
2935 | Oremotedest _ | Ouri _ | Ohistory _ ->
2936 loop (n+1) best bestd
2938 loop 0 ~-1 max_int
2941 let enteroutlinemode, enterbookmarkmode, enterhistmode =
2942 let fetchoutlines sourcetype () =
2943 match sourcetype with
2944 | `bookmarks -> Array.of_list !S.bookmarks
2945 | `outlines -> !S.outlines
2946 | `history -> genhistoutlines () |> Array.of_list
2948 let so = outlinesource (fetchoutlines `outlines) in
2949 let sb = outlinesource (fetchoutlines `bookmarks) in
2950 let sh = outlinesource (fetchoutlines `history) in
2951 let mkselector sourcetype source =
2952 (fun emptymsg ->
2953 let outlines = fetchoutlines sourcetype () in
2954 if Array.length outlines = 0
2955 then showtext ' ' emptymsg
2956 else (
2957 resetmstate ();
2958 Wsi.setcursor Wsi.CURSOR_INHERIT;
2959 let anchor = getanchor () in
2960 source#reset anchor outlines;
2961 S.text := source#greetmsg;
2962 new outlinelistview ~zebra:(sourcetype=`history) ~source |> setuioh;
2963 Glutils.postRedisplay "enter selector";
2967 let mkenter src errmsg s = fun () -> mkselector src s errmsg in
2968 ( mkenter `outlines "document has no outline" so
2969 , mkenter `bookmarks "document has no bookmarks (yet)" sb
2970 , mkenter `history "history is empty" sh )
2972 let addbookmark title a =
2973 let b = List.filter (fun (title', _, _) -> title <> title') !S.bookmarks in
2974 S.bookmarks := (title, 0, Oanchor a) :: b
2976 let quickbookmark ?title () =
2977 match !S.layout with
2978 | [] -> ()
2979 | l :: _ ->
2980 let title =
2981 match title with
2982 | None ->
2983 Unix.(
2984 let tm = localtime (now ()) in
2985 Printf.sprintf
2986 "Quick (page %d) (bookmarked on %02d/%02d/%d at %02d:%02d)"
2987 (l.pageno+1)
2988 tm.tm_mday (tm.tm_mon+1) (tm.tm_year+1900) tm.tm_hour tm.tm_min
2990 | Some title -> title
2992 addbookmark title (getanchor1 l)
2994 let setautoscrollspeed step goingdown =
2995 let incr = max 1 ((abs step) / 2) in
2996 let incr = if goingdown then incr else -incr in
2997 let astep = boundastep !S.winh (step + incr) in
2998 S.autoscroll := Some astep
3000 let canpan () =
3001 match conf.columns with
3002 | Csplit _ -> true
3003 | Csingle _ | Cmulti _ -> !S.x != 0 || conf.zoom > 1.0
3005 let existsinrow pageno (columns, coverA, coverB) p =
3006 let last = ((pageno - coverA) mod columns) + columns in
3007 let rec any = function
3008 | [] -> false
3009 | l :: rest ->
3010 if l.pageno = coverA - 1 || l.pageno = !S.pagecount - coverB
3011 then p l
3012 else (
3013 if not (p l)
3014 then (if l.pageno = last then false else any rest)
3015 else true
3018 any !S.layout
3020 let nextpage () =
3021 match !S.layout with
3022 | [] ->
3023 let pageno = page_of_y !S.y in
3024 gotoxy !S.x (getpagey (pageno+1))
3025 | l :: rest ->
3026 match conf.columns with
3027 | Csingle _ ->
3028 if conf.presentation && rest == [] && l.pageh > l.pagey + l.pagevh
3029 then
3030 let y = U.clamp (U.pgscale !S.winh) in
3031 gotoxy !S.x y
3032 else
3033 let pageno = min (l.pageno+1) (!S.pagecount-1) in
3034 gotoxy !S.x (getpagey pageno)
3035 | Cmulti ((c, _, _) as cl, _) ->
3036 if conf.presentation
3037 && (existsinrow l.pageno cl
3038 (fun l -> l.pageh > l.pagey + l.pagevh))
3039 then
3040 let y = U.clamp (U.pgscale !S.winh) in
3041 gotoxy !S.x y
3042 else
3043 let pageno = min (l.pageno+c) (!S.pagecount-1) in
3044 gotoxy !S.x (getpagey pageno)
3045 | Csplit (n, _) ->
3046 if l.pageno < !S.pagecount - 1 || l.pagecol < n - 1
3047 then
3048 let pagey, pageh = getpageyh l.pageno in
3049 let pagey = pagey + pageh * l.pagecol in
3050 let ips = if l.pagecol = 0 then 0 else conf.interpagespace in
3051 gotoxy !S.x (pagey + pageh + ips)
3053 let prevpage () =
3054 match !S.layout with
3055 | [] ->
3056 let pageno = page_of_y !S.y in
3057 gotoxy !S.x (getpagey (pageno-1))
3058 | l :: _ ->
3059 match conf.columns with
3060 | Csingle _ ->
3061 if conf.presentation && l.pagey != 0
3062 then gotoxy !S.x (U.clamp (U.pgscale ~-(!S.winh)))
3063 else
3064 let pageno = max 0 (l.pageno-1) in
3065 gotoxy !S.x (getpagey pageno)
3066 | Cmulti ((c, _, coverB) as cl, _) ->
3067 if conf.presentation &&
3068 (existsinrow l.pageno cl (fun l -> l.pagey != 0))
3069 then gotoxy !S.x (U.clamp (U.pgscale ~-(!S.winh)))
3070 else
3071 let decr =
3072 if l.pageno = !S.pagecount - coverB
3073 then 1
3074 else c
3076 let pageno = max 0 (l.pageno-decr) in
3077 gotoxy !S.x (getpagey pageno)
3078 | Csplit (n, _) ->
3079 let y =
3080 if l.pagecol = 0
3081 then
3082 if l.pageno = 0
3083 then l.pagey
3084 else
3085 let pageno = max 0 (l.pageno-1) in
3086 let pagey, pageh = getpageyh pageno in
3087 pagey + (n-1)*pageh
3088 else
3089 let pagey, pageh = getpageyh l.pageno in
3090 pagey + pageh * (l.pagecol-1) - conf.interpagespace
3092 gotoxy !S.x y
3094 let save () =
3095 if emptystr conf.savecmd
3096 then adderrmsg "savepath-command is empty"
3097 "don't know where to save modified document"
3098 else
3099 let savecmd = Str.global_replace Re.percent !S.path conf.savecmd in
3100 let path =
3101 getcmdoutput
3102 (adderrfmt savecmd "failed to obtain path to the saved copy: %s")
3103 savecmd
3105 if nonemptystr path
3106 then
3107 let tmp = path ^ ".tmp" in
3108 Ffi.savedoc tmp;
3109 Unix.rename tmp path
3111 let viewkeyboard key mask =
3112 let enttext te =
3113 let mode = !S.mode in
3114 S.mode := Textentry (te, fun _ -> S.mode := mode);
3115 S.text := E.s;
3116 enttext ();
3117 Glutils.postRedisplay "view:enttext"
3118 and histback () =
3119 match !S.nav.past with
3120 | [] -> ()
3121 | prev :: prest ->
3122 S.nav := { past = prest ; future = getanchor () :: !S.nav.future; };
3123 gotoxy !S.x (getanchory prev)
3125 let ctrl = Wsi.withctrl mask in
3126 let open Keys in
3127 match Wsi.ks2kt key with
3128 | Ascii 'Q' -> exit 0
3129 | Ascii 'z' ->
3130 let yloc f =
3131 match List.rev !S.rects with
3132 | [] -> ()
3133 | (pageno, _, (_, y0, _, y1, _, y2, _, y3)) :: _ ->
3134 f pageno (y0, y1, y2, y3)
3135 and fsel f (y0, y1, y2, y3) = f y0 y1 |> f y2 |> f y3 |> truncate in
3136 let ondone msg = S.text := msg
3137 and zmod _ _ k =
3138 match [@warning "-fragile-match"] k with
3139 | Keys.Ascii 'z' ->
3140 let f pageno ys =
3141 let miny = fsel min ys in
3142 let hh = (fsel max ys - miny)/2 in
3143 gotopage1 pageno (miny + hh - !S.winh/2)
3145 yloc f;
3146 TEdone "center"
3147 | Keys.Ascii 't' ->
3148 let f pageno ys = gotopage1 pageno @@ fsel min ys in
3149 yloc f;
3150 TEdone "top"
3151 | Keys.Ascii 'b' ->
3152 let f pageno ys = gotopage1 pageno (fsel max ys - !S.winh) in
3153 yloc f;
3154 TEdone "bottom"
3155 | _ -> TEstop
3157 enttext (": ", E.s, None, zmod !S.mode, ondone, true)
3158 | Ascii 'W' ->
3159 if Ffi.hasunsavedchanges ()
3160 then save ()
3161 | Insert ->
3162 if conf.angle mod 360 = 0 && not (isbirdseye !S.mode)
3163 then (
3164 S.mode := (
3165 match !S.lnava with
3166 | None -> LinkNav (Ltgendir 0)
3167 | Some pn -> LinkNav (Ltexact pn)
3169 gotoxy !S.x !S.y;
3171 else impmsg "keyboard link navigation does not work under rotation"
3172 | Escape | Ascii 'q' ->
3173 begin match !S.mstate with
3174 | Mzoomrect _ ->
3175 resetmstate ();
3176 Glutils.postRedisplay "kill rect";
3177 | Msel _
3178 | Mpan _
3179 | Mscrolly | Mscrollx
3180 | Mzoom _
3181 | Mnone ->
3182 begin match !S.mode with
3183 | LinkNav ln ->
3184 begin match ln with
3185 | Ltexact pl -> S.lnava := Some pl
3186 | Ltgendir _ | Ltnotready _ -> S.lnava := None
3187 end;
3188 S.mode := View;
3189 Glutils.postRedisplay "esc leave linknav"
3190 | Birdseye _ | Textentry _ | View ->
3191 match !S.ranchors with
3192 | [] -> raise Quit
3193 | (path, password, anchor, origin) :: rest ->
3194 S.ranchors := rest;
3195 S.anchor := anchor;
3196 S.origin := origin;
3197 S.nameddest := E.s;
3198 opendoc path password
3199 end;
3200 end;
3201 | Ascii 'o' -> enteroutlinemode ()
3202 | Ascii 'u' ->
3203 S.rects := [];
3204 S.text := E.s;
3205 Hashtbl.iter (fun _ opaque -> Ffi.clearmark opaque) S.pagemap;
3206 Glutils.postRedisplay "dehighlight";
3207 | Ascii (('/' | '?') as c) ->
3208 let ondone isforw s =
3209 cbput !S.hists.pat s;
3210 S.searchpattern := s;
3211 search s isforw
3213 enttext (String.make 1 c, E.s, Some (onhist !S.hists.pat),
3214 textentry, ondone (c = '/'), true)
3215 | Ascii '+' | Ascii '=' when ctrl ->
3216 let incr = if conf.zoom +. 0.01 > 0.1 then 0.1 else 0.01 in
3217 pivotzoom (conf.zoom +. incr)
3218 | Ascii '+' ->
3219 let ondone s =
3220 let n =
3221 try int_of_string s with exn ->
3222 S.text := Printf.sprintf "bad integer `%s': %s" s @@ exntos exn;
3223 max_int
3225 if n != max_int
3226 then (
3227 conf.pagebias <- n;
3228 S.text := "page bias is now " ^ string_of_int n;
3231 enttext ("page bias: ", E.s, None, intentry, ondone, true)
3232 | Ascii '-' when ctrl ->
3233 let decr = if conf.zoom -. 0.1 < 0.1 then 0.01 else 0.1 in
3234 pivotzoom (max 0.01 (conf.zoom -. decr))
3235 | Ascii '-' ->
3236 let ondone msg = S.text := msg in
3237 enttext ("option: ", E.s, None,
3238 optentry !S.mode, ondone, true)
3239 | Ascii '0' when ctrl ->
3240 if conf.zoom = 1.0
3241 then gotoxy 0 !S.y
3242 else setzoom 1.0
3243 | Ascii ('1'|'2' as c) when ctrl && conf.fitmodel != FitPage ->
3244 let cols =
3245 match conf.columns with
3246 | Csingle _ | Cmulti _ -> 1
3247 | Csplit (n, _) -> n
3249 let h = !S.winh -
3250 conf.interpagespace lsl (if conf.presentation then 1 else 0)
3252 let zoom = Ffi.zoomforh !S.winw h 0 cols in
3253 if zoom > 0.0 && (c = '2' || zoom < 1.0)
3254 then setzoom zoom
3255 | Ascii '3' when ctrl ->
3256 let fm =
3257 match conf.fitmodel with
3258 | FitWidth -> FitProportional
3259 | FitProportional -> FitPage
3260 | FitPage -> FitWidth
3262 S.text := "fit model: " ^ FMTE.to_string fm;
3263 reqlayout conf.angle fm
3264 | Ascii '4' when ctrl ->
3265 let zoom = Ffi.getmaxw () /. float !S.winw in
3266 if zoom > 0.0 then setzoom zoom
3267 | Fn 9 | Ascii '9' when ctrl -> togglebirdseye ()
3268 | Ascii ('0'..'9' as c) when not ctrl ->
3269 let ondone s =
3270 let n =
3271 try int_of_string s with exn ->
3272 adderrfmt "int_of_string" "`%s': %s" s @@ exntos exn;
3275 if n >= 0
3276 then (
3277 addnav ();
3278 cbput !S.hists.pag (string_of_int n);
3279 gotopage1 (n + conf.pagebias - 1) 0;
3282 let pageentry text = function [@warning "-fragile-match"]
3283 | Keys.Ascii 'g' -> TEdone text
3284 | key -> intentry text key
3286 enttext (":", String.make 1 c, Some (onhist !S.hists.pag),
3287 pageentry, ondone, true)
3288 | Ascii 'b' ->
3289 conf.scrollb <- if conf.scrollb = 0 then (scrollbvv lor scrollbhv) else 0;
3290 Glutils.postRedisplay "toggle scrollbar";
3291 | Ascii 'B' ->
3292 S.bzoom := not !S.bzoom;
3293 S.rects := [];
3294 showtext ' ' ("block zoom " ^ onoffs !S.bzoom)
3295 | Ascii 'l' ->
3296 conf.hlinks <- not conf.hlinks;
3297 S.text := "highlightlinks " ^ onoffs conf.hlinks;
3298 Glutils.postRedisplay "toggle highlightlinks"
3299 | Ascii 'F' ->
3300 if conf.angle mod 360 = 0
3301 then (
3302 S.glinks := true;
3303 let mode = !S.mode in
3304 let te = ("goto: ", E.s, None, linknentry, linknact gotounder, false) in
3305 S.mode := Textentry (te, (fun _ -> S.glinks := false; S.mode := mode));
3306 S.text := E.s;
3307 Glutils.postRedisplay "view:linkent(F)"
3309 else impmsg "hint mode does not work under rotation"
3310 | Ascii 'y' ->
3311 S.glinks := true;
3312 let mode = !S.mode in
3313 let te = ("copy: ", E.s, None, linknentry,
3314 linknact (fun under -> selstring conf.selcmd (undertext under)),
3315 false) in
3316 S.mode := Textentry (te, (fun _ -> S.glinks := false; S.mode := mode));
3317 S.text := E.s;
3318 Glutils.postRedisplay "view:linkent"
3319 | Ascii 'a' ->
3320 begin match !S.autoscroll with
3321 | Some step ->
3322 conf.autoscrollstep <- step;
3323 S.autoscroll := None
3324 | None -> S.autoscroll := Some conf.autoscrollstep
3326 | Ascii 'p' when ctrl -> launchpath ()
3327 | Ascii 'P' ->
3328 setpresentationmode (not conf.presentation);
3329 showtext ' ' ("presentation mode " ^ onoffs conf.presentation)
3330 | Ascii 'f' ->
3331 if List.mem Wsi.Fullscreen !S.winstate
3332 then Wsi.reshape conf.cwinw conf.cwinh
3333 else Wsi.fullscreen ()
3334 | Ascii ('p'|'N') -> search !S.searchpattern false
3335 | Ascii 'n' | Fn 3 -> search !S.searchpattern true
3336 | Ascii 't' ->
3337 begin match !S.layout with
3338 | [] -> ()
3339 | l :: _ -> gotoxy !S.x (getpagey l.pageno)
3341 | Ascii ' ' -> nextpage ()
3342 | Delete -> prevpage ()
3343 | Ascii '=' -> showtext ' ' (describe_layout !S.layout);
3344 | Ascii 'w' ->
3345 begin match !S.layout with
3346 | [] -> ()
3347 | l :: _ ->
3348 Wsi.reshape l.pagew l.pageh;
3349 Glutils.postRedisplay "w"
3351 | Ascii '\'' -> enterbookmarkmode ()
3352 | Ascii 'i' -> enterinfomode ()
3353 | Ascii 'e' when Buffer.length S.errmsgs > 0 -> entermsgsmode ()
3354 | Ascii 'm' ->
3355 let ondone s =
3356 match !S.layout with
3357 | l :: _ when nonemptystr s -> addbookmark s @@ getanchor1 l
3358 | _ -> ()
3360 enttext ("bookmark: ", E.s, None, textentry, ondone, true)
3361 | Ascii '~' ->
3362 quickbookmark ();
3363 showtext ' ' "Quick bookmark added";
3364 | Ascii 'x' -> !S.roamf ()
3365 | Ascii ('<'|'>' as c) ->
3366 reqlayout (conf.angle + (if c = '>' then 30 else -30)) conf.fitmodel
3367 | Ascii ('['|']' as c) ->
3368 conf.colorscale <-
3369 bound (conf.colorscale +. (if c = ']' then 0.1 else -0.1)) 0.0 1.0;
3370 Glutils.postRedisplay "brightness";
3371 | Ascii 'c' when !S.mode = View ->
3372 if Wsi.withalt mask
3373 then (
3374 if conf.zoom > 1.0
3375 then
3376 let m = (!S.winw - !S.w) / 2 in
3377 gotoxy m !S.y
3379 else
3380 let (c, a, b), z =
3381 match !S.prevcolumns with
3382 | None -> (1, 0, 0), 1.0
3383 | Some (columns, z) ->
3384 let cab =
3385 match columns with
3386 | Csplit (c, _) -> -c, 0, 0
3387 | Cmulti ((c, a, b), _) -> c, a, b
3388 | Csingle _ -> 1, 0, 0
3390 cab, z
3392 setcolumns View c a b;
3393 setzoom z
3394 | Down | Up when ctrl && Wsi.withshift mask ->
3395 let zoom, x = !S.prevzoom in
3396 setzoom zoom;
3397 S.x := x;
3398 | Up ->
3399 begin match !S.autoscroll with
3400 | None ->
3401 begin match !S.mode with
3402 | Birdseye beye -> upbirdseye 1 beye
3403 | Textentry _ | View | LinkNav _ ->
3404 if ctrl
3405 then gotoxy !S.x (U.clamp ~-(!S.winh/2))
3406 else (
3407 if not (Wsi.withshift mask) && conf.presentation
3408 then prevpage ()
3409 else gotoxy !S.x (U.clamp (-conf.scrollstep))
3412 | Some n -> setautoscrollspeed n false
3414 | Down ->
3415 begin match !S.autoscroll with
3416 | None ->
3417 begin match !S.mode with
3418 | Birdseye beye -> downbirdseye 1 beye
3419 | Textentry _ | View | LinkNav _ ->
3420 if ctrl
3421 then gotoxy !S.x (U.clamp (!S.winh/2))
3422 else (
3423 if not (Wsi.withshift mask) && conf.presentation
3424 then nextpage ()
3425 else gotoxy !S.x (U.clamp (conf.scrollstep))
3428 | Some n -> setautoscrollspeed n true
3430 | Ascii 'H' -> enterhistmode ()
3431 | Fn 1 when Wsi.withalt mask -> enterhistmode ()
3432 | Fn 1 -> enterhelpmode ()
3433 | Left | Right when not (Wsi.withalt mask) ->
3434 if canpan ()
3435 then
3436 let dx =
3437 if ctrl
3438 then !S.winw / 2
3439 else conf.hscrollstep
3441 let dx =
3442 let pv = Wsi.ks2kt key in
3443 if pv = Keys.Left then dx else -dx
3445 gotoxy (U.panbound (!S.x + dx)) !S.y
3446 else (
3447 S.text := E.s;
3448 Glutils.postRedisplay "left/right"
3450 | Prior ->
3451 let y =
3452 if ctrl
3453 then
3454 match !S.layout with
3455 | [] -> !S.y
3456 | l :: _ -> !S.y - l.pagey
3457 else U.clamp (U.pgscale ~- !S.winh)
3459 gotoxy !S.x y
3460 | Next ->
3461 let y =
3462 if ctrl
3463 then
3464 match List.rev !S.layout with
3465 | [] -> !S.y
3466 | l :: _ -> getpagey l.pageno
3467 else U.clamp (U.pgscale !S.winh)
3469 gotoxy !S.x y
3470 | Ascii 'g' | Home ->
3471 addnav ();
3472 gotoxy 0 0
3473 | Ascii 'G' | End ->
3474 addnav ();
3475 gotoxy 0 (U.clamp !S.maxy)
3476 | Right when Wsi.withalt mask ->
3477 (match !S.nav.future with
3478 | [] -> ()
3479 | next :: frest ->
3480 S.nav := { past = getanchor () :: !S.nav.past; future = frest; };
3481 gotoxy !S.x (getanchory next)
3483 | Left when Wsi.withalt mask -> histback ()
3484 | Backspace -> histback ()
3485 | Ascii 'r' -> reload ()
3486 | Ascii 'v' when conf.debug ->
3487 S.rects := [];
3488 List.iter (fun l ->
3489 match getopaque l.pageno with
3490 | exception Not_found -> ()
3491 | opaque ->
3492 let x0, y0, x1, y1 = Ffi.pagebbox opaque in
3493 let rect = (float x0, float y0,
3494 float x1, float y0,
3495 float x1, float y1,
3496 float x0, float y1) in
3497 debugrect rect;
3498 let color = (0.0, 0.0, 1.0 /. (l.pageno mod 3 |> float), 0.5) in
3499 S.rects := (l.pageno, color, rect) :: !S.rects;
3500 ) !S.layout;
3501 Glutils.postRedisplay "v";
3502 | Ascii '|' ->
3503 let mode = !S.mode in
3504 let cmd = ref E.s in
3505 let onleave = function
3506 | Cancel -> S.mode := mode
3507 | Confirm ->
3508 List.iter (fun l ->
3509 match getopaque l.pageno with
3510 | exception Not_found -> ()
3511 | opaque -> pipesel opaque !cmd) !S.layout;
3512 S.mode := mode
3514 let ondone s =
3515 cbput !S.hists.sel s;
3516 cmd := s
3518 let te =
3519 "| ", !cmd, Some (onhist !S.hists.sel), textentry, ondone, true
3521 Glutils.postRedisplay "|";
3522 S.mode := Textentry (te, onleave);
3523 | (Ascii _|Fn _|Enter|Left|Right|Code _|Ctrl _) ->
3524 vlog "huh? %s" (Wsi.keyname key)
3526 let linknavkeyboard key mask linknav =
3527 let pv = Wsi.ks2kt key in
3528 let getpage pageno =
3529 let rec loop = function
3530 | [] -> None
3531 | l :: _ when l.pageno = pageno -> Some l
3532 | _ :: rest -> loop rest
3533 in loop !S.layout
3535 let doexact (pageno, n) =
3536 match getopaque pageno, getpage pageno with
3537 | opaque, Some l ->
3538 if pv = Keys.Enter
3539 then
3540 let under = Ffi.getlink opaque n in
3541 Glutils.postRedisplay "link gotounder";
3542 gotounder under;
3543 S.mode := View;
3544 else
3545 let opt, dir =
3546 let open Keys in
3547 match pv with
3548 | Home -> Some (Ffi.findlink opaque LDfirst), -1
3549 | End -> Some (Ffi.findlink opaque LDlast), 1
3550 | Left -> Some (Ffi.findlink opaque (LDleft n)), -1
3551 | Right -> Some (Ffi.findlink opaque (LDright n)), 1
3552 | Up -> Some (Ffi.findlink opaque (LDup n)), -1
3553 | Down -> Some (Ffi.findlink opaque (LDdown n)), 1
3554 | Delete|Escape|Insert|Enter|Next|Prior|Ascii _
3555 | Code _|Fn _|Ctrl _|Backspace -> None, 0
3557 let pwl l dir =
3558 begin match Ffi.findpwl l.pageno dir with
3559 | Pwlnotfound -> ()
3560 | Pwl pageno ->
3561 let notfound dir =
3562 S.mode := LinkNav (Ltgendir dir);
3563 let y, h = getpageyh pageno in
3564 let y =
3565 if dir < 0
3566 then y + h - !S.winh
3567 else y
3569 gotoxy !S.x y
3571 begin match getopaque pageno, getpage pageno with
3572 | opaque, Some _ ->
3573 let link =
3574 let ld = if dir > 0 then LDfirst else LDlast in
3575 Ffi.findlink opaque ld
3577 begin match link with
3578 | Lfound m ->
3579 showlinktype (Ffi.getlink opaque m);
3580 S.mode := LinkNav (Ltexact (pageno, m));
3581 Glutils.postRedisplay "linknav jpage";
3582 | Lnotfound -> notfound dir
3583 end;
3584 | _ | exception Not_found -> notfound dir
3585 end;
3586 end;
3588 begin match opt with
3589 | Some Lnotfound -> pwl l dir;
3590 | Some (Lfound m) ->
3591 if m = n
3592 then pwl l dir
3593 else (
3594 let _, y0, _, y1 = Ffi.getlinkrect opaque m in
3595 if y0 < l.pagey
3596 then gotopage1 l.pageno y0
3597 else (
3598 let d = fstate.fontsize + 1 in
3599 if y1 - l.pagey > l.pagevh - d
3600 then gotopage1 l.pageno (y1 - !S.winh + d)
3601 else Glutils.postRedisplay "linknav";
3603 showlinktype (Ffi.getlink opaque m);
3604 S.mode := LinkNav (Ltexact (l.pageno, m));
3607 | None -> viewkeyboard key mask
3608 end;
3609 | _ | exception Not_found -> viewkeyboard key mask
3611 if pv = Keys.Insert
3612 then (
3613 begin match linknav with
3614 | Ltexact pa -> S.lnava := Some pa
3615 | Ltgendir _ | Ltnotready _ -> ()
3616 end;
3617 S.mode := View;
3618 Glutils.postRedisplay "leave linknav"
3620 else
3621 match linknav with
3622 | Ltgendir _ | Ltnotready _ -> viewkeyboard key mask
3623 | Ltexact exact -> doexact exact
3625 let keyboard key mask =
3626 if (key = Char.code 'g' && Wsi.withctrl mask) && not (istextentry !S.mode)
3627 then wcmd U.interrupt ""
3628 else !S.uioh#key key mask |> setuioh
3630 let birdseyekeyboard key mask
3631 ((oconf, leftx, pageno, hooverpageno, anchor) as beye) =
3632 let incr =
3633 match conf.columns with
3634 | Csingle _ -> 1
3635 | Cmulti ((c, _, _), _) -> c
3636 | Csplit _ -> error "bird's eye split mode"
3638 let pgh layout = List.fold_left
3639 (fun m l -> max l.pageh m) !S.winh layout in
3640 let open Keys in
3641 match Wsi.ks2kt key with
3642 | Ascii 'l' when Wsi.withctrl mask ->
3643 let y, h = getpageyh pageno in
3644 let top = (!S.winh - h) / 2 in
3645 gotoxy !S.x (max 0 (y - top))
3646 | Enter -> leavebirdseye beye false
3647 | Escape -> leavebirdseye beye true
3648 | Up -> upbirdseye incr beye
3649 | Down -> downbirdseye incr beye
3650 | Left -> upbirdseye 1 beye
3651 | Right -> downbirdseye 1 beye
3653 | Prior ->
3654 begin match !S.layout with
3655 | l :: _ ->
3656 if l.pagey != 0
3657 then (
3658 S.mode := Birdseye (oconf, leftx, l.pageno, hooverpageno, anchor);
3659 gotopage1 l.pageno 0;
3661 else (
3662 let layout = layout !S.x (!S.y - !S.winh)
3663 !S.winw
3664 (pgh !S.layout) in
3665 match layout with
3666 | [] -> gotoxy !S.x (U.clamp ~- !S.winh)
3667 | l :: _ ->
3668 S.mode := Birdseye (oconf, leftx, l.pageno, hooverpageno, anchor);
3669 gotopage1 l.pageno 0
3672 | [] -> gotoxy !S.x (U.clamp ~- !S.winh)
3673 end;
3675 | Next ->
3676 begin match List.rev !S.layout with
3677 | l :: _ ->
3678 let layout = layout !S.x
3679 (!S.y + (pgh !S.layout))
3680 !S.winw !S.winh in
3681 begin match layout with
3682 | [] ->
3683 let incr = l.pageh - l.pagevh in
3684 if incr = 0
3685 then (
3686 S.mode :=
3687 Birdseye (
3688 oconf, leftx, !S.pagecount - 1, hooverpageno, anchor
3690 Glutils.postRedisplay "birdseye pagedown";
3692 else gotoxy !S.x (U.clamp (incr + conf.interpagespace*2));
3694 | l :: _ ->
3695 S.mode := Birdseye (oconf, leftx, l.pageno, hooverpageno, anchor);
3696 gotopage1 l.pageno 0;
3699 | [] -> gotoxy !S.x (U.clamp !S.winh)
3700 end;
3702 | Home ->
3703 S.mode := Birdseye (oconf, leftx, 0, hooverpageno, anchor);
3704 gotopage1 0 0
3706 | End ->
3707 let pageno = !S.pagecount - 1 in
3708 S.mode := Birdseye (oconf, leftx, pageno, hooverpageno, anchor);
3709 if not (U.pagevisible !S.layout pageno)
3710 then
3711 let h =
3712 match List.rev !S.pdims with
3713 | [] -> !S.winh
3714 | (_, _, h, _) :: _ -> h
3716 gotoxy
3717 !S.x
3718 (max 0 (getpagey pageno - (!S.winh - h - conf.interpagespace)))
3719 else Glutils.postRedisplay "birdseye end";
3721 | Delete|Insert|Ascii _|Code _|Ctrl _|Fn _|Backspace -> viewkeyboard key mask
3723 let drawpage l =
3724 let color =
3725 match !S.mode with
3726 | Textentry _ -> U.scalecolor 0.4
3727 | LinkNav _ | View -> U.scalecolor 1.0
3728 | Birdseye (_, _, pageno, hooverpageno, _) ->
3729 if l.pageno = hooverpageno
3730 then U.scalecolor 0.9
3731 else (
3732 if l.pageno = pageno
3733 then (
3734 let c = U.scalecolor 1.0 in
3735 GlDraw.color c;
3736 GlDraw.line_width 3.0;
3737 let dispx = l.pagedispx in
3738 Glutils.linerect
3739 (float (dispx-1)) (float (l.pagedispy-1))
3740 (float (dispx+l.pagevw+1))
3741 (float (l.pagedispy+l.pagevh+1));
3742 GlDraw.line_width 1.0;
3745 else U.scalecolor 0.8
3748 drawtiles l color
3750 let postdrawpage l linkindexbase =
3751 match getopaque l.pageno with
3752 | exception Not_found -> 0
3753 | opaque ->
3754 if tileready l l.pagex l.pagey
3755 then
3756 let x = l.pagedispx - l.pagex
3757 and y = l.pagedispy - l.pagey in
3758 let hlmask =
3759 match conf.columns with
3760 | Csingle _ | Cmulti _ ->
3761 (if conf.hlinks then 1 else 0)
3762 + (if !S.glinks
3763 && not (isbirdseye !S.mode) then 2 else 0)
3764 | Csplit _ -> 0
3766 let s =
3767 match !S.mode with
3768 | Textentry ((_, s, _, _, _, _), _) when !S.glinks -> s
3769 | Textentry _
3770 | Birdseye _
3771 | View
3772 | LinkNav _ -> E.s
3774 let n =
3775 Ffi.postprocess opaque hlmask x y
3776 (linkindexbase, s, conf.hfsize, conf.hcs) in
3777 if n < 0
3778 then (Glutils.redisplay := not @@ hasdata !S.ss; 0)
3779 else n
3780 else 0
3782 let scrollindicator () =
3783 let sbw, ph, sh = !S.uioh#scrollph in
3784 let sbh, pw, sw = !S.uioh#scrollpw in
3786 let x0,x1,hx0 =
3787 if conf.leftscroll
3788 then (0, sbw, sbw)
3789 else ((!S.winw - sbw), !S.winw, 0)
3792 Gl.enable `blend;
3793 GlFunc.blend_func ~src:`src_alpha ~dst:`one_minus_src_alpha;
3794 let (r, g, b, alpha) = conf.sbarcolor in
3795 GlDraw.color (r, g, b) ~alpha;
3796 Glutils.filledrect (float x0) 0. (float x1) (float !S.winh);
3797 Glutils.filledrect
3798 (float hx0) (float (!S.winh - sbh))
3799 (float (hx0 + !S.winw)) (float !S.winh);
3800 let (r, g, b, alpha) = conf.sbarhndlcolor in
3801 GlDraw.color (r, g, b) ~alpha;
3803 Glutils.filledrect (float x0) ph (float x1) (ph +. sh);
3804 let pw = pw +. float hx0 in
3805 Glutils.filledrect pw (float (!S.winh - sbh)) (pw +. sw) (float !S.winh);
3806 Gl.disable `blend
3808 let showsel () =
3809 match !S.mstate with
3810 | Mnone | Mscrolly | Mscrollx | Mpan _ | Mzoom _ | Mzoomrect _ -> ()
3811 | Msel ((x0, y0), (x1, y1)) ->
3812 let identify opaque l px py = Some (opaque, l.pageno, px, py) in
3813 let o0,n0,px0,py0 =
3814 onppundermouse identify x0 y0 (Opaque.of_string E.s, -1, 0, 0) in
3815 let _o1,n1,px1,py1 =
3816 onppundermouse identify x1 y1 (Opaque.of_string E.s, -1, 0, 0) in
3817 if n0 != -1 && n0 = n1 then Ffi.seltext o0 (px0, py0, px1, py1)
3819 let showrects = function
3820 | [] -> ()
3821 | rects ->
3822 Gl.enable `blend;
3823 GlDraw.color (0.0, 0.0, 1.0) ~alpha:0.5;
3824 GlFunc.blend_func ~src:`src_alpha ~dst:`one_minus_src_alpha;
3825 List.iter
3826 (fun (pageno, c, (x0, y0, x1, y1, x2, y2, x3, y3)) ->
3827 List.iter (fun l ->
3828 if l.pageno = pageno
3829 then
3830 let dx = float (l.pagedispx - l.pagex) in
3831 let dy = float (l.pagedispy - l.pagey) in
3832 let r, g, b, alpha = c in
3833 GlDraw.color (r, g, b) ~alpha;
3834 Glutils.filledrect2
3835 (x0+.dx) (y0+.dy)
3836 (x1+.dx) (y1+.dy)
3837 (x3+.dx) (y3+.dy)
3838 (x2+.dx) (y2+.dy);
3839 ) !S.layout
3840 ) rects;
3841 Gl.disable `blend
3843 let display () =
3844 let sc (r, g, b) = let s = conf.colorscale in (r *. s, g *. s, b *. s) in
3845 GlDraw.color (sc conf.bgcolor);
3846 GlClear.color (sc conf.bgcolor);
3847 GlClear.clear [`color];
3848 List.iter drawpage !S.layout;
3849 let rects =
3850 match !S.mode with
3851 | LinkNav (Ltgendir _) | LinkNav (Ltnotready _)
3852 | Birdseye _
3853 | Textentry _
3854 | View -> !S.rects
3855 | LinkNav (Ltexact (pageno, linkno)) ->
3856 match getopaque pageno with
3857 | exception Not_found -> !S.rects
3858 | opaque ->
3859 let x0, y0, x1, y1 = Ffi.getlinkrect opaque linkno in
3860 let color =
3861 if conf.invert
3862 then (1.0, 1.0, 1.0, 0.5)
3863 else (0.0, 0.0, 0.5, 0.5)
3865 (pageno, color,
3866 (float x0, float y0,
3867 float x1, float y0,
3868 float x1, float y1,
3869 float x0, float y1)
3870 ) :: !S.rects
3872 showrects rects;
3873 let rec postloop linkindexbase = function
3874 | l :: rest ->
3875 let linkindexbase = linkindexbase + postdrawpage l linkindexbase in
3876 postloop linkindexbase rest
3877 | [] -> ()
3879 showsel ();
3880 postloop 0 !S.layout;
3881 !S.uioh#display;
3882 begin match !S.mstate with
3883 | Mzoomrect ((x0, y0), (x1, y1)) ->
3884 Gl.enable `blend;
3885 GlDraw.color (0.3, 0.3, 0.3) ~alpha:0.5;
3886 GlFunc.blend_func ~src:`src_alpha ~dst:`one_minus_src_alpha;
3887 Glutils.filledrect (float x0) (float y0) (float x1) (float y1);
3888 Gl.disable `blend;
3889 | Msel _
3890 | Mpan _
3891 | Mscrolly | Mscrollx
3892 | Mzoom _
3893 | Mnone -> ()
3894 end;
3895 enttext ();
3896 scrollindicator ();
3897 Wsi.swapb ()
3899 let display () =
3900 match !S.reload with
3901 | Some (x, y, t) ->
3902 if x != !S.x || y != !S.y || abs_float @@ now () -. t > 0.5
3903 || (!S.layout != [] && layoutready !S.layout)
3904 then (
3905 S.reload := None;
3906 display ()
3908 | None -> display ()
3910 let zoomrect x y x1 y1 =
3911 let x0 = min x x1
3912 and x1 = max x x1
3913 and y0 = min y y1 in
3914 let zoom = (float !S.w) /. float (x1 - x0) in
3915 let margin =
3916 let simple () =
3917 if !S.w < !S.winw
3918 then (!S.winw - !S.w) / 2
3919 else 0
3921 match conf.fitmodel with
3922 | FitWidth | FitProportional -> simple ()
3923 | FitPage ->
3924 match conf.columns with
3925 | Csplit _ ->
3926 onppundermouse (fun _ l _ _ -> Some l.pagedispx) x0 y0 x0
3927 | Cmulti _ | Csingle _ -> simple ()
3929 gotoxy ((!S.x + margin) - x0) (!S.y + y0);
3930 S.anchor := getanchor ();
3931 setzoom zoom;
3932 resetmstate ()
3934 let annot inline x y =
3935 match unproject x y with
3936 | Some (opaque, n, ux, uy) ->
3937 let add text =
3938 Ffi.addannot opaque ux uy text;
3939 wcmd1 U.freepage opaque;
3940 Hashtbl.remove S.pagemap (n, !S.gen);
3941 flushtiles ();
3942 gotoxy !S.x !S.y
3944 if inline
3945 then
3946 let mode = !S.mode in
3947 let te = ("annotation: ", E.s, None, textentry, add, true) in
3948 S.mode := Textentry (te, fun _ -> S.mode := mode);
3949 S.text := E.s;
3950 enttext ();
3951 Glutils.postRedisplay "annot"
3952 else add @@ getusertext E.s
3953 | _ -> ()
3955 let zoomblock x y =
3956 let g opaque l px py =
3957 match Ffi.rectofblock opaque px py with
3958 | Some a ->
3959 let x0 = a.(0) -. 20. in
3960 let x1 = a.(1) +. 20. in
3961 let y0 = a.(2) -. 20. in
3962 let zoom = (float !S.w) /. (x1 -. x0) in
3963 let pagey = getpagey l.pageno in
3964 let margin = (!S.w - l.pagew)/2 in
3965 let nx = -truncate x0 - margin in
3966 gotoxy nx (pagey + truncate y0);
3967 S.anchor := getanchor ();
3968 setzoom zoom;
3969 None
3970 | None -> None
3972 match conf.columns with
3973 | Csplit _ ->
3974 impmsg "block zooming does not work properly in split columns mode"
3975 | Cmulti _ | Csingle _ -> onppundermouse g x y ()
3977 let scrollx x =
3978 let winw = !S.winw - 1 in
3979 let s = float x /. float winw in
3980 let destx = truncate (float (!S.w + winw) *. s) in
3981 gotoxy (winw - destx) !S.y;
3982 S.mstate := Mscrollx
3984 let scrolly y =
3985 let s = float y /. float !S.winh in
3986 let desty = truncate (s *. float (U.maxy ())) in
3987 gotoxy !S.x desty;
3988 S.mstate := Mscrolly
3990 let viewmulticlick clicks x y mask =
3991 let g opaque l px py =
3992 let mark =
3993 match clicks with
3994 | 2 -> Mark_word
3995 | 3 -> Mark_line
3996 | 4 -> Mark_block
3997 | _ -> Mark_page
3999 if Ffi.markunder opaque px py mark
4000 then (
4001 Some (fun () ->
4002 let dopipe cmd =
4003 match getopaque l.pageno with
4004 | exception Not_found -> ()
4005 | opaque -> pipesel opaque cmd
4007 S.roamf := (fun () -> dopipe conf.paxcmd);
4008 if not (Wsi.withctrl mask) then dopipe conf.selcmd;
4011 else None
4013 Glutils.postRedisplay "viewmulticlick";
4014 onppundermouse g x y (fun () -> impmsg "nothing to select") ()
4016 let canselect () =
4017 match conf.columns with
4018 | Csplit _ -> false
4019 | Csingle _ | Cmulti _ -> conf.angle mod 360 = 0
4021 let viewmouse button down x y mask =
4022 match button with
4023 | n when (n == 4 || n == 5) && not down ->
4024 if Wsi.withctrl mask
4025 then (
4026 let incr =
4027 if n = 5
4028 then if conf.zoom +. 0.01 > 0.1 then 0.1 else 0.01
4029 else if conf.zoom -. 0.1 < 0.1 then -0.01 else -0.1
4031 let fx, fy =
4032 match !S.mstate with
4033 | Mzoom (oldn, _, pos) when n = oldn -> pos
4034 | Mzoomrect _ | Mnone | Mpan _
4035 | Msel _ | Mscrollx | Mscrolly | Mzoom _ -> (x, y)
4037 let zoom = conf.zoom -. incr in
4038 S.mstate := Mzoom (n, 0, (x, y));
4039 if false && abs (fx - x) > 5 || abs (fy - y) > 5
4040 then pivotzoom ~x ~y zoom
4041 else pivotzoom zoom
4043 else (
4044 match !S.autoscroll with
4045 | Some step -> setautoscrollspeed step (n=4)
4046 | None ->
4047 if conf.wheelbypage || conf.presentation
4048 then (
4049 if n = 4
4050 then prevpage ()
4051 else nextpage ()
4053 else
4054 let incr = if n = 4 then -conf.scrollstep else conf.scrollstep in
4055 let incr = incr * 2 in
4056 let y = U.clamp incr in
4057 gotoxy !S.x y
4060 | n when (n = 6 || n = 7) && not down && canpan () ->
4061 let x =
4062 U.panbound (!S.x + (if n = 7 then -2 else 2) * conf.hscrollstep) in
4063 gotoxy x !S.y
4065 | 1 when Wsi.withshift mask ->
4066 S.mstate := Mnone;
4067 if not down
4068 then (
4069 match unproject x y with
4070 | None -> ()
4071 | Some (_, pageno, ux, uy) ->
4072 let cmd = Printf.sprintf "%s %s %d %d %d" conf.stcmd !S.path
4073 pageno ux uy
4075 match spawn cmd [] with
4076 | exception exn ->
4077 adderrfmt "spawn" "execution of synctex command(%S) failed: %S"
4078 conf.stcmd @@ exntos exn
4079 | _pid -> ()
4082 | 1 when Wsi.withctrl mask ->
4083 if down
4084 then (
4085 Wsi.setcursor Wsi.CURSOR_FLEUR;
4086 S.mstate := Mpan (x, y)
4088 else S.mstate := Mnone
4090 | 3 ->
4091 if down
4092 then (
4093 if Wsi.withshift mask
4094 then (
4095 annot conf.annotinline x y;
4096 Glutils.postRedisplay "addannot"
4098 else
4099 let p = (x, y) in
4100 Wsi.setcursor Wsi.CURSOR_CYCLE;
4101 S.mstate := Mzoomrect (p, p)
4103 else (
4104 match !S.mstate with
4105 | Mzoomrect ((x0, y0), _) ->
4106 if abs (x-x0) > 10 && abs (y - y0) > 10
4107 then zoomrect x0 y0 x y
4108 else (
4109 resetmstate ();
4110 Glutils.postRedisplay "kill accidental zoom rect";
4112 | Msel _
4113 | Mpan _
4114 | Mscrolly | Mscrollx
4115 | Mzoom _
4116 | Mnone -> resetmstate ()
4119 | 1 when vscrollhit x ->
4120 if down
4121 then
4122 let _, position, sh = !S.uioh#scrollph in
4123 if y > truncate position && y < truncate (position +. sh)
4124 then S.mstate := Mscrolly
4125 else scrolly y
4126 else S.mstate := Mnone
4128 | 1 when y > !S.winh - hscrollh () ->
4129 if down
4130 then
4131 let _, position, sw = !S.uioh#scrollpw in
4132 if x > truncate position && x < truncate (position +. sw)
4133 then S.mstate := Mscrollx
4134 else scrollx x
4135 else S.mstate := Mnone
4137 | 1 when !S.bzoom -> if not down then zoomblock x y
4139 | 1 ->
4140 let dest = if down then getunder x y else Unone in
4141 begin match dest with
4142 | Ulinkuri _ -> gotounder dest
4143 | Unone when down ->
4144 Wsi.setcursor Wsi.CURSOR_FLEUR;
4145 S.mstate := Mpan (x, y);
4146 | Utextannot (opaque, slinkindex) -> enterannotmode opaque slinkindex
4147 | Unone | Utext _ | Ufileannot _ ->
4148 if down
4149 then (
4150 if canselect ()
4151 then (
4152 S.mstate := Msel ((x, y), (x, y));
4153 Glutils.postRedisplay "mouse select";
4156 else (
4157 match !S.mstate with
4158 | Mnone -> ()
4159 | Mzoom _ | Mscrollx | Mscrolly -> S.mstate := Mnone
4160 | Mzoomrect ((x0, y0), _) -> zoomrect x0 y0 x y
4161 | Mpan _ ->
4162 Wsi.setcursor Wsi.CURSOR_INHERIT;
4163 S.mstate := Mnone
4164 | Msel ((x0, y0), (x1, y1)) ->
4165 let rec loop = function
4166 | [] -> ()
4167 | l :: rest ->
4168 let inside =
4169 let a0 = l.pagedispy in
4170 let a1 = a0 + l.pagevh in
4171 let b0 = l.pagedispx in
4172 let b1 = b0 + l.pagevw in
4173 ((y0 >= a0 && y0 <= a1) || (y1 >= a0 && y1 <= a1))
4174 && ((x0 >= b0 && x0 <= b1) || (x1 >= b0 && x1 <= b1))
4176 if inside
4177 then
4178 match getopaque l.pageno with
4179 | exception Not_found -> ()
4180 | opaque ->
4181 let dosel cmd () =
4182 pipef ~closew:false "Msel"
4183 (fun w ->
4184 Ffi.copysel w opaque;
4185 Glutils.postRedisplay "Msel") cmd
4187 dosel conf.selcmd ();
4188 S.roamf := dosel conf.paxcmd;
4189 else loop rest
4191 loop !S.layout;
4192 resetmstate ();
4195 | _ -> ()
4197 let birdseyemouse button down x y mask
4198 (conf, leftx, _, hooverpageno, anchor) =
4199 match button with
4200 | 1 when down ->
4201 let rec loop = function
4202 | [] -> ()
4203 | l :: rest ->
4204 if y > l.pagedispy && y < l.pagedispy + l.pagevh
4205 && x > l.pagedispx && x < l.pagedispx + l.pagevw
4206 then
4207 leavebirdseye (conf, leftx, l.pageno, hooverpageno, anchor) false
4208 else loop rest
4210 loop !S.layout
4211 | 3 -> ()
4212 | _ -> viewmouse button down x y mask
4214 let uioh = object
4215 method display = ()
4216 method infochanged _ = ()
4218 method key key mask =
4219 begin match !S.mode with
4220 | Textentry textentry -> textentrykeyboard key mask textentry
4221 | Birdseye birdseye -> birdseyekeyboard key mask birdseye
4222 | View -> viewkeyboard key mask
4223 | LinkNav linknav -> linknavkeyboard key mask linknav
4224 end;
4225 !S.uioh
4227 method button button bstate x y mask =
4228 begin match !S.mode with
4229 | LinkNav _ | View -> viewmouse button bstate x y mask
4230 | Birdseye beye -> birdseyemouse button bstate x y mask beye
4231 | Textentry _ -> ()
4232 end;
4233 !S.uioh
4235 method multiclick clicks x y mask =
4236 begin match !S.mode with
4237 | LinkNav _ | View -> viewmulticlick clicks x y mask
4238 | Birdseye _ | Textentry _ -> ()
4239 end;
4240 !S.uioh
4242 method motion x y =
4243 begin match !S.mode with
4244 | Textentry _ -> ()
4245 | View | Birdseye _ | LinkNav _ ->
4246 match !S.mstate with
4247 | Mzoom _ | Mnone -> ()
4248 | Mpan (x0, y0) ->
4249 let dx = x - x0
4250 and dy = y0 - y in
4251 S.mstate := Mpan (x, y);
4252 let x = if canpan () then U.panbound (!S.x + dx) else !S.x in
4253 let y = U.clamp dy in
4254 gotoxy x y
4256 | Msel (a, _) ->
4257 S.mstate := Msel (a, (x, y));
4258 Glutils.postRedisplay "motion select";
4260 | Mscrolly ->
4261 let y = min !S.winh (max 0 y) in
4262 scrolly y
4264 | Mscrollx ->
4265 let x = min !S.winw (max 0 x) in
4266 scrollx x
4268 | Mzoomrect (p0, _) ->
4269 S.mstate := Mzoomrect (p0, (x, y));
4270 Glutils.postRedisplay "motion zoomrect";
4271 end;
4272 !S.uioh
4274 method pmotion x y =
4275 begin match !S.mode with
4276 | Birdseye (conf, leftx, pageno, hooverpageno, anchor) ->
4277 let rec loop = function
4278 | [] ->
4279 if hooverpageno != -1
4280 then (
4281 S.mode := Birdseye (conf, leftx, pageno, -1, anchor);
4282 Glutils.postRedisplay "pmotion birdseye no hoover";
4284 | l :: rest ->
4285 if y > l.pagedispy && y < l.pagedispy + l.pagevh
4286 && x > l.pagedispx && x < l.pagedispx + l.pagevw
4287 then (
4288 S.mode := Birdseye (conf, leftx, pageno, l.pageno, anchor);
4289 Glutils.postRedisplay "pmotion birdseye hoover";
4291 else loop rest
4293 loop !S.layout
4295 | Textentry _ -> ()
4297 | LinkNav _ | View ->
4298 match !S.mstate with
4299 | Mpan _ | Msel _ | Mzoom _ | Mscrolly | Mscrollx | Mzoomrect _ -> ()
4300 | Mnone ->
4301 updateunder x y;
4302 if canselect ()
4303 then
4304 match conf.pax with
4305 | None -> ()
4306 | Some past ->
4307 let now = now () in
4308 let delta = now -. past in
4309 if delta > 0.01
4310 then paxunder x y
4311 else conf.pax <- Some now
4312 end;
4313 !S.uioh
4315 method scrollph =
4316 let maxy = U.maxy () in
4317 let p, h =
4318 if maxy = 0
4319 then 0.0, float !S.winh
4320 else scrollph !S.y maxy
4322 vscrollw (), p, h
4324 method scrollpw =
4325 let fwinw = float (!S.winw - vscrollw ()) in
4326 let sw =
4327 let sw = fwinw /. float !S.w in
4328 let sw = fwinw *. sw in
4329 max sw (float conf.scrollh)
4331 let position =
4332 let maxx = !S.w + !S.winw in
4333 let x = !S.winw - !S.x in
4334 let percent = float x /. float maxx in
4335 (fwinw -. sw) *. percent
4337 hscrollh (), position, sw
4339 method modehash =
4340 let modename =
4341 match !S.mode with
4342 | LinkNav _ -> "links"
4343 | Textentry _ -> "textentry"
4344 | Birdseye _ -> "birdseye"
4345 | View -> "view"
4347 findkeyhash conf modename
4349 method eformsgs = true
4350 method alwaysscrolly = false
4351 method scroll dx dy =
4352 let x = if canpan () then U.panbound (!S.x + dx) else !S.x in
4353 gotoxy x (U.clamp (2 * dy));
4354 !S.uioh
4355 method zoom z x y =
4356 pivotzoom ~x ~y (conf.zoom *. exp z);
4359 let ract cmds =
4360 let cl = splitatchar cmds ' ' in
4361 let scan s fmt f =
4362 try Scanf.sscanf s fmt f
4363 with exn -> adderrfmt "remote exec" "error processing '%S': %s\n"
4364 cmds @@ exntos exn
4366 let rectx s pageno (r, g, b, a) x0 y0 x1 y1 =
4367 vlog "%s page %d color (%f %f %f %f) x0,y0,x1,y1 = %f %f %f %f"
4368 s pageno r g b a x0 y0 x1 y1;
4369 onpagerect
4370 pageno
4371 (fun w h ->
4372 let _,w1,h1,_ = getpagedim pageno in
4373 let sw = float w1 /. float w
4374 and sh = float h1 /. float h in
4375 let x0s = x0 *. sw
4376 and x1s = x1 *. sw
4377 and y0s = y0 *. sh
4378 and y1s = y1 *. sh in
4379 let rect = (x0s,y0s,x1s,y0s,x1s,y1s,x0s,y1s) in
4380 let color = (r, g, b, a) in
4381 if conf.verbose then debugrect rect;
4382 S.rects := (pageno, color, rect) :: !S.rects;
4383 Glutils.postRedisplay s;
4386 match cl with
4387 | "reload", "" -> reload ()
4388 | "goto", args ->
4389 scan args "%u %f %f"
4390 (fun pageno x y ->
4391 let cmd, _ = !S.geomcmds in
4392 if emptystr cmd
4393 then gotopagexy pageno x y
4394 else
4395 let f prevf () =
4396 gotopagexy pageno x y;
4397 prevf ()
4399 S.reprf := f !S.reprf
4401 | "goto1", args -> scan args "%u %f" gotopage
4402 | "gotor", args -> scan args "%S" gotoremote
4403 | "rect", args ->
4404 scan args "%u %u %f %f %f %f"
4405 (fun pageno c x0 y0 x1 y1 ->
4406 let color = (0.0, 0.0, 1.0 /. float c, 0.5) in
4407 rectx "rect" pageno color x0 y0 x1 y1;
4409 | "pgoto", args ->
4410 scan args "%u %f %f"
4411 (fun pageno x y ->
4412 let optopaque =
4413 match getopaque pageno with
4414 | exception Not_found -> Opaque.of_string E.s
4415 | opaque -> opaque
4417 pgoto optopaque pageno x y;
4418 let rec fixx = function
4419 | [] -> ()
4420 | l :: rest ->
4421 if l.pageno = pageno
4422 then gotoxy (!S.x - l.pagedispx) !S.y
4423 else fixx rest
4425 let layout =
4426 let mult =
4427 match conf.columns with
4428 | Csingle _ | Csplit _ -> 1
4429 | Cmulti ((n, _, _), _) -> n
4431 layout 0 !S.y (!S.winw * mult) !S.winh
4433 fixx layout
4435 | "activatewin", "" -> Wsi.activatewin ()
4436 | "quit", "" -> raise Quit
4437 | "keys", keys ->
4438 begin try
4439 let l = Config.keys_of_string keys in
4440 List.iter (fun (k, m) -> keyboard k m) l
4441 with exn -> adderrfmt "error processing keys" "`%S': %s\n"
4442 cmds @@ exntos exn
4444 | _ ->
4445 adderrfmt "remote command"
4446 "error processing remote command: %S\n" cmds
4448 let remote =
4449 let scratch = Bytes.create 80 in
4450 let buf = Buffer.create 80 in
4451 fun fd ->
4452 match tempfailureretry (Unix.read fd scratch 0) 80 with
4453 | exception Unix.Unix_error (Unix.EAGAIN, _, _) -> None
4454 | 0 ->
4455 Unix.close fd;
4456 if Buffer.length buf > 0
4457 then (
4458 let s = Buffer.contents buf in
4459 Buffer.clear buf;
4460 ract s;
4462 None
4463 | n ->
4464 let rec eat ppos =
4465 let nlpos =
4466 match Bytes.index_from scratch ppos '\n' with
4467 | exception Not_found -> -1
4468 | pos -> if pos >= n then -1 else pos
4470 if nlpos >= 0
4471 then (
4472 Buffer.add_subbytes buf scratch ppos (nlpos-ppos);
4473 let s = Buffer.contents buf in
4474 Buffer.clear buf;
4475 ract s;
4476 eat (nlpos+1);
4478 else (
4479 Buffer.add_subbytes buf scratch ppos (n-ppos);
4480 Some fd
4482 in eat 0
4484 let remoteopen path =
4485 try Some (Unix.openfile path [Unix.O_NONBLOCK; Unix.O_RDONLY] 0o0)
4486 with exn ->
4487 adderrfmt "remoteopen" "error opening %S: %s" path @@ exntos exn;
4488 None
4490 let () =
4491 vlogf := (fun s -> if conf.verbose then print_endline s else ignore s);
4492 let gc = ref false in
4493 let redirstderr = Unix.isatty Unix.stderr |> not |> ref in
4494 let rcmdpath = ref E.s in
4495 let dcfpath = ref E.s in
4496 let pageno = ref None in
4497 let openlast = ref false in
4498 let doreap = ref false in
4499 let csspath = ref None in
4500 S.selfexec := Sys.executable_name;
4501 let spec =
4502 [("-p", Arg.Set_string S.password, "<password> Set password");
4503 ("-f", Arg.String
4504 (fun s ->
4505 S.fontpath := s;
4506 S.selfexec := !S.selfexec ^ " -f " ^ Filename.quote s;
4507 ), "<path> Set path to the user interface font");
4508 ("-c", Arg.String
4509 (fun s ->
4510 S.selfexec := !S.selfexec ^ " -c " ^ Filename.quote s;
4511 S.confpath := s), "<path> Set path to the configuration file");
4512 ("-last", Arg.Set openlast, " Open last document");
4513 ("-page", Arg.Int (fun pageno1 -> pageno := Some (pageno1-1)),
4514 "<page-number> Jump to page");
4515 ("-dest", Arg.Set_string S.nameddest, "<dest-name> Set named destination");
4516 ("-remote", Arg.Set_string rcmdpath, "<path> Set path to the remote fifo");
4517 ("-gc", Arg.Set gc, " Collect garbage");
4518 ("-v",
4519 Arg.Unit (fun () ->
4520 Printf.printf "%s\nconfiguration file: %s\n" (Help.version ())
4521 Config.defconfpath;
4522 exit 0), " Print version and exit");
4523 ("-css", Arg.String (fun s -> csspath := Some s),
4524 "<path> Set path to the style sheet to use with EPUB/HTML");
4525 ("-origin", Arg.Set_string S.origin, "<origin> <undocumented>");
4526 ("-no-title", Arg.Set S.ignoredoctitlte, " Ignore document title");
4527 ("-dcf", Arg.Set_string dcfpath, "<path> <undocumented>");
4528 ("-flip-stderr-redirection",
4529 Arg.Unit (fun () -> redirstderr := not !redirstderr), " <undocumented>");
4532 Arg.parse (Arg.align spec) (fun s -> S.path := s)
4533 ("Usage: " ^ Sys.argv.(0) ^ " [options] some.pdf\nOptions:");
4535 let histmode = emptystr !S.path && not !openlast in
4537 if !gc
4538 then (
4539 Config.gc ();
4540 if histmode then exit 0;
4543 if not (Config.load !openlast)
4544 then dolog "failed to load configuration";
4546 if nonemptystr !dcfpath
4547 then conf.dcf <- !dcfpath;
4549 begin match !pageno with
4550 | Some pageno -> S.anchor := (pageno, 0.0, 0.0)
4551 | None -> ()
4552 end;
4554 fillhelp ();
4555 let mu =
4556 object (self)
4557 val mutable m_clicks = 0
4558 val mutable m_click_x = 0
4559 val mutable m_click_y = 0
4560 val mutable m_lastclicktime = infinity
4562 method private cleanup =
4563 S.roamf := noroamf;
4564 Hashtbl.iter (fun _ opaque -> Ffi.clearmark opaque) S.pagemap
4565 method expose = Glutils.postRedisplay "expose"
4566 method visible v =
4567 let name =
4568 match v with
4569 | Wsi.Unobscured -> "unobscured"
4570 | Wsi.PartiallyObscured -> "partiallyobscured"
4571 | Wsi.FullyObscured -> "fullyobscured"
4573 vlog "visibility change %s" name
4574 method display = display ()
4575 method map mapped = vlog "mapped %b" mapped
4576 method reshape w h =
4577 self#cleanup;
4578 reshape w h
4579 method mouse b d x y m =
4580 (* http://blogs.msdn.com/b/oldnewthing/archive/2004/10/18/243925.aspx *)
4581 m_click_x <- x;
4582 setuioh @@
4583 if d && canselect ()
4584 then (
4585 m_click_y <- y;
4586 if b = 1
4587 then (
4588 let t = now () in
4589 if abs x - m_click_x > 10
4590 || abs y - m_click_y > 10
4591 || abs_float (t -. m_lastclicktime) > 0.3
4592 then m_clicks <- 0;
4593 m_clicks <- m_clicks + 1;
4594 m_lastclicktime <- t;
4595 if m_clicks = 1
4596 then (
4597 self#cleanup;
4598 Glutils.postRedisplay "cleanup";
4599 !S.uioh#button b d x y m
4601 else !S.uioh#multiclick m_clicks x y m
4603 else (
4604 self#cleanup;
4605 m_clicks <- 0;
4606 m_lastclicktime <- infinity;
4607 !S.uioh#button b d x y m
4610 else !S.uioh#button b d x y m
4611 method motion x y =
4612 S.mpos := (x, y);
4613 !S.uioh#motion x y |> setuioh
4614 method pmotion x y =
4615 S.mpos := (x, y);
4616 !S.uioh#pmotion x y |> setuioh
4617 method key k m =
4618 vlog "k=%#x m=%#x" k m;
4619 let mascm = m land (
4620 Wsi.altmask + Wsi.shiftmask + Wsi.ctrlmask + Wsi.metamask
4621 ) in
4622 let keyboard k m =
4623 let x = !S.x and y = !S.y in
4624 keyboard k m;
4625 if x != !S.x || y != !S.y then self#cleanup
4627 match !S.keystate with
4628 | KSnone ->
4629 let km = k, mascm in
4630 begin
4631 match
4632 let modehash = !S.uioh#modehash in
4633 try Hashtbl.find modehash km
4634 with Not_found ->
4635 try Hashtbl.find (findkeyhash conf "global") km
4636 with Not_found -> KMinsrt (k, m)
4637 with
4638 | KMinsrt (k, m) -> keyboard k m
4639 | KMinsrl l -> List.iter (fun (k, m) -> keyboard k m) l
4640 | KMmulti (l, r) -> S.keystate := KSinto (l, r)
4642 | KSinto ((k', m') :: [], insrt) when k'=k && m' land mascm = m' ->
4643 List.iter (fun (k, m) -> keyboard k m) insrt;
4644 S.keystate := KSnone
4645 | KSinto ((k', m') :: keys, insrt) when k'=k && m' land mascm = m' ->
4646 S.keystate := KSinto (keys, insrt)
4647 | KSinto _ -> S.keystate := KSnone
4648 method enter x y =
4649 S.mpos := (x, y);
4650 !S.uioh#pmotion x y |> setuioh
4651 method leave = S.mpos := (-1, -1)
4652 method winstate wsl = S.winstate := wsl
4653 method quit : 'a. 'a = raise Quit
4654 method scroll dx dy =
4655 !S.uioh#scroll dx dy |> setuioh
4656 method zoom z x y = !S.uioh#zoom z x y
4657 method opendoc path =
4658 S.mode := View;
4659 setuioh uioh;
4660 Glutils.postRedisplay "opendoc";
4661 opendoc path !S.password
4664 let wsfd, winw, winh = Wsi.init mu conf.cwinw conf.cwinh in
4665 S.wsfd := wsfd;
4667 let cs, ss =
4668 match Unix.socketpair Unix.PF_UNIX Unix.SOCK_STREAM 0 with
4669 | exception exn ->
4670 dolog "socketpair failed: %s" @@ exntos exn;
4671 exit 1
4672 | (r, w) ->
4673 Unix.set_close_on_exec r;
4674 Unix.set_close_on_exec w;
4675 r, w
4678 begin match !csspath with
4679 | None -> ()
4680 | Some "" -> conf.css <- E.s
4681 | Some path ->
4682 let css = filecontents path in
4683 let l = String.length css in
4684 conf.css <-
4685 if substratis css (l-2) "\r\n"
4686 then String.sub css 0 (l-2)
4687 else (if css.[l-1] = '\n' then String.sub css 0 (l-1) else css)
4688 end;
4689 S.stderr := Ffi.init cs (
4690 conf.angle, conf.fitmodel, (conf.trimmargins, conf.trimfuzz),
4691 conf.texcount, conf.sliceheight, conf.mustoresize,
4692 conf.colorspace, !S.fontpath, !redirstderr
4694 List.iter GlArray.enable [`texture_coord; `vertex];
4695 GlTex.env (`color conf.texturecolor);
4696 S.ss := ss;
4697 reshape ~firsttime:true winw winh;
4698 setuioh uioh;
4699 if histmode
4700 then (Wsi.settitle "previously visited - llpp"; enterhistmode ())
4701 else opendoc !S.path !S.password;
4702 display ();
4703 Wsi.mapwin ();
4704 Wsi.setcursor Wsi.CURSOR_INHERIT;
4705 Sys.set_signal Sys.sighup (Sys.Signal_handle (fun _ -> reload ()));
4707 let rec reap () =
4708 match Unix.waitpid [Unix.WNOHANG] ~-1 with
4709 | exception (Unix.Unix_error (Unix.ECHILD, _, _)) -> ()
4710 | exception exn -> dolog "Unix.waitpid: %s" @@ exntos exn
4711 | 0, _ -> ()
4712 | _pid, _status -> reap ()
4714 Sys.set_signal Sys.sigchld (Sys.Signal_handle (fun _ -> doreap := true));
4716 let optrfd =
4717 ref (if nonemptystr !rcmdpath then remoteopen !rcmdpath else None)
4719 dologf := (adderrfmt "stderr" "%s\n");
4721 let fdl =
4722 let l = [!S.ss; !S.wsfd] in if !redirstderr then !S.stderr :: l else l
4724 let rec loop deadline =
4725 if !doreap
4726 then (
4727 doreap := false;
4728 reap ()
4730 let r =
4731 match !optrfd with
4732 | None -> fdl
4733 | Some fd -> fd :: fdl
4735 if !Glutils.redisplay
4736 then (
4737 Glutils.redisplay := false;
4738 display ();
4740 let timeout =
4741 let now = now () in
4742 if deadline > now
4743 then (
4744 if deadline = infinity
4745 then ~-.1.0
4746 else max 0.0 (deadline -. now)
4748 else 0.0
4750 let r, _, _ =
4751 try Unix.select r [] [] timeout
4752 with Unix.Unix_error (Unix.EINTR, _, _) -> [], [], []
4754 begin match r with
4755 | [] ->
4756 let newdeadline =
4757 match !S.autoscroll with
4758 | Some step when step != 0 ->
4759 let y = !S.y + step in
4760 let fy = if conf.maxhfit then !S.winh else 0 in
4761 let y =
4762 if y < 0
4763 then !S.maxy - fy
4764 else
4765 if y >= !S.maxy - fy
4766 then 0
4767 else y
4769 gotoxy !S.x y;
4770 deadline +. 0.01
4771 | _ -> infinity
4773 loop newdeadline
4775 | l ->
4776 let rec checkfds = function
4777 | [] -> ()
4778 | fd :: rest when fd = !S.ss ->
4779 let cmd = Ffi.rcmd !S.ss in
4780 act cmd;
4781 checkfds rest
4783 | fd :: rest when fd = !S.wsfd ->
4784 Wsi.readresp fd;
4785 checkfds rest
4787 | fd :: rest when fd = !S.stderr ->
4788 let b = Bytes.create 80 in
4789 begin match Unix.read fd b 0 80 with
4790 | exception Unix.Unix_error (Unix.EINTR, _, _) -> ()
4791 | exception exn -> adderrmsg "Unix.read exn" @@ exntos exn
4792 | 0 -> ()
4793 | n -> adderrmsg "stderr" @@ Bytes.sub_string b 0 n
4794 end;
4795 checkfds rest
4797 | fd :: rest when Some fd = !optrfd ->
4798 begin match remote fd with
4799 | None -> optrfd := remoteopen !rcmdpath;
4800 | opt -> optrfd := opt
4801 end;
4802 checkfds rest
4804 | _ :: rest ->
4805 adderrmsg "mainloop" "select returned unknown descriptor";
4806 checkfds rest
4808 checkfds l;
4809 let newdeadline =
4810 match !S.autoscroll with
4811 | Some step when step != 0 ->
4812 if deadline = infinity
4813 then now () +. 0.01
4814 else deadline
4815 | _ -> infinity
4817 loop newdeadline
4818 end;
4820 match loop infinity with
4821 | exception Quit ->
4822 (match Buffer.length S.errmsgs with
4823 | 0 -> ()
4824 | n ->
4825 match Unix.write Unix.stdout (Buffer.to_bytes S.errmsgs) 0 n with
4826 | exception _ | _ -> ());
4827 Config.save leavebirdseye;
4828 if Ffi.hasunsavedchanges ()
4829 then save ()
4830 | _ -> error "umpossible - infinity reached"